Rails prolongeant ActiveRecord :: Base

J’ai fait quelques recherches sur l’extension d’ActiveRecord: Classe de base, mes modèles auraient donc des méthodes spéciales. Quel est le moyen facile de l’étendre (tutoriel étape par étape)?

Il y a plusieurs approches:

Utiliser ActiveSupport :: Concern (Préféré)

Lisez la documentation ActiveSupport :: Concern pour plus de détails.

Créez un fichier appelé active_record_extension.rb dans le répertoire lib .

 module ActiveRecordExtension extend ActiveSupport::Concern # add your instance methods here def foo "foo" end # add your static(class) methods here class_methods do #Eg: 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 répertoire config/initializers appelé extensions.rb et ajoutez la ligne suivante au fichier:

 require "active_record_extension" 

Héritage (préféré)

Reportez-vous à la réponse de Toby.

Patch de singe (à éviter)

Créez un fichier dans le répertoire config/initializers appelé active_record_monkey_patch.rb .

 class ActiveRecord::Base #instance method, Eg: Order.new.foo def foo "foo" end #class method, Eg: Order.top_ten def self.top_ten limit(10) end end 

La fameuse citation sur les expressions régulières de Jamie Zawinski peut être réutilisée pour illustrer les problèmes associés au patch-singe.

Certaines personnes, confrontées à un problème, pensent «Je sais, je vais utiliser le patch de singe». Maintenant, elles ont deux problèmes.

Le patch de singe est simple et rapide. Cependant, le temps et les efforts enregistrés sont toujours extraits dans le futur. avec intérêt composé. Ces jours-ci, je limite les correctifs de singe pour prototyper rapidement une solution dans la console des rails.

Vous pouvez simplement étendre la classe et simplement utiliser l’inheritance.

 class AbstractModel < ActiveRecord::Base self.abstract_class = true end class Foo < AbstractModel end class Bar < AbstractModel end 

Vous pouvez également utiliser ActiveSupport::Concern et être plus idiomatique du kernel Rails comme:

 module MyExtension extend ActiveSupport::Concern def foo end module ClassMethods def bar end end end ActiveRecord::Base.send(:include, MyExtension) 

[Edit] en suivant le commentaire de @daniel

Ensuite, tous les modèles auront la méthode foo incluse comme méthode d’instance et les méthodes de ClassMethods comme méthodes de classe. Par exemple, sur un FooBar < ActiveRecord::Base vous aurez: FooBar.bar et FooBar#foo

http://api.rubyonrails.org/classes/ActiveSupport/Concern.html

Avec Rails 4, le concept d’utilisation des problèmes de modularisation et de séchage de vos modèles a été mis en évidence.

Les préoccupations vous permettent essentiellement de regrouper un code similaire d’un modèle ou entre plusieurs modèles dans un seul module, puis d’utiliser ce module dans les modèles. Voici un exemple:

Considérons un modèle d’article, un modèle d’événement et un modèle de commentaire. Un article ou un événement comporte 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 end 

Modè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 end 

Modè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 end 

Comme nous pouvons le constater, il existe un élément important de code commun à la fois au modèle d'événement et au modèle 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 / 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 

Et maintenant, vos modèles ressemblent à ceci:

Modèle de commentaire:

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

Modèle d'article:

 class Article < ActiveRecord::Base include Commentable end 

Modèle d'événement

 class Event < ActiveRecord::Base include Commentable end 

Un point que je voudrais souligner en utilisant Préoccupations est que les Préoccupations doivent être utilisées pour un regroupement «par domaine» plutôt que par un regroupement «technique». Par exemple, un regroupement de domaines est comme "Commentable", "Taggable", etc. Un regroupement basé sur des techniques sera similaire à "FinderMethods", "ValidationMethods".

Voici un lien vers un article que j'ai trouvé très utile pour comprendre les problèmes dans les modèles.

J'espère que la rédaction aide 🙂

É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 :) 

Juste pour append à ce sujet, j’ai passé du temps à déterminer comment tester ces extensions (j’ai suivi la ActiveSupport::Concern ).

Voici comment je configure 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.ssortingng :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 end 

Rails 5 fournit un mécanisme intégré pour l’extension d’ 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 end 

et tous les modèles héritent de celui-là:

 class Post < ApplicationRecord end 

Voir par exemple cet article de blog .

Avec Rails 5, tous les modèles sont hérités d’ApplicationRecord et permettent d’inclure ou d’étendre d’autres bibliothèques d’extension.

 # 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 end 

Supposons 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 appliquer cela à un ensemble particulier de modèles, alors 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 end 

Si 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 end 

J'espère que cela aidera les autres!

j’ai

 ActiveRecord::Base.extend Foo::Bar 

dans un initialiseur

Pour un module comme ci-dessous

 module Foo module Bar end end