it-swarm.com.de

Verwendung von Bedenken in Rails 4

Der Standardprojektgenerator Rails 4 erstellt jetzt das Verzeichnis "concern" unter Controllern und Modellen. Ich habe einige Erklärungen zur Verwendung von Routing-Bedenken gefunden, aber nichts über Controller oder Modelle.

Ich bin mir ziemlich sicher, dass es mit dem aktuellen "DCI-Trend" in der Community zu tun hat und würde es gerne ausprobieren.

Die Frage ist, wie ich diese Funktion verwenden soll. Gibt es eine Konvention, wie die Benennungs-/Klassenhierarchie definiert werden soll, damit sie funktioniert? Wie kann ich ein Anliegen in ein Modell oder einen Controller aufnehmen?

615
yagooar

Also habe ich es selbst herausgefunden. Es ist eigentlich ein ziemlich einfaches, aber leistungsfähiges Konzept. Dies hat mit der Wiederverwendung von Code wie im folgenden Beispiel zu tun. Grundsätzlich besteht die Idee darin, allgemeine und/oder kontextspezifische Codestücke zu extrahieren, um die Modelle zu bereinigen und zu vermeiden, dass sie zu fett und unordentlich werden.

Als Beispiel stelle ich ein bekanntes Muster vor, das Taggable-Muster:

# 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

Wenn Sie also dem Produktbeispiel folgen, können Sie jeder gewünschten Klasse Taggable hinzufügen und deren Funktionalität freigeben.

Dies ist ziemlich gut erklärt durch DHH :

In Rails 4 werden wir Programmierer einladen, Bedenken mit den Standardverzeichnissen app/models/concern und app/controller/concern zu verwenden, die automatisch Teil des Ladepfads sind. Zusammen mit dem ActiveSupport :: Concern-Wrapper ist dies gerade genug Unterstützung, um diesen leichten Faktorisierungsmechanismus zum Leuchten zu bringen.

606
yagooar

Ich habe darüber gelesen, Modellbedenken zu verwenden, um fette Modelle sowie DRY Ihre Modellcodes zu verfeinern. Hier eine Erklärung mit Beispielen:

1) Modellcodes austrocknen

Betrachten Sie ein Artikelmodell, ein Ereignismodell und ein Kommentarmodell. Ein Artikel oder eine Veranstaltung enthält viele Kommentare. Ein Kommentar gehört entweder zum Artikel oder zum Ereignis.

Traditionell könnten die Modelle so aussehen:

Kommentar Modell:

class Comment < ActiveRecord::Base
  belongs_to :commentable, polymorphic: true
end

Artikel Modell:

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

Ereignismodell

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

Wie wir feststellen können, gibt es einen signifikanten Code, der sowohl für Event als auch für Article gilt. Mit Bedenken können wir diesen gemeinsamen Code in einem separaten Modul Commentable extrahieren.

Erstellen Sie dazu eine commentable.rb-Datei in app/models/concern.

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

Und jetzt sehen deine Modelle so aus:

Kommentar Modell:

class Comment < ActiveRecord::Base
  belongs_to :commentable, polymorphic: true
end

Artikel Modell:

class Article < ActiveRecord::Base
  include Commentable
end

Ereignismodell:

class Event < ActiveRecord::Base
  include Commentable
end

2) Skin-nizing Fat Models.

Betrachten Sie ein Ereignismodell. Eine Veranstaltung hat viele Teilnehmer und Kommentare.

In der Regel sieht das Ereignismodell folgendermaßen aus

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

Modelle mit vielen Assoziationen und ansonsten Tendenz, immer mehr Code anzusammeln und nicht mehr zu verwalten. Bedenken bieten eine Möglichkeit, Fettmodule zu verkleinern, um sie modularer und verständlicher zu gestalten.

Das obige Modell kann mit den folgenden Bedenken überarbeitet werden: Erstellen Sie eine attendable.rb- und eine commentable.rb -Datei im Ordner app/models/concern/event

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

Mit Concerns reduziert sich Ihr Ereignismodell auf

class Event < ActiveRecord::Base
  include Commentable
  include Attendable
end

* Wenn Sie Bedenken haben, ist es ratsam, eine domänenbasierte Gruppierung anstelle einer technischen Gruppierung zu wählen. Eine domänenbasierte Gruppierung ist wie "Kommentierbar", "Fotierbar", "Erreichbar". Technische Gruppierung bedeutet "Validierungsmethoden". 'FinderMethods' etc

371
Aaditi Jain

Es ist erwähnenswert, dass die Verwendung von Bedenken von vielen als schlechte Idee angesehen wird.

  1. wie dieser Typ
  2. nd dieses

Einige Gründe:

  1. Hinter den Kulissen geschieht dunkle Magie - Die Sorge ist das Patchen der include Methode, es gibt ein ganzes Abhängigkeitsbehandlungssystem - viel zu viel Komplexität für etwas, das ein triviales altes Ruby Mischungsmuster ist.
  2. Ihre Klassen sind nicht weniger trocken. Wenn Sie 50 öffentliche Methoden in verschiedene Module packen und diese einbeziehen, hat Ihre Klasse immer noch 50 öffentliche Methoden. Es ist nur so, dass Sie diesen Code-Geruch verbergen und sozusagen Ihren Müll in die Schubladen werfen.
  3. Codebase ist tatsächlich schwieriger zu navigieren mit all diesen Bedenken.
  4. Sind Sie sicher, dass alle Mitglieder Ihres Teams das gleiche Verständnis haben, was die Besorgnis wirklich ersetzen sollte?

Bedenken sind eine einfache Möglichkeit, sich in das Bein zu schießen, seien Sie vorsichtig mit ihnen.

94
Dr.Strangelove

Dieser Beitrag hat mir geholfen, Bedenken zu verstehen.

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

# app/models/concerns/shared/schedule.rb
module Shared::Schedule
  extend ActiveSupport::Concern
  ...
end
54
aminhotob

Ich habe gespürt, dass die meisten Beispiele hier die Kraft von module demonstrieren und nicht, wie ActiveSupport::Concern den Wert von module erhöht.

Beispiel 1: Mehr lesbare Module.

Also ohne Bedenken, wie typisch module sein wird.

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

Nach dem Refactoring mit 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

Sie sehen, dass Instanzmethoden, Klassenmethoden und eingeschlossene Blöcke weniger chaotisch sind. Bedenken werden sie für Sie angemessen injizieren. Das ist ein Vorteil der Verwendung von ActiveSupport::Concern.


Beispiel 2: Behandeln Sie Modulabhängigkeiten ordnungsgemäß.

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

In diesem Beispiel ist Bar das Modul, das Host wirklich benötigt. Aber da Bar eine Abhängigkeit von Foo hat, muss die Host -Klasse include Foo (aber warte, warum Host etwas über Foo wissen möchte? Gemieden werden?).

Bar fügt also überall Abhängigkeiten hinzu. Und ** Reihenfolge der Aufnahme ist auch hier wichtig. ** Dies erhöht die Komplexität/Abhängigkeit der großen Codebasis.

Nach dem Refactoring mit 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

Jetzt sieht es einfach aus.

Wenn Sie sich überlegen, warum wir die Abhängigkeit Foo nicht in das Modul Bar selbst einfügen können? Das wird nicht funktionieren, da method_injected_by_foo_to_Host_klass in Klassen injiziert werden muss, die Bar und nicht das Modul Bar selbst enthalten.

Quelle: Rails ActiveSupport :: Concern

38
shiva

Bei Bedenken Datei Dateiname.rb machen

Zum Beispiel möchte ich in meiner Anwendung, in der das Attribut create_by existiert, den Wert um 1 und den Wert 0 für updated_by aktualisieren

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

Wenn Sie Argumente in Aktion übergeben möchten

included do
   before_action only: [:create] do
     blaablaa(options)
   end
end

anschließend fügen Sie in Ihr Modell Folgendes ein:

class Role < ActiveRecord::Base
  include TestConcern
end
6
Sajjad Murtaza