Как использовать проблемы в Rails 4
По умолчанию генератор проекта Rails 4 создает каталог "проблемы" под контроллерами и моделями. Я нашел несколько объяснений о том, как использовать проблемы маршрутизации, но ничего о контроллерах или моделях.
Я уверен, что это связано с текущей тенденцией DCI в сообществе и хотел бы попробовать.
Вопрос в том, как я должен использовать эту функцию, существует ли соглашение о том, как определить иерархию именования/класса, чтобы заставить его работать? Как включить проблему в модель или контроллер?
Ответы
Ответ 1
Итак, я нашел это сам. На самом деле это довольно простая, но мощная концепция. Это связано с повторным использованием кода, как в приведенном ниже примере. В принципе, идея состоит в том, чтобы извлечь общие и/или контекстно-специфические фрагменты кода, чтобы очистить модели и избежать их слишком толстых и грязных.
В качестве примера, я поставлю один хорошо известный шаблон, taggable pattern:
# app/models/product.rb
class Product
include Taggable
...
end
# app/models/concerns/taggable.rb
# notice that the file name has to match the module name
# (applying Rails conventions for autoloading)
module Taggable
extend ActiveSupport::Concern
included do
has_many :taggings, as: :taggable
has_many :tags, through: :taggings
class_attribute :tag_limit
end
def tags_string
tags.map(&:name).join(', ')
end
def tags_string=(tag_string)
tag_names = tag_string.to_s.split(', ')
tag_names.each do |tag_name|
tags.build(name: tag_name)
end
end
# methods defined here are going to extend the class, not the instance of it
module ClassMethods
def tag_limit(value)
self.tag_limit_value = value
end
end
end
Итак, следуя образ продукта, вы можете добавить Taggable в любой класс, который вы хотите, и поделиться своими возможностями.
Это довольно хорошо объясняется DHH:
В Rails 4 собирались пригласить программистов использовать проблемы с приложения по умолчанию/модели/проблемы по умолчанию и приложения/контроллеры/каталоги проблем которые автоматически входят в путь загрузки. Вместе с ActiveSupport:: Контейнер-обертка, его достаточная поддержка, чтобы сделать это светлый механизм факторингового механизма.
Ответ 2
Я читал об использовании проблем с моделью для моделирования жировых моделей кожи, а также для СУШКИ ваших кодов модели. Вот объяснение с примерами:
1) Сушка кодов моделей
Рассмотрим модель статьи, модель события и модель комментария. В статье или событии есть много комментариев. Комментарий принадлежит либо к статье, либо к событию.
Традиционно модели могут выглядеть так:
Модель комментария:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
Модель статьи:
class Article < ActiveRecord::Base
has_many :comments, as: :commentable
def find_first_comment
comments.first(created_at DESC)
end
def self.least_commented
#return the article with least number of comments
end
end
Модель события
class Event < ActiveRecord::Base
has_many :comments, as: :commentable
def find_first_comment
comments.first(created_at DESC)
end
def self.least_commented
#returns the event with least number of comments
end
end
Как мы можем заметить, существует значительный фрагмент кода, общий для обоих событий и статьи. Используя проблемы, мы можем извлечь этот общий код в отдельный модуль Commentable.
Для этого создайте файл commentable.rb в app/models/problems.
module Commentable
extend ActiveSupport::Concern
included do
has_many :comments, as: :commentable
end
# for the given article/event returns the first comment
def find_first_comment
comments.first(created_at DESC)
end
module ClassMethods
def least_commented
#returns the article/event which has the least number of comments
end
end
end
И теперь ваши модели выглядят так:
Модель комментария:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
Модель статьи:
class Article < ActiveRecord::Base
include Commentable
end
Модель события:
class Event < ActiveRecord::Base
include Commentable
end
2) Моделирование жировых отложений.
Рассмотрим модель события. В мероприятии много посетителей и комментариев.
Как правило, модель события может выглядеть так:
class Event < ActiveRecord::Base
has_many :comments
has_many :attenders
def find_first_comment
# for the given article/event returns the first comment
end
def find_comments_with_word(word)
# for the given event returns an array of comments which contain the given word
end
def self.least_commented
# finds the event which has the least number of comments
end
def self.most_attended
# returns the event with most number of attendes
end
def has_attendee(attendee_id)
# returns true if the event has the mentioned attendee
end
end
Модели со многими ассоциациями и в противном случае имеют тенденцию накапливать все больше и больше кода и становятся неуправляемыми. Озабоченность представляет собой способ сглаживания жировых модулей, что делает их более модульными и понятными.
Вышеупомянутая модель может быть реорганизована с использованием проблем, как показано ниже:
Создайте файл attendable.rb
и commentable.rb
в папке приложений/моделей/проблем/событий
attendable.rb
module Attendable
extend ActiveSupport::Concern
included do
has_many :attenders
end
def has_attender(attender_id)
# returns true if the event has the mentioned attendee
end
module ClassMethods
def most_attended
# returns the event with most number of attendes
end
end
end
commentable.rb
module Commentable
extend ActiveSupport::Concern
included do
has_many :comments
end
def find_first_comment
# for the given article/event returns the first comment
end
def find_comments_with_word(word)
# for the given event returns an array of comments which contain the given word
end
module ClassMethods
def least_commented
# finds the event which has the least number of comments
end
end
end
И теперь, используя Концерны, ваша модель событий сводится к
class Event < ActiveRecord::Base
include Commentable
include Attendable
end
* При использовании проблем целесообразно использовать группировку на основе "домен", а не "техническую" группировку. Группировка на основе домена похожа на "Commentable", "Photoable", "Attendable". Техническая группировка будет означать "ValidationMethods", "FinderMethods" и т.д.
Ответ 3
Стоит упомянуть, что использование проблем считается плохой идеей для многих.
Некоторые причины:
- Существует некоторая темная магия, происходящая за кулисами. Концерн исправляет метод
include
, существует целая система обработки зависимостей - слишком сложная задача для чего-то, что тривиально-хороший старый шаблон Ruby mixin.
- Ваши классы не менее сухие. Если вы заполняете 50 общедоступных методов в разных модулях и включаете их, ваш класс по-прежнему имеет 50 общедоступных методов, это просто то, что вы скрываете этот запах кода, вроде как помещать ваш мусор в ящики.
- Codebase на самом деле сложнее ориентироваться со всеми этими проблемами.
- Вы уверены, что все члены вашей команды имеют такое же понимание, что на самом деле должно заменить беспокойство?
Обеспокоенность - это простой способ стрелять в ногу, быть осторожным с ними.
Ответ 4
Этот пост помог мне понять проблемы.
# app/models/trader.rb
class Trader
include Shared::Schedule
end
# app/models/concerns/shared/schedule.rb
module Shared::Schedule
extend ActiveSupport::Concern
...
end
Ответ 5
Я чувствовал, что большинство примеров здесь демонстрируют мощь module
а не то, как ActiveSupport::Concern
добавляет ценность module
.
Пример 1. Более читаемые модули.
Так что без проблем это, как типичный module
будет.
module M
def self.included(base)
base.extend ClassMethods
base.class_eval do
scope :disabled, -> { where(disabled: true) }
end
end
def instance_method
...
end
module ClassMethods
...
end
end
После рефакторинга с ActiveSupport::Concern
.
require 'active_support/concern'
module M
extend ActiveSupport::Concern
included do
scope :disabled, -> { where(disabled: true) }
end
class_methods do
...
end
def instance_method
...
end
end
Вы видите, что методы экземпляра, методы класса и включенный блок менее грязны. Проблемы будут вводить их соответственно для вас. Это одно из преимуществ использования ActiveSupport::Concern
.
Пример 2: Изящная обработка зависимостей модуля.
module Foo
def self.included(base)
base.class_eval do
def self.method_injected_by_foo_to_host_klass
...
end
end
end
end
module Bar
def self.included(base)
base.method_injected_by_foo_to_host_klass
end
end
class Host
include Foo # We need to include this dependency for Bar
include Bar # Bar is the module that Host really needs
end
В этом примере Bar
- это модуль, который действительно нужен Host
. Но так как Bar
зависит от Foo
класс Host
должен include Foo
(но подождите, почему Host
хочет знать о Foo
можно ли этого избежать?).
Таким образом, Bar
добавляет зависимость везде, где он есть. И ** порядок включения также имеет значение здесь. ** Это добавляет много сложности/зависимости к огромной базе кода.
После рефакторинга с ActiveSupport::Concern
require 'active_support/concern'
module Foo
extend ActiveSupport::Concern
included do
def self.method_injected_by_foo_to_host_klass
...
end
end
end
module Bar
extend ActiveSupport::Concern
include Foo
included do
self.method_injected_by_foo_to_host_klass
end
end
class Host
include Bar # It works, now Bar takes care of its dependencies
end
Теперь это выглядит просто.
Если вы думаете, почему мы не можем добавить зависимость Foo
в сам модуль Bar
? Это не сработает, так как method_injected_by_foo_to_host_klass
должен быть введен в класс, включая Bar
не сам модуль Bar
.
Источник: Rails ActiveSupport :: Концерн
Ответ 6
В отношении make file filename.rb
Например, я хочу в своем приложении, где атрибут create_by существует обновление там значение на 1 и 0 для updated_by
module TestConcern
extend ActiveSupport::Concern
def checkattributes
if self.has_attribute?(:created_by)
self.update_attributes(created_by: 1)
end
if self.has_attribute?(:updated_by)
self.update_attributes(updated_by: 0)
end
end
end
после этого включите в свою модель:
class Role < ActiveRecord::Base
include TestConcern
end