高見龍

iOS app/Ruby/Rails Developer & Instructor, 喜愛非主流的新玩具 :)

Ruby 1.8 v.s. 1.9

image

其實Ruby 1.9已經推出N年,也算有點年紀的東西了。我想Ruby 1.8跟1.9的版本差別,對一般的developer來說,比較關心的點也許是這兩個版本在語法上有沒有哪邊不同? 是不是在1.8版寫的程式到1.9版都能完全不出問題? 讓我們繼續看下去…

Gems變內建的了

Rubygems在1.8版需要另外安裝,但在1.9安裝的時候也會一併裝進去,另外在1.8版可能偶爾會需要來一行:

1
require "rubygems"

這個在1.9也可以不需要了。

載入的路徑”有一點”不

在1.9版,預設的載入路徑把”.”(小數點一點,也就是目前的同錄)給拿掉了,所以在require的時候可能要注意一下。

支援Unicode

因為1.9起開始支援unicode,所以tr跟Regexp也都開始看得懂unicode了

老祖宗變了

直接來段程式碼:

1
2
3
4
5
6
7
8
class Test
end

# ver 1.8
p Test.ancestors # [Test, Object, Kernel]

# ver 1.9
p Test.ancestors # [Test, Object, Kernel, BasicObject]

Ruby 1.9版在Object上面還多了個BasicObject:

1
2
3
4
5
6
# ver 1.8
p Object.superclass # nil

# ver 1.9
p Object.superclass # BasicObject
p BasicObject.superclass # nil

BasicObject變成 1.9版的最上層類別了。

字串處理

1
2
3
4
5
6
7
# ver 1.8
p "hello"[1]   # 得到101
p ?a           # 得到97

# ver 1.9
p "hello"[1]   # 得到"e"
p ?a           # 得到"a"

另外,String#each這個方法在1.9已經被拿掉囉,請改用String#each_byteString#each_char以及String#each_line

陣列

1
2
3
4
5
# ver 1.8
p [1, 2, 3, 4, 5].to_s  # 得到"12345"

# ver 1.9
p [1, 2, 3, 4, 5].to_s  # 得到"[1, 2, 3, 4, 5]"

印出來的結果不一樣了

Hash

1
2
3
4
5
6
7
me = {:name => 'eddie', :age => 18, :gender => 1}

# ver 1.8
p me # 得到 {:age=>18, :gender=>1, :name=>"eddie"}

# ver 1.9
p me # 得到 {:name=>"eddie", :age=>18, :gender=>1}

在1.9版的hash,不管是新建立或是後來再附加上去,它都變得有”順序”了

另外1.9版有支援新的寫法,上面這段就可以改寫成:

1
me = {name: "eddie", age: 18, gender: 1}

它會很聰明的自動把key轉成symbol。在Rails裡常用到的redirect_to,原本這樣寫:

1
redirect_to :action => index

也可以改寫成:

1
redirect_to action: index

Block

看一下這段程式碼:

1
2
3
4
5
6
i = 0
p i
(1..5).each do |i|
  # do something
end
p i

在1.8版本,最後一行會印出數字5,但在1.9則是印出數字0,block裡的區域變數不會影響到外部的變數了。

Method

使用methods方法可以印出該類別或物件的方法:

1
2
3
# ver 1.8
p String.methods.sort
["<", "<=", "<=>", "==", "===", "=~", ">", ">=", "__id__", "__send__", "allocate", "ancestors", "autoload", "autoload?", "class", "class_eval", "class_exec", "class_variable_defined?", "class_variables", "clone", "const_defined?", "const_get", "const_missing", "const_set", "constants", "display", "dup", "enum_for", "eql?", "equal?", "extend", "freeze", "frozen?", "hash", "id", "include?", "included_modules", "inspect", "instance_eval", "instance_exec", "instance_method", "instance_methods", "instance_of?", "instance_variable_defined?", "instance_variable_get", "instance_variable_set", "instance_variables", "is_a?", "kind_of?", "method", "method_defined?", "methods", "module_eval", "module_exec", "name", "new", "nil?", "object_id", "private_class_method", "private_instance_methods", "private_method_defined?", "private_methods", "protected_instance_methods", "protected_method_defined?", "protected_methods", "public_class_method", "public_instance_methods", "public_method_defined?", "public_methods", "respond_to?", "send", "singleton_methods", "superclass", "taint", "tainted?", "tap", "to_a", "to_enum", "to_s", "type", "untaint"]

回傳的是方法的字串陣列。

1
2
3
p String.methods.sort
# ver 1.9
[:!, :!=, :!~, :<, :<=, :<=>, :==, :===, :=~, :>, :>=, :__id__, :__send__, :allocate, :ancestors, :autoload, :autoload?, :class, :class_eval, :class_exec, :class_variable_defined?, :class_variable_get, :class_variable_set, :class_variables, :clone, :const_defined?, :const_get, :const_missing, :const_set, :constants, :define_singleton_method, :display, :dup, :enum_for, :eql?, :equal?, :extend, :freeze, :frozen?, :hash, :include?, :included_modules, :initialize_clone, :initialize_dup, :inspect, :instance_eval, :instance_exec, :instance_method, :instance_methods, :instance_of?, :instance_variable_defined?, :instance_variable_get, :instance_variable_set, :instance_variables, :is_a?, :kind_of?, :method, :method_defined?, :methods, :module_eval, :module_exec, :name, :new, :nil?, :object_id, :private_class_method, :private_instance_methods, :private_method_defined?, :private_methods, :protected_instance_methods, :protected_method_defined?, :protected_methods, :public_class_method, :public_instance_method, :public_instance_methods, :public_method, :public_method_defined?, :public_methods, :public_send, :remove_class_variable, :respond_to?, :respond_to_missing?, :send, :singleton_class, :singleton_methods, :superclass, :taint, :tainted?, :tap, :to_enum, :to_s, :trust, :try_convert, :untaint, :untrust, :untrusted?]

在1.9變成回傳symbol的陣列了。那會有什麼影響? 如果你有寫到這樣的程式碼來判斷某個類別是否有實作某個方法的話:

1
p String.methods.include? "to_s"

在1.8會是true,但在1.9就會是false了。所以如果你要做這樣的判斷,請使用respond_to?method_defined?

1
2
p String.respond_to? "to_s"
p String.method_defined? "to_s"

Lambda

1
2
3
4
5
6
7
# ver 1.8
my_calc = lambda { |a, b| a * b }
p my_calc.call(10, 20)

# ver 1.9
my_calc2 = -> a, b { a * b }
p my_calc2.(10, 20)

在1.9版的lambda可以用上面這個方法來寫,而且call方法也可以省略。

預設參數

1
2
3
4
5
6
7
def hello(a, b = 10, c)
  p a, b, c
end

p hello(1)        # ArgumentError
p hello(1, 2)     # [1, 10, 2]
p hello(1, 2, 3)  # [1, 2, 3]

在1.9版在參數個數不夠的時候,可以直接填給沒有預設參數的部份。

結論

其實這兩個版本的差異當然不只這樣,我僅就我個人比較常用到的部份做個筆記,順便也提醒自己。以上內容若有任何錯誤再請讓我知道,感謝。

參考資料:

Do..End v.s Braces

話說在Ruby裡,Block可以用do..end的方式來寫,也可以用大括號來寫,例如用do..end的方式寫:

1
2
3
(1..5).each do |i|
  puts i   # 得到1 2 3 4 5
end

用大括號的方式寫的話:

1
2
3
(1..5).each { |i|
  puts i   # 一樣也是得到1 2 3 4 5
}

不過這兩天在看The Ruby Way的時候,看到一段是這樣寫的:

“Note that do and end may be substituted for the braces that delimit a block. There are differences, but they are fairly subtle.”

The Ruby Way(2nd edition) Page.22

書裡沒看到特別講到底哪邊不一樣,所以好奇的到處翻了網路上的一些資料,發現原來這兩種寫法的優先順序有一些些的不一樣,來看一段範例:

1
2
3
4
5
6
7
8
9
my_array = [1, 2, 3, 4, 5]

p my_array.map { |item|
  item * 2  # 得到[2, 4, 6, 8, 10]
}

p my_array.map do |item|
  item * 2  # 得到[1, 2, 3, 4, 5]
end

為什麼結果跟預期的不一樣? 原來,因為省略了一些小括號,第二段的程式碼本來的意思是:

1
p(my_array.map do |item| item * 2 end)  # 得到[2, 4, 6, 8, 10]

但卻因為do..end的優先順序比較小,原式被解析成:

1
p(my_array.map) do |item| item * 2 end # 得到[1, 2, 3, 4, 5]

結果後面的do..end還沒開始跑,my_array.map就先被p給印出來了..而用大括號的寫法的優先順序比較高,則不會有這個困擾。這樣那到底什麼時候要用do..end? 什麼時候要用大括號? 還是乾脆全部都用大括號就好?

基本上如果Block的執行結果是會有回傳值的,那就建議用大括號來寫,其它的大致上都可以用do..end來寫。

果然魔鬼都躲在細節裡!

如果寫得有錯再請不吝指正,感謝!

用Backup來備份你的網站

image

一直以來備份這種事就是多做沒事,少做就會出事情。有一套很棒的 gem 叫做Backup,可以來完成一些本來也許要用 shell script 才能做的事情(我對 shell script 相當苦手!),而且語法還很好寫、很漂亮。

也許大家看到 gem 就覺得這是 Ruby 吧,其實這跟你寫不寫 Ruby 或 Rails 沒太大關係,這真的是個很方便的工具,推薦大家玩看看!

其實 Backup 這個 gem 不只能備份資料庫,不過我比較需要的是就是資料庫的備份,所以特別把它筆記下來,至於其它的用法可參考官方的使用說明。

安裝

安裝Backup:

> gem install backup

安裝完成後,執行 backup:

> backup generate
Generated configuration file in '/Users/eddie/Backup/config.rb'

它會在你帳號的根目錄底下建立一個Backup資料夾,並產生一個空的config.rb。另外你還可以給它一些參數,例如:

> backup generate --databases='mysql' --storages='s3' --compressors='gzip'
Generated configuration file in '/Users/eddie/Backup/config.rb'

它會幫忙產生好一些空的 block 讓你可以改來用:

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
#檔案:config.rb

Backup::Model.new(:eddie_blog, 'Eddie Blog DB') do

  database MySQL do |db|
    db.name               = "eddie_blog" # 你要備份的資料庫
    db.username           = "eddie_blog_user" # 資料庫登入帳號
    db.password           = "eddie_blog_password" # 資料庫登入密碼
    db.host               = "localhost"
    db.port               = 3306
    db.socket             = "/tmp/mysql.sock"
    db.skip_tables        = ['skip', 'these', 'tables'] # 哪些table不要備份的
    db.only_tables        = ['only', 'these' 'tables']  #只要備份指定的table
    db.utility_path       = "/opt/local/lib/mysql5/bin/mysqldump" # 如果你的電腦找不到mysqldump的話,才需要另外設定utility_path
    db.additional_options = ['--quick', '--single-transaction']
  end

  store_with S3 do |s3|
    s3.access_key_id      = 'my_access_key_id'
    s3.secret_access_key  = 'my_secret_access_key'
    s3.region             = 'ap-northeast-1'  # 因為我的S3 bucket是開在Tokyo,所以改這樣
    s3.bucket             = 'backup-eddie' # 我在S3上開了一個專門放備份的bucket
    s3.path               = '/databases/blog.eddie.com.tw' # 備份存放的路徑
    s3.keep               = 20  # 要保留多少個檔案,超過的話舊的就會被清掉
  end

  compress_with Gzip do |compression|
    compression.best = true
    compression.fast = false
  end

end

以上設定請依你的環境進行修改,語法應該相當簡單易懂,接著開始來進行備份:

> backup perform -t eddie_blog
[2011/05/24 05:24:27][message] Performing backup for Eddie Blog!
[2011/05/24 05:24:27][message] Backup::Database::MySQL started dumping and archiving "eddie_blog".
[2011/05/24 05:24:27][message] Backup started packaging everything to a single archive file.
[2011/05/24 05:24:27][message] Backup::Compressor::Gzip started compressing the archive.
[2011/05/24 05:24:37][message] Backup::Storage::S3 started transferring "2011.05.24.05.24.26.eddie_blog.tar.gz".
[2011/05/24 05:24:43][message] Backup started cleaning up the temporary files.

要注意的是,你在執行這段的時候可能會出現錯誤訊息,告訴你缺裝了什麼gem,它會很貼心的自動幫你安裝需要的gem,如果你權限不足,那就加個sudo吧。

這樣就完成了!

備份通知

除了備份到S3之外,還有別的像是FTP、SCP、RSync等方案,而且Backup這套gem還可以設定notifier,例如可以在備份完成後發Email通知或是自動丟到Twitter通知 (可參考 Notifiers的說明)。不過我個人不喜歡收一堆的信件通知,也不想讓備份這種雜事去洗 twitter,剛好在 Storage 裡有個 Dropbox 可以用,備份完成我同時也會收到 dropbox 的通知,我要備份的檔案都不大,所以 dropbox 對我來說是個不錯的方案。

改寫如下:

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
39
40
41
#檔案:config.rb
Backup::Model.new(:eddie_blog, 'Eddie Blog DB') do

  database MySQL do |db|
    db.name               = "eddie_blog" # 你要備份的資料庫
    db.username           = "eddie_blog_user" # 資料庫登入帳號
    db.password           = "eddie_blog_password" # 資料庫登入密碼
    db.host               = "localhost"
    db.port               = 3306
    db.socket             = "/tmp/mysql.sock"
    db.skip_tables        = ['skip', 'these', 'tables'] # 哪些table不要備份的
    db.only_tables        = ['only', 'these' 'tables']  #只要備份指定的table
    db.utility_path       = "/opt/local/lib/mysql5/bin/mysqldump" # 如果你的電腦找不到mysqldump的話,才需要另外設定utility_path
    db.additional_options = ['--quick', '--single-transaction']
  end

  store_with S3 do |s3|
    s3.access_key_id      = 'my_access_key_id'
    s3.secret_access_key  = 'my_secret_access_key'
    s3.region             = 'ap-northeast-1'  # 因為我的S3 bucket是開在Tokyo,所以改這樣
    s3.bucket             = 'backup-eddie' # 我在S3上開了一個專門放備份的bucket
    s3.path               = '/databases/blog.eddie.com.tw' # 備份存放的路徑
    s3.keep               = 20  # 要保留多少個檔案,超過的話舊的就會被清掉
  end

  store_with Dropbox do |db|
    db.email      = 'my@email.com' # dropbox登入帳號
    db.password   = 'my_dropbox_password'  # dropbox登入密碼
    db.api_key    = 'my_api_key'
    db.api_secret = 'my_api_secret'
    db.timeout    = 300
    db.path       = '/Backups/Databases'
    db.keep       = 20
  end

  compress_with Gzip do |compression|
    compression.best = true
    compression.fast = false
  end

end

這裡用到的api跟secret可到dropbox的開發者網站免費申請來用。寫好之後再執行一次備份,這次就除了備份到S3之外,也會另外備一份到Dropbox:

> backup perform -t eddie_blog
[2011/05/24 05:35:19][message] Performing backup for Eddie Blog!
[2011/05/24 05:35:19][message] Backup::Database::MySQL started dumping and archiving "eddie_blog".
[2011/05/24 05:35:19][message] Backup started packaging everything to a single archive file.
[2011/05/24 05:35:19][message] Backup::Compressor::Gzip started compressing the archive.
[2011/05/24 05:35:20][message] Backup::Storage::Dropbox started transferring "2011.05.24.05.35.18.eddie_blog.tar.gz".
[2011/05/24 05:35:30][message] Backup::Storage::S3 started transferring "2011.05.24.05.35.18.eddie_blog.tar.gz".
[2011/05/24 05:35:36][message] Backup started cleaning up the temporary files.

備份成功,而且還有個漂亮的提示視窗!

image

加入其它備份工作

那如果要再加其它的備份進來怎麼做? 就照著第一段的寫:

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#檔案:config.rb

# 原來的
Backup::Model.new(:eddie_blog, 'Eddie Blog DB') do

  database MySQL do |db|
    db.name               = "eddie_blog" # 你要備份的資料庫
    db.username           = "eddie_blog_user" # 資料庫登入帳號
    db.password           = "eddie_blog_password" # 資料庫登入密碼
    db.host               = "localhost"
    db.port               = 3306
    db.socket             = "/tmp/mysql.sock"
    db.skip_tables        = ['skip', 'these', 'tables'] # 哪些table不要備份的
    db.only_tables        = ['only', 'these' 'tables']  #只要備份指定的table
    db.utility_path       = "/opt/local/lib/mysql5/bin/mysqldump" # 如果你的電腦找不到mysqldump的話,才需要另外設定utility_path
    db.additional_options = ['--quick', '--single-transaction']
  end

  store_with S3 do |s3|
    s3.access_key_id      = 'my_access_key_id'
    s3.secret_access_key  = 'my_secret_access_key'
    s3.region             = 'ap-northeast-1'  # 因為我的S3 bucket是開在Tokyo,所以改這樣
    s3.bucket             = 'backup-eddie' # 我在S3上開了一個專門放備份的bucket
    s3.path               = '/databases/blog.eddie.com.tw' # 備份存放的路徑
    s3.keep               = 20  # 要保留多少個檔案,超過的話舊的就會被清掉
  end

  store_with Dropbox do |db|
    db.email      = 'my@email.com' # dropbox登入帳號
    db.password   = 'my_password'  # dropbox登入密碼
    db.api_key    = 'my_api_key'
    db.api_secret = 'my_api_secret'
    db.timeout    = 300
    db.path       = '/Backups/Databases'
    db.keep       = 20
  end

  compress_with Gzip do |compression|
    compression.best = true
    compression.fast = false
  end

end

# 另一個
Backup::Model.new(:my_superdb, 'Another Super DB') do

  database MySQL do |db|
    db.name               = "my_superdb" # 你要備份的資料庫
    db.username           = "my_superdb_user" # 資料庫登入帳號
    db.password           = "my_superdb_password" # 資料庫登入密碼
    db.host               = "localhost"
    db.port               = 3306
    db.socket             = "/tmp/mysql.sock"
    db.skip_tables        = ['skip', 'these', 'tables'] # 哪些table不要備份的
    db.only_tables        = ['only', 'these' 'tables']  #只要備份指定的table
    db.utility_path       = "/opt/local/lib/mysql5/bin/mysqldump" # 如果你的電腦找不到mysqldump的話,才需要另外設定utility_path
    db.additional_options = ['--quick', '--single-transaction']
  end

  store_with S3 do |s3|
    s3.access_key_id      = 'my_access_key_id'
    s3.secret_access_key  = 'my_secret_access_key'
    s3.region             = 'ap-northeast-1'  # 因為我的S3 bucket是開在Tokyo,所以改這樣
    s3.bucket             = 'backup-eddie' # 我在S3上開了一個專門放備份的bucket
    s3.path               = '/databases/blog.eddie.com.tw' # 備份存放的路徑
    s3.keep               = 20  # 要保留多少個檔案,超過的話舊的就會被清掉
  end

  store_with Dropbox do |db|
    db.email      = 'my@email.com' # dropbox登入帳號
    db.password   = 'my_password'  # dropbox登入密碼
    db.api_key    = 'my_api_key'
    db.api_secret = 'my_api_secret'
    db.timeout    = 300
    db.path       = '/Backups/Databases'
    db.keep       = 20
  end

  compress_with Gzip do |compression|
    compression.best = true
    compression.fast = false
  end

end

看到這裡我就覺得有點痛苦了,太多重複的東西,沒把它抽出來另外寫就整個很濕很難受(not DRY)。所以我把一些相關的設定抽出來寫在另外的檔案:

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
#檔案:backup-defaults.rb

Backup::Configuration::Storage::S3.defaults do |s3|
    s3.access_key_id      = 'my_access_key_id'
    s3.secret_access_key  = 'my_secret_access_key'
    s3.region             = 'ap-northeast-1'  # 因為我的S3 bucket是開在Tokyo,所以改這樣
    s3.bucket             = 'backup-eddie' # 我在S3上開了一個專門放備份的bucket
    s3.path               = '/databases/blog.eddie.com.tw' # 備份存放的路徑
    s3.keep               = 20  # 要保留多少個檔案,超過的話舊的就會被清掉
end

Backup::Configuration::Storage::Dropbox.defaults do |dropbox|
    dropbox.email      = 'my@email.com' # dropbox登入帳號
    dropbox.password   = 'my_password'  # dropbox登入密碼
    dropbox.api_key    = 'my_api_key'
    dropbox.api_secret = 'my_api_secret'
    dropbox.timeout    = 300
    dropbox.path       = '/Backups/Databases'
    dropbox.keep       = 20
end

Backup::Configuration::Database::MySQL.defaults do |mysql|
    mysql.host               = "localhost"
    mysql.port               = 3306
    mysql.utility_path       = "/opt/local/lib/mysql5/bin/mysqldump" # 如果你的電腦找不到mysqldump的話,才需要另外設定utility_path
    mysql.additional_options = ['--quick', '--single-transaction']
end

Backup::Configuration::Compressor::Gzip.defaults do |compressor|
    compressor.best = true
    compressor.fast = false
end

所以原來的設定檔就可以變成:

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
# 檔案:config.rb

require 'backup-defaults'

Backup::Model.new(:eddie_blog, 'Eddie Blog') do

  compress_with Gzip

  store_with Dropbox
  store_with S3 do |s3|
    s3.path = '/databases/blog.eddie.com.tw'
  end

  database MySQL do |db|
    db.name               = "my_superdb" # 你要備份的資料庫
    db.username           = "my_superdb_user" # 資料庫登入帳號
    db.password           = "my_superdb_password" # 資料庫登入密碼
  end

end

Backup::Model.new(:my_superdb, 'Another Super DB') do

  compress_with Gzip

  store_with Dropbox
  store_with S3 do |s3|
    s3.path = '/databases/blog.eddie.com.tw'
  end

  database MySQL do |db|
    db.name               = "my_superdb" # 你要備份的資料庫
    db.username           = "my_superdb_user" # 資料庫登入帳號
    db.password           = "my_superdb_password" # 資料庫登入密碼
  end

end

乾淨多了!

如果你不介意S3的存放路徑的話,也可以把s3.path寫回defaults值裡,程式碼可以更短。

自動備份

確定整個備份沒問題之後,再來要怎麼把它設定為自動備份? 如果你知道怎麼寫cronjob的話那就直接開起來寫。官網有建議另一套gem叫做whenever,這是一套很棒的gem,能做滿多事情的,不過這裡我們只用它來簡單的執行一個指令而已。

安裝請參考whenever的github project。建一個config資料夾準備放設定檔(其實自己手動建也行,反正它幫你產生的也是空白的):

> wheneverize .
[add] writing `./config/schedule.rb'
[done] wheneverized!

編輯設定檔:

1
2
3
4
5
#檔案 config/schedule.rb

every 12.hours do
  command "backup perform -t eddie_blog"
end

更新到cronjob裡:

> whenever --update-crontab
[write] crontab file updated

看一下cronjob:

> crontab -e
# Begin Whenever generated tasks for: /home/eddie/Backup/config/schedule.rb
0 0,12 * * * /bin/bash -l -c 'backup perform -t eddie_blog'

它設定在每天的0點及12點的時候會進行備份的工作。

打完,收工!

結論

有人會懷疑像這種S3或Dropbox的備份安全性如何? 會不會被其它人或是雲端主機自己內部的員工看到你的檔案?

對我來說,我要備份的檔案都不會太機密,我要的只是萬一出問題,可以找得到檔案來快速還原,有真正重要的我會另外找方案處理。我想這些雲端主機的安全性應該是不會太差,但如果你信不過它,而且要備份的東西是非常極機密,萬一外流就有人會死的那種,那你也許可以試著把Storage的地方改成SCP或SFTP之類的,把檔案備到你的機器上囉。

最後,感謝神人們寫出這麼方便的gem,讓我睡覺可以睡得更安穩一些了!

參考資料:

補充:

2011.5.27 我用whenever的時候發現cronjob沒辦法正常執行,在/var/log/syslog裡會出現”failed with exit status 127″的錯誤訊息(OS: Ubuntu),似乎是找不到執行的執令的關係,不知道是哪邊環境沒設定好,不過我後來手動把crontab修改成:

0 0,12 * * * /bin/bash -l -c '/usr/local/bin/backup perform -t eddie_blog'

把backup改成完整路徑就ok了!

Users in my blog

今天從Google Analytics翻了一些我自己這個小blog的數字出來看:

image

雖然僅列出前10名,但看到IE只剩2%多,感覺真好!

image

從前10名的數字來看螢幕解析度的話,大家的螢幕都越來越大,1024×768以下的都快消失了,也許以後設計網站的時候可以有更大的發揮空間了。

不過因為這只是個小小小站,樣本數不多(小於一萬),而且也不是很平民化的內容,所以應該會有滿大的偏差,僅供參考。