opt_struct 0.9.0 → 1.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 54518e9eca18b69463f611c221bd896a94cbd5293bd804a23319b0dd7441573d
4
- data.tar.gz: 36584d846f30ebf6517d541a31668678a1a8dbd9025a4d9bef032eea2372c50f
3
+ metadata.gz: 824012017028badde180165fa6280f398cbf138f09f8600319625e92fbaf67ed
4
+ data.tar.gz: 2c612d601c220eb88aaaf656917cd06d0edc7a87b70f84c67cda6b17e6cbbe94
5
5
  SHA512:
6
- metadata.gz: ff0a53a81253354676e0578028d775785eda00d30c7201b3f36414009b26ad551893f789dc9aabf82ec212b7abd5544432a045cfe4f973e59136ffeec1258867
7
- data.tar.gz: fbbd6f407b889e2cca7b6b9d5ce891cdad58bb73db10a1e437515d74d1221af442da0cbb7715f8f20db05ea0799035951e094d9ad5bece86775f7c05fdd17c1e
6
+ metadata.gz: f7cf9e8c1d9e0b9c40a2f08c1f32784fa61b38a14edac39f4bef3f887845827a57f9b813e2f8df6dbb0436734c8f5895d7b25f45adb2bff3fa82eb7e87766fee
7
+ data.tar.gz: aea1084efba08f15951e7d8629c1a8211d66bddd720dfee19a1c1ec5a8eb52d6c73f047ac4ae8c5b7a969912113b0093249f48f991415ca932170d64e69b956d
data/README.md CHANGED
@@ -1,4 +1,7 @@
1
- # The Opt Struct
1
+ # The Opt Struct [![Build Status][travis-image]][travis-link]
2
+
3
+ [travis-image]: https://travis-ci.org/carlzulauf/opt_struct.svg?branch=master
4
+ [travis-link]: http://travis-ci.org/carlzulauf/opt_struct
2
5
 
3
6
  A struct around a hash. Great for encapsulating actions with complex configuration, like interactor/action classes.
4
7
 
@@ -6,137 +9,342 @@ A struct around a hash. Great for encapsulating actions with complex configurati
6
9
  gem "opt_struct"
7
10
  ```
8
11
 
9
- ## Example 1
12
+ # Examples
10
13
 
11
- Can work mostly like a regular struct, while accepting options
14
+ ## Creating an OptStruct
12
15
 
13
16
  ```ruby
14
- MyClass = OptStruct.new(:foo, :bar)
17
+ class User < OptStruct.new
18
+ required :email, :name
19
+ option :role, default: "member"
20
+
21
+ def formatted_email
22
+ %{"#{name}" <#{email}>}
23
+ end
24
+ end
25
+ ```
15
26
 
16
- MyClass.new
17
- # => argument error
27
+ ## Using an OptStruct
18
28
 
19
- MyClass.new "foo", "bar"
20
- # => #<MyClass>
29
+ ```ruby
30
+ user = User.new(email: "admin@user.com", name: "Ms. Admin", role: "admin")
31
+
32
+ # option accessors are available
33
+ user.name
34
+ # => "Ms. Admin"
35
+ user.formatted_email
36
+ # => "\"Ms. Admin\" <admin@user.com>"
37
+ user.name = "Amber Admin"
38
+ # => "Amber Admin"
39
+
40
+ # values are also accessible through the `#options` Hash
41
+ user.options
42
+ # => {:email=>"admin@user.com", :name=>"Amber Admin", :role=>"admin"}
43
+ user.options.fetch(:role)
44
+ # => "admin"
45
+ ```
21
46
 
22
- MyClass.new foo: "foo", bar: "bar"
23
- # => #<MyClass>
47
+ # Documentation
24
48
 
25
- i = MyClass.new "foo", "bar", yin: "yang"
26
- i.options
27
- # => {yin: "yang"}
28
- i.fetch(:yin)
29
- # => "yang"
30
- ```
49
+ ## Use As Inheritable Class
50
+
51
+ `OptStruct.new` returns an instance of `Class` that can be inherited or initialized directly.
31
52
 
32
- ## Example 2
53
+ The following are functionally equivalent
33
54
 
34
- Passing a hash promotes the keys (`:foo` below) to an `option` giving it getter/setter methods on the class. The value becomes the default. This is equivalent and can be combined with using the `option` macro.
55
+ ```ruby
56
+ class User < OptStruct.new
57
+ required :email
58
+ option :name
59
+ end
60
+ ```
61
+
62
+ ```ruby
63
+ User = OptStruct.new do
64
+ required :email
65
+ option :name
66
+ end
67
+ ```
35
68
 
36
- If an option is required it needs to be called out as such using `required`.
69
+ `OptStruct` classes can safely have descendants with their own isolated options.
37
70
 
38
71
  ```ruby
39
- class MyClass < OptStruct.new(foo: "bar")
40
- required :yin # equivalent to: `option :yin, required: true`
41
- option :bar, default: "foo"
72
+ class AdminUser < User
73
+ required :token
42
74
  end
43
75
 
44
- MyClass.new
45
- # => missing keyword argument :yin
76
+ User.new(email: "regular@user.com")
77
+ # => #<User:0x0... @options={:email=>"regular@user.com"}>
78
+
79
+ AdminUser.new(email: "admin@user.com")
80
+ # ArgumentError: missing required keywords: [:token]
46
81
 
47
- i = MyClass.new yin: "yang"
48
- # => #<MyClass>
49
- i.foo
50
- # => "bar"
51
- i.bar
52
- # => "foo"
53
- i.foo = "foo"
54
- i.options
55
- # => {foo: "foo", bar: "foo", yin: "yang"}
82
+ AdminUser.new(email: "admin@user.com", token: "a2236843f0227af2")
83
+ # => #<AdminUser:0x0... @options={:email=>"admin@user.com", :token=>"..."}>
56
84
  ```
57
85
 
58
- ## Example 3
86
+ ## Use As Mixin Module
59
87
 
60
- Works as a plain old mixin as well.
88
+ `OptStruct.build` returns an instance of `Module` that can be included into a class or another module.
89
+
90
+ The following are functionally equivalent
61
91
 
62
92
  ```ruby
63
- class MyClass
64
- include OptStruct
65
- required :foo
93
+ module Visitable
94
+ include OptStruct.build
95
+ options :expected_at, :arrived_at, :departed_at
96
+ end
97
+
98
+ class AuditLog
99
+ include Visitable
66
100
  end
101
+ ```
67
102
 
68
- MyClass.new
69
- # => missing keyword argument :foo
103
+ ```ruby
104
+ Visitable = OptStruct.build { options :expected_at, :arrived_at, :departed_at }
70
105
 
71
- MyClass.new(foo: "bar").foo
72
- # => "bar"
106
+ class AuditLog
107
+ include Visitable
108
+ end
73
109
  ```
74
110
 
75
- ## Example 4
111
+ These examples result in an `AuditLog` class with identical behavior, but no explicit `Visitable` module.
76
112
 
77
- Options passed to `new` can be passed to `build` when used in module form.
113
+ ```ruby
114
+ class AuditLog
115
+ include OptStruct.build
116
+ options :expected_at, :arrived_at, :departed_at
117
+ end
118
+ ```
78
119
 
79
120
  ```ruby
80
- class MyClass
81
- include OptStruct.build(:foo, bar: nil)
121
+ class AuditLog
122
+ include(OptStruct.build do
123
+ options :expected_at, :arrived_at, :departed_at
124
+ end)
82
125
  end
126
+ ```
127
+
128
+ ## Optional Arguments
129
+
130
+ Optional arguments are simply accessor methods for values expected to be in the `#options` Hash. Optional arguments can be defined in multiple ways.
83
131
 
84
- MyClass.new
85
- # => argument error
132
+ All of the examples in this section are functionally equivalent.
86
133
 
87
- i = MyClass.new("something", bar: "foo")
88
- [i.foo, i.bar]
89
- # => ["something", "foo"]
134
+ ```ruby
135
+ class User < OptStruct.new
136
+ option :email
137
+ option :role, default: "member"
138
+ end
90
139
  ```
91
140
 
92
- ## Example 5
141
+ ```ruby
142
+ class User < OptStruct.new
143
+ options :email, role: "member"
144
+ end
145
+ ```
146
+
147
+ ```ruby
148
+ class User < OptStruct.new
149
+ options email: nil, role: "member"
150
+ end
151
+ ```
93
152
 
94
- Both `build` and `new` accept a block.
153
+ Passing a Hash to `.new` or `.build` is equivalent to passing the same hash to `options`
95
154
 
96
155
  ```ruby
97
- PersonClass = OptStruct.new do
98
- required :first_name
99
- option :last_name
156
+ User = OptStruct.new(email: nil, role: "member")
157
+ ```
100
158
 
101
- def name
102
- [first_name, last_name].compact.join(" ")
103
- end
159
+ Default blocks can also be used and are late evaluated on the each struct instance.
160
+
161
+ ```ruby
162
+ class User < OptStruct.new
163
+ option :email, default: -> { nil }
164
+ option :role, -> { "member" }
104
165
  end
166
+ ```
105
167
 
106
- t = PersonClass.new(first_name: "Trish")
107
- # => #<PersonClass>
108
- t.name
109
- # => "Trish"
110
- t.last_name = "Smith"
111
- t.name
112
- # => "Trish Smith"
168
+ ```ruby
169
+ class User < OptStruct.new
170
+ options :email, role: -> { "member" }
171
+ end
172
+ ```
113
173
 
114
- CarModule = OptStruct.build do
115
- required :make, :model
116
- options year: -> { Date.today.year }, transmission: :default_transmission
174
+ ```ruby
175
+ class User < OptStruct.new
176
+ option :email, nil
177
+ option :role, -> { default_role }
178
+
179
+ private
180
+
181
+ def default_role
182
+ "member"
183
+ end
184
+ end
185
+ ```
117
186
 
118
- def default_transmission
119
- "Automatic"
187
+ Default symbols are treated as method calls if the struct `#respond_to?` the method.
188
+
189
+ ```ruby
190
+ class User < OptStruct.new
191
+ options :email, :role => :default_role
192
+
193
+ def default_role
194
+ "member"
120
195
  end
196
+ end
197
+ ```
198
+
199
+ ## Required Arguments
200
+
201
+ Required arguments are just like optional arguments, except they are also added to the `.required_keys` collection, which is checked when an OptStruct is initialized. If the `#options` Hash does not contain all `.required_keys` then an `ArgumentError` is raised.
202
+
203
+ The following examples are functionally equivalent.
204
+
205
+ ```ruby
206
+ class Student < OptStruct.new
207
+ required :name
208
+ end
209
+ ```
210
+
211
+ ```ruby
212
+ class Student < OptStruct.new
213
+ option :name, required: true
214
+ end
215
+ ```
216
+
217
+ ```ruby
218
+ class Student < OptStruct.new
219
+ option :name
220
+ required_keys << :name
221
+ end
222
+ ```
223
+
224
+ ### Expected Arguments
225
+
226
+ OptStructs can accept non-keyword arguments if the struct knows to expect them.
227
+
228
+ For code like this to work...
229
+
230
+ ```ruby
231
+ user = User.new("admin@user.com", "admin")
232
+ user.email # => "admin@user.com"
233
+ user.role # => "admin"
234
+ ```
235
+
236
+ ... the OptStruct needs to have some `.expected_arguments`.
237
+
238
+ The following `User` class examples are functionally equivalent and allow the code above to function.
239
+
240
+ ```ruby
241
+ User = OptStruct.new(:email, :role)
242
+ ```
243
+
244
+ ```ruby
245
+ class User < OptStruct.new(:email)
246
+ expect_argument :role
247
+ end
248
+ ```
249
+
250
+ ```ruby
251
+ class User
252
+ include OptStruct.build(:email, :role)
253
+ end
254
+ ```
255
+
256
+ ```ruby
257
+ class User
258
+ include OptStruct.build
259
+ expect_arguments :email, :role
260
+ end
261
+ ```
121
262
 
263
+ ```ruby
264
+ class User < OptStruct.new(:email)
265
+ expected_arguments << :role
266
+ end
267
+ ```
268
+
269
+ Expected arguments are similar to required arguments, except they are in `.expected_arguments` collection, which is checked when an OptStruct is initialized.
270
+
271
+ Expected arguments can also be supplied using keywords. An `ArgumentError` is only raised if the expected argument is not in the list of arguments passed to `OptStruct#new` **and** the argument is not present in the `#options` Hash.
272
+
273
+ The following examples will initialize any of the `User` class examples above without error.
274
+
275
+ ```ruby
276
+ User.new(email: "example@user.com", role: "member")
277
+ User.new("example@user.com", role: "member")
278
+ User.new(role: "member", email: "example@user.com")
279
+ ```
280
+
281
+ ## The `#options` Hash
282
+
283
+ All OptStruct arguments are read from and stored in a single `Hash` instance. This Hash can be accessed directly using the `options` method.
284
+
285
+ ```ruby
286
+ Person = OptStruct.new(:name)
287
+ Person.new(name: "John", age: 32).options
288
+ # => {:name=>"John", :age=>32}
289
+ ```
290
+
291
+ Feel free to write your own accessor methods for things like dependent options or other complex/private behavior.
292
+
293
+ ```ruby
294
+ class Person < OptStruct.new
295
+ option :given_name
296
+ option :family_name
297
+
122
298
  def name
123
- [year, make, model].compact.join(" ")
299
+ options.fetch(:name) { "#{given_name} #{family_name}" }
124
300
  end
125
301
  end
302
+ ```
303
+
304
+ ## On Initialization
126
305
 
127
- class CarClass
128
- include CarModule
306
+ All of the following examples are functionally equivalent.
307
+
308
+ OptStruct classes are initialized in an `initialize` method (in `OptStruct::InstanceMethods`) like most classes. Also, like most classes, you can override `initialize` as long as you remember to call `super` properly to retain `OptStruct` functionality.
309
+
310
+ ```ruby
311
+ class UserReportBuilder < OptStruct.new(:user)
312
+ attr_reader :report
313
+
314
+ def initialize(*)
315
+ super
316
+ @report = []
317
+ end
129
318
  end
319
+ ```
130
320
 
131
- c = CarClass.new(make: "Infiniti", model: "G37", year: 2012)
132
- c.name
133
- # => "2012 Infinit G37"
321
+ `OptStruct` also provides initialization callbacks to make hooking into and customizing the initialization of OptStruct classes easier and require less code.
134
322
 
135
- c = CarClass.new(model: "WRX", make: "Subaru", year: nil)
136
- c.name
137
- # => "Subaru WRX"
323
+ ```ruby
324
+ class UserReportBuilder < OptStruct.new(:user)
325
+ attr_reader :report
326
+ init { @report = [] }
327
+ end
328
+ ```
138
329
 
139
- c = CarClass.new(model: "BRZ", make: "Subaru")
140
- c.name
141
- # => "2017 Subaru BRZ"
330
+ ```ruby
331
+ class UserReportBuilder < OptStruct.new(:user)
332
+ attr_reader :report
333
+
334
+ around_init do |instance|
335
+ instance.call
336
+ @report = []
337
+ end
338
+ end
142
339
  ```
340
+
341
+ Available callbacks
342
+
343
+ * `around_init`
344
+ * `before_init`
345
+ * `init`
346
+ * `after_init`
347
+
348
+ ## Inheritance, Expanded
349
+
350
+ See `spec/inheritance_spec.rb` for examples of just how crazy you can get.
data/lib/opt_struct.rb CHANGED
@@ -3,10 +3,11 @@ require "opt_struct/module_methods"
3
3
  require "opt_struct/instance_methods"
4
4
 
5
5
  module OptStruct
6
+ RESERVED_WORDS = %i(class defaults options fetch check_required_args check_required_keys).freeze
6
7
 
7
- def self._inject_struct(target, source, args = [], defaults = {}, &callback)
8
+ def self._inject_struct(target, source, args = [], **defaults, &callback)
8
9
  structs = Array(source.instance_variable_get(:@_opt_structs)).dup
9
- if args.any? || defaults.any? || callback
10
+ if args.any? || defaults.any? || block_given?
10
11
  structs << [args, defaults, callback]
11
12
  end
12
13
  target.instance_variable_set(:@_opt_structs, structs)
@@ -18,7 +19,7 @@ module OptStruct
18
19
  end
19
20
  structs.each do |s_args, s_defaults, s_callback|
20
21
  target.expect_arguments *s_args if s_args.any?
21
- target.options s_defaults if s_defaults.any?
22
+ target.options **s_defaults if s_defaults.any?
22
23
  target.class_exec(&s_callback) if s_callback
23
24
  end
24
25
  else
@@ -38,10 +39,10 @@ module OptStruct
38
39
  end
39
40
 
40
41
  def self.new(*args, **defaults, &callback)
41
- _inject_struct(Class.new, self, args.map(&:to_sym), defaults, &callback)
42
+ _inject_struct(Class.new, self, args.map(&:to_sym), **defaults, &callback)
42
43
  end
43
44
 
44
45
  def self.build(*args, **defaults, &callback)
45
- _inject_struct(Module.new, self, args.map(&:to_sym), defaults, &callback)
46
+ _inject_struct(Module.new, self, args.map(&:to_sym), **defaults, &callback)
46
47
  end
47
48
  end
@@ -1,75 +1,81 @@
1
1
  module OptStruct
2
2
  module ClassMethods
3
3
  def inherited(subclass)
4
- instance_variables.each do |v|
5
- ivar = instance_variable_get(v)
6
- subclass.send(:instance_variable_set, v, ivar.dup) if ivar
4
+ opt_struct_class_constants.each do |c|
5
+ subclass.const_set(c, const_get(c)) if const_defined?(c)
7
6
  end
8
7
  end
9
8
 
9
+ # overwritten if `required` is called
10
10
  def required_keys
11
- @required_keys ||= []
11
+ [].freeze
12
12
  end
13
13
 
14
- def required(*keys)
15
- required_keys.concat keys
16
- option_accessor *keys
14
+ def required(*keys, **options)
15
+ add_required_keys *keys
16
+ option_accessor *keys, **options
17
17
  end
18
18
 
19
- def option_reader(*keys)
20
- check_reserved_words(keys)
21
-
19
+ def option_reader(*keys, **options)
22
20
  keys.each do |key|
23
- define_method(key) do
24
- if options.key?(key)
25
- options[key]
26
- elsif defaults.key?(key)
27
- options[key] = read_default_value(key)
21
+ class_eval <<~RUBY
22
+ #{options[:private] ? "private" : ""} def #{key}
23
+ options[:#{key}]
28
24
  end
29
- end
25
+ RUBY
30
26
  end
31
27
  end
32
28
 
33
- def option_writer(*keys)
34
- check_reserved_words(keys)
35
-
29
+ def option_writer(*keys, **options)
36
30
  keys.each do |key|
37
- define_method("#{key}=") { |value| options[key] = value }
31
+ class_eval <<~RUBY
32
+ #{options[:private] ? "private" : ""} def #{key}=(value)
33
+ options[:#{key}] = value
34
+ end
35
+ RUBY
38
36
  end
39
37
  end
40
38
 
41
- def option_accessor(*keys)
42
- option_reader *keys
43
- option_writer *keys
39
+ def option_accessor(*keys, **options)
40
+ check_reserved_words(keys)
41
+ option_reader *keys, **options
42
+ option_writer *keys, **options
44
43
  end
45
44
 
46
- def option(key, default = nil, **options)
45
+ def option(key, default = nil, required: false, **options)
47
46
  default = options[:default] if options.key?(:default)
48
- defaults[key] = default
49
- required_keys << key if options[:required]
50
- option_accessor key
47
+ add_defaults key => default
48
+ add_required_keys key if required
49
+ option_accessor key, **options
51
50
  end
52
51
 
53
52
  def options(*keys, **keys_defaults)
54
53
  option_accessor *keys if keys.any?
55
54
  if keys_defaults.any?
56
- defaults.merge!(keys_defaults)
55
+ add_defaults keys_defaults
57
56
  option_accessor *(keys_defaults.keys - expected_arguments)
58
57
  end
59
58
  end
60
59
 
61
60
  def defaults
62
- @defaults ||= {}
61
+ const_defined?(:OPT_DEFAULTS) ? const_get(:OPT_DEFAULTS) : {}
63
62
  end
64
63
 
65
- def expect_arguments(*arguments)
66
- required(*arguments)
67
- expected_arguments.concat(arguments)
64
+ # overwritten if `expect_arguments` is called
65
+ def expected_arguments
66
+ [].freeze
68
67
  end
69
68
 
70
- def expected_arguments
71
- @expected_arguments ||= []
69
+ def expect_arguments(*arguments)
70
+ required(*arguments)
71
+ combined = expected_arguments + arguments
72
+ class_eval <<~EVAL
73
+ def self.expected_arguments
74
+ #{combined.inspect}.freeze
75
+ end
76
+ EVAL
72
77
  end
78
+ alias_method :expect_argument, :expect_arguments
73
79
 
74
80
  def init(meth = nil, &blk)
75
81
  add_callback(:init, meth || blk)
@@ -84,23 +90,60 @@ module OptStruct
84
90
  add_callback(:around_init, meth || blk)
85
91
  end
86
92
 
87
- def add_callback(name, callback)
88
- @_callbacks ||= {}
89
- @_callbacks[name] ||= []
90
- @_callbacks[name] << callback
93
+ def all_callbacks
94
+ const_defined?(:OPT_CALLBACKS) ? const_get(:OPT_CALLBACKS) : {}.freeze
91
95
  end
92
96
 
93
- def all_callbacks
94
- @_callbacks
97
+ def shareable?
98
+ const_defined?(:SHAREABLE) && const_get(:SHAREABLE)
99
+ end
100
+
101
+ def shareable!
102
+ return if shareable?
103
+ const_set(:SHAREABLE, true)
95
104
  end
96
105
 
97
106
  private
98
107
 
99
- RESERVED_WORDS = %i(class defaults options fetch check_required_args check_required_keys)
108
+ def share(value)
109
+ return value unless shareable?
110
+ defined?(Ractor) ? Ractor.make_shareable(value) : value
111
+ end
112
+
113
+ def add_required_keys(*keys)
114
+ combined = required_keys + keys
115
+ class_eval <<~RUBY
116
+ def self.required_keys
117
+ #{combined.inspect}.freeze
118
+ end
119
+ RUBY
120
+ end
121
+
122
+ def add_defaults(defaults_to_add)
123
+ freezer = defaults.dup
124
+ defaults_to_add.each { |k, v| freezer[k] = share(v) }
125
+ remove_const(:OPT_DEFAULTS) if const_defined?(:OPT_DEFAULTS)
126
+ const_set(:OPT_DEFAULTS, freezer.freeze)
127
+ end
128
+
129
+ def add_callback(name, callback)
130
+ if const_defined?(:OPT_CALLBACKS)
131
+ callbacks_for_name = (all_callbacks[name] || []) + [callback]
132
+ callbacks_hash = all_callbacks.merge(name => callbacks_for_name).freeze
133
+ remove_const(:OPT_CALLBACKS)
134
+ const_set(:OPT_CALLBACKS, callbacks_hash)
135
+ else
136
+ const_set(:OPT_CALLBACKS, { name => [ callback ] })
137
+ end
138
+ end
139
+
140
+ def opt_struct_class_constants
141
+ [:OPT_DEFAULTS, :OPT_CALLBACKS]
142
+ end
100
143
 
101
144
  def check_reserved_words(words)
102
145
  Array(words).each do |word|
103
- if RESERVED_WORDS.member?(word)
146
+ if OptStruct::RESERVED_WORDS.member?(word)
104
147
  raise ArgumentError, "Use of reserved word is not permitted: #{word.inspect}"
105
148
  end
106
149
  end
@@ -4,6 +4,7 @@ module OptStruct
4
4
  with_init_callbacks do
5
5
  @options = options
6
6
  assign_arguments(arguments)
7
+ assign_defaults
7
8
  check_required_keys
8
9
  end
9
10
  end
@@ -24,23 +25,21 @@ module OptStruct
24
25
  raise ArgumentError, "missing required keywords: #{missing.inspect}"
25
26
  end
26
27
  end
27
-
28
+
29
+ def assign_defaults
30
+ defaults.each do |key, default_value|
31
+ next if options.key?(key) # || default_value.nil?
32
+ options[key] = read_default_value(default_value)
33
+ end
34
+ end
35
+
28
36
  def assign_arguments(args)
29
37
  self.class.expected_arguments.map.with_index do |key, i|
30
- if args.length > i
31
- options[key] = args[i]
32
- elsif !options.key?(key)
33
- if defaults.key?(key)
34
- options[key] = read_default_value(key)
35
- else
36
- raise ArgumentError, "missing required argument: #{key}"
37
- end
38
- end
38
+ options[key] = args[i] if args.length > i
39
39
  end
40
40
  end
41
-
42
- def read_default_value(key)
43
- default = defaults[key]
41
+
42
+ def read_default_value(default)
44
43
  case default
45
44
  when Proc
46
45
  instance_exec(&default)
@@ -22,8 +22,8 @@ module OptStruct
22
22
  options
23
23
  expect_arguments
24
24
  ).each do |class_method|
25
- define_method(class_method) do |*args|
26
- @_opt_structs << [[], {}, -> { send(class_method, *args) }]
25
+ define_method(class_method) do |*args, **options|
26
+ @_opt_structs << [[], {}, -> { send(class_method, *args, **options) }]
27
27
  end
28
28
  end
29
29
  end
@@ -1,3 +1,3 @@
1
1
  module OptStruct
2
- VERSION = "0.9.0"
2
+ VERSION = "1.2.0"
3
3
  end
data/opt_struct.gemspec CHANGED
@@ -16,8 +16,4 @@ Gem::Specification.new do |spec|
16
16
  spec.files = `git ls-files`.split("\n").grep(/^lib/)
17
17
  spec.files += %w(README.md opt_struct.gemspec)
18
18
  spec.require_paths = ["lib"]
19
-
20
- spec.add_development_dependency "bundler", "~> 1.14"
21
- spec.add_development_dependency "rake", "~> 10.0"
22
- spec.add_development_dependency "rspec", "~> 3.0"
23
19
  end
metadata CHANGED
@@ -1,57 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: opt_struct
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Carl Zulauf
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-10-05 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: bundler
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '1.14'
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '1.14'
27
- - !ruby/object:Gem::Dependency
28
- name: rake
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '10.0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '10.0'
41
- - !ruby/object:Gem::Dependency
42
- name: rspec
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '3.0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '3.0'
11
+ date: 2021-03-30 00:00:00.000000000 Z
12
+ dependencies: []
55
13
  description: Struct with support for keyword params and mixin support
56
14
  email:
57
15
  - carl@linkleaf.com
@@ -85,7 +43,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
85
43
  - !ruby/object:Gem::Version
86
44
  version: '0'
87
45
  requirements: []
88
- rubygems_version: 3.0.3
46
+ rubygems_version: 3.1.4
89
47
  signing_key:
90
48
  specification_version: 4
91
49
  summary: The Option Struct