之前写 Rails 测试都是从 models 来写的,原因很简单,controller 的逻辑一般比较简单,不会太容易出错,而 model 里的逻辑比较复杂,稍不留神就会出现问题,只能多测。

但是这种思想就导致了 controller 甚至都不想跑,写完了就直接运行。随着项目的进度不断前进,controller 里的 action 越来越多,同时一次写对的概率也变少了很多,特别是在跟前端对接的时候,状况不断。

本着 吃自己的狗食 的精神,我也开始慢慢的写了一些 controller 的测试

网上关于 controller 的测试大多数都是基于 RSepc 的,由于我不太喜欢这个东西,所以自己使用的是官方默认的 MiniTest, 看了一下官方的源码 大概是这么个样子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
require 'test_helper'

class FlowTemplatesControllerTest < ActionController::TestCase

  test "could list all flow templates" do
    get :index
    assert_response :success
    response = get :index, language: "ruby"
    json = JSON.parse(response.body)
    assert_response :success
    assert_equal FlowTemplate.where(language: "ruby").count, json.count
  end

  test "could show flow template" do
    flow_template = FlowTemplate.first
    response = get :show, id: flow_template.id.to_s
    json = JSON.parse(response.body)
    assert_equal flow_template.name, json["name"]
  end
end

能说的可能就是 这个地方可以支持 get post patch delete 了,之后这里会默认找这个 test 命名中对应的 controller。

上面的是最普通的情况,在实际的测试当中,有时候我们还会遇到诸如 这个地方需要登录的情况,但是我在 controller 里手动的去登录非常麻烦(这也是我不想写 controller 的测试的主要原因),其实可以用 instance_eval 来解决这个问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
require 'test_helper'

class ProjectsControllerTest < ActionController::TestCase
  setup do
    user = User.first || Fabricate(:user)
    @controller.instance_eval do
      @current_user = user
    end
    @user = user
  end

  test "could list all projects" do
    Fabricate(:project, user: @user)
    assert_equal User.first.id.to_s, Project.first.user.id.to_s
    get :index
    assert_response :success
  end

  test "could create project" do
    assert_equal 0, Project.count
    post :create, { name: "jicheng1014/test", source: "github", git_url: "git@github.com:jicheng1014/test.git" }
    assert_response :success
    assert_equal 1, Project.count
  end
end

大概就是上面这样, have fun

testerhome 是我比较喜欢的网站, 但是这个网站我觉得有一个点我不是很赞同:让测试人员去猜测诸如内存抖动、cpu 消耗、弱网状态下的问题的原因。

当然这跟测试本身的定位有关,我更觉得这些事情应该是开发来干的。

比如内存抖动,作为测试,很难直接知道具体的问题。他能做的最多的就是告诉你:我在加载某个 list 的时候,发现内存抖动,所以我判断是你在 item 的渲染的时候没有做很好处理。但是如果这个事情是研发拿到了,事情就好办多了:哦,内存抖了. 恩,是跑这块代码的,,我来看看是怎么写的,图片加载了没缓存,git blame … 小王你不能这么业余啊。

那测试的作用是什么呢? 我觉得更多的是教研发怎么测,写自动化测试,把结果给研发,至于具体的哪里出问题,直接喊研发找就好了,测试在那里苦思冥想 1 个小时不如研发直接看代码是哪个地方出问题。

或者这也是现在 QA 团队规模越来越小的原因吧。

Asset Pipeline 的复习

概念

是什么

  • Asset pipeline 是一款压缩css 和js 的预处理工具。中间件叫Sprockets

目的

  • 降低连接次数
  • 有效建立缓存

构成

  • uglifier
  • sass-rails
  • coffee-rails

使用

静态资源放置 放在app/assets 文件夹中

引入资源

有两种方案

  1. 一次性引入所有文件 (使用require_tree)
  2. 只引用对应控制器的assets(不使用require_tree)
1
2
<%= javascript_include_tag params[:controller] %>
<%= stylesheet_link_tag params[:controller] %>

加载时搜索的路径

在使用静态资源的时候Sprockets 会再默认的三个位置中查找对应的文件 - apps/assets/(images|javascripts|stylesheets) - 也会寻找其他文件夹?

资源引用

1
2
//= require home (初级目录,不用加后缀)
//= require sub/something   (子目录)

使用Rails.application.config.assets.paths就可以看到所有的搜索路径

如果需要自定义路径,则需要在application.rb 增加配置

1
config.assets.paths << Rails.root.join("lib", "videoplayer", "flash")

app/assets 优先级别高一点

注意,如果清单文件没有包含某个需要引用的文件,则我们需要手动的将其加入在包含里 参见上面的代码

在Production 中的预处理

$ RAILS_ENV=production bin/rake assets:precompile

添加其他的mainfest 文件

1
Rails.application.config.assets.precompile += ['admin.js', 'admin.css', 'swfObject.js']

这个地方注意一下,这里的后缀不是文件的后缀 而是得到的文件的后缀( scss coffee)

将静态文件设置过期时间

因为已经生成了指纹,所以缓存时间可以设置成无限长

1
2
3
4
5
6
7
8
//nginx
location ~ ^/assets/ {
  expires 1y;
  add_header Cache-Control public;

  add_header ETag "";
  break;
}
1
2
3
4
5
6
7
8
9
10
# apache

# The Expires* directives requires the Apache module
# `mod_expires` to be enabled.
<Location /assets/>
  # Use of ETag is discouraged when Last-Modified is present
  Header unset ETag FileETag None
  # RFC says only cache for 1 year
  ExpiresActive On ExpiresDefault "access plus 1 year"
</Location>

初探ActiveJob

简介

ActiveJob 是 Rails 4.2 新加入的功能。这个东西在beta阶段rubyChina就已经有很多高手关注了,无奈自己的项目使用的是4.1.5,升级到4.2 的时候其他gem又有很多依赖有问题,所以没在第一时间使用。今天补个课。

ActiveJob 是Rails自己开发运行后台程序的模块,常用于执行运行时间可能很长的工作(比如发送注册邮件)。

当然这种需求实际上非常普遍,所以rails 也有相应的第三方gem来解决这个需求,比如著名的Sidekiq和Resque等。ActiveJob的出现不是为了代替他们,而是统一了原来Resque、Sidekiq 等其他gem对后台运行程序的各种千奇百怪的写法。

定义

ActiveJob 的使用官方文档已经给出了示例 首先在命令行中使用 rails g job JOBNAME 来新建一个任务

比如这里

1
2
3
4
5
➜  my_rails42 git:(master) ✗ rails g job add_lots_of_users
      invoke  test_unit
      create    test/jobs/add_lots_of_users_job_test.rb
      create  app/jobs/add_lots_of_users_job.rb
➜  my_rails42 git:(master) ✗

我们打开add_lots_of_users_job.rb,可以看到下列内容

1
2
3
4
5
6
7
class AddLotsOfUsersJob < ActiveJob::Base
  queue_as :default

  def perform(*args)
    # Do something later
  end
end

我们可以将耗时的内容写入perform

1
2
3
4
5
6
7
8
9
  def perform(*args)
    # Do something later
    sleep 10
    1000.times do |index|
      user = User.new
      user.name = "atpking#{index}"
      user.save
    end
  end

我们可以看到,我在job 里建立了一个作业,先睡10秒,再插入1000条数据到数据库中

至此,我们就成功建立了一个job了,接下来,就是使用了

使用

官方demo讲的非常简单,就是在你使用的地方用这个句子:XXJob.perform_later PARAMS

比如我这里

1
2
3
4
5
6
7
8
9
10
class UsersController < ApplicationController
  before_action :set_user, only: [:show, :edit, :update, :destroy]

  # GET /users
  # GET /users.json
  def index
    AddLotsOfUsersJob.perform_later
    @users = User.all
  end
end

我们运行一下试试

运行过程中发现不像我们预想的那样,而是访问index 的时候,活生生的等待了10多秒,在获得@users

1
2
3
4
5
6
7
8
[ActiveJob] [AddLotsOfUsersJob] [e191c62d-68a9-425f-8a94-b9fe080c141c]    (0.8ms)  commit transaction
[ActiveJob] [AddLotsOfUsersJob] [e191c62d-68a9-425f-8a94-b9fe080c141c]    (0.0ms)  begin transaction
[ActiveJob] [AddLotsOfUsersJob] [e191c62d-68a9-425f-8a94-b9fe080c141c]   SQL (0.2ms)  INSERT INTO "users" ("name", "created_at", "updated_at") VALUES (?, ?, ?)  [["name", "atpking999"], ["created_at", "2015-04-20 04:30:29.320703"], ["updated_at", "2015-04-20 04:30:29.320703"]]
[ActiveJob] [AddLotsOfUsersJob] [e191c62d-68a9-425f-8a94-b9fe080c141c]    (0.6ms)  commit transaction
[ActiveJob] [AddLotsOfUsersJob] [e191c62d-68a9-425f-8a94-b9fe080c141c] Performed AddLotsOfUsersJob from Inline(default) in 12789.38ms
  User Load (2.1ms)  SELECT "users".* FROM "users"
  Rendered users/index.html.erb within layouts/application (186.7ms)
Completed 200 OK in 13210ms (Views: 392.6ms | ActiveRecord: 1166.0ms)

注意看上面,很多歌ActiveJob 完毕了之后,输出Performed AddLotsOfUsersJob from Inline(default) in 12789.38ms,再执行的select * from users

也就是说,默认情况下的ActiveJob 跟我们使用的方法没什么区别,是阻塞的,实际上官方文档也说明了

4 Job Execution If no adapter is set, the job is immediately executedIf no adapter is set, the job is immediately executed.

那么我们需要给ActiveJob 指定一个Adapter 了。官方有支持以下的adapter,功能有所不同,需要注意。如果没设定,则是默认的 Active Job Inline ,可以看到一个悲剧,不支持异步(Async),这也是为何我们刚刚等了很长时间

function Async Queues Delayed Priorities Timeout Retries
Backburner Yes Yes Yes Yes Job Global
Delayed Job Yes Yes Yes Job Global Global
Qu Yes Yes No No No Global
Que Yes Yes Yes Job No Job
queue_classic Yes Yes No* No No No
Resque Yes Yes Yes (Gem) Queue Global Yes
Sidekiq Yes Yes Yes Queue No Job
Sneakers Yes Yes No Queue Queue No
Sucker Punch Yes Yes No No No No
Active Job Inline(默认的) No Yes N/A N/A N/A N/A
Active Job Yes Yes Yes No No No

我选用了sidekiq 作为Adapter,注意这里,你必须要安装过sidekiq, 没安装自然需要你Gemfile 里加一句咯 同时还要安装redis,之后在命令行使用 redis-server & 启用redis 同时也得在命令行里启用sidekiq,直接输入sidekiq & 即可

之后我们需要指定sidekiq 为我们的adapter,我们需要在application.rb 里加入一句话

config.active_job.queue_adapter = :sidekiq

再次运行index 页面,我们看rails 的日志,就变成了先执行Select * from Users,返回页面结果,再继续执行jobs的内容了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Started GET "/users" for ::1 at 2015-04-20 12:16:20 +0800
Processing by UsersController#index as HTML
[ActiveJob] Enqueued AddLotsOfUsersJob (Job ID: 50372234-6cf7-4ad2-b886-fd3029f4ea3d) to Sidekiq(default)
2015-04-20T04:16:20Z 79432 TID-ouzlum9ig ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper JID-eb96be6e314f6926583a1267 INFO: start
  User Load (0.3ms)  SELECT "users".* FROM "users"
  Rendered users/index.html.erb within layouts/application (4.1ms)
Completed 200 OK in 72ms (Views: 57.8ms | ActiveRecord: 0.5ms)


Started GET "/assets/jquery_ujs.self-8e98a7a072a6cee1372d19fff9ff3e6aa1e39a37d89d6f06861637d061113ee7.js?body=1" for ::1 at 2015-04-20 12:16:21 +0800


Started GET "/assets/users.self-e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.css?body=1" for ::1 at 2015-04-20 12:16:21 +0800


Started GET "/assets/jquery.self-d03a5518f45df77341bdbe6201ba3bfa547ebba8ed64f0ea56bfa5f96ea7c074.js?body=1" for ::1 at 2015-04-20 12:16:21 +0800


Started GET "/assets/application.self-e80e8f2318043e8af94dddc2adad5a4f09739a8ebb323b3ab31cd71d45fd9113.css?body=1" for ::1 at 2015-04-20 12:16:21 +0800


Started GET "/assets/scaffolds.self-a98ac27100e3e5ca7065dbd7c898e5afa02690ec2ef84ccc02f65c4c20057b83.css?body=1" for ::1 at 2015-04-20 12:16:21 +0800


Started GET "/assets/turbolinks.self-c37727e9bd6b2735da5c311aa83fead54ed0be6cc8bd9a65309e9c5abe2cbfff.js?body=1" for ::1 at 2015-04-20 12:16:21 +0800


Started GET "/assets/users.self-877aef30ae1b040ab8a3aba4e3e309a11d7f2612f44dde450b5c157aa5f95c05.js?body=1" for ::1 at 2015-04-20 12:16:21 +0800


Started GET "/assets/application.self-3a3c8b61bda630ee689740ce7cbd0dd8ea6fdd45e2c42eef4661ab38cf268afe.js?body=1" for ::1 at 2015-04-20 12:16:21 +0800
2015-04-20T04:16:33Z 79432 TID-ouzlum9ig ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper JID-eb96be6e314f6926583a1267 INFO: done: 12.828 sec

请注意上面的这两句话

1
2
3
2015-04-20T04:16:20Z 79432 TID-ouzlum9ig ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper JID-eb96be6e314f6926583a1267 INFO: start

2015-04-20T04:16:33Z 79432 TID-ouzlum9ig ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper JID-eb96be6e314f6926583a1267 INFO: done: 12.828 sec

怎样在 rails 中进行调试

背景

我个人感觉 ruby on rails 是不鼓励调试的,按照设计原则来说,应该是使用测试与 console 来 debug 。但是其实使用断点等调试确实有些时候比较方便,而且其他语言都可以这样做,所以 Ruby on rails 没理由不支持。

大名鼎鼎的 RubyMine 本身当然是实现了这种功能了,但是对于我们这些不喜欢大块头的Vim党来说,怎样处理呢?

答案就是byebug 这个Gem

gem byebug

最新的Rails 4.2 已经内置了这个Gem

1
2
3
4
5
6
7
8
9
10
11
# File Gemfile
group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug'

  # Access an IRB console on exception pages or by using <%= console %> in views
  gem 'web-console', '~> 2.0'

  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
end

如果没有,就自己安装一下

安装完毕之后,在你想加断点的地方加入byebug即可,比如下面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
  def create
    @user = User.new(user_params)

    respond_to do |format|
      byebug
      if @user.save
        format.html { redirect_to @user, notice: 'User was successfully created.' }
        format.json { render :show, status: :created, location: @user }
      else
        format.html { render :new }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end

当你访问这个页面的时候,rails s 会停留在这里

1
2
3
4
5
6
7
8
9
10
11
12
[26, 35] in /Users/atpking/Code/Ruby/my_rails42/app/controllers/users_controller.rb
   26:   def create
   27:     @user = User.new(user_params)
   28:
   29:     respond_to do |format|
   30:       byebug
=> 31:       if @user.save
   32:         format.html { redirect_to @user, notice: 'User was successfully created.' }
   33:         format.json { render :show, status: :created, location: @user }
   34:       else
   35:         format.html { render :new }
(byebug)

此时我们可以查看在断点处的所有变量信息,基本等效于console。比如下面我来查看@user 下面有什么信息

1
2
3
4
5
6
7
8
9
10
11
12
13
   26:   def create
   27:     @user = User.new(user_params)
   28:
   29:     respond_to do |format|
   30:       byebug
=> 31:       if @user.save
   32:         format.html { redirect_to @user, notice: 'User was successfully created.' }
   33:         format.json { render :show, status: :created, location: @user }
   34:       else
   35:         format.html { render :new }
(byebug) @user.to_json
"{\"id\":null,\"name\":\"atpking\",\"born_at\":\"2015-04-19\",\"created_at\":null,\"updated_at\":null}"
(byebug)

紧接着是常用的 step、next、continue和quit了

在这里大家可以直接敲首字母,敲完首字母之后直接敲回车,代表的是 执行上一条指令 移动执行指针,有step (s)和next(n) 分别对应进入和执行一条语句

continue(c)代表执行直至下一个断点,quit(q)代表退出

有时候我们想查看执行指针附近的代码,则需要输入 list

动态加入断点,则使用break + 行号 如 break 33, 行号可以通过list 查询

当然还有很多复杂高级的用法,但是基本的调试,这些指令就够了。

Ruby的Proc.new 与Lambda 的区别

缘起,Block 与 Proc

Block 在ruby 中会像空气一般的存在于大量的代码当中。块最开始是用来循环时候用的东西,但慢慢的,block变成了委托利器,贯穿于各种代码之间。

举个例子,我有一个数组1,2,3,4,5,6 我想得到这个数组的平方 在传统的程序里面,我们这样实现

1
2
3
4
5
6
var arr = [1,2,3,4,5,6];
var answer = []`

for(var i = 0; i<arr.length;i++){
  answer[] = arr[i]**2;
}

如果在ruby中,我们即可使用块来完成

1
answer = [1,2,3,4,5].map {|item| item**2}

这里的{|item| item**2} 就是一个Block。

当然之所以这里的ruby 代码简洁的原因是因为map本身隐藏了一些逻辑,实际上map由模块Emuneration 实现 其实通过上面的代码基本上可以看出,Block类似其他语言中的C#或者java中的委托、事件的实现。

我们来仿照着写一个Block的应用(自行实现map)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class MyArray < Array

  def my_map
    for item in self
      p yield(item)
    end
  end
end

arr = MyArray.new
arr.push 1,3,5

arr.my_map do item
  item**2
end

# 结果输出 
# 1
# 9
# 25

我们把目光还是集中在{|item| item **2} 中,这个代码是我们手敲上去的,那有没有方式将其放入变量里呢? 答案是 有,就是Proc对象 就是我们的Proc.new 和lambda,他们会返回Proc对象,记得在做参数的时候要加&符号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class MyArray < Array

  def my_map
    for item in self
      yield(item)
    end

  end

  def my_map_using_proc(&proc)   # 定义的时候就需要表示这是个Proc 对象
    answer = self.class.new
    for item in self
      #p proc.class    输出为Proc 对象
      answer.push(proc.call(item))
    end
    return answer

  end
end



arr = MyArray.new
arr.push 1,3,5

my_proc = Proc.new do |i|
  i**2
end

my_lambda = lambda do |i|
  i**2
end

arr2 = arr.my_map_using_proc(&my_proc)  # 使用的时候也要说明
p arr2

arr3 = arr.my_map_using_proc(&my_lambda)
p arr3

Ruby 下的Proc.new 和Lambda 都是为了生成我们的Proc 而使用的指令,从大方向上来说,他们功能上非常相似,但从细节上来讲,他们又有一些不同点

相同点非常简单,他们都返回Proc对象,结构一样,那么不同点呢?

  1. Proc.new 的参数不需要严格匹配,但是lambda 需要严格匹配

比如这里的func1 = Proc.new {|p,q| p p,q};func1.call("x"), 则输出 "x" nil,但是换做lambda,就不行:

  2.2.0 :014 > func2 = lambda {|p,q| p p,q};func2.call("x")
ArgumentError: wrong number of arguments (1 for 2)
  from (irb):14:in `block in irb_binding'
  from (irb):14:in `call'
  from (irb):14
  from /Users/atpking/.rvm/rubies/ruby-2.2.0/bin/irb:11:in `<main>'
  1. Proc.new 如果里面出现了return,则代表的外部的return,而不是Proc自己的return,则是返回一个Proc对象,比如说
  2.2.0 :035 > def Hello
  2.2.0 :036?>   p = Proc.new{return "inner proc"}
  2.2.0 :037?>   p.call
  2.2.0 :038?>   return "func return"
  2.2.0 :039?>   end
  2.2.0 :041 > Hello()
   => "inner proc"

注意此处,返回的是inner proc, 注意,如果直接在irb 里写p = Proc.new{return 1}
之后p.call 的化,是要报localJumpError的,因为此时irb 并没有结束,还没有return

  2.2.0 :061 > def Hello2
  2.2.0 :062?>   p = lambda{return "inner proc"}
  2.2.0 :063?>   p.call
  2.2.0 :064?>   return "func return"
  2.2.0 :065?>   end
   => :Hello2
  2.2.0 :068 > Hello2()
   => "func return"

此处是func return。

那么这些区别会带来什么变化呢?答案是,lambda 可以带参数的回传, 更加灵活了。比如下面的一个例子

1
2
3
4
5
6
7
8
9
2.2.0 :087 > def my_func(n)
2.2.0 :088?>   b = lambda {|item| return item*n }
2.2.0 :089?>   end
 => :my_func
2.2.0 :090 > my_func(2).call 5
 => 10
2.2.0 :091 > my_func(2).class
 => Proc
2.2.0 :092 >

而proc,就会出现下列的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
2.2.0 :094 > def my_func(n)
2.2.0 :095?>   b = proc {|item| return item*n }
2.2.0 :096?>   end
 => :my_func
2.2.0 :097 > my_func(2).class
 => Proc
2.2.0 :098 > my_func(2).call 5
LocalJumpError: unexpected return
  from (irb):95:in `block in my_func'
  from (irb):98:in `call'
  from (irb):98
  from /Users/atpking/.rvm/rubies/ruby-2.2.0/bin/irb:11:in `<main>'
2.2.0 :099 >

背景

既然“管理时间”是不可能的,那么解决方法就只能是,想尽一切办法真正了解自己,真正了解时间、精确地感知时间;而后再想尽一切办法是自己以及自己的行为与时间“合拍”,就是我的说法–“与时间做朋友”

—-李笑来,把时间当做朋友

这个项目的灵感来自于一本叫做《奇特的一生》的一本书,这本书描述的主人公擅长把自己的时间记录起来,之后做一些分析统计,从而提高自己对时间的利用。

在主人公的时代,纸和笔是记录的方式,在我们的时代,就喜欢电子化了。

我最开始是通过APP 操作,将数据写入 谷歌日历 上的,但是我发现了一些问题:

  • 打开日历太麻烦了
  • 日历的时间选择很麻烦,要点很多次手机屏幕,还得精确操作
  • 选完日期还得选标题 又要切换

我觉得更快速的方式应该是使用 Evernote 直接以文本形式来记录时间开销,直接打字就好了,不用转换,而且同步起来个人觉得还是挺方便的

但是文字记录可视化程度不高,很难直观的看出时间利用率,那么我们就简单点,写个程序,将文字转化为Google Calendar 的事件。

我在做拆字的时候,有一个原则,就是 文字输入,一定要简单 这是一条典型的记录

1
1000 1130 做RubyOnRails的练习

代表从10点到11点30 做了ruby on rails 的练习。


初次使用配置

1. 确认能访问Google的服务器

没什么好说的,不行就挂VPN

2. 开启Google Calendar API,授权为Service account

为何是Service Account ?

因为我觉得证书登陆比输入密码简单,还不受Cookie的影响什么的,也许以后会改成Desktop Account

怎样开启

  1. 拥有Google 账户
  2. 使用这个账户去开启一个新的Project 项目 https://console.developers.google.com/project
  3. 点击进入你新开的Project 账户,进入API,打开 Google+ (用来做用户验证) 和 Google calendar 的API
  4. 进入credential 凭证,在OAuth 建立新的Client ID ,之后选择service account,保存,之后系统会提示你下载一个 p12结尾的签名文件,存好了别掉了,这是登录的凭证

3. 添加Google Project 里 Service Account 的Email 到 Google Calendar 的你记录时间的日历的共享账户,并将其权限选择为“拥有这个日历”

**非常重要,我被坑了好久** 你会在你申请的Project 看到在Oauth下的Service account 有一个Email Address 请把这个地址添加到你Google Calendar 里记录时间的日历的账户共享中

如果你不这么做,程序就读不到这个日历

4. 确认拥有Ruby 环境

安装ruby请移步ruby china 的教程

5. 安装对应Gem && 修改配置文件

脚本运行

1
2
3
4
git clone https://github.com/jicheng1014/timelog_to_google_cal
cd timelog_to_google_cal
bundle install
touch raw.txt

之后把刚下载的.p12 的签名文件保存到项目同级目录下。

修改配置文件 config.yml ,修改 service_account_email 和key_file 的具体值


日志格式格式

我在做日志格式的时候,有一个原则,就是 文字输入,一定要简单,无论是在电脑上写,还是在手机、平板上输入,都应该力图简洁。

默认是在raw.txt 中写日志,当然我自己是这么干的,写在Evernote 里,之后需要将数据展现的时候,再拷贝到这个文件当中

格式为

1
开始时间 结束时间 做的事情
  • 使用24小时进制
  • 时间为双位,中间没有空格 如下午1点30分 则是1330
  • 24点为0点,系统会自动增加一天(对夜猫子友好) 如2330 0010 跑步 代表 晚上11点30到凌晨24:10 跑步

这是一条典型的记录

1
2
3
4
1000 1130 做RubyOnRails的练习
1130 1215 吃饭

2300 0010 跑步

使用方法

项目目录下输入 ruby my_date_log [日期差] [日志文件名]

日期差

考虑到有可能在晚上0点之后更新,故可减少天数,比如现在是凌晨0147 我的日志记录的实际上是昨天的内容,则我的日期差就是 1

即可执行ruby my_date_log 1 可不填写,默认是0

日志文件

日志的文件名,默认是raw.txt

特别说明

  • 当你多次同步某天的日志到日历的时候也是可以的,程序会自动删除日志当天的老数据(根据内容标签为日志当天日期,你可以在Google Calendar 里点一个日志看他的详细内容,就是以那个为标注删除的)
  • 联系方式 atpking#gmail com

TODO

  • 直接通过Evernote 的API获取日志,之后自动的同步到Google Calendar
  • 做成指令,之后不在项目当前路径上输入

今年其实买了很多东西,从最开始的hi-vps,之后是macbook pro,再是macbook pro,iphone 6 plus, 还有linode的主机, 今天,又买了一个大件 codeschool的一年期会员

老美黑色星期五打折,收到邮件说codeschool 现在只要189 USD就可以混一年,以前看过1-2期codeschool 的视频,感觉不错,主持人蛮幽默的.外加制作个人觉得还是比较用心,有一定的水准,但是因为穷癌导致一直没有入手。

其实190USD差不多只折合人民币不到1200块,相当于每个月100块,一天3.3,还没口香糖烧钱,但身为非90互联网原住民,还是有一些思维上的劣势,感觉买这种在线教育类的,总是觉得花上千块略奢侈

当然其实从道理上我还是能理解 金钱换知识是划算的,从逻辑上能够推理出来这笔投资是值得的。

仔细回忆了下今年买的虚拟物品,其实还是蛮多的,小列出来一下

  • hi-vps
  • 阿里云主机 若干
  • greenvpn
  • linode vps
  • 电子书若干本 (主要在多看买,似乎还在另外的平台买了几本部署、测试的书,还好都不贵)
  • 史上最坑爹的 air display 2 (特别列出此app,刷新率低的吓人,请广大群众注意,千万别买)
  • 其他apps (不超过100)
  • railscasts 的一个月会员 (由于版主没更新,所以一直只花了一个月,自动续期)
  • doit.im 一年会员
  • pomotodo 一年期会员
  • 迅雷一年期白金会员(估计不会续费了 哈哈哈哈哈哈哈)
  • codeschool 一年期会员

这么一看,还是买了不老少,目前感觉最划算的时linode 除了当主机用,还肩负着ssh 端口转发以及自建vpn的光荣任务,最不值的就是air display2,retina屏幕扩展几本没办法用,卡的直接崩溃,60多块吧,记不清了

昨天折腾了一天关于vagrant、capistrano 部署rails程序, 可能是因为自己不是很懂,基本上搞了一天最后才搞出一点点名堂, 很是惭愧

关于vagrant ,是一个可用于快速配置开发环境的一个虚拟机的应用,可以将常规项目放到虚拟机中进行开发, 官方是说vagrant是用来统一项目组的开发环境云云,其实我觉得倒也还好,真正要统一环境还不如去玩docker, 个人使用vagrant的最方便的就是可以先在自己的机器上实验部署环境,而又不用去运行完整的系统(比如我放的ubuntu 14.04), 另外一个比较方便的是vagrant 和宿主机器传文件特别方便,实际上是公用一个文件夹 最后的一个比较伤心的理由是你要真正的在virtualbox上跑ubuntu实际上还是略吃资源的,而vagrant上跑它优化过的ubuntu,应该是砍掉了一些资源, 硬盘消耗的略小一些,这样拥有屌丝120G 硬盘的mbp的我不会因为虚拟机而占太多硬盘

在这里推荐happycasts网站的一个视频,我觉得peter的这期视频应该是说的很清楚了,有兴趣的朋友可以点击这里查看一下

关于capistrano这个自动化部署工具,最开始我把它想的十分复杂,觉得应当从官方网站一行一行的读,由于ruby和英文都不是恨过关,结果非常痛苦,耗费了很多时间。 我不是说啃官方的document不好,这个capistrano的官网写的有些详细,陡然看上去感觉不太好懂(或者我比较愚钝),应该是有所了解了再上这个懒人包,感觉效果会好一些

这里我要说明一些capistrano的使用的一个细节

capistrano 是一个部署每次都要用的东西,所以,如果只需要运行一次的东西,是否有必要写成task?如果要发布到多个计算机上,需要(或者使用一个叫做chef 的工具),如果只有一台服务器,写task的麻烦程度是大于自己配置

我觉得capistrano管理的是每次发布时都需要做的事情,而不是单独一次发布的时候做的事情

另外,capistrano 也没有自动化到可以帮你安装nginx、passenger、rvm,写配置的程度(也许可以,只是很麻烦),所以这个要在新服务器自己部署搞定

可以参考下这篇文章,案例是使用rbenv的,替换成rvm即可,Use Capistrano 3 to Deploy Rails Applications - a Step-by-Step Tutorial

我当时做的时候有个小插曲:当一切配置完毕的时候 访问这个rails程序,始终报403 错误,但是我current/public 权限肯定够读的。 几经研究之后,发现是nginx的一个配置问题:我使用的是默认的server,它里面有一个location / { … } 的配置,这个location这段删掉即可(原理未知,求解答)

另外使用capistrano 部署后,直接使用bundle 命令在服务器做事情似乎是不行的 需要使用rvm 指定才行,类似这样 ~/.rvm/bin/rvm default do bundle exec rake assets:precompile

之后直接在服务器运行rails 命令也会失败,我是又再服务器运行了gem install rails 才能使用的

今天跟老爸打电话,聊天中,我得知了一个很有意思的事情。 我老爸告诉我,他手机流量用超了,超了80多M. 我说,这是正常的,我也偶尔超一下,下次办一个稍微大点的流量包就可以了。之后顺便问了下老爸流量包办的是多大的, 他的回答,让我有些意外

700MB

想起我自己的plus,也只是办了400MB的流量。一般一个月也就差不多300-500M左右

一位中老年人在移动网络上花去了超过700M流量,如果有1/100的中老年有这种强需求,那这块的市场有多大?

我就在想,现在的移动互联网市场是不是过分的针对着年轻人,还忽略了一块叫做中老年的用户群体? 我们做IT的总是习惯于把自己的用户定位成15-30岁的年轻人,总觉得人一旦上了年龄了,就不会玩互联网这一套了 但是在我的感觉里,似乎越来越多的中老年,也开始加入到互联网,特别是移动互联网的领域当中了 而且,他们通常,还有一个可以支付他们消费的子女