ActiveRecord :: बेस का विस्तार करने वाली रेल


160

मैंने ActiveRecord को कैसे आगे बढ़ाया जाए, इसके बारे में कुछ रीडिंग की है: बेस क्लास इसलिए मेरे मॉडल में कुछ विशेष विधियाँ होंगी। इसे बढ़ाने का आसान तरीका क्या है (स्टेप बाय स्टेप ट्यूटोरियल)?


किस तरह के एक्सटेंशन? हमें वास्तव में आगे बढ़ने की अधिक आवश्यकता है।
जौनी

जवाबों:


336

कई दृष्टिकोण हैं:

ActiveSupport का प्रयोग :: चिंता (पसंदीदा)

ActiveSupport :: अधिक जानकारी के लिए चिंता दस्तावेज़ पढ़ें ।

निर्देशिका 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"

वंशानुक्रम (पसंदीदा)

टोबी के उत्तर का संदर्भ लें ।

बंदर पैचिंग (टाला जाना चाहिए)

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

जॅमी ज़्विन्स्की द्वारा नियमित भावों के बारे में प्रसिद्ध उद्धरण को बंदर-पेटिंग से जुड़ी समस्याओं का वर्णन करने के लिए फिर से प्रस्तुत किया जा सकता है।

कुछ लोग, जब एक समस्या का सामना करते हैं, तो सोचते हैं कि "मुझे पता है, मैं बंदर के पेटिंग का उपयोग करूंगा।" अब उन्हें दो समस्याएं हैं।

बंदर पालना आसान और त्वरित है। लेकिन, बचाए गए समय और प्रयास को हमेशा भविष्य में वापस ले लिया जाता है; चक्रवृद्धि ब्याज के साथ। इन दिनों मैं रेलिंग को सांत्वना देने के लिए रेलिंग कंसोल में घोल को सीमित करता हूं।


3
आप requireके अंत में फ़ाइल है environment.rb। मैंने अपने उत्तर में यह अतिरिक्त कदम जोड़ा है।
हरीश शेट्टी

1
@ हर्टलेब्रॉडी यह केवल वरीयता का मामला है। यदि आप इनहेरिटेंस का उपयोग करते हैं, तो आपको एक नया ImprovedActiveRecordऔर इनहेरिट करना होगा, जब आप उपयोग कर रहे हैं, तो आप moduleप्रश्न में कक्षा की परिभाषा को अपडेट कर रहे हैं। मैं वंशानुक्रम (जावा / सी ++ अनुभव के वर्षों का कारण) का उपयोग करता था। इन दिनों मैं ज्यादातर मॉड्यूल का उपयोग करता हूं।
हरीश शेट्टी

1
यह थोड़ा विडंबना है कि आपका लिंक वास्तव में संदर्भ और यह इंगित कर रहा था कि लोग कैसे गलत हैं- और उद्धरण का अत्यधिक उपयोग करते हैं। लेकिन सभी गंभीरता से मुझे यह समझने में परेशानी हो रही है कि "मंकी पैचिंग" इस मामले में सबसे अच्छा तरीका क्यों नहीं होगा। यदि आप कई वर्गों को जोड़ना चाहते हैं तो एक मॉड्यूल स्पष्ट रूप से जाने का रास्ता है। लेकिन अगर आपका लक्ष्य एक वर्ग का विस्तार करना है तो क्या यह नहीं है कि रूबी ने इस तरह से कक्षाओं का विस्तार किया है?
MCB

1
@MCB, हर बड़े प्रोजेक्ट में बन्दर पैचिंग के कारण शुरू की गई बग का पता लगाने में मुश्किल के बारे में कुछ कहानियाँ हैं। यहाँ अवधी द्वारा पैचिंग की बुराइयों के बारे में एक लेख है: devblog.avdi.org/2008/02/23/… । रूबी 2.0 ने एक नई सुविधा का परिचय दिया है, Refinementsजिसमें अधिकांश मामलों को संबोधित किया गया है ( बंदरहट्ज.कॉम डॉट कॉम/11/11/30/ruby-2-0-refinements-in-ults )। कभी-कभी एक सुविधा सिर्फ आपको भाग्य को लुभाने के लिए मजबूर करने के लिए होती है। और कभी-कभी आप करते हैं।
हरीश शेट्टी

1
@ ट्रैंटोरलियू हां। मैंने नवीनतम दस्तावेज़ को प्रतिबिंबित करने के लिए उत्तर को अपडेट किया है (2014 में github.com/rails/rails/commit/… पर class_methods की तरह दिखता है )
हरीश शेट्टी

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। रेल अब मॉडल को एक सार मॉडल के रूप में पहचानेंगे।
हरीश शेट्टी

वाह! यह संभव नहीं था। इसे पहले ही आज़मा लिया और तब छोड़ दिया जब ActiveRecord ने AbstractModelडेटाबेस की तलाश की। कौन जानता था कि एक साधारण सेटर मुझे DRY चीजों में मदद करेगा! (मैं ऐंठने लगा था ... बुरा लगा था)। धन्यवाद टोबी और हरीश!
डोलियो

मेरे मामले में यह निश्चित रूप से ऐसा करने का सबसे अच्छा तरीका है: मैं अपने मॉडल की क्षमताओं को विदेशी तरीकों से यहां नहीं बढ़ा रहा हूं, लेकिन अपने ऐप के समान व्यवहार करने वाली वस्तुओं के लिए कमोमोन तरीकों को फिर से बनाना। वंशानुक्रम यहाँ बहुत अधिक समझ में आता है। वहाँ कोई पसंदीदा तरीका नहीं है, लेकिन आप क्या हासिल करना चाहते हैं इसके आधार पर 2 समाधान!
ऑगस्टिन रीडिंगर

यह मेरे लिए Rails4 में काम नहीं करता है। मैंने abstract_model.rb बनाया और अपनी मॉडल निर्देशिका में डाला। मॉडल के अंदर यह self.abstract_class = true था फिर मैंने अपने अन्य मॉडलों की विरासत बनाई ... उपयोगकर्ता <AbstractModel । कंसोल में मुझे मिलता है: उपयोगकर्ता (एक कनेक्शन स्थापित करने के लिए 'User.connection' को कॉल करें)
जोएल ग्राननास

21

आप भी उपयोग कर सकते हैं ActiveSupport::Concernऔर अधिक रेल कोर मुहावरेदार हो सकते हैं जैसे:

module MyExtension
  extend ActiveSupport::Concern

  def foo
  end

  module ClassMethods
    def bar
    end
  end
end

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

[संपादित करें] @ डैनियल की टिप्पणी के बाद

फिर आपके सभी मॉडल fooमें एक विधि विधि के ClassMethodsरूप में शामिल विधि और वर्ग विधियों के रूप में शामिल तरीके होंगे। जैसे FooBar < ActiveRecord::Baseआपके पास होगा: FooBar.barऔरFooBar#foo

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


5
ध्यान दें कि InstanceMethodsरेल 3.2 से हटा दिया गया है, बस अपने तरीकों को मॉड्यूल बॉडी में डालें।
डैनियल रिकोव्स्की

मैं ActiveRecord::Base.send(:include, MyExtension)एक इनिशियलाइज़र में रखा और फिर यह मेरे लिए काम किया। रेल्स 4.1.9
6 फीट डैन

18

रेल 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

जैसा कि हम देख सकते हैं, इवेंट और आर्टिकल मॉडल दोनों के लिए कोड कॉमन का एक महत्वपूर्ण हिस्सा है। चिंताओं का उपयोग करके हम एक अलग मॉड्यूल में इस सामान्य कोड को निकाल सकते हैं।

इसके लिए एप्लिकेशन / मॉडल / चिंताओं में एक टिप्पणी करने योग्य .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

एक बात यह है कि मैं कंसर्न का उपयोग करते समय हाइलाइट करना चाहूंगा कि कंसर्न का इस्तेमाल 'टेक्निकल' ग्रुपिंग के बजाय 'डोमेन आधारित' ग्रुपिंग के लिए किया जाना चाहिए। उदाहरण के लिए, एक डोमेन ग्रुपिंग 'कमेंटेबल', 'टैगेबल' आदि की तरह है। एक तकनीकी आधारित ग्रुपिंग 'फाइंडरमैथोड्स', 'वैलिडेशनमेथोड्स' की तरह होगा।

यहां एक पोस्ट का लिंक दिया गया है जो मुझे मॉडल्स में चिंताओं को समझने के लिए बहुत उपयोगी लगा।

आशा है कि राइटअप मदद करता है :)


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

रेल 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

रेल 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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.