Как добавить в конкретный Ruby-объект какое-нибудь свойство
January 11, 2010 in Ruby on RailsВообще задача такая:
class Book < ActiveRecord::Base has_many :ratings has_many :tags, :through => :ratings end class Rating < ActiveRecord::Base belongs_to :book belongs_to :tag validates_presence_of :score end
Надо возвращать для каждой книжки не только все ее теги, но и сумму оценок, поставленных для каждого тега.
Ущербные решения
Затык был не в том, как закодить саму выборку суммы, а в каком виде возвращать результат. Первая мысль – массивом хешей типа {:tag => tag, :score => score}
. Мало того, что это попахивает PHP, так с такими хешами еще и неудобно работать. Потом я хотел сделать total_score
методом Tag
:
class Tag < ActiveRecord::Base def total_score(book) return score_computed_some_way end end @book.tags.first.total_score(@book)
Но это тоже бредовая тавтология, как по мне.
Расширение Ruby-объектов на уровне объекта
Итак. Есть такой метод Object#instance_eval
– он позволяет запихнуть в объект практически все, что угодно. (Если бы что-то угодно было чем-то более сложным, чем возвращение объекта, можно было бы вынести его в модуль и подключать через Object#extend
)
# scores = {tag_id => score, ...} def inject_scores_into_tags(tags, scores) tags.each do |tag| tag.instance_eval do @total_score = scores[tag.id] def total_score @total_score end end end end
После этого у тегов появляется метод total_score
.
Понравился пост? Купи мне кофе