Adding Class Level Enumerable Mixin in Ruby

I use postgres schemes in my Rails application, so there is no clear way to query all companies (for our own analytics). I would like to implement every method that iterates through all companies and switches the postgres schema accordingly.

I would like to call:

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

but I would also like to get some of the other Enumerable methods, such as collect , in this example, getting all the managers in all companies:

 Company.collect do |company| Users.managers end 

Currently it works fine for me

 class Company < ActiveRecord::Base # ... def self.each(&block) Company.all.each do |company| if Schemas.include? company.subdomain # this changes to that company schema so all queries are scoped Apartment::Database.switch company.subdomain yield company if block_given? end end end 

but how do I get an enumerated mixin at the class level, and not at the instance level.

ie, when include Enumerable is in a class, Enumerable methods are called as

 company = Company.new # which might iterate over the contents (users?) in a company company.collect {|u| u} 

but i want to call

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

and use

  Company.each 

It seems to me that the answer is obvious, but my metaprogramming foo is weak today.

+7
ruby ruby-on-rails
source share
2 answers

You can use include from 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] 

This template is used in the Ruby Prime class . Writing include to include the module seems cleaner for me, but you can probably use extend (see Uri Agassi's answer ).

It would be a difference if the included module relied on the included callback (no 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 

As noted by Green , you can define each directly inside the block: (and without using self )

 class Foo class << self include Enumerable def each # ... end end end 
+4
source share

To include Enumerable in a class, use extend

 class Company extend Enumerable def self.each yield 'a' yield 'b' yield 'c' end end Company.inject(&:+) # => "abc" 
+3
source share

All Articles