J'ai lu quelques articles sur la manière d'étendre ActiveRecord: classe de base afin que mes modèles disposent de méthodes spéciales. Quel est le moyen facile de l'étendre (tutoriel pas à pas)?
Il y a plusieurs approches:
Lisez la documentation ActiveSupport :: Concern pour plus de détails.
Créez un fichier appelé active_record_extension.rb
Dans le répertoire lib
.
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 répertoire config/initializers
Appelé extensions.rb
Et ajoutez la ligne suivante au fichier:
require "active_record_extension"
Voir Toby réponse .
Créez un fichier dans le répertoire config/initializers
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 fameuse citation sur les expressions rationnelles de Jamie Zawinski peut être réaffectée pour illustrer les problèmes associés à la correction de singe.
Certaines personnes, confrontées à un problème, se disent: "Je sais, je vais utiliser la correction de singe". Maintenant, elles ont deux problèmes.
La réparation de singe est facile et rapide. Mais, le temps et les efforts économisés sont toujours extraits dans le futur; avec intérêt composé. Ces jours-ci, je limite l'application des correctifs à un singe à la création rapide d'un prototype de solution dans la console Rails.
Vous pouvez simplement étendre la classe et simplement utiliser l'héritage.
class AbstractModel < ActiveRecord::Base
self.abstract_class = true
end
class Foo < AbstractModel
end
class Bar < AbstractModel
end
Vous pouvez aussi utiliser ActiveSupport::Concern
et être plus Rails idiomatique de base comme:
module MyExtension
extend ActiveSupport::Concern
def foo
end
module ClassMethods
def bar
end
end
end
ActiveRecord::Base.send(:include, MyExtension)
[Edit] suite au commentaire de @daniel
Tous vos modèles auront alors la méthode foo
incluse en tant que méthode d’instance et les méthodes de ClassMethods
incluses en tant que méthodes de classe. Par exemple. sur un FooBar < ActiveRecord::Base
Tu vas avoir: FooBar.bar
et FooBar#foo
http://api.rubyonrails.org/classes/ActiveSupport/Concern.html
Avec Rails 4, le concept d’utilisation des préoccupations pour modulariser et DRY vos modèles ont été mis en évidence.
Les problèmes vous permettent en principe de regrouper le code similaire d'un modèle ou de 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 beaucoup de commentaires. Un commentaire appartient à l'article ou à l'événement.
Traditionnellement, les modèles peuvent ressembler à ceci:
Commentaire modèle:
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 aux modèles d’événement et d’article. En utilisant des préoccupations, nous pouvons extraire ce code commun dans un module séparé Commentable.
Pour cela, créez un fichier commentable.rb dans app/model/concerne.
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:
Commentaire modèle:
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 lors de l’utilisation de Concerns est que Les préoccupations doivent être utilisées pour un groupement "basé sur un domaine" plutôt que pour un groupement "technique". Par exemple, un groupe de domaines s'apparente à 'Commentable', 'Taggable', etc. Un groupe technique s'apparente à 'FinderMethods', 'ValidationMethods'.
Voici un lien vers un message que j'ai trouvé très utile pour comprendre les préoccupations de 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
There is no step 3 :)
Rails 5 fournit un mécanisme intégré pour prolonger 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 de ApplicationRecord, ce qui permet à Nice 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 souhaitons l'appliquer à un ensemble de modèles particulier, 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 ça aidera les autres!
Juste pour ajouter à ce sujet, j'ai passé un certain temps à essayer de tester de telles extensions (j'ai descendu le ActiveSupport::Concern
route.)
Voici comment j'ai configuré 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
J'ai
ActiveRecord::Base.extend Foo::Bar
dans un initialiseur
Pour un module comme ci-dessous
module Foo
module Bar
end
end