named-parameters 0.0.6 → 0.0.7
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/README.md +126 -19
- data/RELEASENOTES +24 -6
- data/VERSION +1 -1
- data/lib/named-parameters/module.rb +69 -13
- data/named-parameters.gemspec +2 -2
- data/spec/named-parameters_spec.rb +26 -1
- metadata +4 -4
data/README.md
CHANGED
@@ -33,7 +33,7 @@ Then include the `NamedParameters` module into your class:
|
|
33
33
|
include NamedParameters
|
34
34
|
end
|
35
35
|
|
36
|
-
Either way, you would now be able to use the
|
36
|
+
Either way, you would now be able to use the **`has_named_parameters`** clause
|
37
37
|
as needed:
|
38
38
|
|
39
39
|
class YourClass
|
@@ -101,44 +101,151 @@ And is applicable to both class and instance methods:
|
|
101
101
|
end
|
102
102
|
end
|
103
103
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
104
|
+
Shortcuts
|
105
|
+
---------
|
106
|
+
In addition to the `has_named_parameters` method, `NamedParameters` also comes
|
107
|
+
with two convenience methods for applying a parameter spec for constructors:
|
108
|
+
|
109
|
+
Use the **`requires`** clause to declare what parameters a class expects when it
|
110
|
+
is instantiated:
|
111
|
+
|
112
|
+
class GoogleStorage
|
113
|
+
requires [ :'access-key', :'secret-key' ]
|
114
|
+
|
115
|
+
def initialize options
|
116
|
+
# ... do googly stuff here ...
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
Use the **`recognizes`** clause to specify optional parameters for constructors:
|
121
|
+
|
122
|
+
class GoogleStorage
|
123
|
+
recognizes [ :'group-email', :'apps-domain' ]
|
124
|
+
|
125
|
+
def initialize options
|
126
|
+
# ... do googly stuff here ...
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
You may also specify default values for parameters when using these clauses:
|
131
|
+
|
132
|
+
class GoogleStorage
|
133
|
+
requires [ :'access-key', :'secret-key' ]
|
134
|
+
recognizes [ [ :'group-email', 'group@example.org' ], [ :'apps-domain', 'example.org' ] ]
|
135
|
+
|
136
|
+
def initialize options
|
137
|
+
# ... do googly stuff here ...
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
Permissive Mode
|
142
|
+
---------------
|
143
|
+
When a method is declared with `has_named_parameters` that method will only
|
144
|
+
accept keys that were listed as `:required`, `:optional`, or `:oneof` -
|
145
|
+
passing any other key to the `Hash` arg will raise an `ArgumentError` on
|
146
|
+
method call:
|
147
|
+
|
148
|
+
has_named_parameters :exec, :required => :w, :optional => [ :x, :y ]
|
149
|
+
def exec opts
|
150
|
+
# ...
|
151
|
+
end
|
152
|
+
|
153
|
+
# the following will raise an ArgumentError since :z was not declared
|
154
|
+
exec :w => 1, :x => 2, :y => 3, :z => 4
|
155
|
+
|
156
|
+
But sometimes you need to be able to pass additional keys and you don't know
|
157
|
+
what those keys are. Setting the optional `mode` parameter for
|
158
|
+
`has_named_parameters` to `:permissive` will relax this restriction:
|
159
|
+
|
160
|
+
has_named_parameters :exec, { :required => :w, :optional => [ :x, :y ] }, :permissive
|
161
|
+
def exec opts
|
162
|
+
# ...
|
163
|
+
end
|
109
164
|
|
110
|
-
|
111
|
-
|
165
|
+
# the following will no longer raise an ArgumentError
|
166
|
+
exec :w => 1, :x => 2, :y => 3, :z => 4
|
167
|
+
|
168
|
+
The `:required` and `:oneof` parameters will still be expected:
|
169
|
+
|
170
|
+
# the following will still raise an ArgumentError since :w is required
|
171
|
+
exec :x => 2, :y => 3, :z => 4
|
172
|
+
|
173
|
+
For clarity you should skip the `:optional` parameters list altogether when
|
174
|
+
using the `:permissive` mode.
|
175
|
+
|
176
|
+
The `requires` and `recognizes` clause for constructors will also accept a
|
177
|
+
`mode` setting:
|
178
|
+
|
179
|
+
requires [ :x ], :permissive
|
180
|
+
recognizes [ :y, :z ], :permissive
|
181
|
+
|
182
|
+
And just like the `:optional` parameter list in the `has_named_parameters`
|
183
|
+
clause, when `:permissive` mode is used, it's clearer to omit the `recognizes`
|
184
|
+
clause altogether.
|
185
|
+
|
186
|
+
How It Works
|
187
|
+
------------
|
188
|
+
When the `has_named_parameters` is declared in a class, it instruments the
|
189
|
+
class so that when the method in the declaration is invoked, a validation is
|
190
|
+
performed on the last `Hash` argument that was received by the method.
|
191
|
+
|
192
|
+
It expects that the last argument is the the `Hash` args representing the
|
193
|
+
named parameters when a method is invoked. If no `Hash` args was supplied
|
194
|
+
then it creates one.
|
112
195
|
|
113
196
|
So you can mix-and-match argument types in a method, and still declare that
|
114
197
|
it `has_named_parameters`:
|
115
198
|
|
116
|
-
has_named_parameters :request,
|
199
|
+
has_named_parameters :request,
|
200
|
+
:required => :key,
|
201
|
+
:optional => [ :etc, 'howl' ]
|
117
202
|
def request path, options
|
118
203
|
"path: #{path}, options: #{options.inspect}"
|
119
204
|
end
|
120
205
|
|
121
206
|
# invocation:
|
122
|
-
request "/xxx", :
|
207
|
+
request "/xxx", :key => '0925'
|
123
208
|
|
124
209
|
# result:
|
125
|
-
# => path: /xxx, options: {:
|
126
|
-
|
127
|
-
|
210
|
+
# => path: /xxx, options: {:key => '0925', :etc => 'howl'}
|
211
|
+
|
212
|
+
Gotchas
|
213
|
+
-------
|
214
|
+
It has no idea if the last argument really is the last argument. So be careful
|
215
|
+
when you have something similar to the following:
|
128
216
|
|
129
|
-
has_named_parameters :request, :
|
130
|
-
def request path, options = { }
|
217
|
+
has_named_parameters :request, :optional => :key
|
218
|
+
def request path = "/xxx", options = { }
|
131
219
|
"path: #{path}, options: #{options.inspect}"
|
132
220
|
end
|
133
221
|
|
134
222
|
# invocation:
|
135
|
-
request :
|
223
|
+
request :key => '0925'
|
136
224
|
|
137
|
-
#
|
225
|
+
# expecting:
|
226
|
+
# => path: /xxx, options: {:key => '0925'}
|
227
|
+
|
228
|
+
# but actual result is:
|
138
229
|
# => path: {:accesskey => '0925'}, options: {}
|
139
230
|
|
140
|
-
|
141
|
-
|
231
|
+
For the above case, it might be better to refactor:
|
232
|
+
|
233
|
+
has_named_parameters :request, :optional => [ :key, [ :path, "/xxx" ] ]
|
234
|
+
def request options = { }
|
235
|
+
"path: #{options.delete :path}, options: #{options.inspect}"
|
236
|
+
end
|
237
|
+
|
238
|
+
# invocation:
|
239
|
+
request :key => '0925'
|
240
|
+
|
241
|
+
# result:
|
242
|
+
# => path: /xxx, options: {:key => '0925'}
|
243
|
+
|
244
|
+
# invocation:
|
245
|
+
request
|
246
|
+
|
247
|
+
# result:
|
248
|
+
# => path: /xxx, options: {}
|
142
249
|
|
143
250
|
Dependencies
|
144
251
|
------------
|
data/RELEASENOTES
CHANGED
@@ -1,3 +1,27 @@
|
|
1
|
+
0.0.7 [Nov 18, 2010]
|
2
|
+
- [FEATURE] Added support to distinguish named parameters declaration for
|
3
|
+
class and instance methods that uses the same names. So now you could do:
|
4
|
+
|
5
|
+
class Mailer
|
6
|
+
has_named_parameters :send_mail,
|
7
|
+
:required => :to,
|
8
|
+
:optional => [ :from, 'yourself@example.org' ]
|
9
|
+
def send_mail options = { }
|
10
|
+
Mailer.send_mail options
|
11
|
+
end
|
12
|
+
|
13
|
+
has_named_parameters :'self.send_mail', :required => [ :to, :from ]
|
14
|
+
def self.send_mail options = { }
|
15
|
+
# ... do mail sending stuff here ...
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
- [FEATURE] Added support for requires clause.
|
20
|
+
- [FEATURE] Added support for recognizes clause.
|
21
|
+
- [FEATURE] Added support for permissive evaluation of optional parameters.
|
22
|
+
- [INTERNAL] Will now expect that the Hash args for the named parameters is
|
23
|
+
the last argument of the method.
|
24
|
+
|
1
25
|
0.0.6 [Nov 13, 2010]
|
2
26
|
- [FEATURE] Added support for requiring one-of from a list of named
|
3
27
|
parameters.
|
@@ -8,27 +32,21 @@
|
|
8
32
|
- [INTERNAL] Updated tests.
|
9
33
|
|
10
34
|
0.0.5 [Nov 11, 2010]
|
11
|
-
--------------------
|
12
35
|
- [BUGFIX] Same as 0.0.4 - except this time it's really fixed :-)
|
13
36
|
|
14
37
|
0.0.4 [Nov 11, 2010]
|
15
|
-
--------------------
|
16
38
|
- [BUGFIX] Reported method name when ArgumentError is raised was ugly.
|
17
39
|
|
18
40
|
0.0.3 [Nov 11, 2010]
|
19
|
-
--------------------
|
20
41
|
- [FEATURE] Added support for has_named_parameter declaration for class
|
21
42
|
methods.
|
22
43
|
|
23
44
|
0.0.2 [Nov 10, 2010]
|
24
|
-
--------------------
|
25
45
|
- [BUGFIX] ArgumentError incorrectly references Module instead of actual class
|
26
46
|
that raised it.
|
27
47
|
|
28
48
|
0.0.1 [Nov 10, 2010]
|
29
|
-
--------------------
|
30
49
|
- Initial release.
|
31
50
|
|
32
51
|
0.0.0 [Nov 10, 2010]
|
33
|
-
--------------------
|
34
52
|
- Initial release. (yanked)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.7
|
@@ -37,14 +37,14 @@ module NamedParameters
|
|
37
37
|
required = spec[:required].map &mapper
|
38
38
|
oneof = spec[:oneof].map &mapper
|
39
39
|
|
40
|
-
# determine what keys are allowed
|
41
|
-
|
42
|
-
|
40
|
+
# determine what keys are allowed, unless mode is :permissive
|
41
|
+
# in which case we don't care unless its listed as required or oneof
|
42
|
+
order = lambda{ |x, y| x.to_s <=> y.to_s }
|
43
|
+
allowed = spec[:mode] == :permissive ? [] : (optional + required + oneof).sort(&order)
|
43
44
|
|
44
45
|
# determine what keys were passed;
|
45
46
|
# also, plugin the names of parameters assigned with default values
|
46
|
-
|
47
|
-
keys = params.keys.map{ |k| k.to_sym }
|
47
|
+
keys = params.keys.map{ |k| k.to_sym }
|
48
48
|
keys.sort! &order
|
49
49
|
required.sort! &order
|
50
50
|
|
@@ -103,7 +103,12 @@ module NamedParameters
|
|
103
103
|
# * `:optional` means that all or none of these parameters may be used.
|
104
104
|
# * `:oneof` means that one of these parameters must be specified.
|
105
105
|
#
|
106
|
-
|
106
|
+
# @param [Symbol] mode enforces that only parameters that were named in
|
107
|
+
# either the `:required`, `:optional`, and `:oneof` list may be allowed.
|
108
|
+
# Set it to `:permissive` to relax the requirement - `:required` and `:oneof`
|
109
|
+
# parameters will still be expected.
|
110
|
+
#
|
111
|
+
def has_named_parameters method, spec, mode = :strict
|
107
112
|
# ensure spec entries are initialized and the proper types
|
108
113
|
[ :required, :optional, :oneof ].each{ |k| spec[k] ||= [] }
|
109
114
|
spec = Hash[ spec.map{ |k, v|
|
@@ -111,7 +116,52 @@ module NamedParameters
|
|
111
116
|
v.map!{ |entry| entry.instance_of?(Array) ? Hash[*entry] : entry }
|
112
117
|
[ k, v ]
|
113
118
|
} ]
|
114
|
-
|
119
|
+
spec[:mode] = mode
|
120
|
+
specs[key_for(method)] = spec
|
121
|
+
end
|
122
|
+
|
123
|
+
# Convenience method, equivalent to declaring:
|
124
|
+
#
|
125
|
+
# has_named_parameters :'self.new', :required => params
|
126
|
+
# has_named_parameters :initialize, :required => params
|
127
|
+
#
|
128
|
+
# @param [Array] params the lists of parameters. The list is expected
|
129
|
+
# to be an `Array` of symbols matching the names of the required
|
130
|
+
# parameters.
|
131
|
+
#
|
132
|
+
# @param [Symbol] mode enforces that only parameters that were named in
|
133
|
+
# either the `:required`, `:optional`, and `:oneof` list may be allowed.
|
134
|
+
# Set it to `:permissive` to relax the requirement - `:required` and `:oneof`
|
135
|
+
# parameters will still be expected.
|
136
|
+
#
|
137
|
+
def requires params, mode = :strict
|
138
|
+
[ :new, :initialize ].each do |method|
|
139
|
+
spec = specs[key_for method] || { }
|
140
|
+
spec.merge!(:required => params)
|
141
|
+
has_named_parameters method, spec, mode
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Convenience method, equivalent to declaring:
|
146
|
+
#
|
147
|
+
# has_named_parameters :'self.new', :optional => params
|
148
|
+
# has_named_parameters :initialize, :optional => params
|
149
|
+
#
|
150
|
+
# @param [Array] params the lists of parameters. The list is expected
|
151
|
+
# to be an `Array` of symbols matching the names of the optional
|
152
|
+
# parameters.
|
153
|
+
#
|
154
|
+
# @param [Symbol] mode enforces that only parameters that were named in
|
155
|
+
# either the `:required`, `:optional`, and `:oneof` list may be allowed.
|
156
|
+
# Set it to `:permissive` to relax the requirement - `:required` and `:oneof`
|
157
|
+
# parameters will still be expected.
|
158
|
+
#
|
159
|
+
def recognizes params, mode = :strict
|
160
|
+
[ :new, :initialize ].each do |method|
|
161
|
+
spec = specs[key_for method] || { }
|
162
|
+
spec.merge!(:optional => params)
|
163
|
+
has_named_parameters method, spec, mode
|
164
|
+
end
|
115
165
|
end
|
116
166
|
|
117
167
|
protected
|
@@ -151,7 +201,7 @@ module NamedParameters
|
|
151
201
|
def singleton_method_added name # :nodoc:
|
152
202
|
instrument name do
|
153
203
|
method = self.eigenclass.instance_method name
|
154
|
-
spec = specs
|
204
|
+
spec = specs[key_for :"self.#{name}"] || specs[key_for name]
|
155
205
|
owner = "#{self.name}::"
|
156
206
|
eigenclass.instance_eval do
|
157
207
|
intercept method, owner, name, spec
|
@@ -164,7 +214,7 @@ module NamedParameters
|
|
164
214
|
def method_added name # :nodoc:
|
165
215
|
instrument name do
|
166
216
|
method = instance_method name
|
167
|
-
spec = specs
|
217
|
+
spec = specs[key_for name] || specs[key_for :"self.#{name}"]
|
168
218
|
owner = "#{self.name}#"
|
169
219
|
intercept method, owner, name, spec
|
170
220
|
end
|
@@ -174,7 +224,7 @@ module NamedParameters
|
|
174
224
|
private
|
175
225
|
# apply instrumentation to method
|
176
226
|
def instrument method # :nodoc:
|
177
|
-
if specs.include? method and !instrumenting?
|
227
|
+
if specs.include? key_for(method) and !instrumenting?
|
178
228
|
@instrumenting = true
|
179
229
|
yield method
|
180
230
|
@instrumenting = false
|
@@ -187,13 +237,14 @@ module NamedParameters
|
|
187
237
|
define_method name do |*args, &block|
|
188
238
|
# locate the argument representing the named parameters value
|
189
239
|
# for the method invocation
|
190
|
-
params = args.
|
191
|
-
args << (params = { })
|
240
|
+
params = args.last
|
241
|
+
args << (params = { }) unless params.instance_of? Hash
|
192
242
|
|
193
243
|
# merge the declared default values for params into the arguments
|
194
244
|
# used when the method is invoked
|
195
245
|
defaults = { }
|
196
246
|
spec.each do |k, v|
|
247
|
+
next if k == :mode
|
197
248
|
v.each{ |entry| defaults.merge! entry if entry.instance_of? Hash }
|
198
249
|
end
|
199
250
|
params = defaults.merge params
|
@@ -203,7 +254,7 @@ module NamedParameters
|
|
203
254
|
|
204
255
|
# inject the updated argument values for params into the arguments
|
205
256
|
# before actually making method invocation
|
206
|
-
args.
|
257
|
+
args[args.length - 1] = params
|
207
258
|
method.bind(self).call(*args, &block)
|
208
259
|
end
|
209
260
|
end
|
@@ -213,6 +264,11 @@ module NamedParameters
|
|
213
264
|
@specs ||= { }
|
214
265
|
end
|
215
266
|
|
267
|
+
def key_for method
|
268
|
+
type = method.to_s =~ /^self\./ ? :singleton : :instance
|
269
|
+
:"#{self.name}::#{type}.#{method}"
|
270
|
+
end
|
271
|
+
|
216
272
|
# check if in the process of instrumenting a method
|
217
273
|
def instrumenting? # :nodoc:
|
218
274
|
@instrumenting
|
data/named-parameters.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{named-parameters}
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.7"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Juris Galang"]
|
12
|
-
s.date = %q{2010-11-
|
12
|
+
s.date = %q{2010-11-18}
|
13
13
|
s.description = %q{This gem simulates named-parameters in Ruby. It's a complement to the common
|
14
14
|
Ruby idiom of using `Hash` args to emulate the use of named parameters.
|
15
15
|
|
@@ -5,7 +5,7 @@ describe "NamedParameters" do
|
|
5
5
|
class Foo
|
6
6
|
has_named_parameters :initialize, :required => :x, :optional => [ :y, :z ]
|
7
7
|
def initialize opts = {}; end
|
8
|
-
|
8
|
+
|
9
9
|
has_named_parameters :method_one, :required => :x, :optional => [ :y, :z ]
|
10
10
|
def method_one x, y, opts = {}; end
|
11
11
|
|
@@ -172,4 +172,29 @@ describe "NamedParameters" do
|
|
172
172
|
lambda { Quux.new :x => :x, :y => :y }.should raise_error ArgumentError
|
173
173
|
lambda { Quux.new :x => :x }.should_not raise_error
|
174
174
|
end
|
175
|
+
|
176
|
+
it "should be able to specify optional parameters using the recognizes method" do
|
177
|
+
class Recognizes
|
178
|
+
recognizes [ :x, :y ]
|
179
|
+
def self.new opts = { }; end
|
180
|
+
def initialize opts = { }; end
|
181
|
+
end
|
182
|
+
lambda { Recognizes.new }.should_not raise_error
|
183
|
+
lambda { Recognizes.new :x => :x }.should_not raise_error
|
184
|
+
lambda { Recognizes.new :y => :y }.should_not raise_error
|
185
|
+
lambda { Recognizes.new :x => :x, :y => :y }.should_not raise_error
|
186
|
+
lambda { Recognizes.new :z => :z }.should raise_error ArgumentError
|
187
|
+
end
|
188
|
+
|
189
|
+
it "should be able to specify required parameters using the recognizes method" do
|
190
|
+
class Required
|
191
|
+
requires [ :x, :y ]
|
192
|
+
def self.new opts = { }; end
|
193
|
+
def initialize opts = { }; end
|
194
|
+
end
|
195
|
+
lambda { Required.new }.should raise_error ArgumentError
|
196
|
+
lambda { Required.new :x => :x }.should raise_error ArgumentError
|
197
|
+
lambda { Required.new :y => :y }.should raise_error ArgumentError
|
198
|
+
lambda { Required.new :x => :x, :y => :y }.should_not raise_error
|
199
|
+
end
|
175
200
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: named-parameters
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 17
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 7
|
10
|
+
version: 0.0.7
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Juris Galang
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-11-
|
18
|
+
date: 2010-11-18 00:00:00 -08:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|