ruby on rails रनटाइम पर विधि कहां परिभाषित की जाती है?




ruby-on-rails runtime (8)

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

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

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

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


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

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

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

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


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

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]

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

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

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

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

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


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

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

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

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)
#<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]

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

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


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

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

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

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

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

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

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

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

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

m.source_location

यह फ़ाइल नाम और रेखा संख्या के साथ एक सरणी वापस करेगा। ActiveRecord::Base#validates इस रिटर्न को ActiveRecord::Base#validates करता है:

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

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

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

where_is(ActiveRecord::Base, :validates)

# => ["/Users/laas/.rvm/gems/[email protected]/gems/activemodel-3.2.2/lib/active_model/validations/validates.rb", 81]

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





definition