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

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

想实现一个像 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 方法

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