Как динамически генерировать имена ассоциаций?
Я использую Ruby on Rails 3.2.2 и самоцвет Squeel. У меня есть следующие заявления, и я пытаюсь рефакторинг my_squeel_query
метод в модуле Mixin (так как он используется многими моими моделями):
# Note: 'article_comment_associations' and 'model_as_like_article_comment_associations'
# refer to database table names.
class Article < ActiveRecord::Base
def my_squeel_query
commenters.
.where{
article_comment_associations.article_id.eq(my{self.id}) & ...
}
end
end
class ModelAsLikeArticle < ActiveRecord::Base
def my_squeel_query
commenters.
.where{
model_as_like_article_comment_associations.article_id.eq(my{self.id}) & ...
}
end
end
Моя проблема в том, что я не могу рефакторинг article_comment_associations
а также model_as_like_article_comment_associations
заявления путем генерации динамического имени в модуле Mixin. То есть если бы это было String
Я мог бы динамически генерировать связанное имя, используя что-то вроде "#{self.class.to_s.singularize}_comment_associations"
в дальнейшем:
class Article < ActiveRecord::Base
include MyModule
end
class ModelAsLikeArticle < ActiveRecord::Base
include MyModule
end
module MyModule
def my_squeel_query
commenters.
.where{
# Note: This code doesn't work. It is just an sample.
"#{self.class.to_s.singularize}_comment_associations".article_id.eq(my{self.id}) & ...
}
end
end
Но, поскольку это не мой случай, я не могу "построить" имя и сделать my_squeel_query
быть "разделенным" между моделями.
Как я могу динамически генерировать имена ассоциаций, связанные с самоцветом Squeel? Стоит ли думать о рефакторинге по-другому? О чем вы советуетесь?
3 ответа
Поскольку DSL является instance_evaled, вы можете сказать что-то вроде:
def my_squeel_query
base = self
commenters.
.where{
# Note: This code does work. Because it's awesome.
__send__("#{base.class.to_s.singularize}_comment_associations").
article_id.eq(my{self.id})
}
end
Вы можете сделать это, если вы генерируете методы динамически. Module.included
Для этого предусмотрен метод:
module ModuleAsLikeArticle
def self.included(base)
base.send(:define_method, "#{base.to_s.singularize}_comment_associations") do
# ...
end
end
end
Это срабатывает, когда модуль импортируется с include
и позволяет создавать методы, специально предназначенные для этого.
Как примечание вы можете использовать base.name.underscore.singularize
для более удобочитаемого имени метода. По соглашению имена методов не должны содержать заглавных букв, особенно в качестве первого символа.
Обычные приложения типа Rails используют другой подход, но вместо этого определяют метод класса, который можно использовать для создания этих по требованию:
module ModuleAsLikeArticle
def has_comments
base.send(:define_method, "#{base.to_s.singularize}_comment_associations") do
# ...
end
end
end
Это будет использоваться так:
class ModelAsLikeArticle < ActiveRecord::Base
extend MyModule
has_comments
end
Так как метод не создан до has_comments
называется, вы можете смело продлевать ActiveRecord::Base
и затем вставьте соответствующий вызов во все классы, которым требуется эта функциональность.
Я думаю, что вы можете найти то, что вам нужно в Rails Reflection
класс (http://api.rubyonrails.org/classes/ActiveRecord/Reflection/ClassMethods.html), который, как говорится на странице, позволяет запрашивать классы ActiveRecord об их ассоциациях и объединениях.