Как использовать проблемы в Rails 4

По умолчанию генератор проекта Rails 4 создает каталог "проблемы" под контроллерами и моделями. Я нашел несколько объяснений о том, как использовать проблемы маршрутизации, но ничего о контроллерах или моделях.

Я уверен, что это связано с текущей тенденцией DCI в сообществе и хотел бы попробовать.

Вопрос в том, как я должен использовать эту функцию, существует ли соглашение о том, как определить иерархию именования/класса, чтобы заставить его работать? Как включить проблему в модель или контроллер?

540
задан 27 янв. '13 в 0:36
источник поделиться
6 ответов

Итак, я нашел это сам. На самом деле это довольно простая, но мощная концепция. Это связано с повторным использованием кода, как в приведенном ниже примере. В принципе, идея состоит в том, чтобы извлечь общие и/или контекстно-специфические фрагменты кода, чтобы очистить модели и избежать их слишком толстых и грязных.

В качестве примера, я поставлю один хорошо известный шаблон, 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:: Контейнер-обертка, его достаточная поддержка, чтобы сделать это светлый механизм факторингового механизма.

542
ответ дан 26 февр. '13 в 1:50
источник

Я читал об использовании проблем с моделью для моделирования жировых моделей кожи, а также для СУШКИ ваших кодов модели. Вот объяснение с примерами:

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" и т.д.

322
ответ дан 16 сент. '14 в 1:50
источник

Стоит упомянуть, что использование проблем считается плохой идеей для многих.

Некоторые причины:

  • Существует некоторая темная магия, происходящая за кулисами. Концерн исправляет метод include, существует целая система обработки зависимостей - слишком сложная задача для чего-то, что тривиально-хороший старый шаблон Ruby mixin.
  • Ваши классы не менее сухие. Если вы заполняете 50 общедоступных методов в разных модулях и включаете их, ваш класс по-прежнему имеет 50 общедоступных методов, это просто то, что вы скрываете этот запах кода, вроде как помещать ваш мусор в ящики.
  • Codebase на самом деле сложнее ориентироваться со всеми этими проблемами.
  • Вы уверены, что все члены вашей команды имеют такое же понимание, что на самом деле должно заменить беспокойство?

Обеспокоенность - это простой способ стрелять в ногу, быть осторожным с ними.

73
ответ дан 23 марта '15 в 17:11
источник

Этот пост помог мне понять проблемы.

# app/models/trader.rb
class Trader
  include Shared::Schedule
end

# app/models/concerns/shared/schedule.rb
module Shared::Schedule
  extend ActiveSupport::Concern
  ...
end
52
ответ дан 17 марта '13 в 22:20
источник

Я чувствовал, что большинство примеров демонстрируют силу 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:: Концерн

26
ответ дан 03 дек. '15 в 12:11
источник

В отношении 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
5
ответ дан 15 янв. '15 в 11:45
источник

Другие вопросы по меткам или Задайте вопрос