1##
2# AnyMethod is the base class for objects representing methods
3
4class RDoc::AnyMethod < RDoc::MethodAttr
5
6  ##
7  # 2::
8  #   RDoc 4
9  #   Added calls_super
10  #   Added parent name and class
11  #   Added section title
12
13  MARSHAL_VERSION = 2 # :nodoc:
14
15  ##
16  # Don't rename \#initialize to \::new
17
18  attr_accessor :dont_rename_initialize
19
20  ##
21  # The C function that implements this method (if it was defined in a C file)
22
23  attr_accessor :c_function
24
25  ##
26  # Different ways to call this method
27
28  attr_accessor :call_seq
29
30  ##
31  # Parameters for this method
32
33  attr_accessor :params
34
35  ##
36  # If true this method uses +super+ to call a superclass version
37
38  attr_accessor :calls_super
39
40  include RDoc::TokenStream
41
42  ##
43  # Creates a new AnyMethod with a token stream +text+ and +name+
44
45  def initialize text, name
46    super
47
48    @c_function = nil
49    @dont_rename_initialize = false
50    @token_stream = nil
51    @calls_super = false
52    @superclass_method = nil
53  end
54
55  ##
56  # Adds +an_alias+ as an alias for this method in +context+.
57
58  def add_alias an_alias, context = nil
59    method = self.class.new an_alias.text, an_alias.new_name
60
61    method.record_location an_alias.file
62    method.singleton = self.singleton
63    method.params = self.params
64    method.visibility = self.visibility
65    method.comment = an_alias.comment
66    method.is_alias_for = self
67    @aliases << method
68    context.add_method method if context
69    method
70  end
71
72  ##
73  # Prefix for +aref+ is 'method'.
74
75  def aref_prefix
76    'method'
77  end
78
79  ##
80  # The call_seq or the param_seq with method name, if there is no call_seq.
81  #
82  # Use this for displaying a method's argument lists.
83
84  def arglists
85    if @call_seq then
86      @call_seq
87    elsif @params then
88      "#{name}#{param_seq}"
89    end
90  end
91
92  ##
93  # Dumps this AnyMethod for use by ri.  See also #marshal_load
94
95  def marshal_dump
96    aliases = @aliases.map do |a|
97      [a.name, parse(a.comment)]
98    end
99
100    [ MARSHAL_VERSION,
101      @name,
102      full_name,
103      @singleton,
104      @visibility,
105      parse(@comment),
106      @call_seq,
107      @block_params,
108      aliases,
109      @params,
110      @file.relative_name,
111      @calls_super,
112      @parent.name,
113      @parent.class,
114      @section.title,
115    ]
116  end
117
118  ##
119  # Loads this AnyMethod from +array+.  For a loaded AnyMethod the following
120  # methods will return cached values:
121  #
122  # * #full_name
123  # * #parent_name
124
125  def marshal_load array
126    initialize_visibility
127
128    @dont_rename_initialize = nil
129    @is_alias_for           = nil
130    @token_stream           = nil
131    @aliases                = []
132    @parent                 = nil
133    @parent_name            = nil
134    @parent_class           = nil
135    @section                = nil
136    @file                   = nil
137
138    version        = array[0]
139    @name          = array[1]
140    @full_name     = array[2]
141    @singleton     = array[3]
142    @visibility    = array[4]
143    @comment       = array[5]
144    @call_seq      = array[6]
145    @block_params  = array[7]
146    #                      8 handled below
147    @params        = array[9]
148    #                      10 handled below
149    @calls_super   = array[11]
150    @parent_name   = array[12]
151    @parent_title  = array[13]
152    @section_title = array[14]
153
154    array[8].each do |new_name, comment|
155      add_alias RDoc::Alias.new(nil, @name, new_name, comment, @singleton)
156    end
157
158    @parent_name ||= if @full_name =~ /#/ then
159                       $`
160                     else
161                       name = @full_name.split('::')
162                       name.pop
163                       name.join '::'
164                     end
165
166    @file = RDoc::TopLevel.new array[10] if version > 0
167  end
168
169  ##
170  # Method name
171  #
172  # If the method has no assigned name, it extracts it from #call_seq.
173
174  def name
175    return @name if @name
176
177    @name = @call_seq[/^.*?\.(\w+)/, 1] || @call_seq if @call_seq
178  end
179
180  ##
181  # A list of this method's method and yield parameters.  +call-seq+ params
182  # are preferred over parsed method and block params.
183
184  def param_list
185    if @call_seq then
186      params = @call_seq.split("\n").last
187      params = params.sub(/.*?\((.*)\)/, '\1')
188      params = params.sub(/(\{|do)\s*\|([^|]*)\|.*/, ',\2')
189    elsif @params then
190      params = @params.sub(/\((.*)\)/, '\1')
191
192      params << ",#{@block_params}" if @block_params
193    elsif @block_params then
194      params = @block_params
195    else
196      return []
197    end
198
199    params = params.gsub(/\s+/, '').split ','
200
201    params.map { |param| param.sub(/=.*/, '') }
202  end
203
204  ##
205  # Pretty parameter list for this method.  If the method's parameters were
206  # given by +call-seq+ it is preferred over the parsed values.
207
208  def param_seq
209    if @call_seq then
210      params = @call_seq.split("\n").last
211      params = params.sub(/[^( ]+/, '')
212      params = params.sub(/(\|[^|]+\|)\s*\.\.\.\s*(end|\})/, '\1 \2')
213    elsif @params then
214      params = @params.gsub(/\s*\#.*/, '')
215      params = params.tr("\n", " ").squeeze(" ")
216      params = "(#{params})" unless params[0] == ?(
217    else
218      params = ''
219    end
220
221    if @block_params then
222      # If this method has explicit block parameters, remove any explicit
223      # &block
224      params.sub!(/,?\s*&\w+/, '')
225
226      block = @block_params.gsub(/\s*\#.*/, '')
227      block = block.tr("\n", " ").squeeze(" ")
228      if block[0] == ?(
229        block.sub!(/^\(/, '').sub!(/\)/, '')
230      end
231      params << " { |#{block}| ... }"
232    end
233
234    params
235  end
236
237  ##
238  # Sets the store for this method and its referenced code objects.
239
240  def store= store
241    super
242
243    @file = @store.add_file @file.full_name if @file
244  end
245
246  ##
247  # For methods that +super+, find the superclass method that would be called.
248
249  def superclass_method
250    return unless @calls_super
251    return @superclass_method if @superclass_method
252
253    parent.each_ancestor do |ancestor|
254      if method = ancestor.method_list.find { |m| m.name == @name } then
255        @superclass_method = method
256        break
257      end
258    end
259
260    @superclass_method
261  end
262
263end
264
265