Ruby / Rails meta programming 的實現


#1

各位大大好,實在是不知道該怎麼形容這個問題,總之我們可以看一下這個
attr_reader :hihi
attr_writer :yoyo

好了就是這樣,請問像這樣的方法是怎麼做出來讓它導出

attr_reader :hihi
def hihi
  @hihi
end
attr_writer :yoyo
def yoyo(value)
   @yoyo = value
end

這樣的東西是怎麼被做出來的呢?為什麼我們空格:hihi 他自己就會建出這樣的function?


關於class裡呼叫class Method觀念想要釐清
class_eval injection
#2

修改標題為:Ruby / Rails meta programming 的實現

hmm…這邊屬於 meta programming 的一環,我寫一個可愛的東西給你看好了,上星期三才 demo 過

class A
  10.times do |i|
    class_eval "def yoo_#{i} ; return #{i} ; end"
    instance_eval "def hoo_#{i} ; return #{i} ; end"
  end
end

A.hoo_9 #=> 9
A.new.yoo_9 #=> 9

事實上所有 Rails 的的 relation,類似 has_many / has_one / belongs_to … 都是用類似的方式製成,也就是即時 build 一個 methods 出來( 所以我也一直說你可以自幹relation,因為同義 ),而你的 attr_reader 其實是 method 而已,我寫一個完全同義的給你看

class B
  def self.attr_accessor_new(source_name)
    class_eval "def #{source_name} ; return @#{source_name} ; end"
    class_eval "def #{source_name}=(temp) ; return @#{source_name} = temp ; end"
  end
end
class B
  attr_accessor_new :yoo
  attr_accessor_new :hoo
end
B.attr_accessor_new :zoo
B.new.yoo = 123
B.new.yoo #=> nil (( 裝可愛錯誤用法,因為重新 new 了
b = B.new
b.yoo = 123
b.yoo #=> 123
b.hoo = 234
b.hoo #=> 234
b.zoo #=> nil
b.zoo = 999
b.zoo #=> 999

anyway Ruby 一切都很簡單,如果你之前知道怎樣蓋 Ruby 的 code,那麼這邊的東西其實就理所當然而已,而我是從古老的 Ruby 1.8.6 / Rails 1.2.6 活到現在的老人家,以前 acts_as_tree / acts_as_list 一票都這樣幹出來的 X"DD…雖然很 evil ( eval ) 不過很好用,but 後期有別的寫法來做類似的事情,且比較漂亮些,但意思其實都非常類似,而如果你再去研究一下,其實 mixin 也很像這種東西的

最後再補充一下 … method系列都可以上刮號,類似

attr_reader :hihi
attr_reader(:hihi)

兩個同義,所以空白只是簡寫而已,你可以再去看一下我之前寫的這篇

真正瞭解後,你會得到 Ruby 的世界的 : )


#3

把code 丟進去玩一玩,結果class_eval 真的要很小心,因為是一整串字串,呼叫出來把symbol 丟進去還真的不知道要怎麼debug。anyway 這真的是很棒的範例,我知道該怎麼使用這個東西了^^謝謝


#4

debug 其實有技巧,上面也沒談到"正規化",簡單的來說和製作 method_missing 相同,“你不應該在這種動態產生的 method 中把事情做完”,而是把 build 出來的 method 當作方便用的入口點,然後該入口再去 call 另外一個 method,才在最後的 method 中把事情做完,這樣就非常容易了

類似

User.find_by_name('hihi')
#get  :name , 'hihi' , and call like
User.where(:name => 'hihi').first

其中 find_by_xxx 是build出來的快速向,而 where 是一般使用全能向(可能為 private ),轉一圈過去即可,這樣就可以寫得很漂亮,且debug也方便就是了