रनटाइम पर एक विधि को कैसे परिभाषित किया जाए?


328

हमें हाल ही में एक समस्या हुई थी, जहां कई तरह के कमिटमेंट आने के बाद, बैकएंड प्रक्रिया चलने में विफल रही। अब, हम अच्छे छोटे लड़के और लड़कियां थे और rake testहर चेक-इन के बाद भागते थे , लेकिन रेल की लाइब्रेरी लोडिंग में कुछ विषमताओं के कारण, यह केवल तब हुआ जब हमने इसे सीधे प्रोडक्शन मोड में मोंगरेल से चलाया।

मैंने बग को नीचे ट्रैक किया और यह एक नई रेल्स मणि के कारण स्ट्रिंग क्लास में एक तरह से एक तरीका था जिसने रनटाइम रेल कोड में एक संकीर्ण उपयोग को तोड़ दिया।

वैसे भी, लंबी कहानी छोटी है, क्या रूबी से पूछने के लिए कोई रास्ता है, जहां एक विधि को परिभाषित किया गया है? कुछ ऐसा ही whereami( :foo )रिटर्न /path/to/some/file.rb line #45? इस मामले में, मुझे यह बताना कि यह कक्षा में परिभाषित किया गया था स्ट्रिंग अनहेल्दी होगा, क्योंकि यह कुछ पुस्तकालय द्वारा अतिभारित था।

मैं अपने प्रोजेक्ट में जीवन की गारंटी नहीं दे सकता, इसलिए मेरे लिए ग्रेपिंग 'def foo'जरूरी नहीं है कि मुझे क्या चाहिए, यह उल्लेख करने के लिए नहीं कि मेरे पास कई हैं def foo , कभी-कभी मुझे रनटाइम तक पता नहीं होता है कि मैं किसका उपयोग कर सकता हूं।


1
रूबी 1.8.7 में, विशेष रूप से इस जानकारी को खोजने के लिए एक विशेष विधि जोड़ी गई (और यह अभी भी 1.9.3 में है) ... नीचे मेरे उत्तर में विवरण।
एलेक्स डी

जवाबों:


419

यह वास्तव में देर हो चुकी है, लेकिन यहां बताया गया है कि आप कैसे पा सकते हैं जहां एक विधि परिभाषित है:

http://gist.github.com/76951

# How to find out where a method comes from.
# Learned this from Dave Thomas while teaching Advanced Ruby Studio
# Makes the case for separating method definitions into
# modules, especially when enhancing built-in classes.
module Perpetrator
  def crime
  end
end

class Fixnum
  include Perpetrator
end

p 2.method(:crime) # The "2" here is an instance of Fixnum.
#<Method: Fixnum(Perpetrator)#crime>

यदि आप रूबी 1.9+ पर हैं, तो आप उपयोग कर सकते हैं source_location

require 'csv'

p CSV.new('string').method(:flock)
# => #<Method: CSV#flock>

CSV.new('string').method(:flock).source_location
# => ["/path/to/ruby/1.9.2-p290/lib/ruby/1.9.1/forwardable.rb", 180]

ध्यान दें कि यह सब कुछ पर काम नहीं करेगा, जैसे देशी संकलित कोड। विधि वर्ग कुछ साफ काम करता है, भी हैं, जैसे विधि # मालिक जो फ़ाइल जहां विधि परिभाषित किया गया है देता है।

संपादित करें: यह भी देखें __file__और __line__अन्य जवाब में और REE के लिए नोट, वे भी काम कर रहे हैं। - wg


1
source_location एक्टाइवसुपोर्ट-2.3.14 का उपयोग करके 1.8.7-p334 के लिए काम करता प्रतीत होता है
जेफ

विधि खोजने के बाद, विधि का ownerतरीका आज़माएं
Juguang

1
नंबर दो में क्या है 2.method(:crime)?
स्टैक

1
कक्षा का एक उदाहरणFixnum
किट्टीह

1
महत्वपूर्ण नोट: यह किसी भी गतिशील रूप से परिभाषित तरीकों को नहीं खींचेगा method_missing। इसलिए यदि आपके पास एक मॉड्यूल या पूर्वज वर्ग है class_evalया उसके define_methodअंदर है method_missing, तो यह विधि काम नहीं करेगी।
cdpalmer

83

आप वास्तव में उपरोक्त समाधान की तुलना में थोड़ा आगे जा सकते हैं। रूबी 1.8 एंटरप्राइज संस्करण के लिए, इंस्टेंस पर तरीके __file__और __line__तरीके हैं Method:

require 'rubygems'
require 'activesupport'

m = 2.days.method(:ago)
# => #<Method: Fixnum(ActiveSupport::CoreExtensions::Numeric::Time)#ago>

m.__file__
# => "/Users/james/.rvm/gems/ree-1.8.7-2010.01/gems/activesupport-2.3.8/lib/active_support/core_ext/numeric/time.rb"
m.__line__
# => 64

रूबी 1.9 और उससे आगे के लिए, source_location(धन्यवाद जोनाथन!):

require 'active_support/all'
m = 2.days.method(:ago)
# => #<Method: Fixnum(Numeric)#ago>    # comes from the Numeric module

m.source_location   # show file and line
# => ["/var/lib/gems/1.9.1/gems/activesupport-3.0.6/.../numeric/time.rb", 63]

2
मुझे "NoMethodError: अपरिभाषित विधि" दोनों के लिए __file__और __line__किसी भी Methodवर्ग उदाहरण पर, पूर्व: मिलता है method(:method).__file__
विक्रांत चौधरी

माणिक का कौन सा संस्करण आपके पास है?
जेम्स एडम

रूबी 1.8.7 (2010-06-23 पैचवल 299) [x86_64-linux]
विक्रांत चौधरी

19
रूबी 1.9 पर, m.__file__और m.__line__उसके साथ बदल दिया गया है m.source_location
जोनाथन ट्रान

रूबी 2.1 के बारे में क्या?
लोकेश

38

मैं इस धागे के लिए देर से आ रहा हूं, और आश्चर्यचकित हूं कि किसी ने उल्लेख नहीं किया है Method#owner

class A; def hello; puts "hello"; end end
class B < A; end
b = B.new
b.method(:hello).owner
=> A

2
मुझे आश्चर्य है कि आप विधि वर्ग को स्पष्ट रूप से संदर्भित करने वाले पहले व्यक्ति हैं । एक और कम ज्ञात खजाना 1.9 में पेश किया गया Method#parameters:।
fny

13

एक नए समान प्रश्न से मेरे उत्तर की नकल करना जो इस समस्या में नई जानकारी जोड़ता है।

रूबी 1.9 में source_location नामक विधि है :

रूबी स्रोत फ़ाइल नाम और लाइन नंबर इस विधि या शून्य से देता है यदि यह विधि रूबी (यानी मूल) में परिभाषित नहीं की गई है

इस मणि द्वारा इसे 1.8.7 पर वापस भेज दिया गया है :

तो आप विधि के लिए अनुरोध कर सकते हैं:

m = Foo::Bar.method(:create)

और फिर source_locationउस विधि के लिए पूछें :

m.source_location

यह फ़ाइल नाम और लाइन नंबर के साथ एक सरणी लौटाएगा। ActiveRecord::Base#validatesइस रिटर्न के लिए जैसे :

ActiveRecord::Base.method(:validates).source_location
# => ["/Users/laas/.rvm/gems/ruby-1.9.2-p0@arveaurik/gems/activemodel-3.2.2/lib/active_model/validations/validates.rb", 81]

कक्षाओं और मॉड्यूल के लिए, रूबी समर्थन में निर्मित की पेशकश नहीं करता है, लेकिन वहां एक उत्कृष्ट जिस्ट है जो source_locationकिसी भी विधि के लिए फ़ाइल को वापस करने या किसी वर्ग के लिए पहली फ़ाइल बनाने के लिए बनाता है यदि कोई विधि निर्दिष्ट नहीं की गई थी:

कार्रवाई में:

where_is(ActiveRecord::Base, :validates)

# => ["/Users/laas/.rvm/gems/ruby-1.9.2-p0@arveaurik/gems/activemodel-3.2.2/lib/active_model/validations/validates.rb", 81]

मैक के साथ टेक्स्टमैट स्थापित होने पर, यह निर्दिष्ट स्थान पर संपादक को पॉप अप भी करता है।


7

यह मदद कर सकता है लेकिन आपको इसे स्वयं कोड करना होगा। ब्लॉग से अतीत:

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

http://scie.nti.st/2008/9/17/making-methods-immutable-in-ruby


6

यदि आप विधि को क्रैश कर सकते हैं, तो आपको एक बैकट्रेस मिलेगा जो आपको बताएगा कि वास्तव में यह कहाँ है।

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

दुर्घटनाग्रस्त तरीकों के उपयोगी तरीके:

  1. पास करें nilजहां यह मना करता है - बहुत समय यह विधि एक ArgumentErrorया कभी-कभी NoMethodErrorएक शून्य वर्ग पर खड़ी करेगी ।
  2. यदि आपके पास विधि का ज्ञान है, और आप जानते हैं कि विधि बदले में किसी अन्य विधि को बुलाती है, तो आप दूसरी विधि को अधिलेखित कर सकते हैं, और उसके अंदर उठा सकते हैं।

यदि आपके पास कोड तक पहुंच है, तो आप आसानी require 'ruby-debug'; debugger से अपने कोड में आसानी से सम्मिलित कर सकते हैं जहां आप ड्रॉप करना चाहते हैं।
टिन मैन

"दुर्भाग्य से, यदि आप इसे क्रैश नहीं कर सकते हैं तो आप यह पता नहीं लगा सकते हैं कि इसे कहाँ परिभाषित किया गया है।" यह सच नहीं है। से अन्य उत्तर।
मार्टिन टी।

6

शायद यह #source_locationपता लगाने में मदद कर सकता है कि विधि कहां से है।

उदाहरण के लिए:

ModelName.method(:has_one).source_location

वापसी

[project_path/vendor/ruby/version_number/gems/activerecord-number/lib/active_record/associations.rb", line_number_of_where_method_is]

या

ModelName.new.method(:valid?).source_location

वापसी

[project_path/vendor/ruby/version_number/gems/activerecord-number/lib/active_record/validations.rb", line_number_of_where_method_is]

4

बहुत देर से जवाब :) लेकिन पहले के जवाबों ने मेरी मदद नहीं की

set_trace_func proc{ |event, file, line, id, binding, classname|
  printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname
}
# call your method
set_trace_func nil

क्यों गुजर रहे हो nil?
अरुप रक्षित

@ArupRakshit से प्रलेखन: «ट्रेसिंग के लिए हैंडलर के रूप में खरीद की स्थापना करता है, या यदि पैरामीटर nil»।
टीआईजी

3

आप ऐसा कुछ करने में सक्षम हो सकते हैं:

foo_finder.rb:

 class String
   def String.method_added(name)
     if (name==:foo)
        puts "defining #{name} in:\n\t"
        puts caller.join("\n\t")
     end
   end
 end

फिर सुनिश्चित करें कि foo_finder को पहले कुछ इस तरह से लोड किया गया है

ruby -r foo_finder.rb railsapp

(मैं केवल रेल के साथ गड़बड़ कर रहा हूं, इसलिए मुझे ठीक से पता नहीं है, लेकिन मुझे लगता है कि इसे इस तरह से शुरू करने का एक तरीका है।)

यह आपको स्ट्रिंग # फू की सभी पुन: परिभाषाएं दिखाएगा। थोड़ी मेटा-प्रोग्रामिंग के साथ, आप जो भी फ़ंक्शन चाहते हैं, उसके लिए इसे सामान्य कर सकते हैं। लेकिन यह उस फ़ाइल को पहले से लोड करने की आवश्यकता है जो वास्तव में फिर से परिभाषा करता है।


3

आप हमेशा पा सकते हैं कि आप उपयोग करके कहां हैं caller()


1
यह आपको खोजने के लिए उपयोगी है जो आपको बुलाया जाता है, लेकिन अच्छा नहीं है जब आप यह खोजने की कोशिश कर रहे हैं कि कुछ कहाँ परिभाषित किया गया था।
टीन मैन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.