रूबी, प्रतिनिधिमंडल को ओवरराइड किए बिना किसी ऑब्जेक्ट के लिए रूबी प्रतिनिधिमंडल

रूबी प्रतिनिधिमंडल का एक उदाहरण एक SimpleDelegator का उपयोग करना है:

class FooDecorator < SimpleDelegator
include ActiveModel::Model
# ...
end

यह बहुत सुविधाजनक है, क्योंकि FooDecorator द्वारा प्रतिक्रिया नहीं की गई कुछ भी अंतर्निहित वस्तु को पारित किया गया है।

लेकिन यह निर्माता के हस्ताक्षर को अधिलेखित करता है, जो इसे जैसी चीजों के साथ असंगत बनाता है ActiveModel::Model कि एक विशेष हस्ताक्षर देखने की उम्मीद है।

रूबी प्रतिनिधिमंडल का एक और उदाहरण उपयोग करना है Forwardable:

class Decorator
include ActiveModel::Model
extend Forwardable

attr_accessor :members
def_delegator  :@members, :bar, :baz
def_delegators :@members, :a, :b, :c
end

लेकिन अब आपको इस बारे में स्पष्ट होना होगा कि आप किन तरीकों को सौंपना चाहते हैं, जो भंगुर है।

क्या दोनों दुनिया में सबसे अच्छा पाने का एक तरीका है, जहां मैं ...

  • डॉन "कंस्ट्रक्टर के साथ छेड़छाड़
  • प्रत्यायोजित वस्तु पर किसी भी नामांकित विधि के लिए पास-थ्रू प्रतिनिधिमंडल प्राप्त करें

उत्तर:

जवाब के लिए 2 № 1

क्या आपने देखा है डैलिगेटर डॉक्स? आप मूल रूप से अपने खुद के प्रतिनिधि उपवर्ग को फिर से लागू कर सकते हैं __getobj__ तथा __setobj__ तरीकों। या, आप बस SimpleDelegator को उपवर्ग कर सकते हैं और अपने स्वयं के निर्माता को निर्दिष्ट कर सकते हैं जो कॉल करता है super(obj_to_delegate_to), नहीं?

आप हमेशा लागू कर सकते हैं method_missing अपने डेकोरेटर पर और अंतर्निहित ऑब्जेक्ट के लिए किसी भी नहीं मिली विधि से गुजरें।

संपादित करें: यहाँ हम चलते हैं। एक मध्यवर्ती विरासत वर्ग का उपयोग टूट जाता है super() चेनिंग, आपको वांछित वर्ग को लपेटने की अनुमति देता है:

require "active_model"
require "delegate"

class Foo
include ActiveModel::Model
attr_accessor :bar
end

class FooDelegator < Delegator
def initialize
# Explicitly don"t call super
end

def wrap(obj)
@obj = obj
self
end

def __getobj__
@obj
end
end

class FooDecorator < FooDelegator
include ActiveModel::Model

def name
self.class.name
end
end

decorator = FooDecorator.new.wrap(Foo.new bar: "baz")
puts decorator.name #=> "decorator"
puts decorator.bar  #=> "bar"

उत्तर № 2 के लिए 1

आप ActiveModel क्यों चाहते हैं :: मॉडल? क्या आपको वास्तव में सभी सुविधाओं की आवश्यकता है?

क्या आप केवल एक ही काम कर सकते हैं?

extend  ActiveModel::Naming
extend  ActiveModel::Translation
include ActiveModel::Validations
include ActiveModel::Conversion

मैं जानता हूँ कि ActiveModel में परिवर्तन :: मॉडल आपके डेकोरेटर को अपेक्षित व्यवहार के मामले में तोड़ सकता है, लेकिन आप इसे वैसे भी बहुत कसकर जोड़ रहे हैं।

कंस्ट्रक्टरों को नियंत्रित करने के लिए मॉड्यूल की अनुमति देना एक कोड गंध है। ठीक हो सकता है, लेकिन आपको इसका अनुमान लगाना चाहिए और ऐसा करने के बारे में अच्छी तरह से अवगत होना चाहिए।

ActiveModel :: मॉडल परिभाषित करता है initialize एक हैश की उम्मीद है। मुझे यकीन नहीं है कि आप उस विशेष वस्तु को प्राप्त करने की अपेक्षा कैसे करते हैं जिसे आप लपेटना चाहते हैं। SimpleDelegator का उपयोग करता है __setobj__ इसके अंदर का कंस्ट्रक्टर, इसलिए आप इसका उपयोग उस मॉड्यूल को शामिल करने के बाद कर सकते हैं जो आपके कंस्ट्रक्टर को ओवरराइड करता है।

यदि आप स्वचालित अग्रेषण चाहते हैं, तो आप ऑब्जेक्ट सेट करते समय अपने डेकोरेटर पर अपनी ज़रूरत के तरीकों को परिभाषित कर सकते हैं। यदि आप नियंत्रित कर सकते हैं कि आपकी वस्तु कैसे बनाई गई है, तो बनाएं build विधि (या ऐसा कुछ) जो कॉल करता है initialize ActiveModel :: मॉडल और के लिए उपयोग किए जाने की आवश्यकता है __setobj__ कि SimpleDelegator के लिए प्रयोग किया जाता है:

require "delegate"
require "forwardable"
class FooCollection < SimpleDelegator
extend Forwardable
include ActiveModel::Model

def self.build(hash, obj)
instance = new(hash)
instance.send(:set_object, obj)
instance
end

private

def set_object(obj)
important_methods = obj.methods(false) - self.class.instance_methods
self.class.delegate [*important_methods] => :__getobj__
__setobj__(obj)
end
end

यह आपको ActiveModel इंटरफ़ेस का उपयोग करने की अनुमति देता है, लेकिन डेकोरेटर के सिंगलटन वर्ग में सिंगलफोरवर्डेबल मॉड्यूल जोड़ता है जो आपको देता है। delegate तरीका। इसके बाद आपको केवल यह करना होगा कि इसे विधि के नाम का एक संग्रह और अग्रेषित करने के लिए वस्तु प्राप्त करने के लिए उपयोग करने का एक तरीका दिया जाए।

यदि आपको विशेष विधियों को शामिल करने या बाहर करने की आवश्यकता है, तो बस तरीका बदलें important_methods बनाया गया है। मैं उस बारे में ज्यादा नहीं सोचता, इसलिए इस कोड को हथियाने से पहले वास्तव में वहां क्या इस्तेमाल किया जा रहा है, इसकी दोबारा जांच करें। उदाहरण के लिए, एक बार set_object विधि को एक बार कहा जाता है, आप इसे बाद में कॉल करना छोड़ सकते हैं, लेकिन यह इस उम्मीद के साथ बनाया गया है कि सभी लिपटे ऑब्जेक्ट में एक ही इंटरफ़ेस है।

जैसा कि आपने ट्विटर पर बताया, द ड्रेपर रत्न का उपयोग करता है delegate विधि (ActiveSupport से) के अंदर method_missing। उस दृष्टिकोण के साथ, प्रत्येक छूटी हुई हिट प्रभावित होगीवर्ग खोलने और अग्रेषण के लिए विधि को परिभाषित करने की लागत। उल्टा यह है कि यह आलसी है और आपको यह गणना करने की आवश्यकता नहीं है कि किन तरीकों को अग्रेषित करने की आवश्यकता है और पहली हिट पर केवल हिट होती है; बाद की विधि कॉलों को "याद नहीं किया जाना चाहिए क्योंकि आप उस विधि को फिर से परिभाषित कर रहे हैं"। मेरे द्वारा ऊपर दिया गया कोड उन सभी विधियों को प्राप्त करेगा और उन्हें एक ही बार में परिभाषित करेगा।

यदि आपको अधिक लचीलेपन की आवश्यकता है और आपकी अपेक्षा हैडेकोरेटर एक ही प्रकार की वस्तु नहीं होने के लिए आप एक ही प्रभाव के लिए SingleForwardable का उपयोग कर सकते हैं लेकिन यह डेकोरेटर वर्ग को प्रभावित करने के बजाय प्रत्येक लिपटे उदाहरण के लिए तरीकों को परिभाषित करेगा:

require "delegate"
require "forwardable"
class FooCollection < SimpleDelegator
include ActiveModel::Model

def self.build(hash, obj)
instance = new(hash)
instance.set_object(obj)
instance
end

def set_object(obj)
important_methods = obj.methods(false) - self.class.instance_methods
singleton_class.extend SingleForwardable
singleton_class.delegate [*important_methods] => :__getobj__
__setobj__(obj)
end
end

लेकिन यह सब SimpleDelegator का उपयोग कर रहा है और यदि आप "वास्तव में method_missing का उपयोग नहीं कर रहे हैं, तो आप इसे काट सकते हैं (आपको मानते हुए" गणना की गई है important_methods सही ढंग से भाग:

require "forwardable"
class FooCollection
include ActiveModel::Model

def self.build(hash, obj)
instance = new(hash)
instance.set_object(obj)
instance
end

def set_object(obj)
important_methods = obj.methods(false)# - self.class.instance_methods
singleton_class.extend SingleForwardable
singleton_class.delegate [*important_methods] => :__getobj__
__setobj__(obj)
end

def __getobj__
@obj
end

def __setobj__(obj)
__raise__ ::ArgumentError, "cannot forward to self" if self.equal?(obj)
@obj = obj
end
end

यदि आप ऐसा करते हैं, हालांकि, यह के उपयोग को मारता है super इसलिए आप अपने लिपटे ऑब्जेक्ट और कॉल पर परिभाषित विधि को ओवरराइड नहीं कर सकते super मूल मूल्य पाने के लिए जैसे आप कर सकते हैं method_missing SimpleDelegator में उपयोग किया जाता है।

मैंने लिखा ढलाई बिना किसी चिंता के वस्तुओं में व्यवहार जोड़नारैपर। आप इसके साथ विधियों को ओवरराइड नहीं कर सकते हैं, लेकिन यदि आप सभी कर रहे हैं तो नए व्यवहार और नए तरीकों को जोड़ रहा है, तो मौजूदा ऑब्जेक्ट में केवल एक बाल्टी विधियों को जोड़कर उपयोग करना बहुत सरल होगा। यह जाँच के लायक है। मैंने इसके बारे में एक प्रस्तुति दी delegate तथा forwardable पुस्तकालयों में RubyConf 2013


संबंधित सवाल
सबसे लोकप्रिय