क्या रूबी में विधि तर्कों तक पहुंचने का एक तरीका है?


102

रूबी और आरओआर के लिए नया और प्रत्येक दिन इसे प्यार करना, इसलिए यहां मेरा सवाल है क्योंकि मुझे नहीं पता है कि इसे कैसे गूगल करना है (और मैंने कोशिश की है :))

हमारे पास तरीका है

def foo(first_name, last_name, age, sex, is_plumber)
    # some code
    # error happens here
    logger.error "Method has failed, here are all method arguments #{SOMETHING}"    
end

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

आउटपुट होगा:

Method has failed, here are all method arguments {"Mario", "Super", 40, true, true}

1
रे क्रालज सवेगामी!
एंट

1
मुझे लगता है कि सभी उत्तरों को इंगित करना चाहिए कि यदि "कुछ कोड" तर्क खोज विधि को चलाने से पहले तर्कों के मूल्यों को बदल देता है, तो यह नए मानों को दिखाएगा, न कि उन मानों को जो पारित किए गए थे। इसलिए आपको उन्हें सही से पकड़ लेना चाहिए। निश्चित होना। उस ने कहा, इसके लिए मेरा पसंदीदा वन-लाइनर (पिछले उत्तरों को दिए गए क्रेडिट के साथ) है:method(__method__).parameters.map { |_, v| [v, binding.local_variable_get(v)] }
ब्रायन डिटर्लिंग

जवाबों:


159

रूबी 1.9.2 में और बाद में आप parametersउस विधि के लिए मापदंडों की सूची प्राप्त करने के लिए विधि पर विधि का उपयोग कर सकते हैं । यह पैरामीटर के नाम को इंगित करने वाले जोड़ों की सूची लौटाएगा और क्या यह आवश्यक है।

जैसे

यदि तुम करो

def foo(x, y)
end

फिर

method(:foo).parameters # => [[:req, :x], [:req, :y]]

आप __method__वर्तमान विधि का नाम प्राप्त करने के लिए विशेष चर का उपयोग कर सकते हैं । तो एक विधि के भीतर इसके मापदंडों के नाम प्राप्त किए जा सकते हैं

args = method(__method__).parameters.map { |arg| arg[1].to_s }

आप तब प्रत्येक पैरामीटर का नाम और मान प्रदर्शित कर सकते हैं

logger.error "Method failed with " + args.map { |arg| "#{arg} = #{eval arg}" }.join(', ')

नोट: चूंकि यह उत्तर मूल रूप से लिखा गया था, रूबी के वर्तमान संस्करणों में evalअब एक प्रतीक के साथ नहीं बुलाया जा सकता है। यह पता करने to_sके लिए, पैरामीटर नाम की सूची बनाते समय एक स्पष्ट जोड़ा गया हैparameters.map { |arg| arg[1].to_s }


4
मुझे इस समय को समझने के लिए कुछ समय की आवश्यकता है :)
हारिस क्रेजीना

3
मुझे पता है कि किन बिट्स की आवश्यकता होती है और मैं कुछ स्पष्टीकरण
दूंगा

3
+1 यह वास्तव में भयानक और सुरुचिपूर्ण है; निश्चित रूप से सबसे अच्छा जवाब।
एंड्रयू मार्शल

5
मैंने रूबी 1.9.3 के साथ कोशिश की, और आपको इसे काम करने के लिए # {eval arg.to_s} करना होगा, अन्यथा आपको टाइप टाइप मिलता है: प्रतीक को स्ट्रिंग में नहीं बदल सकते
जावेद जमाते

5
इस बीच, बेहतर और मेरे कौशल और अब इस कोड को समझें।
हरिस क्रिजिना

55

रूबी 2.1 के बाद से आप किसी भी स्थानीय चर के मान को पढ़ने के लिए बाइंडिंग का उपयोग कर सकते हैं । विधि के मापदंडों (तर्कों) सहित किसी भी स्थानीय चर का मूल्य पढ़ें। इसके लिए धन्यवाद कि आप बचने के लिए स्वीकृत उत्तर में सुधार कर सकते हैंबुराई eval।

def foo(x, y)
  method(__method__).parameters.map do |_, name|
    binding.local_variable_get(name)
  end
end

foo(1, 2)  # => 1, 2

जल्द से जल्द 2.1 है?
uchuugaka

@uchuugaka हाँ, यह विधि <2.1 में उपलब्ध नहीं है।
जैकब जिरुटका

धन्यवाद। यह इसे अच्छा बनाता है: logger.info विधि __ + "# {args.inspect}" विधि ( _ethethod ) .parameters.map do | , नाम | logger.info "# {name} =" + बाइंडिंग।लोकल_परिवर्तन_गेट (नाम) अंत
मार्टिन क्लीवर

जाने का यह रास्ता है।
अर्दी अराम

1
इसके अलावा संभावित रूप से उपयोगी - तर्कों को एक नामित हैश में बदलना:Hash[method(__method__).parameters.map.collect { |_, name| [name, binding.local_variable_get(name)] }]
शबा

19

इसे संभालने का एक तरीका है:

def foo(*args)
    first_name, last_name, age, sex, is_plumber = *args
    # some code
    # error happens here
    logger.error "Method has failed, here are all method arguments #{args.inspect}"    
end

2
जब तक बेहतर उत्तर नहीं मिलते, तब तक कार्य करना और स्वीकार किया जाना चाहिए, इसके साथ मेरी एकमात्र समस्या यह है कि मैं विधि हस्ताक्षर खोना नहीं चाहता, कुछ में इंटेलीजेंस होगा और मैं इसे खोने से नफरत करूंगा।
हरिस क्रजिना

7

यह एक दिलचस्प सवाल है। शायद local_variables का उपयोग कर रहे हैं ? लेकिन eval का उपयोग करने के अलावा भी कोई रास्ता होना चाहिए। मैं कर्नेल डॉक्टर में देख रहा हूं

class Test
  def method(first, last)
    local_variables.each do |var|
      puts eval var.to_s
    end
  end
end

Test.new().method("aaa", 1) # outputs "aaa", 1

यह इतना बुरा नहीं है, यह दुष्ट समाधान क्यों है?
हरिस क्रजिना

यह इस मामले में बुरा नहीं है - eval () का उपयोग करके कभी-कभी सुरक्षा छेद हो सकते हैं। बस मुझे लगता है कि एक बेहतर तरीका हो सकता है :) लेकिन मैं मानता हूं कि Google इस मामले में हमारा दोस्त नहीं है
रफेल

मैं इसके साथ जाने जा रहा हूं, नकारात्मक पक्ष यह है कि आप सहायक (मॉड्यूल) नहीं बना सकते हैं जो इस बात का ध्यान रखेगा, क्योंकि जैसे ही यह मूल विधि छोड़ता है यह स्थानीय संस्करण का उपयोग नहीं कर सकता। सभी जानकारी के लिए धन्यवाद।
हारिस क्रजिना

जब तक मैं इसे नहीं बदल देता, यह मुझे "TypeError: Symbol को String में नहीं बदल सकता" eval var.to_s। इसके अलावा, इसके लिए एक चेतावनी यह है कि यदि आप इस लूप को चलाने से पहले किसी भी स्थानीय चर को परिभाषित करते हैं , तो उन्हें विधि मापदंडों के अतिरिक्त शामिल किया जाएगा।
एंड्रयू मार्शल

6
यह सबसे सुरुचिपूर्ण और सुरक्षित दृष्टिकोण नहीं है - यदि आप अपने तरीके के अंदर स्थानीय चर को परिभाषित करते हैं और फिर कॉल करते हैं local_variables, तो यह विधि तर्कों + सभी अन्य चर को वापस कर देगा। जब आपका कोड गलत हो सकता है।
अलकासी कलियुचनिकौ

5

यह मददगार हो सकता है ...

  def foo(x, y)
    args(binding)
  end

  def args(callers_binding)
    callers_name = caller[0][/`.*'/][1..-2]
    parameters = method(callers_name).parameters
    parameters.map { |_, arg_name|
      callers_binding.local_variable_get(arg_name)
    }    
  end

1
इस थोड़े से हैकेड इम्प्लीमेंटेशन के बजाय callers_name, आप भी __method__इसके साथ गुजर सकते हैं binding
टॉम लॉर्ड

3

आप एक स्थिरांक को परिभाषित कर सकते हैं जैसे:

ARGS_TO_HASH = "method(__method__).parameters.map { |arg| arg[1].to_s }.map { |arg| { arg.to_sym => eval(arg) } }.reduce Hash.new, :merge"

और इसे अपने कोड में उपयोग करें जैसे:

args = eval(ARGS_TO_HASH)
another_method_that_takes_the_same_arguments(**args)

2

इससे पहले कि मैं आगे बढ़ूँ, आप फू में बहुत सारे तर्क दे रहे हैं। ऐसा लगता है कि सभी तर्क एक मॉडल पर विशेषता हैं, सही? आपको वास्तव में वस्तु को ही पास करना चाहिए। भाषण का अंत।

आप "स्पैट" तर्क का उपयोग कर सकते हैं। यह सब कुछ एक सरणी में बदल देता है। ऐसा लगेगा:

def foo(*bar)
  ...
  log.error "Error with arguments #{bar.joins(', ')}"
end

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

I, बॉब मार्टिन की तुलना में एक होशियार आदमी को उद्धृत करने के लिए, अपनी पुस्तक, क्लीन कोड में, "एक फ़ंक्शन के लिए तर्कों की आदर्श संख्या शून्य (niladic) है। अगला एक (मोनोएडिक) आता है, जिसके बाद दो (डाइएडिक) तीन तर्क होते हैं। (triadic) जहाँ संभव हो वहाँ से बचना चाहिए। तीन से अधिक (polyadic) के लिए बहुत विशेष औचित्य की आवश्यकता है - और फिर वैसे भी नहीं होना चाहिए। " वह कहता है कि मैंने क्या कहा था, कई संबंधित तर्कों को एक वर्ग में लपेटा जाना चाहिए और एक वस्तु के रूप में पारित किया जाना चाहिए। यह एक अच्छी किताब है, मैं इसकी अत्यधिक अनुशंसा करता हूं।
टॉम एल

इस पर एक बिंदु भी ठीक नहीं है, लेकिन इस पर विचार करें: यदि आप पाते हैं कि आपको अधिक / कम / अलग-अलग तर्क चाहिए, तो आपने अपना एपीआई तोड़ दिया होगा और हर कॉल को उस पद्धति पर अपडेट करना होगा। दूसरी ओर, यदि आप कोई वस्तु पास करते हैं तो आपके ऐप के अन्य भाग (या आपकी सेवा के उपभोक्ता) आसानी से साथ-साथ घूम सकते हैं।
टॉम एल

मैं आपकी बातों से सहमत हूं और उदाहरण के लिए जावा में मैं हमेशा आपके दृष्टिकोण को लागू करूंगा। लेकिन मुझे लगता है कि ROR के साथ अलग है और यहाँ क्यों है:
हारिस Krajina

मैं आपकी बातों से सहमत हूं और उदाहरण के लिए जावा में मैं हमेशा आपके दृष्टिकोण को लागू करूंगा। लेकिन मुझे लगता है कि ROR के साथ अलग है और यहाँ है: यदि आप DB को ActiveRecord को सहेजना चाहते हैं और आपके पास विधि है जो इसे सहेजती है तो आपको विधि को सहेजने के लिए पास करने से पहले हैश को इकट्ठा करना होगा। उपयोगकर्ता उदाहरण के लिए हम पहले, अंतिम नाम, उपयोगकर्ता नाम आदि को सेट करते हैं और पास हैश की तुलना में विधि को बचाने के लिए करते हैं जो कुछ करेगा और इसे बचाएगा। और यहाँ समस्या यह है कि हर डेवलपर को पता है कि हैश में क्या रखा जाता है? यह सक्रिय रिकॉर्ड है, इसलिए आपको हैश इकट्ठा करने की तुलना में db स्कीमा को देखना होगा, और किसी भी प्रतीक को याद न करने के लिए बहुत सावधान रहना चाहिए।
हरिस क्रजिना

2

यदि आप विधि हस्ताक्षर बदलते हैं, तो आप कुछ इस तरह से कर सकते हैं:

def foo(*args)
  # some code
  # error happens here
  logger.error "Method has failed, here are all method arguments #{args}"    
end

या:

def foo(opts={})
  # some code
  # error happens here
  logger.error "Method has failed, here are all method arguments #{opts.values}"    
end

इस मामले में, प्रक्षेपित argsया opts.valuesएक सरणी होगी, लेकिन आप joinअगर अल्पविराम पर हो सकते हैं । चियर्स


2

ऐसा लगता है कि इस प्रश्न को पूरा करने की कोशिश कर रहा है कि मैं सिर्फ जारी किए गए एक रत्न के साथ किया जा सकता है, https://github.com/ericbeland/exception_details । यह स्थानीय चर और vlaues (और उदाहरण चर) को बचाया अपवादों से सूचीबद्ध करेगा। देखने लायक हो सकता है...


1
यह अच्छा रत्न है, रेल उपयोगकर्ताओं के लिए मैं भी better_errorsमणि की सिफारिश करूंगा ।
हारिस क्राजिना

1

यदि आपको एक हैश के रूप में तर्क की आवश्यकता है, और आप मापदंडों के मुश्किल निष्कर्षण के साथ विधि के शरीर को प्रदूषित नहीं करना चाहते हैं, तो इसका उपयोग करें:

def mymethod(firstarg, kw_arg1:, kw_arg2: :default)
  args = MethodArguments.(binding) # All arguments are in `args` hash now
  ...
end

इस श्रेणी को अपनी परियोजना में जोड़ें:

class MethodArguments
  def self.call(ext_binding)
    raise ArgumentError, "Binding expected, #{ext_binding.class.name} given" unless ext_binding.is_a?(Binding)
    method_name = ext_binding.eval("__method__")
    ext_binding.receiver.method(method_name).parameters.map do |_, name|
      [name, ext_binding.local_variable_get(name)]
    end.to_h
  end
end

0

यदि फ़ंक्शन कुछ वर्ग के अंदर है तो आप कुछ इस तरह से कर सकते हैं:

class Car
  def drive(speed)
  end
end

car = Car.new
method = car.method(:drive)

p method.parameters #=> [[:req, :speed]] 
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.