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.rb
dans le lib
ré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/initializers
répertoire appelé extensions.rb
et ajoutez la ligne suivante au fichier:
require "active_record_extension"
Reportez-vous à la réponse de Toby .
Créez un fichier dans le config/initializers
ré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.
require
accéder au fichier à la fin de environment.rb
. J'ai ajouté cette étape supplémentaire à ma réponse.
ImprovedActiveRecord
et 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.
Refinements
qui 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.
AbstractModel
dans 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::Concern
et ê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 foo
incluse comme méthode d'instance et les méthodes ClassMethods
incluses comme méthodes de classe. Par exemple sur un FooBar < ActiveRecord::Base
vous aurez: FooBar.bar
etFooBar#foo
http://api.rubyonrails.org/classes/ActiveSupport/Concern.html
InstanceMethods
c'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
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 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
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 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
end
et tous les modèles héritent de celui-là:
class Post < ApplicationRecord
end
Voir 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::Concern
voie.)
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
end
Avec 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
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 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
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