method-args 0.0.5 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile.lock +1 -1
- data/lib/method_args.rb +32 -187
- data/lib/method_args/args.rb +66 -0
- data/lib/method_args/method_mixins.rb +11 -0
- data/lib/method_args/method_registry.rb +19 -0
- data/lib/method_args/processor.rb +86 -0
- data/lib/method_args/version.rb +1 -1
- data/test/fixtures/5.rb +17 -0
- data/test/test_nested.rb +12 -0
- metadata +22 -3
data/Gemfile.lock
CHANGED
data/lib/method_args.rb
CHANGED
@@ -1,207 +1,52 @@
|
|
1
|
+
require 'set'
|
1
2
|
require 'ruby2ruby'
|
2
3
|
require 'ruby_parser'
|
3
4
|
require 'sexp_processor'
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
end
|
11
|
-
self.args = owner.const_get(:ArgList)[name.to_sym]
|
12
|
-
end
|
13
|
-
|
14
|
-
def args=(a)
|
15
|
-
@args = a.clone
|
16
|
-
@args.owning_method = self
|
17
|
-
@args
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
Method.send(:include, MethodMixin)
|
22
|
-
UnboundMethod.send(:include, MethodMixin)
|
23
|
-
|
6
|
+
require 'method_args/method_mixins'
|
7
|
+
require 'method_args/args'
|
8
|
+
require 'method_args/processor'
|
9
|
+
require 'method_args/version'
|
10
|
+
require 'method_args/method_registry'
|
24
11
|
module MethodArgs
|
25
12
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
attr_accessor :arg_list
|
30
|
-
attr_reader :name, :type
|
31
|
-
|
32
|
-
def initialize(name, type, default = nil)
|
33
|
-
@name, @type, @default = name, type, default
|
34
|
-
end
|
35
|
-
|
36
|
-
def required?
|
37
|
-
@type == :required
|
38
|
-
end
|
39
|
-
|
40
|
-
def optional?
|
41
|
-
@type == :optional
|
42
|
-
end
|
43
|
-
|
44
|
-
def splat?
|
45
|
-
@type == :splat
|
46
|
-
end
|
47
|
-
|
48
|
-
def default_value(receiver = nil)
|
49
|
-
return nil if @default.nil?
|
50
|
-
receiver ||= arg_list.owning_method.receiver if arg_list.owning_method.respond_to?(:receiver)
|
51
|
-
raise "You must specify a receiver for the defaul value" if receiver.nil?
|
52
|
-
raise "You must evaluate defaults in the context of a matching class. #{receiver.class.name} is not a #{@cls.name}." unless receiver.is_a?(arg_list.cls)
|
53
|
-
receiver.instance_eval(@default)
|
54
|
-
end
|
55
|
-
end
|
13
|
+
ClassMethodRegistry = Hash.new{|h, k| h[k] = MethodRegistry.new}
|
14
|
+
FileRegistry = Set.new
|
56
15
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
@cls = cls
|
62
|
-
end
|
63
|
-
|
64
|
-
def clone
|
65
|
-
o = super
|
66
|
-
o.each {|arg| arg.arg_list = o}
|
67
|
-
o
|
68
|
-
end
|
69
|
-
|
70
|
-
def required_size
|
71
|
-
inject(0) {|count, arg| count += arg.required? ? 1 : 0}
|
72
|
-
end
|
73
|
-
alias_method :required_count, :required_size
|
16
|
+
def self.load(file, require_file = true)
|
17
|
+
require file if require_file
|
18
|
+
register file
|
19
|
+
end
|
74
20
|
|
75
|
-
|
76
|
-
|
77
|
-
|
21
|
+
def self.args_for_method(method)
|
22
|
+
k = class_key(method.owner)
|
23
|
+
ClassMethodRegistry[k][method.name.to_sym] if ClassMethodRegistry.key?(k)
|
24
|
+
end
|
78
25
|
|
79
|
-
|
80
|
-
|
26
|
+
def self.register(file)
|
27
|
+
file = expand_path(file)
|
28
|
+
unless FileRegistry.include?(file)
|
29
|
+
FileRegistry << file
|
30
|
+
parse(file).each { |cls, methods| ClassMethodRegistry[cls].add_methods!(methods) }
|
81
31
|
end
|
82
32
|
end
|
83
33
|
|
84
|
-
def self.
|
85
|
-
file = File.expand_path(file)
|
86
|
-
require file if require_file
|
34
|
+
def self.parse(file, require_file = true)
|
87
35
|
parser = RubyParser.new
|
88
|
-
sexp = parser.process(File.read(
|
36
|
+
sexp = parser.process(File.read(expand_path(file)))
|
89
37
|
method_args = Processor.new
|
90
38
|
method_args.process(sexp)
|
39
|
+
method_args.methods
|
91
40
|
end
|
92
41
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
@method_maps = Hash.new{|h,k| h[k] = {}}
|
99
|
-
@current_class = []
|
100
|
-
super()
|
101
|
-
end
|
102
|
-
|
103
|
-
def process_module(exp)
|
104
|
-
exp.shift
|
105
|
-
@current_class << exp.first.to_sym
|
106
|
-
process(exp)
|
107
|
-
@current_class.pop
|
108
|
-
exp.clear
|
109
|
-
exp
|
110
|
-
end
|
111
|
-
|
112
|
-
def process_class(exp)
|
113
|
-
exp.shift
|
114
|
-
current_class_size = @current_class.size
|
115
|
-
case exp.first
|
116
|
-
when Symbol
|
117
|
-
@current_class << exp.first.to_sym
|
118
|
-
process(exp)
|
119
|
-
else
|
120
|
-
if exp.first.first == :colon2
|
121
|
-
exp.first.shift
|
122
|
-
class_exp = exp.shift
|
123
|
-
class_exp[0, class_exp.size - 1].each do |const|
|
124
|
-
@current_class << const.last
|
125
|
-
end
|
126
|
-
@current_class << class_exp.last
|
127
|
-
else
|
128
|
-
raise
|
129
|
-
end
|
130
|
-
exp.shift
|
131
|
-
process(exp.first)
|
132
|
-
end
|
133
|
-
@current_class.slice!(current_class_size, @current_class.size)
|
134
|
-
exp.clear
|
135
|
-
exp
|
136
|
-
end
|
137
|
-
|
138
|
-
def process_defn(exp)
|
139
|
-
exp.shift
|
140
|
-
@current_method = exp.shift
|
141
|
-
@ruby2ruby = Ruby2Ruby.new
|
142
|
-
process_args(exp.shift)
|
143
|
-
scope = exp.shift
|
144
|
-
exp
|
145
|
-
end
|
146
|
-
|
147
|
-
def process_args(exp)
|
148
|
-
exp.shift
|
149
|
-
arg_list = ArgList.new(current_class)
|
150
|
-
while !exp.empty?
|
151
|
-
t = exp.shift
|
152
|
-
case t
|
153
|
-
when Symbol
|
154
|
-
arg_list << case t.to_s[0]
|
155
|
-
when ?* then ArgList::Arg.new(t.to_s[1, t.to_s.size].to_sym, :splat)
|
156
|
-
when ?& then ArgList::Arg.new(t.to_s[1, t.to_s.size].to_sym, :block)
|
157
|
-
else ArgList::Arg.new(t, :required)
|
158
|
-
end
|
159
|
-
when Sexp
|
160
|
-
case t.shift
|
161
|
-
when :block
|
162
|
-
lasgn = t.shift
|
163
|
-
lasgn.shift
|
164
|
-
name = lasgn.shift
|
165
|
-
new_arg = ArgList::Arg.new(name, :optional, @ruby2ruby.process(lasgn.last))
|
166
|
-
arg_list.each_with_index{|arg, idx| arg_list[idx] = new_arg if arg.name == name}
|
167
|
-
end
|
168
|
-
end
|
169
|
-
end
|
170
|
-
@method_maps[current_classname][@current_method] = arg_list
|
171
|
-
add_methods
|
172
|
-
end
|
173
|
-
|
174
|
-
def add_methods
|
175
|
-
unless current_class.method(:const_defined?).arity == -1 ? current_class.const_defined?(:ArgList, false) : current_class.const_defined?(:ArgList)
|
176
|
-
current_class.send(:const_set, :ArgList, @method_maps[current_classname])
|
177
|
-
current_class.module_eval(<<-HERE_DOC, __FILE__, __LINE__)
|
178
|
-
alias_method :__method__, :method
|
179
|
-
|
180
|
-
class << self
|
181
|
-
alias_method :__instance_method__, :instance_method unless method_defined?(:__instance_method__)
|
182
|
-
end
|
183
|
-
|
184
|
-
def self.instance_arg_list(method_name)
|
185
|
-
method = __instance_method__(method_name)
|
186
|
-
if method.owner == self
|
187
|
-
ArgList[method_name] or raise('i don\\'t know this method ' + method_name.inspect)
|
188
|
-
elsif method.owner.respond_to?(:instance_arg_list)
|
189
|
-
method.owner.instance_arg_list(method_name)
|
190
|
-
else
|
191
|
-
raise \"\#{method.owner} has not been loaded with method_args\"
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
HERE_DOC
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
def current_classname
|
200
|
-
@current_class.map{|c| c.to_s}.join('::')
|
201
|
-
end
|
42
|
+
def self.class_key(cls)
|
43
|
+
nesting = cls.class_eval("Module.nesting")
|
44
|
+
nesting.pop
|
45
|
+
nesting.join('::')
|
46
|
+
end
|
202
47
|
|
203
|
-
|
204
|
-
|
205
|
-
|
48
|
+
def self.expand_path(file)
|
49
|
+
file = File.expand_path(file)
|
50
|
+
File.exist?(file) ? file : "#{file}.rb"
|
206
51
|
end
|
207
52
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module MethodArgs
|
2
|
+
class Args < Array
|
3
|
+
class Arg
|
4
|
+
|
5
|
+
attr_accessor :arg_list
|
6
|
+
attr_reader :name, :type
|
7
|
+
|
8
|
+
def initialize(name, type, default = nil)
|
9
|
+
@name, @type, @default = name, type, default
|
10
|
+
end
|
11
|
+
|
12
|
+
def required?
|
13
|
+
@type == :required
|
14
|
+
end
|
15
|
+
|
16
|
+
def optional?
|
17
|
+
@type == :optional
|
18
|
+
end
|
19
|
+
|
20
|
+
def splat?
|
21
|
+
@type == :splat
|
22
|
+
end
|
23
|
+
|
24
|
+
def default?
|
25
|
+
!@default.nil?
|
26
|
+
end
|
27
|
+
|
28
|
+
def default_value(receiver = nil)
|
29
|
+
return nil if @default.nil?
|
30
|
+
receiver ||= arg_list.owning_method.receiver if arg_list.owning_method.respond_to?(:receiver)
|
31
|
+
raise "You must specify a receiver for the defaul value" if receiver.nil?
|
32
|
+
raise "You must evaluate defaults in the context of a matching class. #{receiver.class.name} is not a #{arg_list.cls.name}." unless receiver.is_a?(arg_list.cls)
|
33
|
+
receiver.instance_eval(@default)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
attr_accessor :owning_method
|
38
|
+
|
39
|
+
def initialize(cls)
|
40
|
+
@cls = cls
|
41
|
+
end
|
42
|
+
|
43
|
+
def cls
|
44
|
+
@cls.inject(Module) {|c, m| c.const_get(m)}
|
45
|
+
end
|
46
|
+
|
47
|
+
def clone
|
48
|
+
o = super
|
49
|
+
o.each {|arg| arg.arg_list = o}
|
50
|
+
o
|
51
|
+
end
|
52
|
+
|
53
|
+
def required_size
|
54
|
+
inject(0) {|count, arg| count += arg.required? ? 1 : 0}
|
55
|
+
end
|
56
|
+
alias_method :required_count, :required_size
|
57
|
+
|
58
|
+
def names
|
59
|
+
map(&:name)
|
60
|
+
end
|
61
|
+
|
62
|
+
def types
|
63
|
+
map(&:type)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module MethodMixin
|
2
|
+
def args
|
3
|
+
MethodArgs.register(source_location[0]) if respond_to?(:source_location)
|
4
|
+
args = MethodArgs.args_for_method(self).clone
|
5
|
+
args.owning_method = self
|
6
|
+
args
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
Method.send(:include, MethodMixin)
|
11
|
+
UnboundMethod.send(:include, MethodMixin)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module MethodArgs
|
2
|
+
class MethodRegistry < Hash
|
3
|
+
attr_reader :method_names
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
super
|
7
|
+
@method_names = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_methods!(methods)
|
11
|
+
methods.each do |(method_name, args)|
|
12
|
+
unless key?(method_name)
|
13
|
+
self[method_name] = args
|
14
|
+
method_names << method_name
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module MethodArgs
|
2
|
+
class Processor < SexpProcessor
|
3
|
+
|
4
|
+
attr_reader :methods
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@methods = Hash.new{|h,k| h[k] = []}
|
8
|
+
@current_class = []
|
9
|
+
super()
|
10
|
+
end
|
11
|
+
|
12
|
+
def process_module(exp)
|
13
|
+
exp.shift
|
14
|
+
@current_class << exp.first.to_sym
|
15
|
+
process(exp)
|
16
|
+
@current_class.pop
|
17
|
+
exp.clear
|
18
|
+
exp
|
19
|
+
end
|
20
|
+
|
21
|
+
def process_class(exp)
|
22
|
+
exp.shift
|
23
|
+
current_class_size = @current_class.size
|
24
|
+
case exp.first
|
25
|
+
when Symbol
|
26
|
+
@current_class << exp.first.to_sym
|
27
|
+
process(exp)
|
28
|
+
else
|
29
|
+
if exp.first.first == :colon2
|
30
|
+
exp.first.shift
|
31
|
+
class_exp = exp.shift
|
32
|
+
class_exp[0, class_exp.size - 1].each do |const|
|
33
|
+
@current_class << const.last
|
34
|
+
end
|
35
|
+
@current_class << class_exp.last
|
36
|
+
else
|
37
|
+
raise
|
38
|
+
end
|
39
|
+
exp.shift
|
40
|
+
process(exp.first)
|
41
|
+
end
|
42
|
+
@current_class.slice!(current_class_size, @current_class.size)
|
43
|
+
exp.clear
|
44
|
+
exp
|
45
|
+
end
|
46
|
+
|
47
|
+
def process_defn(exp)
|
48
|
+
exp.shift
|
49
|
+
@current_method = exp.shift
|
50
|
+
@ruby2ruby = Ruby2Ruby.new
|
51
|
+
process_args(exp.shift)
|
52
|
+
exp.shift
|
53
|
+
exp
|
54
|
+
end
|
55
|
+
|
56
|
+
def process_args(exp)
|
57
|
+
exp.shift
|
58
|
+
arg_list = Args.new(@current_class.clone)
|
59
|
+
while !exp.empty?
|
60
|
+
t = exp.shift
|
61
|
+
case t
|
62
|
+
when Symbol
|
63
|
+
arg_list << case t.to_s[0]
|
64
|
+
when ?* then Args::Arg.new(t.to_s[1, t.to_s.size].to_sym, :splat)
|
65
|
+
when ?& then Args::Arg.new(t.to_s[1, t.to_s.size].to_sym, :block)
|
66
|
+
else Args::Arg.new(t, :required)
|
67
|
+
end
|
68
|
+
when Sexp
|
69
|
+
case t.shift
|
70
|
+
when :block
|
71
|
+
lasgn = t.shift
|
72
|
+
lasgn.shift
|
73
|
+
name = lasgn.shift
|
74
|
+
new_arg = Args::Arg.new(name, :optional, @ruby2ruby.process(lasgn.last))
|
75
|
+
arg_list.each_with_index{|arg, idx| arg_list[idx] = new_arg if arg.name == name}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
@methods[current_classname] << [@current_method, arg_list]
|
80
|
+
end
|
81
|
+
|
82
|
+
def current_classname
|
83
|
+
@current_class.map{|c| c.to_s}.join('::')
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
data/lib/method_args/version.rb
CHANGED
data/test/fixtures/5.rb
ADDED
data/test/test_nested.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
class TestNested < MiniTest::Unit::TestCase
|
2
|
+
def setup
|
3
|
+
MethodArgs.load(~'fixtures/5')
|
4
|
+
end
|
5
|
+
|
6
|
+
def test_nested
|
7
|
+
assert_equal 0, Top.instance_method(:one).args.count
|
8
|
+
assert_equal 1, Top::Middle.instance_method(:one).args.count
|
9
|
+
assert_equal 0, Top::Middle.instance_method(:two).args.count
|
10
|
+
assert_equal 1, Top::Middle::End.instance_method(:two).args.count
|
11
|
+
end
|
12
|
+
end
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: method-args
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 0
|
8
|
+
- 1
|
7
9
|
- 0
|
8
|
-
|
9
|
-
version: 0.0.5
|
10
|
+
version: 0.1.0
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Joshua Hull
|
@@ -14,7 +15,7 @@ autorequire:
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date: 2011-01-
|
18
|
+
date: 2011-01-06 00:00:00 -08:00
|
18
19
|
default_executable:
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
@@ -25,6 +26,7 @@ dependencies:
|
|
25
26
|
requirements:
|
26
27
|
- - ~>
|
27
28
|
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
28
30
|
segments:
|
29
31
|
- 2
|
30
32
|
- 0
|
@@ -39,6 +41,7 @@ dependencies:
|
|
39
41
|
requirements:
|
40
42
|
- - ~>
|
41
43
|
- !ruby/object:Gem::Version
|
44
|
+
hash: 23
|
42
45
|
segments:
|
43
46
|
- 1
|
44
47
|
- 2
|
@@ -54,6 +57,7 @@ dependencies:
|
|
54
57
|
requirements:
|
55
58
|
- - ~>
|
56
59
|
- !ruby/object:Gem::Version
|
60
|
+
hash: 15
|
57
61
|
segments:
|
58
62
|
- 3
|
59
63
|
- 0
|
@@ -69,6 +73,7 @@ dependencies:
|
|
69
73
|
requirements:
|
70
74
|
- - ~>
|
71
75
|
- !ruby/object:Gem::Version
|
76
|
+
hash: 23
|
72
77
|
segments:
|
73
78
|
- 0
|
74
79
|
- 0
|
@@ -84,6 +89,7 @@ dependencies:
|
|
84
89
|
requirements:
|
85
90
|
- - ~>
|
86
91
|
- !ruby/object:Gem::Version
|
92
|
+
hash: 23
|
87
93
|
segments:
|
88
94
|
- 1
|
89
95
|
- 0
|
@@ -99,6 +105,7 @@ dependencies:
|
|
99
105
|
requirements:
|
100
106
|
- - ">="
|
101
107
|
- !ruby/object:Gem::Version
|
108
|
+
hash: 3
|
102
109
|
segments:
|
103
110
|
- 0
|
104
111
|
version: "0"
|
@@ -112,6 +119,7 @@ dependencies:
|
|
112
119
|
requirements:
|
113
120
|
- - ">="
|
114
121
|
- !ruby/object:Gem::Version
|
122
|
+
hash: 3
|
115
123
|
segments:
|
116
124
|
- 0
|
117
125
|
version: "0"
|
@@ -125,6 +133,7 @@ dependencies:
|
|
125
133
|
requirements:
|
126
134
|
- - ">="
|
127
135
|
- !ruby/object:Gem::Version
|
136
|
+
hash: 3
|
128
137
|
segments:
|
129
138
|
- 0
|
130
139
|
version: "0"
|
@@ -146,16 +155,22 @@ files:
|
|
146
155
|
- README.markdown
|
147
156
|
- Rakefile
|
148
157
|
- lib/method_args.rb
|
158
|
+
- lib/method_args/args.rb
|
159
|
+
- lib/method_args/method_mixins.rb
|
160
|
+
- lib/method_args/method_registry.rb
|
161
|
+
- lib/method_args/processor.rb
|
149
162
|
- lib/method_args/version.rb
|
150
163
|
- method_args.gemspec
|
151
164
|
- test/fixtures/1.rb
|
152
165
|
- test/fixtures/2.rb
|
153
166
|
- test/fixtures/3.rb
|
154
167
|
- test/fixtures/4.rb
|
168
|
+
- test/fixtures/5.rb
|
155
169
|
- test/helper.rb
|
156
170
|
- test/test_autoload.rb
|
157
171
|
- test/test_block.rb
|
158
172
|
- test/test_bound_method.rb
|
173
|
+
- test/test_nested.rb
|
159
174
|
- test/test_simple.rb
|
160
175
|
- test/test_subclass.rb
|
161
176
|
has_rdoc: true
|
@@ -172,6 +187,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
172
187
|
requirements:
|
173
188
|
- - ">="
|
174
189
|
- !ruby/object:Gem::Version
|
190
|
+
hash: 3
|
175
191
|
segments:
|
176
192
|
- 0
|
177
193
|
version: "0"
|
@@ -180,6 +196,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
180
196
|
requirements:
|
181
197
|
- - ">="
|
182
198
|
- !ruby/object:Gem::Version
|
199
|
+
hash: 3
|
183
200
|
segments:
|
184
201
|
- 0
|
185
202
|
version: "0"
|
@@ -195,9 +212,11 @@ test_files:
|
|
195
212
|
- test/fixtures/2.rb
|
196
213
|
- test/fixtures/3.rb
|
197
214
|
- test/fixtures/4.rb
|
215
|
+
- test/fixtures/5.rb
|
198
216
|
- test/helper.rb
|
199
217
|
- test/test_autoload.rb
|
200
218
|
- test/test_block.rb
|
201
219
|
- test/test_bound_method.rb
|
220
|
+
- test/test_nested.rb
|
202
221
|
- test/test_simple.rb
|
203
222
|
- test/test_subclass.rb
|