在Ruby中的类级别添加Enumerable mixin

时间:2014-07-09 10:55:23

标签: ruby-on-rails ruby

我在我的Rails应用程序中使用postgres架构,因此没有明确的方法可以跨所有公司进行查询(对于我们自己的分析)。我想实现迭代所有公司的每种方法,并适当地切换postgres模式。

我希望能够致电:

Company.each do |company|
  # do something in the context of each company
end

但我也希望获得一些其他可枚举方法,例如collect,这个例子可以让所有公司的所有经理都获得:

Company.collect do |company|
  Users.managers
end

目前我有这个效果很好

class Company < ActiveRecord::Base
  # ...

  def self.each(&block)
    Company.all.each do |company|
      if Schemas.include? company.subdomain
        # this changes to that company's schema so all queries are scoped
        Apartment::Database.switch company.subdomain

        yield company if block_given?
      end
    end
  end

但是如何在类级而不是实例级别获得Enumerable mixin。

即,当include Enumerable在类中时,可以像

一样调用Enumerable方法
company = Company.new
# which might iterate over the contents (users?) in a company
company.collect {|u| u} 

但我想打电话

# iterate over all companies and collect the managers
Company.collect {|c| User.managers} 

并使用

 Company.each

我觉得答案很明显,但我的元编程foo今天早上很弱。

2 个答案:

答案 0 :(得分:4)

您可以在include内使用class << self

class Foo

  class << self
    include Enumerable
  end

  def self.each
    yield 1
    yield 2
    yield 3
  end
end

Foo.map { |x| x * 2 } # => [2, 4, 6]

此模式用于Ruby的Prime class。编写include以包含模块对我来说看起来更干净,但您也可以使用extend(请参阅Uri Agassi's answer)。

如果所包含的模块依赖于included回调(Enumerable没有),则会有所不同:

module M
  def self.included(other)
    puts "included in #{other}"
  end
end

class Foo
  class << self
    include M
  end
end
#=> "included in #<Class:Foo>"

class Bar
  extend M
end
#=> nothing

作为noted by Зелёный,您可以在块内定义each :(并且不使用self

class Foo
  class << self
    include Enumerable

    def each
      # ...
    end
  end
end

答案 1 :(得分:3)

要在课堂上“包含Enumerable”,请使用extend

class Company
  extend Enumerable

  def self.each
    yield 'a'
    yield 'b'
    yield 'c'
  end
end

Company.inject(&:+)
# => "abc"