4.0 के लिए पर्यवेक्षक विकल्प रेल


154

आधिकारिक तौर पर रेल 4.0 से हटाए गए पर्यवेक्षकों के साथ , मैं उत्सुक हूं कि अन्य डेवलपर्स अपने स्थान पर क्या उपयोग कर रहे हैं। (निकाले गए मणि का उपयोग करने के अलावा।) जबकि पर्यवेक्षकों को निश्चित रूप से दुर्व्यवहार किया गया था और कई बार आसानी से बेवजह बन सकते थे, बस कैश-समाशोधन के बाहर कई उपयोग-मामले थे जहां वे फायदेमंद थे।

उदाहरण के लिए, एक एप्लिकेशन जिसे एक मॉडल में परिवर्तनों को ट्रैक करने की आवश्यकता है। एक पर्यवेक्षक आसानी से मॉडल ए पर परिवर्तन के लिए देख सकता है और डेटाबेस में मॉडल बी के साथ उन परिवर्तनों को रिकॉर्ड कर सकता है। यदि आप कई मॉडलों में बदलाव देखना चाहते हैं, तो एक एकल पर्यवेक्षक इसे संभाल सकता है।

रेल 4 में, मैं उत्सुक हूं कि अन्य डेवलपर्स पर्यवेक्षकों के स्थान पर उन कार्यक्षमता का उपयोग करने के लिए कौन सी रणनीतियों का उपयोग कर रहे हैं।

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

ActiveRecord कॉलबैक एक अन्य संभावित विकल्प है, हालांकि एक मैं व्यक्तिगत रूप से पसंद नहीं करता हूं क्योंकि यह दो अलग-अलग मॉडलों को मेरी राय में एक साथ बहुत करीब से जोड़े।

तो रेल 4, नो-ऑब्जर्वर दुनिया में, यदि आपको एक और रिकॉर्ड बनाने / अपडेट / नष्ट होने के बाद एक नया रिकॉर्ड बनाना था, तो आप किस डिज़ाइन पैटर्न का उपयोग करेंगे? वसा नियंत्रक, ActiveRecord कॉलबैक, या पूरी तरह से कुछ और?

धन्यवाद।


4
मैं वास्तव में हैरान हूं कि इस प्रश्न के लिए और अधिक उत्तर पोस्ट नहीं किए गए हैं। डिस्कनेक्ट करने की तरह।
कोर्टिमेस

जवाबों:


82

चिंता पर एक नज़र डालें

अपने मॉडल निर्देशिका में एक फ़ोल्डर बनाएं जिसे चिंता कहा जाता है। वहां एक मॉड्यूल जोड़ें:

module MyConcernModule
  extend ActiveSupport::Concern

  included do
    after_save :do_something
  end

  def do_something
     ...
  end
end

इसके बाद, उन मॉडलों में शामिल करें जिन्हें आप after_save में चलाना चाहते हैं:

class MyModel < ActiveRecord::Base
  include MyConcernModule
end

आप जो कर रहे हैं, उसके आधार पर, यह आपको पर्यवेक्षकों के बिना बंद हो सकता है।


20
इस दृष्टिकोण के साथ समस्याएं हैं। विशेष रूप से, यह आपके मॉडल को साफ नहीं करता है; शामिल प्रतियां अपनी कक्षा में में मॉड्यूल पीछे से तरीकों। एक मॉड्यूल के लिए कक्षा के तरीकों को निकालना चिंता से उन्हें समूहित कर सकता है, लेकिन कक्षा अभी भी फूला हुआ है।
स्टीवन सोरोका

15
शीर्षक है 'रेल्स ऑब्जर्वर ऑल्टरनेटिव्स फॉर 4.0' नॉट 'हाउ आर मिनिमल ब्लोट'। यह कैसे है कि चिंताएं स्टीवन काम नहीं करती हैं? और नहीं, यह सुझाव देते हुए कि 'ब्लोट' एक कारण है कि यह पर्यवेक्षकों के प्रतिस्थापन के रूप में काम नहीं करेगा, बहुत अच्छा नहीं है। आपको समुदाय की मदद करने के लिए एक बेहतर सुझाव देना होगा या यह बताना होगा कि पर्यवेक्षकों के प्रतिस्थापन के रूप में चिंता क्यों काम नहीं करेगी। उम्मीद है कि आप दोनों को = D
UncleAdam

10
ब्लोट हमेशा एक चिंता का विषय है। एक बेहतर विकल्प बुद्धिमानी है , जो यदि ठीक से लागू किया जाता है, तो आप उन्हें अलग-अलग वर्गों के लिए बाहर निकालने के द्वारा चिंताओं को साफ करने की अनुमति देता है जो कि मॉडलों के लिए कसकर युग्मित नहीं हैं। यह अलगाव में परीक्षण करने के लिए भी बहुत आसान बनाता है
स्टीवन सोरोका

4
ऐसा करने के लिए एक मणि में खींचकर मॉडल ब्लोट या होल ऐप ब्लोट - हम इसे व्यक्तिगत प्राथमिकता तक छोड़ सकते हैं। अतिरिक्त सुझाव के लिए धन्यवाद।
अंकलअदाम

यह केवल IDE की विधि ऑटो-कम्प्लीट मेन्यू को ब्लोट करेगा, जो कई लोगों के लिए ठीक होना चाहिए।
लूलालाल

33

वे अभी एक प्लगइन में हैं।

क्या मैं एक विकल्प की भी सिफारिश कर सकता हूँ जो आपको नियंत्रक देगा:

class PostsController < ApplicationController
  def create
    @post = Post.new(params[:post])

    @post.subscribe(PusherListener.new)
    @post.subscribe(ActivityListener.new)
    @post.subscribe(StatisticsListener.new)

    @post.on(:create_post_successful) { |post| redirect_to post }
    @post.on(:create_post_failed)     { |post| render :action => :new }

    @post.create
  end
end

ActiveSupport :: अधिसूचना के बारे में कैसे?
203:

@svoop ActiveSupport::Notificationsइंस्ट्रूमेंटेशन की ओर सक्षम हैं, न कि सामान्य उप / पब।
क्रिश

@ क्रिस - आप सही कह रहे हैं। यह मुख्य रूप से इंस्ट्रूमेंटेशन के लिए उपयोग किया जाता है, लेकिन मुझे आश्चर्य है कि पब / उप के लिए एक सामान्य विधि के रूप में इसका उपयोग करने से क्या रोकता है? यह मूल बिल्डिंग ब्लॉक्स प्रदान करता है, है ना? दूसरे शब्दों में, बुद्धिमान की तुलना में ऊपर / नीचे क्या हैं ActiveSupport::Notifications?
जिंजलिमे

मैंने Notificationsज्यादा इस्तेमाल नहीं किया है, लेकिन मैं कहूंगा Wisperकि एक अच्छे एपीआई और 'ग्लोबल सब्सक्राइबर', 'प्रीफिक्स' और 'इवेंट मैपिंग' जैसे फीचर्स हैं, जो Notificationsनहीं है। Wisperसाइडइक / रेस्क्यू / सेलुलॉइड के माध्यम से एसिंक्स प्रकाशन को भविष्य में जारी करने की भी अनुमति होगी। इसके अलावा, संभावित रूप से, भविष्य की रेल रिलीज़ में, एपीआई के लिए Notificationsऔर अधिक इंस्ट्रूमेंटेशन केंद्रित हो सकता है।
क्रिश

21

मेरा सुझाव http://jamesgolick.com/2010/3/14/crazy-heretical-and-awesome-the-way-i-write-rails-apps.html पर जेम्स गुलिक के ब्लॉग पोस्ट को पढ़ने का है (उपेक्षा कैसे करें शीर्षक लगता है)।

दिन में यह सब "मोटा मॉडल, पतला नियंत्रक" था। फिर वसा मॉडल एक विशाल सिरदर्द बन गए, खासकर परीक्षण के दौरान। हाल ही में स्कीनी मॉडल के लिए धक्का दिया गया है - यह विचार कि प्रत्येक वर्ग को एक जिम्मेदारी संभालनी चाहिए और एक मॉडल का काम एक डेटाबेस में आपके डेटा को बनाए रखना है। तो मेरे सभी जटिल व्यावसायिक तर्क कहां समाप्त होते हैं? व्यापार तर्क कक्षाओं में - लेनदेन का प्रतिनिधित्व करने वाली कक्षाएं।

जब तर्क जटिल होने लगता है तो यह दृष्टिकोण एक दलदल (विशालता) में बदल सकता है। अवधारणा हालांकि ध्वनि है - बजाय कॉलबैक या पर्यवेक्षकों के साथ चीजों को ट्रिगर करने के बजाय जो परीक्षण और डीबग करना कठिन है, चीजों को स्पष्ट रूप से एक वर्ग में ट्रिगर करते हैं जो आपके मॉडल के शीर्ष पर तर्क को परत करता है।


4
मैं पिछले कुछ महीनों से इस परियोजना के लिए कुछ ऐसा कर रहा हूं। आप बहुत सारी छोटी-छोटी सेवाओं के साथ काम करते हैं, लेकिन परीक्षण और इसे बनाए रखने में आसानी निश्चित रूप से नुकसान से आगे निकल जाती है। इस मध्यम आकार की प्रणाली पर मेरा काफी व्यापक चश्मा अभी भी चलाने के लिए केवल 5 सेकंड लगते हैं :)
लुका स्पिलर

PORO (प्लेन ओल्ड रूबी ऑब्जेक्ट्स), या सेवा वस्तुओं के रूप में भी जाना जाता है
सिरिल डचोन-डोरिस

13

सक्रिय रिकॉर्ड कॉलबैक का उपयोग करते हुए बस आपके युग्मन की निर्भरता को बढ़ाता है। उदाहरण के लिए, यदि आपके पास modelAऔर 3 रेल का CacheObserverअवलोकन modelAहै, तो आप CacheObserverबिना किसी समस्या के निकाल सकते हैं । अब, इसके बजाय Aमैन्युअल रूप से आह्वान करना हैCacheObserver बचाने बाद , जो रेल होगा 4. आप बस अपनी निर्भरता स्थानांतरित कर दिया है ताकि आप सुरक्षित रूप से हटा सकते हैं, Aलेकिन नहीं CacheObserver

अब, अपने हाथी दांत के टॉवर से मैं प्रेक्षक को उस मॉडल पर निर्भर होना पसंद करता हूं जो वह देख रहा है। क्या मैं अपने नियंत्रकों को अव्यवस्थित करने के लिए पर्याप्त देखभाल करता हूं? मेरे लिए, जवाब नहीं है।

संभवतः आपने कुछ विचार रखा है कि आपको पर्यवेक्षक की आवश्यकता क्यों है / और इस प्रकार अपने पर्यवेक्षक पर निर्भर मॉडल बनाना एक भयानक त्रासदी नहीं है।

मेरे पास एक भी है (यथोचित आधार पर, मुझे लगता है) किसी भी तरह के पर्यवेक्षक के लिए अरुचिकर एक नियंत्रक कार्रवाई पर निर्भर है। अचानक आपको किसी भी नियंत्रक क्रिया (या किसी अन्य मॉडल) में अपने पर्यवेक्षक को इंजेक्ट करना होगा जो आपके द्वारा देखे गए मॉडल को अपडेट कर सकता है। यदि आप गारंटी दे सकते हैं कि आपका ऐप केवल नियंत्रक / अपडेट कंट्रोलर एक्शन, आपके द्वारा अधिक शक्ति के माध्यम से उदाहरणों को संशोधित करेगा, लेकिन यह धारणा नहीं है कि मैं एक रेल एप्लिकेशन (नेस्टेड फॉर्म, मॉडल बिजनेस लॉजिक अपडेटिंग एसोसिएशन, आदि पर विचार करूंगा)


1
टिप्पणियों के लिए धन्यवाद @agmin। अगर वहाँ एक बेहतर डिजाइन पैटर्न है तो मैं एक ऑब्जर्वर का उपयोग करने से दूर जाने के लिए खुश हूं। मुझे सबसे ज्यादा दिलचस्पी इस बात में है कि अन्य लोग समान कोड (कैशिंग को छोड़कर) प्रदान करने के लिए अपने कोड और निर्भरता को कैसे संरचित कर रहे हैं। मेरे मामले में, मैं किसी मॉडल के परिवर्तनों को कभी भी रिकॉर्ड करना चाहूंगा, जब तक कि उसकी विशेषताएँ अपडेट न हो जाएं। मैंने ऐसा करने के लिए एक ऑब्जर्वर का उपयोग किया। अब मैं एक वसा नियंत्रक, एआर कॉलबैक या कुछ और के बीच फैसला करने की कोशिश कर रहा हूं, जिसके बारे में मैंने नहीं सोचा था। न ही फिलहाल सुरुचिपूर्ण लगता है।
kennyc

13

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

class ApplicationController < ActionController::Base
  around_filter :register_event_listeners

  def register_event_listeners(&around_listener_block)
    Wisper.with_listeners(UserListener.new) do
      around_listener_block.call
    end
  end        
end

class User
  include Wisper::Publisher
  after_create{ |user| publish(:user_registered, user) }
end

class UserListener
  def user_registered(user)
    Analytics.track("user:registered", user.analytics)
  end
end


4

रेल 3 ऑब्जर्वर के लिए मेरा विकल्प एक मैनुअल कार्यान्वयन है जो मॉडल के भीतर परिभाषित एक कॉलबैक का उपयोग करता है जो अभी तक (जैसा कि ऊपर दिए गए अपने जवाब में एग्मिन राज्यों में कहता है) का प्रबंधन करता है "निर्भरता को फ्लिप करें"।

मेरी वस्तुएं एक बेस क्लास से विरासत में मिली हैं जो पर्यवेक्षकों को पंजीकृत करने के लिए प्रदान करती हैं:

class Party411BaseModel

  self.abstract_class = true
  class_attribute :observers

  def self.add_observer(observer)
    observers << observer
    logger.debug("Observer #{observer.name} added to #{self.name}")
  end

  def notify_observers(obj, event_name, *args)
    observers && observers.each do |observer|
    if observer.respond_to?(event_name)
        begin
          observer.public_send(event_name, obj, *args)
        rescue Exception => e
          logger.error("Error notifying observer #{observer.name}")
          logger.error e.message
          logger.error e.backtrace.join("\n")
        end
    end
  end

end

(दी गई, वंशानुक्रम पर रचना की भावना में, उपरोक्त कोड को एक मॉड्यूल में रखा जा सकता है और प्रत्येक मॉडल में मिश्रित किया जा सकता है।)

एक शुरुआती पर्यवेक्षकों को पंजीकृत करता है:

User.add_observer(NotificationSender)
User.add_observer(ProfilePictureCreator)

प्रत्येक मॉडल तब मूल ActiveRecord कॉलबैक से परे, अपनी स्वयं की अवलोकन योग्य घटनाओं को परिभाषित कर सकता है। उदाहरण के लिए, मेरा उपयोगकर्ता मॉडल 2 घटनाओं को उजागर करता है:

class User < Party411BaseModel

  self.observers ||= []

  after_commit :notify_observers, :on => :create

  def signed_up_via_lunchwalla
    self.account_source == ACCOUNT_SOURCES['LunchWalla']
  end

  def notify_observers
    notify_observers(self, :new_user_created)
    notify_observers(self, :new_lunchwalla_user_created) if self.signed_up_via_lunchwalla
  end
end

उन घटनाओं के लिए सूचनाएं प्राप्त करने की इच्छा रखने वाले किसी भी पर्यवेक्षक को घटना को उजागर करने वाले मॉडल के साथ (1) रजिस्टर करने की आवश्यकता होती है और (2) एक विधि होती है जिसका नाम घटना से मेल खाता है। जैसा कि कोई उम्मीद कर सकता है, कई पर्यवेक्षक एक ही घटना के लिए पंजीकरण कर सकते हैं, और (मूल प्रश्न के दूसरे पैराग्राफ के संदर्भ में) एक पर्यवेक्षक कई मॉडलों में घटनाओं के लिए देख सकता है।

नोटिफिकेशनसेन्डर और प्रोफाइलपिक्योरक्रीट ऑब्जर्वर क्लासेस विभिन्न मॉडलों द्वारा उजागर की जाने वाली घटनाओं के तरीकों को परिभाषित करते हैं:

NotificationSender
  def new_user_created(user_id)
    ...
  end

  def new_invitation_created(invitation_id)
    ...
  end

  def new_event_created(event_id)
    ...
  end
end

class ProfilePictureCreator
  def new_lunchwalla_user_created(user_id)
    ...
  end

  def new_twitter_user_created(user_id)
    ...
  end
end

एक चेतावनी यह है कि सभी मॉडलों में उजागर सभी घटनाओं के नाम अद्वितीय होने चाहिए।


3

मुझे लगता है कि ऑब्जर्वरों को हटाए जाने के साथ यह मुद्दा यह नहीं है कि पर्यवेक्षक अपने आप में बुरे थे बल्कि उनका दुरुपयोग किया जा रहा था।

मैं आपके कॉलबैक में बहुत अधिक तर्क जोड़ने या बस ऑब्ज़र्वर के व्यवहार को अनुकरण करने के लिए कोड को इधर-उधर करने के प्रति सावधानी बरतूँगा जब ऑब्जर्वर पैटर्न में इस समस्या का पहले से ही कोई हल है।

यदि यह पर्यवेक्षकों का उपयोग करने के लिए समझ में आता है, तो हर तरह से पर्यवेक्षकों का उपयोग करें। बस यह समझें कि आपको यह सुनिश्चित करने की आवश्यकता होगी कि आपके पर्यवेक्षक तर्क एसओएलआईडी उदाहरण के लिए ध्वनि कोडिंग प्रथाओं का पालन करते हैं।

यदि आप इसे अपनी परियोजना में वापस जोड़ना चाहते हैं, तो रूबीज पर प्रेक्षक मणि उपलब्ध है https://github.com/rails/rails-observers

इस संक्षिप्त सूत्र को देखें, जबकि पूरी तरह से व्यापक चर्चा नहीं है, मुझे लगता है कि मूल तर्क मान्य है। https://github.com/rails/rails-observers/issues/2


2

आप https://github.com/TiagoCardoso1983/association_observers आज़मा सकते हैं । यह अभी तक रेल 4 के लिए परीक्षण नहीं किया गया है (जो अभी तक लॉन्च नहीं किया गया था), और कुछ और सहयोग की आवश्यकता है, लेकिन आप जांच सकते हैं कि क्या यह आपके लिए चाल है।


2

इसके बजाय एक PORO का उपयोग कैसे करें?

इसके पीछे तर्क यह है कि आपके 'अतिरिक्त कार्यों को बचाने' के कारण व्यावसायिक तर्क होने की संभावना है। यह मैं दोनों एआर मॉडल (जो संभव के रूप में सरल होना चाहिए) और नियंत्रकों (जो सही परीक्षण करने के लिए परेशान हैं) से अलग रखना पसंद करते हैं

class LoggedUpdater

  def self.save!(record)
    record.save!
    #log the change here
  end

end

और बस इसे इस तरह से कॉल करें:

LoggedUpdater.save!(user)

आप इस पर विस्तार भी कर सकते हैं, अतिरिक्त पोस्ट-सेव एक्शन ऑब्जेक्ट्स को इंजेक्ट करके

LoggedUpdater.save(user, [EmailLogger.new, MongoLogger.new])

और give एक्स्ट्रा ’का उदाहरण देना है। यद्यपि आप उन्हें थोड़ा ऊपर उठाना चाह सकते हैं:

class EmailLogger
  def call(msg)
    #send email with msg
  end
end

यदि आप इस दृष्टिकोण को पसंद करते हैं, तो मैं ब्रायन हेल्मकैंप्स 7 पैटर्न ब्लॉग पोस्ट को पढ़ने की सलाह देता हूं ।

EDIT: मुझे यह भी उल्लेख करना चाहिए कि उपरोक्त समाधान लेन-देन के तर्क को जोड़ने की अनुमति देता है और जब आवश्यक हो। जैसे ActiveRecord और एक समर्थित डेटाबेस:

class LoggedUpdater

  def self.save!([records])
    ActiveRecord::Base.transaction do
      records.each(&:save!)
      #log the changes here
    end
  end

end

0

यह ध्यान देने योग्य है कि Observableरूबी मानक पुस्तकालय से मॉड्यूल का उपयोग सक्रिय-रिकॉर्ड जैसी वस्तुओं में नहीं किया जा सकता है, उदाहरण के तरीकों से changed?और changedउन लोगों से टकराव होगा ActiveModel::Dirty

रेल 2.3.2 के लिए बग रिपोर्ट


-2

मेरे पास एक ही प्रोबजेम है! मैं एक समाधान ActiveModel :: डर्टी ढूंढता हूँ ताकि आप अपने मॉडल में बदलाव कर सकें!

include ActiveModel::Dirty
before_save :notify_categories if :data_changed? 


def notify_categories
  self.categories.map!{|c| c.update_results(self.data)}
end

http://api.rubyonrails.org/classes/ActiveModel/Dirty.html

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