J'ai fait quelques lectures sur la façon d'étendre ActiveRecord: Classe de base afin que mes modèles aient des méthodes spéciales. Quelle est la manière simple de l'étendre (tutoriel étape par étape)?
J'ai fait quelques lectures sur la façon d'étendre ActiveRecord: Classe de base afin que mes modèles aient des méthodes spéciales. Quelle est la manière simple de l'étendre (tutoriel étape par étape)?
Réponses:
Il existe plusieurs approches:
Lisez la documentation ActiveSupport :: Concern pour plus de détails.
Créez un fichier appelé active_record_extension.rbdans le librépertoire.
require 'active_support/concern'
module ActiveRecordExtension
  extend ActiveSupport::Concern
  # add your instance methods here
  def foo
     "foo"
  end
  # add your static(class) methods here
  class_methods do
    #E.g: Order.top_ten        
    def top_ten
      limit(10)
    end
  end
end
# include the extension 
ActiveRecord::Base.send(:include, ActiveRecordExtension)
Créez un fichier dans le config/initializersrépertoire appelé extensions.rbet ajoutez la ligne suivante au fichier:
require "active_record_extension"Reportez-vous à la réponse de Toby .
Créez un fichier dans le config/initializersrépertoire appelé active_record_monkey_patch.rb.
class ActiveRecord::Base     
  #instance method, E.g: Order.new.foo       
  def foo
   "foo"
  end
  #class method, E.g: Order.top_ten        
  def self.top_ten
    limit(10)
  end
end
La célèbre citation sur les expressions régulières de Jamie Zawinski peut être réutilisée pour illustrer les problèmes associés à la correction des singes.
Certaines personnes, lorsqu'elles sont confrontées à un problème, pensent: «Je sais, j'utiliserai des patchs de singe.» Maintenant, ils ont deux problèmes.
La correction des singes est simple et rapide. Mais, le temps et les efforts économisés sont toujours récupérés dans le futur; avec intérêt composé. Ces jours-ci, je limite monkey patching pour prototyper rapidement une solution dans la console des rails.
requireaccéder au fichier à la fin de environment.rb. J'ai ajouté cette étape supplémentaire à ma réponse.
                    ImprovedActiveRecordet en hériter, lorsque vous utilisez module, vous mettez à jour la définition de la classe en question. J'utilisais l'héritage (à cause d'années d'expérience Java / C ++). Ces jours-ci, j'utilise principalement des modules.
                    Refinementsqui résout la plupart des problèmes liés au patching des singes ( yehudakatz.com/2010/11/30/ruby-2-0-refinements-in-practice ). Parfois, une fonctionnalité est là juste pour vous contraindre à tenter le destin. Et parfois vous le faites.
                    Vous pouvez simplement étendre la classe et utiliser simplement l'héritage.
class AbstractModel < ActiveRecord::Base  
  self.abstract_class = true
end
class Foo < AbstractModel
end
class Bar < AbstractModel
end
abstract_models. Où dois-je le mettre?
                    self.abstract_class = trueà votre AbstractModel. Rails reconnaîtra désormais le modèle comme un modèle abstrait.
                    AbstractModeldans la base de données. Qui savait qu'un simple passeur m'aiderait à SÉCHER les choses! (Je commençais à grincer des dents ... c'était mauvais). Merci Toby et Harish!
                    Vous pouvez également utiliser ActiveSupport::Concernet être plus idiomatique de base de Rails comme:
module MyExtension
  extend ActiveSupport::Concern
  def foo
  end
  module ClassMethods
    def bar
    end
  end
end
ActiveRecord::Base.send(:include, MyExtension)[Modifier] suite au commentaire de @daniel
Ensuite, tous vos modèles auront la méthode fooincluse comme méthode d'instance et les méthodes   ClassMethodsincluses comme méthodes de classe. Par exemple sur un FooBar < ActiveRecord::Basevous aurez: FooBar.baretFooBar#foo
http://api.rubyonrails.org/classes/ActiveSupport/Concern.html
InstanceMethodsc'est obsolète depuis Rails 3.2, mettez simplement vos méthodes dans le corps du module.
                    ActiveRecord::Base.send(:include, MyExtension)un initialiseur, puis cela a fonctionné pour moi. Rails 4.1.9
                    Avec Rails 4, le concept d'utilisation des préoccupations pour modulariser et sécher vos modèles a été mis en évidence.
Les préoccupations vous permettent essentiellement de regrouper le code similaire d'un modèle ou sur plusieurs modèles dans un seul module, puis d'utiliser ce module dans les modèles. Voici un exemple:
Considérez un modèle d'article, un modèle d'événement et un modèle de commentaire. Un article ou un événement a de nombreux commentaires. Un commentaire appartient à un article ou à un événement.
Traditionnellement, les modèles peuvent ressembler à ceci:
Modèle de commentaire:
class Comment < ActiveRecord::Base
  belongs_to :commentable, polymorphic: true
endModèle d'article:
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
endModèle d'événement
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
endComme nous pouvons le constater, il existe un élément de code important commun à la fois au modèle d'événement et d'article. En utilisant les préoccupations, nous pouvons extraire ce code commun dans un module distinct Commentable.
Pour cela, créez un fichier commentable.rb dans app / model / 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 
endEt maintenant, vos modèles ressemblent à ceci:
Modèle de commentaire:
    class Comment < ActiveRecord::Base
      belongs_to :commentable, polymorphic: true
    endModèle d'article:
class Article < ActiveRecord::Base
    include Commentable
endModèle d'événement
class Event < ActiveRecord::Base    
    include Commentable
endUn point que j'aimerais souligner lors de l'utilisation des préoccupations est que les préoccupations devraient être utilisées pour un groupement «basé sur un domaine» plutôt qu'un groupement «technique». Par exemple, un groupement de domaine est comme «Commentable», «Taggable» etc. Un groupement technique sera comme «FinderMethods», «ValidationMethods».
Voici un lien vers un article que j'ai trouvé très utile pour comprendre les préoccupations dans les modèles.
J'espère que la rédaction aidera :)
Étape 1
module FooExtension
  def foo
    puts "bar :)"
  end
end
ActiveRecord::Base.send :include, FooExtensionÉtape 2
# Require the above file in an initializer (in config/initializers)
require 'lib/foo_extension.rb'Étape 3
There is no step 3 :)Les rails 5 fournissent un mécanisme intégré d'extension ActiveRecord::Base.
Ceci est réalisé en fournissant une couche supplémentaire:
# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
  # put your extensions here
endet tous les modèles héritent de celui-là:
class Post < ApplicationRecord
endVoir par exemple cet article de blog .
Juste pour ajouter à ce sujet, j'ai passé un certain temps à essayer de tester de telles extensions (j'ai suivi la ActiveSupport::Concernvoie.)
Voici comment j'ai mis en place un modèle pour tester mes extensions.
describe ModelExtensions do
  describe :some_method do
    it 'should return the value of foo' do
      ActiveRecord::Migration.create_table :test_models do |t|
        t.string :foo
      end
      test_model_class = Class.new(ActiveRecord::Base) do
        def self.name
          'TestModel'
        end
        attr_accessible :foo
      end
      model = test_model_class.new(:foo => 'bar')
      model.some_method.should == 'bar'
    end
  end
endAvec Rails 5, tous les modèles sont hérités d'ApplicationRecord et cela donne un bon moyen d'inclure ou d'étendre d'autres bibliothèques d'extensions.
# app/models/concerns/special_methods.rb
module SpecialMethods
  extend ActiveSupport::Concern
  scope :this_month, -> { 
    where("date_trunc('month',created_at) = date_trunc('month',now())")
  }
  def foo
    # Code
  end
endSupposons que le module de méthodes spéciales doit être disponible sur tous les modèles, incluez-le dans le fichier application_record.rb. Si nous voulons l'appliquer à un ensemble particulier de modèles, incluez-le dans les classes de modèles respectives.
# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
  include SpecialMethods
end
# app/models/user.rb
class User < ApplicationRecord
  include SpecialMethods
  # Code
endSi vous souhaitez que les méthodes définies dans le module soient des méthodes de classe, étendez le module à ApplicationRecord.
# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
  extend SpecialMethods
endJ'espère que cela aidera les autres!
j'ai
ActiveRecord::Base.extend Foo::Bardans un initialiseur
Pour un module comme ci-dessous
module Foo
  module Bar
  end
end