iT邦幫忙

2017 iT 邦幫忙鐵人賽
DAY 5
0
Modern Web

Ruby礦工的Rails地圖系列 第 5

如何自行編寫Ruby的方法

寫ruby一段時間以後,可能會著迷於這個框架的自由度
甚至還可以自行擴充框架本身
來達成想要滿足的商業邏輯

例如新手可能會將程式多次重複在專案各處
漸漸學習到可以將重複的部分整理到model或是service or view helper
如果這些都不滿足
有部分的功能,可以跨專案運用
那可以考慮拉出來成為一隻獨立的gem
或是試著複寫到ruby本身

比如說Time.methods
可以列出所有Time的方法,但是會包含上層繼承而來的方法
所以如果要看Time本身的方法,可以加參數false

Time.methods false
 => [:now, :at, :utc, :gm, :local, :mktime, :zone_offset, :parse, :strptime, :rfc2822, :rfc822, :httpdate, :xmlschema, :iso8601, :zone_default, :zone_default=, :zone, :zone=, :use_zone, :find_zone!, :find_zone, :===, :days_in_month, :current, :at_with_coercion, :at_without_coercion] 

假設我今天有個需求,要計算生日
一開始可能會寫在member model裡面,例如

class Member
    def years_old
        #...先省略實作
    end
end

這時取出任意一個Member實體,都可以用years_old方法

member = Member.find(1)
member.years_old
=> 18 

理想上可以得到這樣的結果
但如果除了Member外,我有另外一個Model叫做Custom也需要計算生日
在Custom裡面也有一個一模一樣的方法,似乎就不是什麼高明之舉

class Member
    def years_old
        #...先省略實作
    end
end
class Custom
    def years_old
        #一模一樣
    end
end

這個時候有兩種做法,看實際運用狀況
可以整理到view helper或是service
如果是後者,比方說

#app/service/time_tool.rb
class TimeTool
    def years_old(birthday)
        #...略
    end
end

那這時,上面兩個model就可以整理為

class Member
    def years_old
        TimeTool.new.years_old(birthday)
    end
end
class Custom
    def years_old
        TimeTool.new.years_old(birthday)
    end
end

等於把相同的部分,整理到Service當中,實現Dry(Don't Repeat Yourself)的精神
那假如更進一步,想要在任意的日期物件呼叫與今日的年份差距(生日)
可以再往上提一層,比方說

#app/models/date.rb
class Date
    def self.years_old
        #略
    end
end

這邊要特別注意,在config/environments/development.rb
要改為true,不然不會生效
config.eager_load = true
重啟console之後
Date.methods false就可以看到剛剛新增的方法:years_old
所以只要 birthday.class => Date
就可以直接使用這個方法,例如

m = Member.find(1)
m.birthday.years_old
=> 18
c = Custom.find(1)
c.birthday.years_old
=> 18

甚至任何日期,想要知道與今天相隔幾年,都可以直接使用這個方法
因為你定義在Date這個class裡面,所有Date或繼承Date的物件,都可以自由地使用這個方法
是不是更方便了呢


上一篇
STI , MTI 與多型關聯(Polymorphic Associations) 系列三
下一篇
Don't use Magic number in your code
系列文
Ruby礦工的Rails地圖30

尚未有邦友留言

立即登入留言