Czytałem trochę o tym, jak rozszerzyć klasę ActiveRecord: Base, aby moje modele miały specjalne metody. Jak łatwo go rozszerzyć (samouczek krok po kroku)?
Czytałem trochę o tym, jak rozszerzyć klasę ActiveRecord: Base, aby moje modele miały specjalne metody. Jak łatwo go rozszerzyć (samouczek krok po kroku)?
Odpowiedzi:
Istnieje kilka podejść:
Przeczytaj dokumentację ActiveSupport :: Concern, aby uzyskać więcej informacji.
Utwórz plik o nazwie active_record_extension.rb
w lib
katalogu.
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)
Utwórz plik w config/initializers
katalogu o nazwie extensions.rb
i dodaj następujący wiersz do pliku:
require "active_record_extension"
Zapoznaj się z odpowiedzią Toby'ego .
Utwórz plik w config/initializers
katalogu o nazwie 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
Słynny cytat Jamiego Zawinskiego o wyrażeniach regularnych może zostać wykorzystany ponownie, aby zilustrować problemy związane z małpami łatania.
Niektórzy ludzie, gdy stają przed problemem, myślą: „Wiem, użyję małpiego łatania”. Teraz mają dwa problemy.
Łatanie małp jest łatwe i szybkie. Jednak zaoszczędzony czas i wysiłek są zawsze wycofywane w przyszłości; z odsetkami składanymi. Obecnie ograniczam patchowanie małp do szybkiego prototypowania rozwiązania w konsoli rails.
require
pliku na końcu environment.rb
. Dodałem ten dodatkowy krok do mojej odpowiedzi.
ImprovedActiveRecord
i dziedziczyć po tym, kiedy używasz module
, aktualizujesz definicję danej klasy. Kiedyś korzystałem z dziedziczenia (przyczyna wieloletniego doświadczenia w Javie / C ++). Obecnie najczęściej używam modułów.
Refinements
która rozwiązuje większość problemów z małpami łatania ( yehudakatz.com/2010/11/30/ruby-2-0-refinements-in-practice ). Czasami jest po to, by zmusić cię do kuszenia losu. A czasami to robisz.
Możesz po prostu rozszerzyć klasę i po prostu użyć dziedziczenia.
class AbstractModel < ActiveRecord::Base
self.abstract_class = true
end
class Foo < AbstractModel
end
class Bar < AbstractModel
end
abstract_models
. Gdzie mam to położyć?
self.abstract_class = true
do swojego AbstractModel
. Railsy będą teraz rozpoznawać model jako model abstrakcyjny.
AbstractModel
w bazie danych. Kto by pomyślał, że prosty seter pomoże mi SUCHO! (Zaczynałem się wzdychać ... było źle). Dzięki Toby i Harish!
Możesz także używać ActiveSupport::Concern
i być bardziej idiomatycznym rdzeniem Rails, takim jak:
module MyExtension
extend ActiveSupport::Concern
def foo
end
module ClassMethods
def bar
end
end
end
ActiveRecord::Base.send(:include, MyExtension)
[Edytuj] po komentarzu od @daniel
Następnie wszystkie modele będą miały metodę foo
dołączoną jako metoda instancji, a metody w niej ClassMethods
zawarte jako metody klas. Np. FooBar < ActiveRecord::Base
Będziesz mieć: FooBar.bar
iFooBar#foo
http://api.rubyonrails.org/classes/ActiveSupport/Concern.html
InstanceMethods
jest to przestarzałe od Railsów 3.2, po prostu umieść swoje metody w treści modułu.
ActiveRecord::Base.send(:include, MyExtension)
inicjalizator i wtedy to zadziałało. Szyny 4.1.9
W przypadku Rails 4, koncepcja wykorzystania zagadnień do modularyzacji i OSUSZANIA modeli była najważniejsza.
Obawy w zasadzie pozwalają na grupowanie podobnego kodu modelu lub w wielu modelach w jednym module, a następnie używanie tego modułu w modelach. Oto przykład:
Rozważmy model artykułu, model zdarzenia i model komentarzy. Artykuł lub wydarzenie ma wiele komentarzy. Komentarz należy do artykułu lub wydarzenia.
Tradycyjnie modele mogą wyglądać tak:
Model komentarzy:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
Model artykułu:
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
Model zdarzenia
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
Jak możemy zauważyć, istnieje znaczący fragment kodu wspólny zarówno dla modelu zdarzenia, jak i artykułu. Korzystając z obaw, możemy wyodrębnić ten wspólny kod w oddzielnym module.
W tym celu utwórz plik commentable.rb w 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
A teraz twoje modele wyglądają tak:
Model komentarzy:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
Model artykułu:
class Article < ActiveRecord::Base
include Commentable
end
Model zdarzenia
class Event < ActiveRecord::Base
include Commentable
end
Jedną kwestią, którą chciałbym podkreślić podczas używania obaw, jest to, że obawy powinny być używane do grupowania „na podstawie domeny”, a nie grupowania „technicznego”. Na przykład grupowanie domen jest takie jak „Możliwość komentowania”, „Możliwość tagowania” itp. Grupowanie techniczne będzie wyglądać jak „FinderMethods”, „ValidationMethods”.
Oto link do posta, który okazał się bardzo przydatny do zrozumienia problemów w modelach.
Mam nadzieję, że napisanie pomoże :)
Krok 1
module FooExtension
def foo
puts "bar :)"
end
end
ActiveRecord::Base.send :include, FooExtension
Krok 2
# Require the above file in an initializer (in config/initializers)
require 'lib/foo_extension.rb'
Krok 3
There is no step 3 :)
Rails 5 zapewnia wbudowany mechanizm przedłużania ActiveRecord::Base
.
Osiąga się to poprzez zapewnienie dodatkowej warstwy:
# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
# put your extensions here
end
i wszystkie modele dziedziczą po nim:
class Post < ApplicationRecord
end
Zobacz np. Ten post na blogu .
Żeby dodać do tego tematu, chwilę zastanawiałem się, jak przetestować takie rozszerzenia (poszedłem dalej ActiveSupport::Concern
).
Oto jak skonfigurowałem model do testowania moich rozszerzeń.
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
W Railsach 5 wszystkie modele są dziedziczone z ApplicationRecord i daje to dobry sposób na dołączanie lub rozszerzanie innych bibliotek rozszerzeń.
# 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
Załóżmy, że moduł metod specjalnych musi być dostępny we wszystkich modelach, dołącz go do pliku application_record.rb. Jeśli chcemy zastosować to do określonego zestawu modeli, uwzględnij go w odpowiednich klasach modeli.
# 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
Jeśli chcesz mieć metody zdefiniowane w module jako metody klasowe, rozszerz moduł do ApplicationRecord.
# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
extend SpecialMethods
end
Mam nadzieję, że pomoże to innym!
mam
ActiveRecord::Base.extend Foo::Bar
w inicjatorze
Dla modułu jak poniżej
module Foo
module Bar
end
end