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.
@@ -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