新手问题 Methods chain: Rails 中像 Scope 这样使用函数的方式是怎么实现的?

zhenjunluo · 2012年09月06日 · 最后由 zhenjunluo 回复于 2012年09月10日 · 5661 次阅读

想实现一个像 rails 中 scope 这样的功能,自己定义的函数可以连接起来调用。代码如下:

# class My_string
#   attr_accessor :m_string
#   def initialize(string)
#     self.m_string = string
#   end

#   def part(b,e)
#     m_string[b,e]
#     self
#   end

#   def limit(length)
#     m_string[0..length]
#     self
#   end
# end

# s = My_string.new("hello world")
# s.part(1,5).limit(3)    ##返回结果为对象,不是所希望的结果

Rails中,用Scope定义了3个函数,假设分别为AB C
然后我们就可以这样使用Model.A.B.C;也可单独使用,比如
Model.A
Model.B
Model.C

Model.A.B.C这样的函数链的调用是怎么实现的,谁能讲下原理,或者简单的写段代码阐述一下也可以?

求高手指点?

只想说 LZ 你好蛋疼。。

我觉得可以参考 Rails 源码,生成过一个中间的 Relation 对象

#3 楼 @chucai Relation 和 ruby 中 String 的表现很像

str = String.new("hello")     ### 结果为"hello",为什么不是<String:0x8b4f390>这种形式呢?
s = My_string.new("hello")  ### 结果为<My_string:0x8b4f390 @m_string="hello">

"dddasfasd"[1,5][0,3] 这样写不行么?

#5 楼 @jjym 我只是用字符串举个例子,我的目的是把自定义的函数连起来调用,测试中也有很多这样的用法,比如:

 describe "responding to GET index" do

    it "should expose all comms as @comms" do
      Comm.should_receive(:find).with(:all).and_return([mock_comm])
      get :index
      assigns[:comms].should == [mock_comm]
    end
# ...

Comm.should_receive(:find).with(:all).and_return([mock_comm]) 这样是怎么实现的?

class String                                                     
  def part(b ,e)                                                 
    String.new self[b, e]                                        
  end                                                            

  def limit(length)                                              
    String.new self[0 .. length]                                 
  end                                                            
end                                                              

s = "Hell, world!"                                               
puts s.part(1 ,5).limit(3)                      

#7 楼 @reus 假设类名不是 String 呢?

#7 楼 @reus Comm.should_receive(:find).with(:all).and_return([mock_comm]) 我是想知道像这样的方式是怎么实现的

class MyString < String                                                                                                           
  def part(b ,e)                                                                                                                  
    MyString.new self[b, e]                                                                                                       
  end                                                                                                                             

  def limit(length)                                                                                                               
    MyString.new self[0 .. length]                                                                                                
  end                                                                                                                             
end                                                                                                                               

s = MyString.new "Hell, world!"                                                                                                   
puts s.part(1 ,5).limit(3)                              

#6 楼 @zhenjunluo 我看不明白这段代码的意思 反正返回的不是 self,就是一个新对象。所以如果想返回字符串,那就让返回的对象是 String 类或者子类

#11 楼 @reus 很抱歉,这段代码举了个例子,可能不好!我想弄明白像 Rails 中用 Scope 定义的函数,假设为 A B C, 让后我就可以这样使用 Model.A.B.C,这是怎么实现的!知道的话讲下原理啊

#12 楼 @zhenjunluo 明白了。scope 就是动态定义一个方法,这个方法先执行 lambda 的操作,然后返回 self,于是就可以链式调用了。最后也是要用 all/count 之类的方法来获得计算结果或者内部状态的。我不熟 ruby,不知道要怎么写

#13 楼 @reus 这样虽然可以,可返回 self,需要的是返回处理结果而不是 self 本身

如果你想要的处理结果和 self 类型不同,那必然需要多一个调用。ruby 又没有 perl 那样根据调用 context 返回不同值的机制

#16 楼 @reus 嗯,之前就是觉得既要返回 self 又要返回处理结果!perl 的话,以后在接触了

链式调用的原理就是返回 self,这点没错。Rails 的 scope 也不例外。说两点:

  1. Rails 的 scope 返回的是 Relation 对象,不是 model 类对象。链式调用返回的都是 Relation
  2. model 类对象和 Relation 对象都可以相应 scope 定义的方法。具体的就没研究过了。

会不会是这样:模型上调用 scope 方法,返回的就是一个关系对象,同时模型会成为关系对象的代理,模型的某个实例变量也会保存这个 scope 的查询条件;如果在关系对象是调用 scope 方法,依然返回 scope;否则,就调用它的代理的对应方法(也就是模型的方法,可以指定特定方法或者在关系对象的 method_missing 中捕捉),这些方法会用到那些查询条件。

看了 rails2 中的 named_scope,似乎是用的上面的方法。不过有些地方没看懂,仅供参考。

#19 楼 @zhangyuan rails2 和 rails3 中实现方法稍有差别,但他们都重写了 inspect 方法

需要 登录 后方可回复, 如果你还没有账号请 注册新账号