Module: SourceLocationDesc

Included in:
Method, UnboundMethod
Defined in:
lib/method_describer/method_desc.rb

Instance Method Summary collapse

Instance Method Details

#desc(want_just_summary = false, want_the_description_returned = false) ⇒ Object

add a Method#desc which spits out all it knows about that method ri, location, local ri, etc. TODO does this work with class methods?



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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/method_describer/method_desc.rb', line 19

def desc want_just_summary = false, want_the_description_returned = false
  doc = []
  # to_s is something like "#<Method: String#strip>"
  # or #<Method: GiftCertsControllerTest(Test::Unit::TestCase)#get>
  # or "#<Method: A.go>"
  # or "#<Method: Order(id: integer, order_number: integer).get_cc_processor>"
  # or "#<Method: Order(id: integer, order_number: integer)(ActiveRecord::Base).get_cc_processor>"

  string = to_s

  # derive class_name
  parenthese_count = string.count '('

  if parenthese_count== 1
    # case #<Method: GiftCertsControllerTest(Test::Unit::TestCase)#get>
    # case #<Method: Order(id: integer, order_number: integer).get_cc_processor>
    if string.include? "id: " # TODO huh?
      string =~ /Method: (.+)\(/
    else
      string =~ /\(([^\(]+)\)[\.#]/ # extract out what is between last parentheses
    end
    class_name = $1
  elsif parenthese_count == 0
    # case "#<Method: A.go>"
    string =~ /Method: ([^#\.]+)/
    class_name = $1
  elsif parenthese_count == 2
    # case "#<Method: Order(id: integer, order_number: integer)(ActiveRecord::Base).get_cc_processor>"
    string =~ /\(([^\(]+)\)[\.#]/
    class_name = $1
  else
    raise 'bad ' + string
  end

  # now get method name, type
  string =~ /Method: .*([#\.])(.*)>/ # include the # or .
  joiner = $1
  method_name = $2
  full_name = "#{class_name}#{joiner}#{method_name}"
  puts "sig: #{to_s}      arity: #{arity}"
  # TODO add to doc, I want it before ri for now though, and only once, so not there yet :)


  # now gather up any other information we now about it, in case there are no rdocs

  if !(respond_to? :source_location)
    # pull out names for 1.8
    begin
      klass = eval(class_name)
      # we don't call to_ruby to overcome ruby2ruby bugs... http://rubyforge.org/tracker/index.php?func=detail&aid=26891&group_id=1513&atid=5921
      if joiner == '#'
        code = RubyToRuby.new.process(ParseTree.translate(klass, method_name))
      else
        code = RubyToRuby.new.process(ParseTree.translate(klass.singleton_class, method_name))
      end
      doc << code
      args = Arguments.names( klass, method_name) rescue Arguments.names(klass.singleton_class, method_name)
      out = []
      args.each{|arg_pair|
        out << arg_pair.join(' = ')
      } if args
      out = out.join(', ')
      return out if want_just_summary

      param_string = "Parameters: #{method_name}(" + out + ")" 
      doc << param_string unless want_the_description_returned
    rescue Exception => e
      puts "fail to parse tree: #{class_name} #{e} #{e.backtrace}" if $VERBOSE
	doc << ParseTree.translate(klass, method_name)
    end
  else
    # 1.9.x
    file, line = source_location
    if file
      # then it's a pure ruby method
      doc << "at #{file}:#{line}"
      all_lines = File.readlines(file)
      head_and_sig = all_lines[0...line]
      sig = head_and_sig[-1]
      head = head_and_sig[0..-2]

      doc << sig
      head.reverse_each do |line|
        break unless line =~ /^\s*#(.*)/
        doc.unshift "     " + $1.strip
      end

      # now the real code will end with 'end' same whitespace as the first
      sig_white_space = sig.scan(/\W+/)[0]
      body = all_lines[line..-1]
      body.each{|line|
        doc << line
        if line.start_with?(sig_white_space + "end")
          break
        end
      }
      # how do I get the rest now?
      already_got_ri = true
      return sig + "\n" + head[0] if want_just_summary
    else
      doc << 'appears to be a c method'
    end
    param_string = to_s
    if respond_to? :parameters
      doc << "Original code signature: %s" % sig.to_s.strip if sig
      doc << "#parameters signature: %s( %p )" % [name, parameters]
    end
  end

  puts doc # always output it since RI does currently [todo make optional I suppose, and non out-putty]


  # now run default RI for it
  begin
    puts 'searching ri for ' + full_name + "..."
    RDoc::RI::Driver.run [full_name, '--no-pager'] unless want_just_summary
  rescue *[StandardError, SystemExit]
    # not found
  ensure
    puts '(end ri)'
  end unless already_got_ri

  if want_the_description_returned # give them something they can examine
    doc
  else
    param_string
  end
end