646 Stimmen

Wie man Bedenken in Rails 4 verwendet

Der Standard Rails 4 Projektgenerator erstellt nun das Verzeichnis "concerns" unter controllers und models. Ich habe einige Erklärungen darüber gefunden, wie man Routing-Belange verwendet, aber nichts über Controller oder Modelle.

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

Die Frage ist, wie soll ich diese Funktion verwenden, gibt es eine Konvention, wie die Benennung / Klassenhierarchie zu definieren, damit es funktioniert? Wie kann ich ein Anliegen in ein Modell oder einen Controller aufnehmen?

631voto

yagooar Punkte 15839

Also habe ich es selbst herausgefunden. Es ist eigentlich ein ziemlich einfaches, aber wirkungsvolles Konzept. Es hat mit der Wiederverwendung von Code zu tun, wie im folgenden Beispiel. Im Grunde genommen geht es darum, gemeinsame und/oder kontextspezifische Codeabschnitte zu extrahieren, um die Modelle zu bereinigen und zu vermeiden, dass sie zu dick und unübersichtlich werden.

Als Beispiel möchte ich ein bekanntes Muster anführen, 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

In Anlehnung an das Produktbeispiel können Sie Taggable zu jeder beliebigen Klasse hinzufügen und deren Funktionalität gemeinsam nutzen.

Dies wird ziemlich gut erklärt durch DHH :

In Rails 4 werden wir Programmierer zu uns einladen Standard app/models/concerns und app/controll die automatisch Teil des Ladepfades sind. Zusammen mit dem ActiveSupport::Concern Wrapper, ist das gerade genug Unterstützung um dieses leichtgewichtigen Factoring-Mechanismus glänzen zu lassen.

388voto

Aaditi Jain Punkte 6769

Ich habe gelesen, dass die Verwendung von Vorbildliche Bedenken um fette Modelle zu enthäuten und Ihre Modellcodes zu TROCKNEN. Hier ist eine Erklärung mit Beispielen:

1) Austrocknen von Modellcodes

Betrachten Sie ein Artikelmodell, ein Ereignismodell und ein Kommentarmodell. Ein Artikel oder ein Ereignis hat viele Kommentare. Ein Kommentar gehört entweder zu einem Artikel oder einem Ereignis.

Traditionell können die Modelle wie folgt 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

Ereignis-Modell

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 wichtigen Teil des Codes, der sowohl für Event als auch für Article gilt. Mit concerns können wir diesen gemeinsamen Code in einem separaten Modul Commentable extrahieren.

Erstellen Sie dazu eine kommentierbare.rb-Datei in app/models/concerns.

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 Ihre Modelle so aus:

Kommentar Modell:

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

Artikel Modell:

class Article < ActiveRecord::Base
  include Commentable
end

Ereignis-Modell:

class Event < ActiveRecord::Base
  include Commentable
end

2) Skin-nizing Fat Models.

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

Typischerweise könnte das Ereignismodell wie folgt aussehen

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 haben die Tendenz, immer mehr Code anzuhäufen und unüberschaubar zu werden. Concerns bieten eine Möglichkeit, fette Module zu verschlanken, so dass sie modularer und leichter zu verstehen sind.

Das obige Modell kann mit Hilfe von Bedenken wie folgt umstrukturiert werden: Erstellen Sie eine attendable.rb y commentable.rb Datei im Ordner app/models/concerns/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

kommentierbar.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

Wenn Sie nun Concerns verwenden, reduziert sich Ihr Ereignismodell auf

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

* Bei der Verwendung von Konzernen ist es ratsam, die Gruppierung auf der Basis von "Domänen" und nicht auf der Basis von "technischen" Gruppierungen vorzunehmen. Bereichsbasierte Gruppierungen sind z.B. 'Kommentierbar', 'Fotografierbar', 'Teilnehmbar'. Technische Gruppierung bedeutet 'ValidationMethods', 'FinderMethods' usw.

102voto

Dr.Strangelove Punkte 1475

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

  1. wie dieser Typ
  2. und diese

Einige Gründe:

  1. Hinter den Kulissen findet dunkle Magie statt - Concern ist Parcheando include Methode gibt es ein ganzes System zur Behandlung von Abhängigkeiten - viel zu viel Komplexität für etwas, das trivial ist gute alte Ruby mixin Muster.
  2. Ihr Unterricht ist nicht weniger trocken. Wenn Sie 50 öffentliche Methoden in verschiedenen Modulen unterbringen und einbinden, hat Ihre Klasse immer noch 50 öffentliche Methoden, Sie verstecken nur den Geruch des Codes, Sie packen den Müll in die Schubladen.
  3. Die Codebase ist tatsächlich schwieriger zu navigieren, wenn all diese Bedenken im Raum stehen.
  4. Sind Sie sicher, dass alle Mitglieder Ihres Teams dasselbe Verständnis davon haben, was wirklich ein Ersatzanliegen sein sollte?

Mit Sorgen kann man sich leicht selbst ins Bein schießen, seien Sie vorsichtig damit.

57voto

aminhotob Punkte 1046

Diese Stelle half mir, 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

52voto

Siva Punkte 7410

Meines Erachtens haben die meisten der hier aufgeführten Beispiele die Macht der module und nicht wie ActiveSupport::Concern einen Mehrwert für module .

Beispiel 1: Besser lesbare Module.

Also ohne Bedenken, wie eine typische 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, Instanzmethoden, Klassenmethoden und eingeschlossene Blöcke sind weniger chaotisch. Concerns wird sie entsprechend für Sie injizieren. Das ist ein Vorteil der Verwendung von ActiveSupport::Concern .


Beispiel 2: Anständiger Umgang mit Modulabhängigkeiten.

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 Bar ist das Modul, das Host wirklich braucht. Aber da Bar hat eine Abhängigkeit mit Foo die Host Klasse müssen include Foo (aber Moment mal, warum ist Host wissen wollen über Foo ? Kann dies vermieden werden?).

Also Bar schafft überall Abhängigkeiten. Und Auch hier spielt die Reihenfolge der Aufnahme eine Rolle. Dies erhöht die Komplexität/Abhängigkeit der riesigen Codebasis um ein Vielfaches.

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 fragen, warum können wir nicht auch Foo Abhängigkeit in Bar Modul selbst? Das wird nicht funktionieren, da method_injected_by_foo_to_host_klass müssen in eine Klasse injiziert werden, die Folgendes enthält Bar nicht an Bar Modul selbst.

Quelle: Rails ActiveSupport::Concern

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X