Rails Extended ActiveRecord:: Base

Czytałem trochę o tym, jak rozszerzyć ActiveRecord: Base class, aby moje modele miały jakieś specjalne metody. Jaki jest łatwy sposób na jego rozszerzenie (samouczek krok po kroku)?

Author: BenMorel, 2010-02-24

9 answers

Istnieje kilka podejść:

Używanie Activesupport:: Concern (Preferowane)

Więcej informacji można znaleźć w dokumentacji ActiveSupport::Concern.

Utwórz plik o nazwie active_record_extension.rb w katalogu 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)

Utwórz plik w katalogu config/initializers o nazwie extensions.rb i dodaj do pliku następujący wiersz:

require "active_record_extension"

Dziedziczenie (Preferowane)

Zobacz odpowiedź Toby ' ego .

Monkey patching (powinno być unika)

Utwórz plik w katalogu config/initializers 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 o wyrażeniach regularnych autorstwa Jamie Zawinski może zostać ponownie wykorzystany, aby zilustrować problemy związane z łataniem małp.

Niektórzy ludzie, w obliczu problemu, myślą: "wiem, użyję łatanie małp."Teraz mają dwa problemy.

Łatanie małp jest łatwe i szybkie. Ale zaoszczędzony czas i wysiłek jest zawsze odzyskiwany kiedyś w przyszłości; z odsetkami złożonymi. W dzisiejszych czasach ograniczam łatanie małp do szybkiego prototypowania rozwiązania w konsoli rails.
 337
Author: Harish Shetty,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-27 00:35:22

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
 70
Author: Toby Hede,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-25 21:33:59

Możesz również użyć 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 @ daniel

Wtedy wszystkie twoje modele będą miały metodę foo dołączoną jako metoda instancji, a metody w ClassMethods jako metody klasowe. Np. na FooBar < ActiveRecord::Base będziesz miał: FooBar.bar i FooBar#foo

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

 21
Author: nikola,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-09 10:27:56

W Rails 4, koncepcja wykorzystania problemów do modularyzacji i wysuszania modeli była w najciekawszych aspektach.

Obawy zasadniczo pozwalają grupować podobny kod modelu lub wielu modeli w jednym module, a następnie używać tego modułu w modelach. Oto przykład:

Rozważ Model artykułu, Model zdarzenia i model komentarza. Artykuł lub wydarzenie ma wiele komentarzy. Komentarz należy do artykułu lub wydarzenia.

Tradycyjnie modele mogą wyglądać to:

Skomentuj Model:

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ąca część kodu wspólna zarówno dla modelu Event, jak i Article. Wykorzystując obawy możemy wyodrębnić ten wspólny kod w osobnym module Komentowalnym.

Do tego utwórz komentarz.plik rb w aplikacji / model/dotyczy.

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:

Skomentuj Model:

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

Artykuł Model:

class Article < ActiveRecord::Base
    include Commentable
end

Model Zdarzenia

class Event < ActiveRecord::Base    
    include Commentable
end

Jedną z kwestii, którą chciałbym podkreślić podczas używania obaw, jest to, że obawy powinny być używane do grupowania "opartego na domenie", a nie "technicznego". na przykład grupowanie domen jest jak 'Komentowalne',' Taggable ' itp. Grupowanie oparte na technice będzie takie jak "FinderMethods", "ValidationMethods".

Oto link do posta , który uważam za bardzo przydatny do zrozumienia problemów w modelach.

Hope the writeup pomaga:)

 21
Author: Aaditi Jain,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-25 07:48:23

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 :)
 8
Author: Vitaly Kushner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-15 14:43:31

Rails 5 zapewnia wbudowany mechanizm rozszerzania ActiveRecord::Base.

Osiąga się to poprzez dostarczenie dodatkowej warstwy:

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

I wszystkie modele dziedziczą z tego:

class Post < ApplicationRecord
end

Zobacz np. Ten blogpost .

 5
Author: Adobe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-24 16:03:42

Aby dodać do tego tematu, spędziłem trochę czasu pracując nad tym, jak przetestować takie rozszerzenia (poszedłem w dół trasy 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
 4
Author: Will Tomlins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-31 11:22:18

W Rails 5, Wszystkie modele są dziedziczone z ApplicationRecord i daje to dobry sposób na dołączenie lub rozszerzenie 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 application_record.plik rb. Jeśli chcemy zastosować to dla określonego zestawu modeli, włączamy je do odpowiednich klas 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 innym !
 4
Author: Ashik Salman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-02 05:24:07

Mam

ActiveRecord::Base.extend Foo::Bar

In an initializer

Dla modułu jak poniżej

module Foo
  module Bar
  end
end
 0
Author: Ed Richards,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-01 02:03:05