Skip to content

Instantly share code, notes, and snippets.

@42thcoder
Last active August 29, 2015 14:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 42thcoder/83aff68e4bdcd8f3f75c to your computer and use it in GitHub Desktop.
Save 42thcoder/83aff68e4bdcd8f3f75c to your computer and use it in GitHub Desktop.

scope 该写在哪里

old days

要把 model 中的代码拿出来, common pattern 是这样的:

module MyModule
  def self.included(base)
    base.extend ClassMethods
    base.class_eval do
      scope :disabled, -> { where(disabled: true) }
    end
  end

  module ClassMethods
    ...
  end
end

class MyClass
  include MyModule
end

解析

钩子方法 included

module MyModule
  def self.included(base)
    p 'hook'
  end
end

class MyClass
  include MyModule    # 打印 'hook'
end
  • included 是一个钩子方法, 当该 module 被 include 进某个类时, 会自动执行;
  • included 接收参数(base), 值为 include 该 module 的类.
thoughts

为什么要在 module 中用 def self.foo 的方式定义方法.

这样做之后, 这个方法可以被当做模块方法 调用, MyModule.foo, 不加 self 是不行的. module 是 class 的超类, self 的这种行为两者保持一致.

这个 module 被 mix-in 时, 该方法不会成为类的实例方法. 如果一个类 include 一个 module, 这个类会继承这个 module(实际上是这个 module 的代理类), 因此module 中定义的方法会成为类的实例方法. 而 module 中定义的 self.test 方法会成为什么样的方法呢? 会成为模块的方法, 但是不会成为代理类的方法, 所以这个类也无法获取的这个方法(这点存疑) 这里的关键问题在于, module 有单件类么, self.test 到底存在于哪里

在 module 里面写 extend self 会起到相同的作用.

http://stackoverflow.com/questions/11550213/ruby-module-function-vs-including-module

有个问题没搞清楚呢: 在 module 定义中里面 self 表示什么; 在其他地方, self 都表示当前对象; module 本身就是对象啊, 所以是给当前 module 增加了方法, 跟上文的解释是一致的. 如果是类的话, self 定义的方法, 其实是给类的单件类添加了实例方法; module 有没有单件类呢? 还是说只有代理类?

extend

module MyModule
  def self.included(base)
    base.extend ClassMethods
  end

  module ClassMethods
    ...
  end
end

class MyClass
  include MyModule
end

在钩子方法内部, 调用了base.extend ClassMethods, ClassMethods 中定义的方法就成为了 Myclass 的类方法.这样等同于

module MyModule
  ...

  module ClassMethods
    ...
  end
end

class MyClass
  include MyModule
  extend MyModule::ClassMethods
end

但是很明显使用钩子方法好一些,这样在类中 include 该 module 时, 自动 extend 了定义类方法的 module.

scope

module MyModule
  def self.included(base)
    base.class_eval do
      scope :disabled, -> { where(disabled: true) }
    end
  end
 
end

class MyClass
  include MyModule
end

这样实现的效果是

class MyClass
  scope :disabled, -> { where(disabled: true) }
end

但是为什么呢?

第一步理解 class_eval class_eval 是在目标类的上下文中执行一个 block, 在 block 中定义的方法会成为目标类的实例方法, 很好理解.

MyClass.class_eval do
  def hi
    p 'hi'
  end
end

MyClass.new.hi => 打印 hi

第二部理解 scope scope 是一种类宏(class macro).

类宏是可以在类定义中使用的普通方法, 看起来很像是关键字, 本质是在类的单例类中定义的实例方法.

所以说在调用类宏时, 应该在类的上下文中调用. problem solved, right?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment