ActiveRecord uzanan raylar :: Base


160

ActiveRecord: Base sınıfını genişletme hakkında bazı okumalar yaptım, böylece modellerim bazı özel yöntemlere sahip olacaktı. Genişletmenin kolay yolu nedir (adım adım eğitici)?


Ne tür uzantılar? Devam etmek için gerçekten daha fazlasına ihtiyacımız var.
jonnii

Yanıtlar:


336

Birkaç yaklaşım vardır:

ActiveSupport kullanma :: Endişe (Tercih Edilen)

Daha fazla bilgi için ActiveSupport :: Endişe belgelerini okuyun .

Adlı bir dosya oluşturun active_record_extension.rbiçinde libdizine.

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)

Adlı config/initializersdizinde bir dosya oluşturun ve dosyaya extensions.rbaşağıdaki satırı ekleyin:

require "active_record_extension"

Kalıtım (Tercih Edilen)

Toby'nin cevabına bakın .

Maymun yaması (Kaçınılması gerekir)

Adlı config/initializersdizinde bir dosya oluşturun 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

Jamie Zawinski'nin Düzenli ifadeler hakkındaki ünlü alıntısı , maymun yamasıyla ilgili sorunları göstermek için yeniden tasarlanabilir.

Bazı insanlar bir sorunla karşılaştıklarında “Biliyorum, maymun yama kullanacağım” diye düşünüyorlar. Şu an iki problemleri var.

Maymun yama kolay ve hızlıdır. Ancak, kazanılan zaman ve çaba her zaman gelecekte geri alınır; bileşik ilgi ile. Bu günlerde ray yama konsolundaki bir çözümü hızlı bir şekilde prototiplemek için maymun yamalarını sınırlandırıyorum.


3
requireSonunda dosyaya ihtiyacınız var environment.rb. Bu ekstra adımı yanıtıma ekledim.
Harish Shetty

1
@HartleyBrody bu sadece bir tercih meselesi. Kalıtım kullanırsanız, yeni bir giriş yapmalısınız ImprovedActiveRecordve bundan miras almalısınız, kullanırken module, söz konusu sınıfın tanımını güncellersiniz. Eskiden kalıtım kullandım (yıllarca Java / C ++ deneyiminin nedeni). Bugünlerde çoğunlukla modül kullanıyorum.
Harish Shetty

1
Bağlantınızın aslında bağlamsallaştırması ve insanların alıntıyı nasıl yanlış ve aşırı kullandığını göstermesi biraz ironik. Ama tüm ciddiyetle, "maymun yama" nın neden bu durumda en iyi yol olmadığını anlamakta güçlük çekiyorum. Birden fazla sınıfa eklemek istiyorsanız, bir modül açık bir şekilde gitmelidir. Ama eğer amacınız bir sınıfı genişletmekse, o zaman Ruby bu nedenle genişletme sınıflarını bu şekilde kolaylaştırmadı mı?
MCB

1
@MCB, Her büyük projede maymun yaması nedeniyle ortaya çıkan bir hata bulmak zor hakkında birkaç hikaye var. İşte Avdi'nin yamaların kötülükleri hakkında bir makalesi: devblog.avdi.org/2008/02/23/… . Ruby 2.0 Refinements, maymun yamasıyla ilgili sorunların çoğunu ele alan yeni bir özellik sunuyor ( yehudakatz.com/2010/11/30/ruby-2-0-refinements-in-practice ). Bazen sizi kaderi cezbetmeye zorlayan bir özellik vardır. Ve bazen yaparsınız.
Harish Shetty

1
@TrantorLiu Evet. Cevabı en son belgeleri yansıtacak şekilde güncelledim (class_methods, 2014'te tanıtıldı github.com/rails/rails/commit/… )
Harish Shetty

70

Sadece sınıfı genişletebilir ve kalıtım kullanabilirsiniz.

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

class Foo < AbstractModel
end

class Bar < AbstractModel
end

Bu fikri sevdim çünkü bunu yapmanın standart bir yolu ama ... Bir hata alıyorum Tablo 'moboolo_development.abstract_models' mevcut değil: ALANLARI GÖSTER abstract_models. Nereye koymalıyım?
xpepermint

23
Ekle self.abstract_class = trueBlogunuza AbstractModel. Raylar şimdi modeli soyut bir model olarak tanıyacak.
Harish Shetty

Vaov! Bunun mümkün olduğunu düşünmemiştim. Daha önce denedim ve ActiveRecord AbstractModelveritabanında aramaya başladığında vazgeçti . Kim basit bir pasör KURU şeyler bana yardımcı olacağını biliyordu! (Küfretmeye başladım ... kötüydü). Toby ve Harish teşekkürler!
dooleyo

Benim durumumda kesinlikle bunu yapmanın en iyi yolu: Model yeteneklerimi burada yabancı yöntemlerle genişletmiyorum, ancak uygulamamın benzer davranışsal nesneleri için ortak yöntemleri yeniden düzenliyorum. Kalıtım burada çok daha mantıklı. Hiçbir Orada yolu tercih ama 2 çözümler elde etmek istediğinize bağlı!
Augustin Riedinger

Rails4'te bu benim için geçerli değil. Abstract_model.rb dosyasını oluşturdum ve modeller dizinime koydum. modelin içinde self.abstract_class = true vardı Daha sonra benim diğer modelleri miras yaptı ... Kullanıcı <AbstractModel . Konsolda: Kullanıcı (bağlantı kurmak için 'User.connection' çağrısını yap)
Joel Grannas

21

Ayrıca ActiveSupport::Concerndeyim gibi daha fazla Rails çekirdek kullanabilirsiniz ve olabilir:

module MyExtension
  extend ActiveSupport::Concern

  def foo
  end

  module ClassMethods
    def bar
    end
  end
end

ActiveRecord::Base.send(:include, MyExtension)

@Daniel yorumundan sonra [Düzenle]

Daha sonra tüm modelleriniz foobir örnek yöntemi olarak dahil edilen metoda ve ClassMethodssınıf yöntemleri olarak dahil edilen metotlara sahip olacaktır. Örneğin bir FooBar < ActiveRecord::Basesahip olacaksınız: FooBar.barveFooBar#foo

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


5
InstanceMethodsRails 3.2'den beri kullanımdan kaldırıldığını unutmayın, yöntemlerinizi modül gövdesine koyun.
Daniel Rikowski

ActiveRecord::Base.send(:include, MyExtension)Bir başlatıcı yerleştirdim ve sonra bu benim için çalıştı. Raylar 4.1.9
6ft Dan

18

Rails 4 ile, modellerinizi modüler hale getirmek ve KURUTMAK için endişeleri kullanma kavramı öne çıkmıştır.

Endişeler temel olarak bir modelin benzer kodunu veya tek bir modülde birden çok model arasında gruplandırmanıza ve daha sonra bu modülü modellerde kullanmanıza izin verir. İşte bir örnek:

Bir Makale modeli, bir Olay modeli ve Yorum Modeli düşünün. Bir makalede veya bir etkinlikte çok sayıda yorum var. Bir yorum, makaleye veya etkinliğe aittir.

Geleneksel olarak, modeller şöyle görünebilir:

Yorum Modeli:

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

Makale Modeli:

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

Etkinlik Modeli

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

Fark edebileceğimiz gibi, hem Etkinlik hem de Makale Modeli için önemli bir kod parçası vardır. Endişeleri kullanarak, bu ortak kodu Commentable adlı ayrı bir modülde çıkarabiliriz.

Bunun için uygulama / model / endişelerde commentable.rb dosyası oluşturun.

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

Ve şimdi modelleriniz şöyle:

Yorum Modeli:

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

Makale Modeli:

class Article < ActiveRecord::Base
    include Commentable
end

Etkinlik Modeli

class Event < ActiveRecord::Base    
    include Commentable
end

Endişeleri kullanırken vurgulamak istediğim bir nokta, Endişelerin 'teknik' gruplama yerine 'alan adı tabanlı' gruplama için kullanılması gerektiğidir. Örneğin, bir etki alanı gruplaması 'Yorumlanabilir', 'Taggable' vb. Gibidir. Teknik tabanlı bir gruplandırma 'FinderMethods', 'ValidationMethods' gibi olacaktır.

İşte Modellerdeki endişeleri anlamak için çok yararlı bulduğum bir gönderinin bağlantısı .

Umarım yazma yardımcı olur :)


7

Aşama 1

module FooExtension
  def foo
    puts "bar :)"
  end
end
ActiveRecord::Base.send :include, FooExtension

Adım 2

# Require the above file in an initializer (in config/initializers)
require 'lib/foo_extension.rb'

Aşama 3

There is no step 3 :)

1
Sanırım 2. adım config / environment.rb dosyasına yerleştirilmelidir. Benim için çalışmıyor :(. Lütfen biraz daha yardım yazabilir misin? Thx.
xpepermint

5

Raylar 5, genişletmek için yerleşik bir mekanizma sağlar ActiveRecord::Base.

Bu, ek katman sağlanarak elde edilir:

# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
  # put your extensions here
end

ve tüm modeller bundan miras kalır:

class Post < ApplicationRecord
end

Bkz. Örneğin bu blog yazısı .


4

Sadece bu konuya eklemek için, bu tür uzantıları nasıl test edeceğimizi araştırdım ( ActiveSupport::Concernrotadan indim .)

Uzantılarımı test etmek için nasıl bir model ayarlayacağım.

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

4

Rails 5 ile, tüm modeller ApplicationRecord'dan devralınır ve diğer uzantı kitaplıklarını dahil etmek veya genişletmek için güzel bir yol sağlar.

# 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

Özel yöntemler modülünün tüm modellerde bulunması gerektiğini varsayalım, application_record.rb dosyasına ekleyin. Bunu belirli bir model seti için uygulamak istiyorsak, ilgili model sınıflarına dahil edin.

# 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

Modülde yöntemlerin sınıf yöntemi olarak tanımlanmasını istiyorsanız, modülü ApplicationRecord'a genişletin.

# app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
  extend SpecialMethods
end

Umarım başkalarına yardım eder!


0

Sahibim

ActiveRecord::Base.extend Foo::Bar

bir başlatıcıda

Aşağıdaki gibi bir modül için

module Foo
  module Bar
  end
end
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.