1 | ######################################################################
|
---|
2 |
|
---|
3 | class RiError < Exception; end
|
---|
4 | #
|
---|
5 | # Break argument into its constituent class or module names, an
|
---|
6 | # optional method type, and a method name
|
---|
7 |
|
---|
8 | class NameDescriptor
|
---|
9 |
|
---|
10 | attr_reader :class_names
|
---|
11 | attr_reader :method_name
|
---|
12 |
|
---|
13 | # true and false have the obvious meaning. nil means we don't care
|
---|
14 | attr_reader :is_class_method
|
---|
15 |
|
---|
16 | # arg may be
|
---|
17 | # 1. a class or module name (optionally qualified with other class
|
---|
18 | # or module names (Kernel, File::Stat etc)
|
---|
19 | # 2. a method name
|
---|
20 | # 3. a method name qualified by a optionally fully qualified class
|
---|
21 | # or module name
|
---|
22 | #
|
---|
23 | # We're fairly casual about delimiters: folks can say Kernel::puts,
|
---|
24 | # Kernel.puts, or Kernel\#puts for example. There's one exception:
|
---|
25 | # if you say IO::read, we look for a class method, but if you
|
---|
26 | # say IO.read, we look for an instance method
|
---|
27 |
|
---|
28 | def initialize(arg)
|
---|
29 | @class_names = []
|
---|
30 | separator = nil
|
---|
31 |
|
---|
32 | tokens = arg.split(/(\.|::|#)/)
|
---|
33 |
|
---|
34 | # Skip leading '::', '#' or '.', but remember it might
|
---|
35 | # be a method name qualifier
|
---|
36 | separator = tokens.shift if tokens[0] =~ /^(\.|::|#)/
|
---|
37 |
|
---|
38 | # Skip leading '::', but remember we potentially have an inst
|
---|
39 |
|
---|
40 | # leading stuff must be class names
|
---|
41 |
|
---|
42 | while tokens[0] =~ /^[A-Z]/
|
---|
43 | @class_names << tokens.shift
|
---|
44 | unless tokens.empty?
|
---|
45 | separator = tokens.shift
|
---|
46 | break unless separator == "::"
|
---|
47 | end
|
---|
48 | end
|
---|
49 |
|
---|
50 | # Now must have a single token, the method name, or an empty
|
---|
51 | # array
|
---|
52 | unless tokens.empty?
|
---|
53 | @method_name = tokens.shift
|
---|
54 | # We may now have a trailing !, ?, or = to roll into
|
---|
55 | # the method name
|
---|
56 | if !tokens.empty? && tokens[0] =~ /^[!?=]$/
|
---|
57 | @method_name << tokens.shift
|
---|
58 | end
|
---|
59 |
|
---|
60 | if @method_name =~ /::|\.|#/ or !tokens.empty?
|
---|
61 | raise RiError.new("Bad argument: #{arg}")
|
---|
62 | end
|
---|
63 | if separator && separator != '.'
|
---|
64 | @is_class_method = separator == "::"
|
---|
65 | end
|
---|
66 | end
|
---|
67 | end
|
---|
68 |
|
---|
69 | # Return the full class name (with '::' between the components)
|
---|
70 | # or "" if there's no class name
|
---|
71 |
|
---|
72 | def full_class_name
|
---|
73 | @class_names.join("::")
|
---|
74 | end
|
---|
75 | end
|
---|