method-args 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- method-args (0.0.4)
4
+ method-args (0.0.5)
5
5
  ruby2ruby (~> 1.2.4)
6
6
  ruby_parser (~> 2.0)
7
7
  sexp_processor (~> 3.0.4)
@@ -1,207 +1,52 @@
1
+ require 'set'
1
2
  require 'ruby2ruby'
2
3
  require 'ruby_parser'
3
4
  require 'sexp_processor'
4
5
 
5
- module MethodMixin
6
- def args(trying_load = false)
7
- if !trying_load && respond_to?(:source_location) && !owner.const_defined?(:ArgList, false)
8
- file, line = source_location
9
- MethodArgs.load(file, false)
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
- class ArgList < Array
27
- class Arg
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
- attr_accessor :owning_method
58
- attr_reader :cls
59
-
60
- def initialize(cls)
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
- def names
76
- map(&:name)
77
- end
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
- def types
80
- map(&:type)
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.load(file, require_file = true)
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(File.exist?(file) ? file : "#{file}.rb"))
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
- class Processor < SexpProcessor
94
-
95
- attr_reader :method_maps
96
-
97
- def initialize
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
- def current_class
204
- @current_class.inject(Module) {|c, m| c.const_get(m)}
205
- end
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
@@ -1,3 +1,3 @@
1
1
  module MethodArgs
2
- VERSION = '0.0.5'
2
+ VERSION = '0.1.0'
3
3
  end
@@ -0,0 +1,17 @@
1
+ class Top
2
+ def one
3
+ end
4
+
5
+ class Middle
6
+ def two
7
+ end
8
+
9
+ def one(two)
10
+ end
11
+
12
+ class End
13
+ def two(three)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -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
- - 5
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-05 00:00:00 -08:00
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