2012-03-01 36 views
5

讓我爲你想象它。ActiveRecord如何檢測鏈中的最後一個方法調用?

class Product < ActiveRecord::Base 
end 

Product.first.title 
#=> "My sample product" 

這裏沒什麼特別的。只是一個簡單的方法調用。現在看看下面的例子。

class Product < ActiveRecord::Base 
    def method_missing 
    end 
end 

Product.first.title 
#=> nil 

Product.first 
Product.first.title 
#=> "My sample product" 

這怎麼可能?在某種程度上,他們決定了方法鏈的結束並對此採取行動?至少這是我的理論。

任何人都可以解釋這種行爲嗎?

回答

7

您正在看到使用irb進行調查的工件。

當你這樣說:

> Product.first.title 
#=> nil 

method_missing將被稱爲懶加載的title方法,你會得到nil

當你這樣說:

> Product.first 

你有效地這樣做:

> p = Product.first; puts p.inspect 

的第一款產品實例將被加載,然後irb將調用inspect它,AR將增加訪問器方法。結果是產品現在有一個title方法。因此,這樣做:

> Product.first 
> Product.first.title 

不會叫你method_missing都一樣會有一個真正的title方法Product.first.title調用。

如果你再試一次這樣的:

> Product.first; nil 
> Product.first.title 

你會看到兩個nil秒。


至於鏈接去,ActiveRecord的並沒有真正檢測結束,它只是一些方法調用自然需要從數據庫中實際數據和一些不。

如果你打電話whereorder,或任何其他的查詢方法,你會得到一個ActiveRecord::Relation實例回來,你可以鏈中關係對象的詳細查詢方法和範圍。例如,where(其中的ActiveRecord ::關係由包括ActiveRecord::QueryMethods獲得)看起來是這樣的:

def where(opts, *rest) 
    return self if opts.blank? 

    relation = clone 
    relation.where_values += build_where(opts, rest) 
    relation 
end 

所以它只是使當前查詢的副本,增加了一些東西到副本,併爲您提供複印件背部。

如果你打電話firstlastto_aall,任何Enumerable方法(即調用each),...那麼你在問具體實例,ActiveRecord將不得不執行查詢來實現有問題的模型實例。例如,ActiveRecord::Relation#to_a看起來是這樣的:

def to_a 
    logging_query_plan do 
    exec_queries 
    end 
end 

all比來包裹to_a多一點。

的ActiveRecord並不真正知道哪裏鏈的末端,它只是不從數據庫中加載任何東西,直到它有,所以你告訴它說鏈端出去,並取回了我一些數據

+0

我認爲它確實檢測到鏈條的末端:它是您無法取回查找代理對象的地方! – phoet 2012-03-01 19:58:36

+0

這不是問題所在。問題在於''''''''''''''''''當''''''method_missing''在模型中被使用時'''Product.first''被調用直到''Product.first.title''返回'''nil'''。一旦你調用'''Product.first'''標題返回'''我的示例博客''''。 – user544941 2012-03-01 20:22:39

+0

@ user544941:啊,我明白他在問什麼:替換或繞過他的'method_missing'。 – 2012-03-01 20:41:36

相關問題