Module: Magic::Help
- Defined in:
- lib/magic/help.rb,
lib/magic/help/tracepoint.rb,
lib/magic/help/set_trace_func.rb
Class Method Summary collapse
- .help_method_extract(m) ⇒ Object
-
.postprocess(m, res = nil) ⇒ Object
Magic::Help.postprocess is used to postprocess queries in two cases: * help “Foo.bar” queries - res defined, more hacks * help { Foo.bar } queries - res not defined, fewer hacks.
- .resolve_help_block(&block) ⇒ Object
- .resolve_help_query(*args, &block) ⇒ Object
- .resolve_help_res(res) ⇒ Object
Class Method Details
.help_method_extract(m) ⇒ Object
6 7 8 9 10 11 |
# File 'lib/magic/help.rb', line 6 def self.help_method_extract(m) unless m.inspect =~ %r[\A#<(?:Unbound)?Method: (.*?)>\Z] raise "Cannot parse result of #{m.class}#inspect: #{m.inspect}" end $1.sub(/\A.*?\((.*?)\)(.*)\Z/){ "#{$1}#{$2}" }.sub(/\./, "::").sub(/#<Class:(.*?)>#/) { "#{$1}::" } end |
.postprocess(m, res = nil) ⇒ Object
Magic::Help.postprocess is used to postprocess queries in two cases:
-
help “Foo.bar” queries - res defined, more hacks
-
help { Foo.bar } queries - res not defined, fewer hacks
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/magic/help.rb', line 16 def self.postprocess(m, res=nil) # Kernel#method_missing here means class was found but method wasn't # It is possible that such method exists, it was simply not included. # Example - Time::rfc2822 from time.rb. # # Do not correct it if actual method_missing was called. if res and m == "Kernel#method_missing" m = res unless res =~ /\A(?:.*)(?:\#|::|\.)method_missing\Z/ # Most classes do not override Foo::new, but provide new documentation anyway ! # Two cases are possible # * Class#new is used # * Bar::new is used, for Bar being some ancestor of Foo elsif res and (m =~ /\A(.*)\#new\Z/ or m =~ /\A(.*)::new\Z/) cls = $1 # Do not correct requests for Foo#new # If Foo#new became Class#new, it must have been # by some evil metaclass hackery. # # Foo.new or Foo::new both become Foo::new if res =~ /\A(.*)(::|\.)new\Z/ cls_requested, k = $1, $2 # Condition just to get "Class#new" working correctly # Otherwise it would be changed to "Class::new" m = "#{cls_requested}::new" unless cls == cls_requested end end m end |
.resolve_help_block(&block) ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/magic/help/tracepoint.rb', line 3 def self.resolve_help_block(&block) call_event = nil done = false res = nil # We want to capture calls to method_missing too # This should be available via TracePoint but isn't original_method_missing = BasicObject.instance_method(:method_missing) BasicObject.class_eval do define_method(:method_missing) do |*args| if args.empty? # This is presumably called on self, and without arguments throw :done, {cls: method(:method_missing).owner, meth: :method_missing, self: self} end if self.is_a?(Class) throw :done, {cls: self.singleton_class, meth: args[0], self: self} else throw :done, {cls: self.class, meth: args[0], self: self} end end end trace = TracePoint.new do |ev| next if done case ev.event when :call, :c_call if ev.defined_class == BasicObject and ev.method_id == :method_missing done = true # Let it reach our special handler # elsif ev.self == ArgumentError and ev.method_id == :new # Function was called without full number of arguments # There doesn't seem to be any way to recover from this in ruby 2.x # In 1.8 we'd get extra return event # # It's possible to hack argument name from stack trace, # (with massive hacking) # but not self/class, so it's not most useful else done = true throw :done, {cls: ev.defined_class, meth: ev.method_id, self: ev.self} end else # Ignore everything eles end end call_event = catch(:done) do trace.enable res = yield nil end done = true trace.disable BasicObject.instance_eval do define_method(:method_missing, original_method_missing) # It was originally private, restore it as such private :method_missing end if call_event cls = call_event[:cls] meth = call_event[:meth] bound_self = call_event[:self] is_singleton = (cls.is_a?(Class) and cls.singleton_class?) if is_singleton or meth == :new query = "#{bound_self}::#{meth}" else query = "#{cls}##{meth}" end postprocess(query) else resolve_help_res(res) end end |
.resolve_help_query(*args, &block) ⇒ Object
91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/magic/help.rb', line 91 def self.resolve_help_query(*args, &block) raise ArgumentError, "wrong number of arguments (#{args.size} for 1)" if args.size > 1 raise ArgumentError, "help cannot take both arguments and block" if args.size > 0 and block_given? if block_given? resolve_help_block(&block) elsif args.empty? # No block, no arguments nil else resolve_help_res(args[0]) end end |
.resolve_help_res(res) ⇒ Object
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/magic/help.rb', line 46 def self.resolve_help_res(res) query = case res when Module res.to_s when UnboundMethod, Method help_method_extract(res) when /\A(.*)(#|::|\.)(.*)\Z/ cp, k, m = $1, $2, $3 begin # For multielement paths like File::Stat const_get must be # called multiple times, that is: # Object.const_get("File").const_get("Stat") cls = cp.split(/::/).inject(Object){|c, path_elem| c.const_get(path_elem) } case k when "#" m = cls.instance_method(m) m = help_method_extract(m) when "::" m = cls.method(m) # Make sure a module method is returned # It fixes `Class::new' resolving to `Class#new' # (Class::new and Class#new are the same thing, # but their documentations are different) m = help_method_extract(m) m = m.sub(/\#/, "::") if cls == Class && m == "Class#new" when "." begin m = cls.instance_method(m) rescue NameError m = cls.method(m) end m = help_method_extract(m) end postprocess(m, res) rescue NameError res end when String res else res.class.to_s end query end |