Istnieje wiele sposobów, jak to zrobić, a Twoja jest całkowicie ważna (choć ja osobiście wolę owijać metody klasy w oddzielne sprawdzanie bloku this), ale jako ludzie dodają do swoich modeli więcej logiki biznesowej i ślepo podążają za "chudymi kontrolerami, grubymi modelami", modele zamieniają się w kompletny bałagan.
Aby uniknąć tego bałaganu, że to dobry pomysł, aby wprowadzić obiektów usługowych, w danym przypadku byłoby tak:
class AverageWeatherData
class << self
def data(collection)
new(collection).data
end
end
def initialize(collection)
@collection = collection
end
def data
@collection.reduce do |avg, post|
# reduce goes through every post, each next iteration receives in avg a value of the last line of iteration
# do something with avg and post
end
# no need for explicit return, every line of Ruby code returns it's value
# so this method would return result of the reduce
# more on reduce: http://ruby-doc.org/core-2.0.0/Enumerable.html#method-i-reduce
end
end
Teraz można wywołać tej klasy bezpośrednio przekazując swoją kolekcję do niego. Ale można też proxy wywołanie tak:
def self.my_complicated_averaging_method
AverageWeatherData.data(@relation)
end
Zachęcam do Lear więcej tego podejścia czytając ten blog: http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
UPD
masz rację używając zmiennej instancji jest możliwym sposobem zepsucia elementów wewnętrznych (dodatkowo nie jest to interfejs publiczny i może się zmienić w przyszłości). Moja propozycja tutaj jest użycie metody scoped
. Zasadniczo zamień @relation
na scoped
.
Sprawdź ten przykład.Użyłem model z własnego projektu, aby pokazać, że to rzeczywiście działa
2.0.0p247 :001 > Tracking # just asking console to load this class before modifying it
# => Tracking(id: integer, action: string, cookie_id: string, ext_object_id: integer, created_at: datetime, updated_at: datetime)
2.0.0p247 :002 > class Tracking
2.0.0p247 :003?> def self.fetch_ids
2.0.0p247 :004?> scoped.map(&:id)
2.0.0p247 :005?> end
2.0.0p247 :006?> end
# => nil
2.0.0p247 :007 >
2.0.0p247 :008 > Tracking.where(id: (1..100)).fetch_ids
# Tracking Load (2.0ms) SELECT "trackings".* FROM "trackings" WHERE ("trackings"."id" BETWEEN 1 AND 100)
# => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
UPD
W Rails 4 scoped
jest przestarzała, więc jest to poprawne użycie all
.
all.map(&:id)
Moje pytanie było złe :(Byłem rea Lly sprawdzanie, czy "@relation" był dobry w użyciu lub czy istnieje lepszy sposób dodawania metod do kolekcji. Bardzo podoba mi się twój post. – Leon
Zrozumiałem teraz, sprawdź moją aktualizację. –
Daje mi błąd z szynami 4, ale znalazłem problem: https://github.com/voxdolo/decent_exposure/pull/73 Dzięki za pomoc i link do świetnego posta na blogu z kodeksu! – Leon