2014-07-09 14 views
7

Używam schematów postgres w mojej aplikacji Rails, więc nie ma jasnego sposobu na zapytanie wszystkich firm (dla naszych własnych analiz). Chciałbym zaimplementować każdą metodę, która sprawdza się we wszystkich firmach i odpowiednio zmienia schemat postgres.Dodawanie zmiennej mixin na poziomie klasy w Ruby

Chciałbym móc zadzwonić:

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

ale Chciałbym również, aby niektóre z innych metod, takich jak collect przeliczalny w tym przykładzie na uzyskanie wszystkich menedżerów we wszystkich spółkach:

Company.collect do |company| 
    Users.managers 
end 

Obecnie mam ten, który działa dobrze

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 

ale jak zrobić Dostaję Zmienna liczba na poziomie klasy, a nie na poziomie instancji.

czyli kiedy include Enumerable jest w klasie, metody przeliczalny sprawdzony jak

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

ale chcę zadzwonić

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

i mieć go używać

Company.each 

czuję podobna odpowiedź jest oczywista, ale moje metaprogramowane foo jest dziś słabe.

+0

To pytanie ma wiele rzeczy do nauki dla mnie .. –

+0

FYI: Początkowo próbowałem zrobić to z klasą ActiveRecord. Nie rób tego! Dostaniesz naprawdę dziwne niepowodzenia, gdy zadzwonisz na Company.count i nie będzie już wywoływał metody licznika ActiveRecord do zliczania wierszy baz danych. Pamiętaj, że moduł Enumerable jest naprawdę duży – ideasasylum

Odpowiedz

4

Można użyć include od wewnątrz 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] 

Ten wzór jest stosowany w Ruby Prime class. Zapisanie include w celu włączenia modułu wygląda na czystsze, ale prawdopodobnie możesz również użyć extend (patrz Uri Agassi's answer).

Nie byłoby różnicy, jeżeli włączone moduł oparł się na included zwrotnego (Enumerable nie robi):

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 

Jak noted by Зелёный można określić each prawo w bloku: (i bez użycia self)

class Foo 
    class << self 
    include Enumerable 

    def each 
     # ... 
    end 
    end 
end 
+1

Wygrasz mój Rubin teraz .. +1 –

+1

dlaczego nie zawijać wszystkich ('include' i' def') zarówno? –

+0

@ Зелёный dzięki za sugestię, zaktualizowałem swoją odpowiedź – Stefan

3

"Dołącz Enumerable" do wykorzystania w klasie extend

class Company 
    extend Enumerable 

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

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

Wygrasz mój Rubin .. :-) –

+0

Proste! Dziękuję – ideasasylum

Powiązane problemy