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.bar및FooBar#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.