hash_keyword_args 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.rdoc +222 -0
- data/Rakefile +7 -0
- data/hash_keyword_args.gemspec +24 -0
- data/lib/hash_keyword_args.rb +4 -0
- data/lib/hash_keyword_args/hash.rb +115 -0
- data/lib/hash_keyword_args/version.rb +3 -0
- data/spec/keyword_args_spec.rb +104 -0
- data/spec/spec_helper.rb +17 -0
- metadata +116 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Ronen Barzel
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,222 @@
|
|
1
|
+
= hash_keyword_args
|
2
|
+
|
3
|
+
Defines Hash#keyword_args, which provides convenient features when
|
4
|
+
using a Hash to provide keyword args to a method:
|
5
|
+
|
6
|
+
* Argument checking
|
7
|
+
* Accessor methods for values
|
8
|
+
* Default values
|
9
|
+
* Required vs. optional arguments
|
10
|
+
* Argument value validation
|
11
|
+
|
12
|
+
Typical simplest usage is as follows:
|
13
|
+
|
14
|
+
def my_method(args={})
|
15
|
+
args = args.keyword_args(:name, :rank)
|
16
|
+
|
17
|
+
puts "name is #{args.name}" if args.name
|
18
|
+
puts "rank is #{name.rank}" if args.rank
|
19
|
+
end
|
20
|
+
|
21
|
+
my_method() # prints nothing
|
22
|
+
my_method(:name => "Kilroy") # prints: name is Kilroy
|
23
|
+
my_method(:name => "Kilroy", :rank => "Pawn") # prints: name is Kilroy / rank is Pawn
|
24
|
+
my_method(:name => "Kilroy", :serial => 666) # raises ArgumentError
|
25
|
+
|
26
|
+
Notice that you declare the keyword arguments you're willing to accept, and
|
27
|
+
+keyword_args+ returns a new object that has accessors for each argument.
|
28
|
+
If an non-matching keyword is detected, +keyword_args+ raises ArgumentError with a descriptive message.
|
29
|
+
|
30
|
+
For fancier features (such as required arguments, default values, and
|
31
|
+
whatnot) you specify properties for keywords, as discussed below.
|
32
|
+
|
33
|
+
Note another common idiom is to define the keyword args after positional args ("rails style"), e.g.:
|
34
|
+
|
35
|
+
def find(id, opts={})
|
36
|
+
opts = opts.keyword_args(:conditions, :order)
|
37
|
+
...
|
38
|
+
end
|
39
|
+
find(123, :order => :date)
|
40
|
+
|
41
|
+
== Details
|
42
|
+
|
43
|
+
=== Default Values
|
44
|
+
|
45
|
+
Normally if a keyword isn't included in the args, the corresponding accessor will return nil. But
|
46
|
+
you can provide default values that will be filled in if the keyword isn't provided. E.g.
|
47
|
+
|
48
|
+
def my_method(args={})
|
49
|
+
args = args.keyword_args(:name, :rank => "Knight")
|
50
|
+
|
51
|
+
puts "name is #{args.name}" if args.name
|
52
|
+
puts "rank is #{name.rank}"
|
53
|
+
end
|
54
|
+
|
55
|
+
my_method() # prints: rank is Knight
|
56
|
+
my_method(:name => "Kilroy") # prints: name is Kilroy / rank is Knight
|
57
|
+
my_method(:name => "Kilroy", :rank => "Pawn") # prints: name is Kilroy / rank is Pawn
|
58
|
+
|
59
|
+
Notice that because unadorned hashes must be at the end of ruby calls, any
|
60
|
+
keywords with default values or other properties need to come after the ordinary optional
|
61
|
+
keywords. If you care about the order (or like symmetry in your code), you
|
62
|
+
can specify the magic symbol :optional instead of a default value. So this is equivalent to the above:
|
63
|
+
|
64
|
+
args = args.keyword_args(:rank => "Pawn",
|
65
|
+
:name => :optional)
|
66
|
+
|
67
|
+
The above actually show the shortcut form for specifying a default. There's also a long form, which can be used in combination with other properties or if the default value conflicts with a shortcut:
|
68
|
+
|
69
|
+
args = args.keyword_args(:rank => { :default => "Pawn" },
|
70
|
+
:name => { },
|
71
|
+
)
|
72
|
+
|
73
|
+
|
74
|
+
=== Required Keyword Args
|
75
|
+
|
76
|
+
By default, keyword arguments are optional; and if not provided the value is +nil+ or the specified default. But you can require that an argument be specified:
|
77
|
+
|
78
|
+
def my_method(args={})
|
79
|
+
args = args.keyword_args(:name => :required, :rank => "Pawn")
|
80
|
+
|
81
|
+
puts "name is #{args.name}"
|
82
|
+
puts "rank is #{name.rank}"
|
83
|
+
end
|
84
|
+
|
85
|
+
my_method() # raises ArgumentError with a descriptive message
|
86
|
+
my_method(:name => "Kilroy") # prints: name is Kilroy / rank is Pawn
|
87
|
+
|
88
|
+
Again, the above is the shortcut form. The equivalent long form would be:
|
89
|
+
|
90
|
+
args = args.keyword_args(:name => { :required => true },
|
91
|
+
:rank => { :default => "Pawn"},
|
92
|
+
)
|
93
|
+
|
94
|
+
=== Value Validation
|
95
|
+
|
96
|
+
+keyword_args+ can check that the provided values have a given type or are
|
97
|
+
chosen from among a specified array of values.
|
98
|
+
|
99
|
+
def my_roll(args={})
|
100
|
+
args = args.keyword_args(:lucky => Integer,
|
101
|
+
:dice => [:d6, :d10, :d20])
|
102
|
+
|
103
|
+
...
|
104
|
+
end
|
105
|
+
|
106
|
+
my_roll(:lucky => 17, :dice => :d20) # OK
|
107
|
+
my_roll(:lucky => "yes", :dice => :d20) # raises ArgumentError with a descriptive message for :lucky
|
108
|
+
my_roll(:lucky => 17, :dice => :d4) # raises ArgumentError with a descriptive message for :dice
|
109
|
+
my_roll(:dice => :d4) # raises ArgumentError; :dice is OK but :lucky (nil) isn't an Integer
|
110
|
+
|
111
|
+
Note that since the default value, +nil+ isn't a valid Integer and wasn't listed in the collection, the above declaration has implicitly caused those keywords to be required. But it's possible to specify default values and/or allow nil using the long form:
|
112
|
+
|
113
|
+
args = args.keyword_args(:lucky => {:valid => Integer, :allow_nil => true})
|
114
|
+
args = args.keyword_args(:lucky => {:valid => Integer, :default => 7})
|
115
|
+
args = args.keyword_args(:lucky => {:valid => Integer, :default => 7, :allow_nil => true})
|
116
|
+
|
117
|
+
In the third form above, the default value is 7, but if args explicitly included :lucky => nil it would override the default.
|
118
|
+
|
119
|
+
=== Enumerable Values
|
120
|
+
|
121
|
+
You can specify that you expect a keyword to take an array of values (or other +Enumerable+), via
|
122
|
+
|
123
|
+
def my_report(args={})
|
124
|
+
args = args.keyword_args(:winners => :enumerable)
|
125
|
+
|
126
|
+
args.winners.each do |winner|
|
127
|
+
puts "#{winner} is a winner"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
my_report(:winners => ["Bonnie", "Clyde"]) # prints: Bonnie is a winner / Clyde is a winner
|
132
|
+
my_report(:winners => "Nero") # prints: Nero is a winner
|
133
|
+
my_report() # prints nothing
|
134
|
+
|
135
|
+
Notice that a non-enumerable value gets automatically wrapped in an array for you, and the default value is an empty array. If you want to do type checking on the values, you can use the long form:
|
136
|
+
|
137
|
+
args = args.keyword_args(:winners => { :enumerable => true, :valid => String})
|
138
|
+
|
139
|
+
which will perform validity checking on element of the array. You can combine :enumerable with a default as well:
|
140
|
+
|
141
|
+
args = args.keyword_args(:winners => { :enumerable => true, :default => ["Huey", "Dewey"] }
|
142
|
+
|
143
|
+
=== Can I have my keywords & values in a Hash?
|
144
|
+
|
145
|
+
The returned object is actually a +Hash+ with the accessors defined in its singleton class, so you use hash operations on it if needed, and you can pass it in turn to another method. For example:
|
146
|
+
|
147
|
+
def my_wrapper(args={})
|
148
|
+
args = args.keyword_args(:name, :rank, :serial_number)
|
149
|
+
@serial_number = args.delete(:serial_number)
|
150
|
+
my_method(args)
|
151
|
+
end
|
152
|
+
|
153
|
+
=== Suppress Argument Checking
|
154
|
+
|
155
|
+
If you want to suppress argument checking you can specify the magic keyword :OTHERS, e.g.:
|
156
|
+
|
157
|
+
def execute(operator, opts={})
|
158
|
+
opts = opts.keyword_args(:immediately, :OTHERS)
|
159
|
+
immediately = opts.delete(:immediately) # take :immediately out of the list...
|
160
|
+
opts.each do |opt, value| # ...and loop over all the others
|
161
|
+
...
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
execute(operator, :yabba => 1, :dabba => 2, :doo => 3) # not an argument error
|
166
|
+
|
167
|
+
No accessor methods are defined for undeclared keywords, but the values will be available in the hash. The properties to :OTHERS are ignored, but if you want to make it look nice in at the end of list it's suggested that you use +:OTHERS => :optional:
|
168
|
+
|
169
|
+
args = args.keyword_args(:name => String,
|
170
|
+
:OTHERS => :optional)
|
171
|
+
|
172
|
+
== Summary
|
173
|
+
|
174
|
+
=== Complete list of long form properties that can be specified
|
175
|
+
|
176
|
+
:key => { :required => boolean }
|
177
|
+
:key => { :default => "your default value" }
|
178
|
+
:key => { :valid => Class-or-enumeration, :allow_nil => boolean }
|
179
|
+
:key => { :enumerable => boolean } # implies :default => []
|
180
|
+
|
181
|
+
=== Complete list of shortcuts
|
182
|
+
|
183
|
+
:key => :optional # short for :key => {}
|
184
|
+
:key => :required # short for :key => {:required => true}
|
185
|
+
:key => :enumerable # short for :key => {:enumerable => true} which in turn implies :default => []
|
186
|
+
:key => [1, 2, 3] # short for :key => {:valid => [1, 2, 3]} validates inclusion in the list
|
187
|
+
:key => Class # short for :key => {:valid => Class} validates is_a Class
|
188
|
+
:key => "whatever" # anything else is short for :key => {:default => "whatever"}
|
189
|
+
|
190
|
+
|
191
|
+
== Installation
|
192
|
+
|
193
|
+
Install via:
|
194
|
+
|
195
|
+
% gem install hash_keyword_args
|
196
|
+
|
197
|
+
or in your Gemfile:
|
198
|
+
|
199
|
+
gem "hash_keyword_args
|
200
|
+
|
201
|
+
== Versions
|
202
|
+
|
203
|
+
Has been tested on MRI 1.8.7 and MRI 1.9.3
|
204
|
+
|
205
|
+
== History
|
206
|
+
|
207
|
+
Past: I've been using this for years and carrying it around from project to project. Finally got around to bundling it into a gem and putting it on rubygems, since that's now so darn easy to do! Maybe somebody other than me will find this gem useful too.
|
208
|
+
|
209
|
+
Future: I hope that this gem will be obviated in future versions of ruby.
|
210
|
+
|
211
|
+
== Note on Patches/Pull Requests
|
212
|
+
|
213
|
+
* Fork the project.
|
214
|
+
* Make your feature addition or bug fix.
|
215
|
+
* Add tests for it. Make sure that the coverage report
|
216
|
+
(generated automatically when you run rspec with ruby >= 1.9) is at 100%
|
217
|
+
* Send me a pull request.
|
218
|
+
|
219
|
+
== Copyright
|
220
|
+
|
221
|
+
Released under the MIT License. See LICENSE for details.
|
222
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/hash_keyword_args/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["ronen barzel"]
|
6
|
+
gem.email = ["ronen@barzel.org"]
|
7
|
+
gem.description = %q{Makes it easier and more robust to use a hash for keyword args to a method. In particular, performs argument checking and default values.}
|
8
|
+
gem.summary = %q{Helper for using a hash for keyword args to a method. Performs argument checking, provides accessor methods for values, supports default values, required arguments, and argument value validation.}
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
gem.name = "hash_keyword_args"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = HashKeywordArgs::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency 'enumerable_hashify'
|
19
|
+
|
20
|
+
gem.add_development_dependency 'rake'
|
21
|
+
gem.add_development_dependency 'rspec'
|
22
|
+
gem.add_development_dependency 'simplecov'
|
23
|
+
gem.add_development_dependency 'simplecov-gem-adapter'
|
24
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'enumerable_hashify'
|
2
|
+
|
3
|
+
module HashKeywordArgs
|
4
|
+
module Hash
|
5
|
+
# given an argument hash and a description of acceptable keyword args and
|
6
|
+
# their default values, checks validity of the arguments and raises any
|
7
|
+
# errors, otherwise returns a new hash containing all arg values with
|
8
|
+
# defaults filled in as needed.
|
9
|
+
#
|
10
|
+
# args = hash.keyword_args(:a, :b, :c, # these are all optional args
|
11
|
+
# :d => :optional, # same as listing it the arg up front
|
12
|
+
# :e => :required, # raises an error if arg not provided
|
13
|
+
# :f => "hello" # default value
|
14
|
+
# :g => [:x,:y,:z] # valid values
|
15
|
+
# :h => Integer # valid type
|
16
|
+
# :i => :enumerable # expect/coerce an enumerable, check all items, default is []
|
17
|
+
# :j => { :valid => Integer, :allow_nil => true} # Integer or nil
|
18
|
+
# :j => { :valid => [:x,:y,:z], :default => :x, :required => true } # combo
|
19
|
+
# )
|
20
|
+
#
|
21
|
+
# by default this will raise an error if the hash contains any keys not
|
22
|
+
# listed. however, if :OTHERS is specified as a keyword arg, that test
|
23
|
+
# will be disabled and any other key/value pairs will be passed through.
|
24
|
+
#
|
25
|
+
# Returns a Struct whose values are the
|
26
|
+
#
|
27
|
+
def keyword_args(*args)
|
28
|
+
argshash = args[-1].is_a?(Hash) ? args.pop : {}
|
29
|
+
argshash = args.hashify(:optional).merge(argshash)
|
30
|
+
others_OK = argshash.delete(:OTHERS)
|
31
|
+
ret = {}
|
32
|
+
|
33
|
+
# defaults, required, and checked
|
34
|
+
required = []
|
35
|
+
check = {}
|
36
|
+
argshash.each do |key, val|
|
37
|
+
# construct fleshed-out attribute hash for all args
|
38
|
+
attrs = case val
|
39
|
+
when Hash
|
40
|
+
val[:default] ||= [] if val[:enumerable]
|
41
|
+
val
|
42
|
+
when :required
|
43
|
+
{ :required => true }
|
44
|
+
when :optional
|
45
|
+
{}
|
46
|
+
when :enumerable
|
47
|
+
{ :enumerable => true, :default => [] }
|
48
|
+
when Array
|
49
|
+
{ :valid => val }
|
50
|
+
when Class, Module
|
51
|
+
{ :valid => val }
|
52
|
+
else
|
53
|
+
{ :default => val }
|
54
|
+
end
|
55
|
+
|
56
|
+
# extract required flag, validity checks, and default vlues from attribute hash
|
57
|
+
required << key if attrs[:required]
|
58
|
+
check[key] = case valid = attrs[:valid]
|
59
|
+
when Enumerable
|
60
|
+
[:one_of, valid]
|
61
|
+
when Class, Module
|
62
|
+
[:is_a, valid]
|
63
|
+
else
|
64
|
+
[:ok]
|
65
|
+
end
|
66
|
+
check[key][2] = [:allow_nil, :enumerable].select{|mod| attrs[mod]}
|
67
|
+
ret[key] = attrs[:default] if attrs.include?(:default)
|
68
|
+
end
|
69
|
+
|
70
|
+
# check validity of keys
|
71
|
+
unless others_OK or (others = self.keys - argshash.keys).empty?
|
72
|
+
raise ArgumentError, "Invalid keyword arg#{others.length>1 && "s" or ""} #{others.collect{|a| a.inspect}.join(', ')}", caller
|
73
|
+
end
|
74
|
+
|
75
|
+
# process values, checking validity
|
76
|
+
self.each do |key, val|
|
77
|
+
code, valid, mods = check[key]
|
78
|
+
mods ||= []
|
79
|
+
val = [ val ] unless mods.include?(:enumerable) and val.is_a?(Enumerable)
|
80
|
+
ok = val.all? { |v|
|
81
|
+
if mods.include?(:allow_nil) and v.nil?
|
82
|
+
true
|
83
|
+
else
|
84
|
+
case code
|
85
|
+
when nil then true # for OTHERS args, there's no code
|
86
|
+
when :ok then true
|
87
|
+
when :one_of then valid.include?(v)
|
88
|
+
when :is_a then v.is_a?(valid)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
}
|
92
|
+
val = val.first unless mods.include?(:enumerable)
|
93
|
+
raise ArgumentError, "Invalid value for keyword arg #{key.inspect} => #{val.inspect}", caller unless ok
|
94
|
+
ret[key] = val
|
95
|
+
required.delete(key)
|
96
|
+
end
|
97
|
+
|
98
|
+
unless required.empty?
|
99
|
+
raise ArgumentError, "Missing required keyword arg#{required.length>1 && "s" or ""} #{required.collect{|a| a.inspect}.join(', ')}", caller
|
100
|
+
end
|
101
|
+
|
102
|
+
(class << ret ; self ; end).class_eval do
|
103
|
+
argshash.keys.each do |key|
|
104
|
+
define_method(key) do
|
105
|
+
self[key]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
ret
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "keyword_args" do
|
4
|
+
|
5
|
+
it "unadorned args should be optional" do
|
6
|
+
expect { @args = {:a => 17}.keyword_args(:a, :b) }.should_not raise_error
|
7
|
+
@args.a.should == 17
|
8
|
+
@args.b.should be_nil
|
9
|
+
end
|
10
|
+
|
11
|
+
it ":optional args should be optional" do
|
12
|
+
expect { @args = {:a => 17}.keyword_args(:a => :optional, :b => :optional) }.should_not raise_error
|
13
|
+
@args.a.should == 17
|
14
|
+
@args.b.should be_nil
|
15
|
+
end
|
16
|
+
|
17
|
+
it ":required args should be required" do
|
18
|
+
expect { @args = {:a => 17}.keyword_args(:a => :required, :b => :required) }.should raise_error(ArgumentError)
|
19
|
+
expect { @args = {:a => 17, :b => 32}.keyword_args(:a => :required, :b => :required) }.should_not raise_error(ArgumentError)
|
20
|
+
@args.a.should == 17
|
21
|
+
@args.b.should == 32
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should handle default value" do
|
25
|
+
@args = {:a => "alpha"}.keyword_args(:a => "adam", :b => "baker")
|
26
|
+
@args.a.should == "alpha"
|
27
|
+
@args.b.should == "baker"
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should complain about invalid args" do
|
31
|
+
expect { @args = {:a => "whatever"}.keyword_args(:b) }.should raise_error(ArgumentError)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should not complain about invalid args if :OTHERS is given" do
|
35
|
+
expect { @args = {:a => "whatever"}.keyword_args(:b) }.should raise_error(ArgumentError)
|
36
|
+
expect { @args = {:a => "whatever"}.keyword_args(:b, :OTHERS) }.should_not raise_error
|
37
|
+
@args[:a].should == "whatever"
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should not define method for OTHERS arg" do
|
41
|
+
@args = {:a => "whatever"}.keyword_args(:b, :OTHERS)
|
42
|
+
expect { @args.a }.should raise_error(NoMethodError)
|
43
|
+
@args[:a].should == "whatever"
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should validate against list of values" do
|
47
|
+
expect { @args = {:a => :BAD}.keyword_args(:a => [:OK, :GOOD]) }.should raise_error(ArgumentError)
|
48
|
+
expect { @args = {:a => :OK}.keyword_args(:a => [:OK, :GOOD]) }.should_not raise_error
|
49
|
+
@args.a.should == :OK
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should validate against class" do
|
53
|
+
expect { @args = {:a => "hello"}.keyword_args(:a => Integer) }.should raise_error(ArgumentError)
|
54
|
+
expect { @args = {:a => 17}.keyword_args(:a => Integer) }.should_not raise_error
|
55
|
+
@args.a.should == 17
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should allow validity set via hash" do
|
59
|
+
expect { @args = {:a => "hello"}.keyword_args(:a => {:valid => Integer}) }.should raise_error(ArgumentError)
|
60
|
+
expect { @args = {:a => 17}.keyword_args(:a => {:valid => Integer}) }.should_not raise_error
|
61
|
+
@args.a.should == 17
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should allow nil if :allow_nil is set" do
|
65
|
+
expect { @args = {:a => nil}.keyword_args(:a => {:valid => Integer}) }.should raise_error(ArgumentError)
|
66
|
+
expect { @args = {:a => nil}.keyword_args(:a => {:valid => Integer, :allow_nil => true}) }.should_not raise_error
|
67
|
+
@args.a.should be_nil
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should accept enumerable if :enumerable" do
|
71
|
+
@args = {:a => 1..10}.keyword_args(:a => :enumerable)
|
72
|
+
@args.a.should == (1..10)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should coerce enumerable if :enumerable" do
|
76
|
+
@args = {:a => 3}.keyword_args(:a => :enumerable)
|
77
|
+
@args.a.should == [3]
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should default enumerable if :enumerable" do
|
81
|
+
@args = {}.keyword_args(:a => :enumerable)
|
82
|
+
@args.a.should == []
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should default enumerable if specified long form" do
|
86
|
+
@args = {}.keyword_args(:a => { :enumerable => true })
|
87
|
+
@args.a.should == []
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should validate if :enumerable and :valid" do
|
91
|
+
expect {@args = {:a => 3}.keyword_args(:a => { :enumerable => true, :valid => String })}.should raise_error(ArgumentError)
|
92
|
+
expect {@args = {:a => ["OK", 3]}.keyword_args(:a => { :enumerable => true, :valid => String })}.should raise_error(ArgumentError)
|
93
|
+
expect {@args = {:a => ["OK", "YY"]}.keyword_args(:a => { :enumerable => true, :valid => String })}.should_not raise_error
|
94
|
+
@args.a.should == ["OK", "YY"]
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should not include undefaulted arguments in hash" do
|
98
|
+
@args = {:a =>3}.keyword_args(:a, :b)
|
99
|
+
@args.include?(:a).should be_true
|
100
|
+
@args.include?(:b).should_not be_true
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
if RUBY_VERSION > "1.9"
|
2
|
+
require 'simplecov'
|
3
|
+
require 'simplecov-gem-adapter'
|
4
|
+
SimpleCov.start 'gem'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rspec'
|
8
|
+
require 'enumerable_hashify'
|
9
|
+
require 'hash_keyword_args'
|
10
|
+
|
11
|
+
# Requires supporting files with custom matchers and macros, etc,
|
12
|
+
# in ./support/ and its subdirectories.
|
13
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
14
|
+
|
15
|
+
RSpec.configure do |config|
|
16
|
+
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hash_keyword_args
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- ronen barzel
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-12-03 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: enumerable_hashify
|
16
|
+
requirement: &70192151724620 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70192151724620
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rake
|
27
|
+
requirement: &70192151724120 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70192151724120
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec
|
38
|
+
requirement: &70192151723620 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70192151723620
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: simplecov
|
49
|
+
requirement: &70192151722980 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70192151722980
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: simplecov-gem-adapter
|
60
|
+
requirement: &70192151722020 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *70192151722020
|
69
|
+
description: Makes it easier and more robust to use a hash for keyword args to a method.
|
70
|
+
In particular, performs argument checking and default values.
|
71
|
+
email:
|
72
|
+
- ronen@barzel.org
|
73
|
+
executables: []
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- .gitignore
|
78
|
+
- Gemfile
|
79
|
+
- LICENSE
|
80
|
+
- README.rdoc
|
81
|
+
- Rakefile
|
82
|
+
- hash_keyword_args.gemspec
|
83
|
+
- lib/hash_keyword_args.rb
|
84
|
+
- lib/hash_keyword_args/hash.rb
|
85
|
+
- lib/hash_keyword_args/version.rb
|
86
|
+
- spec/keyword_args_spec.rb
|
87
|
+
- spec/spec_helper.rb
|
88
|
+
homepage: ''
|
89
|
+
licenses: []
|
90
|
+
post_install_message:
|
91
|
+
rdoc_options: []
|
92
|
+
require_paths:
|
93
|
+
- lib
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ! '>='
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ! '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
requirements: []
|
107
|
+
rubyforge_project:
|
108
|
+
rubygems_version: 1.8.10
|
109
|
+
signing_key:
|
110
|
+
specification_version: 3
|
111
|
+
summary: Helper for using a hash for keyword args to a method. Performs argument checking,
|
112
|
+
provides accessor methods for values, supports default values, required arguments,
|
113
|
+
and argument value validation.
|
114
|
+
test_files:
|
115
|
+
- spec/keyword_args_spec.rb
|
116
|
+
- spec/spec_helper.rb
|