ActiveRecord :: Base를 확장하는 레일


160

모델에 특별한 메소드가 있도록 ActiveRecord : Base 클래스를 확장하는 방법에 대해 약간의 독서를했습니다. 그것을 확장하는 쉬운 방법은 무엇입니까 (단계별 튜토리얼)?


어떤 종류의 확장 프로그램? 우리는 계속해서 더 많은 것을 필요로합니다.
jonnii

답변:


336

몇 가지 접근 방식이 있습니다.

ActiveSupport :: Concern 사용 (선호)

자세한 내용은 ActiveSupport :: Concern 설명서를 읽으십시오 .

디렉토리 active_record_extension.rb에서 호출 된 파일을 작성하십시오 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)

config/initializers라는 디렉토리에 파일을 작성하고 파일에 extensions.rb다음 행을 추가하십시오.

require "active_record_extension"

상속 (권장)

Toby의 답변을 참조하십시오 .

원숭이 패치 (피해야 함)

config/initializers디렉토리에 파일을 작성하십시오 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의 정규 표현식에 대한 유명한 인용문은 원숭이 패치와 관련된 문제를 설명하기 위해 용도 변경 될 수 있습니다.

어떤 사람들은 문제에 직면했을 때“나는 원숭이 패치를 사용할 것입니다”라고 생각합니다. 이제 두 가지 문제가 있습니다.

원숭이 패치는 쉽고 빠릅니다. 그러나 절약 된 시간과 노력은 항상 언젠가 다시 추출됩니다. 복리 관심. 요즘에는 레일 콘솔에서 솔루션을 신속하게 프로토 타입하기 위해 원숭이 패치를 제한합니다.


3
require끝에 파일 이 있어야합니다 environment.rb. 이 추가 단계를 답변에 추가했습니다.
Harish Shetty

1
@HartleyBrody 그것은 단지 선호의 문제입니다. 상속을 사용하는 경우 새로운 것을 도입하고 ImprovedActiveRecord그로부터 상속해야합니다.를 사용할 때 module해당 클래스의 정의가 업데이트됩니다. 상속을 사용했습니다 (수년간의 Java / C ++ 경험으로 인해). 요즘에는 주로 모듈을 사용합니다.
Harish Shetty

1
귀하의 링크가 실제로 상황을 파악하고 사람들이 견적을 오용하고 남용하는 방법을 지적한 것은 조금 아이러니합니다. 그러나 모든 심각성에서이 경우 "원숭이 패치"가 왜 최선의 방법이 아닌지 이해하는 데 어려움을 겪고 있습니다. 여러 클래스에 추가하려면 모듈이 있어야합니다. 그러나 당신의 목표가 하나의 클래스를 확장하는 것이라면 Ruby가 왜 이런 식으로 클래스를 확장하는 것이 그렇게 쉬운가요?
MCB

1
@MCB, 모든 큰 프로젝트에는 원숭이 패치로 인한 버그 찾기가 어렵다는 이야기가 거의 없습니다. 다음은 패치의 악에 대한 Avdi의 글입니다 devblog.avdi.org/2008/02/23/...은 . 루비 2.0 Refinements에는 원숭이 패치 ( yehudakatz.com/2010/11/30/ruby-2-0-refinements-in-practice ) 와 관련된 대부분의 문제를 해결 하는 새로운 기능이 도입되었습니다 . 때로는 운명을 유혹하도록 강요하는 기능이 있습니다. 때로는 그렇습니다.
Harish Shetty

1
@TrantorLiu 예. 최신 문서를 반영하도록 답변을 업데이트했습니다 (class_methods가 2014 github.com/rails/rails/commit/… 에 도입 된 것 같습니다 )
Harish Shetty

70

클래스를 확장하고 상속을 사용하면됩니다.

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

class Foo < AbstractModel
end

class Bar < AbstractModel
end

나는 그것을하는 표준 방법이기 때문에이 아이디어를 좋아하지만 ... 오류 테이블 'moboolo_development.abstract_models'가 존재하지 않습니다 : SHOW FIELDS FROM abstract_models. 어디에 두어야합니까?
xpepermint

23
에 추가 self.abstract_class = true하십시오 AbstractModel. Rails는 이제 모델을 추상 모델로 인식합니다.
Harish Shetty 2012

와! 이것이 가능하지 않다고 생각했습니다. 이전에 그것을 시도하고 ActiveRecord AbstractModel가 데이터베이스에서를 찾고 질식했을 때 포기했습니다 . 간단한 세터가 나를 건조시키는 데 도움이 될 줄 누가 알았습니까! (나는 울기 시작했다.. 그것은 나빴다). 고마워 Toby와 Harish!
dooleyo

필자의 경우이 작업을 수행하는 가장 좋은 방법입니다. 여기서는 외부 기능을 사용하여 모델 기능을 확장하지 않지만 앱의 유사한 동작 객체에 대한 공통 메소드를 리팩토링합니다. 상속 은 여기에서 훨씬 더 의미가 있습니다. 더이되는 방법은 선호하지 않습니다 하지만이 개 솔루션은 달성하려는 작업에 따라!
Augustin Riedinger

Rails4에서는 작동하지 않습니다. abstract_model.rb를 작성 하여 모델 디렉토리에 넣었습니다. 모델 내부에 self.abstract_class = true를 가졌습니다. 그러면 다른 모델로 만들어졌습니다 ... User <AbstractModel . 콘솔에서 나는 : User (연결을 설정하려면 'User.connection'을 호출하십시오)
Joel Grannas

21

다음 ActiveSupport::Concern과 같은 Rails 핵심 관용어를 더 많이 사용할 수 있습니다 .

module MyExtension
  extend ActiveSupport::Concern

  def foo
  end

  module ClassMethods
    def bar
    end
  end
end

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

@daniel의 의견에 따라 [편집]

그런 다음 모든 모델에 메소드 foo가 인스턴스 메소드로 ClassMethods포함되고 메소드가 클래스 메소드 로 포함됩니다. 예는에 FooBar < ActiveRecord::Base, 당신은 것입니다 : FooBar.barFooBar#foo

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


5
참고 InstanceMethods레일 3.2 이후 사용되지 않으며, 그냥 모듈 본체에 당신의 방법을 넣어.
Daniel Rikowski

나는 ActiveRecord::Base.send(:include, MyExtension)이니셜 라이저에 넣고 이것이 나를 위해 일했다. 레일 4.1.9
6ft Dan

18

Rails 4에서는 모델을 모듈화하고 건조시키기 위해 우려 사항을 사용하는 개념이 강조되었습니다.

우려 사항은 기본적으로 하나의 모듈에서 유사한 모델 코드 또는 여러 모델을 그룹화 한 다음이 모듈을 모델에서 사용할 수 있습니다. 예를 들면 다음과 같습니다.

기사 모델, 이벤트 모델 및 주석 모델을 고려하십시오. 기사 또는 이벤트에는 많은 의견이 있습니다. 의견은 기사 또는 이벤트에 속합니다.

전통적으로 모델은 다음과 같습니다.

댓글 모델 :

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

기사 모델 :

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

이벤트 모델

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

우리가 알 수 있듯이 이벤트 및 기사 모델 모두에 공통적 인 중요한 코드가 있습니다. 우려 사항을 사용하여이 공통 코드를 별도의 모듈 Commentable에서 추출 할 수 있습니다.

이를 위해 app / model / concerns에 commentable.rb 파일을 만듭니다.

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

이제 모델은 다음과 같습니다.

댓글 모델 :

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

기사 모델 :

class Article < ActiveRecord::Base
    include Commentable
end

이벤트 모델

class Event < ActiveRecord::Base    
    include Commentable
end

관심사를 사용하면서 강조하고 싶은 한 가지는 '기술적'그룹화보다는 '도메인 기반'그룹화에 관심사를 사용해야한다는 것입니다. 예를 들어 도메인 그룹은 'Commentable', 'Taggable'등과 같습니다. 기술 기반 그룹은 'FinderMethods', 'ValidationMethods'와 같습니다.

다음은 모델의 관심사를 이해하는 데 매우 유용한 게시물링크 입니다.

글쓰기가 도움이되기를 바랍니다 :)


7

1 단계

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

2 단계

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

3 단계

There is no step 3 :)

1
2 단계는 config / environment.rb에 있어야한다고 생각합니다. 그것은 나를 위해 작동하지 않습니다 :(. 좀 더 도움을 줄 수 있습니까? Thx.
xpepermint

5

Rails 5는 확장을위한 내장 메커니즘을 제공합니다 ActiveRecord::Base.

추가 레이어를 제공하면됩니다.

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

모든 모델은 그 모델에서 상속됩니다.

class Post < ApplicationRecord
end

예를 들어이 블로그 게시물을 참조하십시오 .


4

이 주제에 추가하기 위해 그러한 확장을 테스트하는 방법을 연구하는 데 잠시 시간을 보냈습니다 ( ActiveSupport::Concern경로를 따라갔습니다 ).

내 확장 프로그램을 테스트하기위한 모델을 설정하는 방법은 다음과 같습니다.

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를 사용하면 모든 모델이 ApplicationRecord에서 상속되며 다른 확장 라이브러리를 포함하거나 확장 할 수 있습니다.

# 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

모든 메소드에서 특수 메소드 모듈을 사용할 수 있어야한다고 가정하십시오 (application_record.rb 파일에 포함). 특정 모델 집합에 적용하려면 해당 모델 클래스에 포함하십시오.

# 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

모듈에 메소드를 클래스 메소드로 정의하려면 모듈을 ApplicationRecord로 확장하십시오.

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

그것이 다른 사람들을 돕기를 바랍니다!


0

나는 가지고있다

ActiveRecord::Base.extend Foo::Bar

이니셜 라이저에서

아래와 같은 모듈

module Foo
  module Bar
  end
end
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.