option_initializer 1.5.0 → 1.5.1
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 +4 -4
- data/lib/option_initializer.rb +91 -39
- data/lib/option_initializer/version.rb +1 -1
- data/option_initializer.gemspec +1 -0
- data/test/test_option_initializer.rb +28 -0
- metadata +18 -2
data/README.md
CHANGED
@@ -17,10 +17,10 @@ class Person
|
|
17
17
|
include OptionInitializer
|
18
18
|
|
19
19
|
option_initializer :id,
|
20
|
-
:name
|
20
|
+
:name => String,
|
21
21
|
:greetings => :&,
|
22
|
-
:birthday
|
23
|
-
:sex
|
22
|
+
:birthday => 1..3,
|
23
|
+
:sex => Set[:male, :female]
|
24
24
|
|
25
25
|
option_validator :name do |v|
|
26
26
|
raise ArgumentError, "invalid name" if v.empty?
|
@@ -78,7 +78,7 @@ class MyClass
|
|
78
78
|
:c => 1..3, # 1, 2, or 3 objects of any type
|
79
79
|
:d => :*, # Any number of objects of any type
|
80
80
|
:e => :&, # Block
|
81
|
-
:f => Fixnum,
|
81
|
+
:f => Fixnum | Range, # Single Fixnum or Range object
|
82
82
|
:g => [Fixnum, String, Symbol], # Fixnum, String, and Symbol
|
83
83
|
:h => Set[true, false], # Value must be either true or false
|
84
84
|
:i => [Fixnum, Set[true, false]] # Fixnum and boolean
|
data/lib/option_initializer.rb
CHANGED
@@ -1,57 +1,94 @@
|
|
1
1
|
require 'option_initializer/version'
|
2
2
|
require 'set'
|
3
3
|
|
4
|
-
|
5
|
-
class
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
@base = base
|
12
|
-
@options = options
|
4
|
+
unless Class.respond_to?(:|)
|
5
|
+
class Class
|
6
|
+
def | other_class
|
7
|
+
unless other_class.is_a?(Class)
|
8
|
+
raise TypeError, "wrong argument type (expected: Class)"
|
9
|
+
end
|
10
|
+
OptionInitializer::ClassMatch.new(self, other_class)
|
13
11
|
end
|
12
|
+
end
|
13
|
+
else
|
14
|
+
Kernel.warn "Class already has `|' method. OptionInitializer will not override its behavior."
|
15
|
+
end
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
-
|
17
|
+
# @private
|
18
|
+
class OptionInitializingTemplate
|
19
|
+
attr_reader :options
|
20
|
+
alias to_h options
|
18
21
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
else
|
25
|
-
opts = opts.dup
|
26
|
-
end
|
22
|
+
def initialize base, options, need_validation
|
23
|
+
validate options if need_validation
|
24
|
+
@base = base
|
25
|
+
@options = options
|
26
|
+
end
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
def new *args, &block
|
29
|
+
args = args.dup
|
30
|
+
opts = @options
|
31
|
+
|
32
|
+
# Convention. Deal with it.
|
33
|
+
if args.last.is_a?(Hash)
|
34
|
+
validate args.last
|
35
|
+
opts = opts.merge(args.last)
|
36
|
+
args.pop
|
37
|
+
else
|
38
|
+
opts = opts.dup
|
39
|
+
end
|
40
|
+
|
41
|
+
opts.instance_eval do
|
42
|
+
def option_validated?
|
43
|
+
true
|
32
44
|
end
|
33
|
-
|
45
|
+
end
|
46
|
+
args << opts
|
34
47
|
|
35
|
-
|
48
|
+
@base.new(*args, &block)
|
49
|
+
end
|
50
|
+
|
51
|
+
def merge opts
|
52
|
+
validate opts
|
53
|
+
self.class.new @base, @options.merge(opts), false
|
54
|
+
end
|
55
|
+
|
56
|
+
def validate hash
|
57
|
+
avals, vals = [:ARG_VALIDATORS, :VALIDATORS].map { |s|
|
58
|
+
self.class.const_get(s)
|
59
|
+
}
|
60
|
+
hash.each do |k, v|
|
61
|
+
avals[k] && avals[k].call(v)
|
62
|
+
vals[k] && vals[k].call(v)
|
63
|
+
vals[nil] && vals[nil].call(k, v)
|
36
64
|
end
|
65
|
+
end
|
66
|
+
end
|
37
67
|
|
38
|
-
|
39
|
-
|
40
|
-
|
68
|
+
module OptionInitializer
|
69
|
+
class ClassMatch
|
70
|
+
def initialize *classes
|
71
|
+
@classes = Set[*classes]
|
41
72
|
end
|
42
73
|
|
43
|
-
def
|
44
|
-
|
45
|
-
|
46
|
-
}
|
47
|
-
hash.each do |k, v|
|
48
|
-
avals[k] && avals[k].call(v)
|
49
|
-
vals[k] && vals[k].call(v)
|
50
|
-
vals[nil] && vals[nil].call(k, v)
|
74
|
+
def | other_class
|
75
|
+
unless other_class.is_a?(Class)
|
76
|
+
raise TypeError, "wrong argument type (expected: Class)"
|
51
77
|
end
|
78
|
+
ClassMatch.new(*@classes.union([other_class]))
|
79
|
+
end
|
80
|
+
|
81
|
+
def match object
|
82
|
+
@classes.any? { |k| object.is_a? k }
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_s
|
86
|
+
a = @classes.map(&:to_s)
|
87
|
+
[a[0...-1].join(', '), a.last].reject(&:empty?).join(', or ')
|
52
88
|
end
|
53
89
|
end
|
54
90
|
|
91
|
+
# @private
|
55
92
|
module MethodCallShortcut
|
56
93
|
def method_missing sym, *args, &block
|
57
94
|
# 1.8
|
@@ -79,6 +116,7 @@ module OptionInitializer
|
|
79
116
|
options
|
80
117
|
end
|
81
118
|
|
119
|
+
# @private
|
82
120
|
def self.included base
|
83
121
|
unless base.constants.map(&:to_sym).include?(:OptionInitializing)
|
84
122
|
base.const_set :OptionInitializing, oi = OptionInitializingTemplate.dup
|
@@ -120,9 +158,13 @@ module OptionInitializer
|
|
120
158
|
when Set
|
121
159
|
raise ArgumentError, "empty set of values specified for #{k}" if v.length == 0
|
122
160
|
when Array
|
123
|
-
|
161
|
+
unless v.all? { |e| [Class, Set, ClassMatch].any? { |kl| e.is_a?(kl) } }
|
162
|
+
raise ArgumentError, "invalid option definition: `#{v}'"
|
163
|
+
end
|
124
164
|
when Class, :*, :&
|
125
165
|
# noop
|
166
|
+
when ClassMatch
|
167
|
+
# noop
|
126
168
|
else
|
127
169
|
raise ArgumentError, "invalid option definition: `#{v}'"
|
128
170
|
end
|
@@ -191,6 +233,10 @@ module OptionInitializer
|
|
191
233
|
unless c.include?(e)
|
192
234
|
raise ArgumentError, "invalid option value: `#{e}' (expected one of #{c.to_a.inspect})"
|
193
235
|
end
|
236
|
+
when ClassMatch
|
237
|
+
unless c.match e
|
238
|
+
raise TypeError, "wrong argument type #{e.class} (expected #{c})"
|
239
|
+
end
|
194
240
|
end
|
195
241
|
end
|
196
242
|
end
|
@@ -201,6 +247,12 @@ module OptionInitializer
|
|
201
247
|
raise TypeError, "wrong argument type #{v.class} (expected #{nargs})"
|
202
248
|
end
|
203
249
|
}
|
250
|
+
when ClassMatch
|
251
|
+
vals[sym] = proc { |v|
|
252
|
+
unless nargs.match v
|
253
|
+
raise TypeError, "wrong argument type #{v.class} (expected #{nargs})"
|
254
|
+
end
|
255
|
+
}
|
204
256
|
end
|
205
257
|
end
|
206
258
|
|
@@ -234,7 +286,7 @@ module OptionInitializer
|
|
234
286
|
raise ArgumentError, "block not expected"
|
235
287
|
else
|
236
288
|
case nargs
|
237
|
-
when 1, Class, Set
|
289
|
+
when 1, Class, Set, ClassMatch
|
238
290
|
if v.length == 1
|
239
291
|
merge(sym => v.first)
|
240
292
|
else
|
data/option_initializer.gemspec
CHANGED
@@ -122,6 +122,19 @@ class MyClassWithTypes
|
|
122
122
|
end
|
123
123
|
end
|
124
124
|
|
125
|
+
class MyClassWithClassOrOperator
|
126
|
+
include OptionInitializer
|
127
|
+
option_initializer :a => Fixnum | Float | Range,
|
128
|
+
:b => String,
|
129
|
+
:c => [String | Symbol, Numeric]
|
130
|
+
|
131
|
+
attr_reader :options
|
132
|
+
def initialize options
|
133
|
+
validate_options options
|
134
|
+
@options = options
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
125
138
|
# Excerpt from README
|
126
139
|
class Person
|
127
140
|
include OptionInitializer
|
@@ -356,6 +369,21 @@ class TestOptionInitializer < MiniTest::Unit::TestCase
|
|
356
369
|
Person.name('John Doe').birthday(1990, 1, 1).
|
357
370
|
greetings { |name| "Hi, I'm #{name}!" }.id(1000).say_hello
|
358
371
|
end
|
372
|
+
|
373
|
+
def test_disjunctive_class
|
374
|
+
init = MyClassWithClassOrOperator.a(1).a(1..2).a(3.14).b('hello').c(:hello, 100)
|
375
|
+
[init, init.new].each do |obj|
|
376
|
+
assert_equal 3.14, obj.options[:a]
|
377
|
+
assert_equal 'hello', obj.options[:b]
|
378
|
+
assert_equal [:hello, 100], obj.options[:c]
|
379
|
+
end
|
380
|
+
assert_raises(TypeError) { MyClassWithClassOrOperator.a(:hello) }
|
381
|
+
assert_raises(TypeError) { MyClassWithClassOrOperator.b(1) }
|
382
|
+
assert_raises(TypeError) { MyClassWithClassOrOperator.c(1, 1) }
|
383
|
+
assert_raises(TypeError) { MyClassWithClassOrOperator.new(:a => :hello) }
|
384
|
+
assert_raises(TypeError) { MyClassWithClassOrOperator.new(:b => 1) }
|
385
|
+
assert_raises(TypeError) { MyClassWithClassOrOperator.new(:c => [1, 1]) }
|
386
|
+
end
|
359
387
|
end
|
360
388
|
|
361
389
|
class MyReadmeClass
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: option_initializer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.5.
|
4
|
+
version: 1.5.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-04-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: simplecov
|
@@ -27,6 +27,22 @@ dependencies:
|
|
27
27
|
- - ! '>='
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: minitest
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
30
46
|
description: Object construction with method chaining
|
31
47
|
email:
|
32
48
|
- junegunn.c@gmail.com
|