opt_struct 0.9.0 → 1.0.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 +4 -4
- data/README.md +291 -83
- data/lib/opt_struct/class_methods.rb +2 -4
- data/lib/opt_struct/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a55fe71d013b67b97989363367748f78f0cf50ba890aeafdd69183b11c9bcac
|
4
|
+
data.tar.gz: ee9465e01cac7b9978a3860cbabf0e26322246b7eda546b01a23ff6afb9715bf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d688e9d507c70fcc588aff5a6b9c7109eff834a7f121f0508796758b29e4c86d93c134c6e43396eca023fadc05113ca79ddae40087f20e049445c4d4343a7335
|
7
|
+
data.tar.gz: 21a4f2a0a46034a18c9973596ff0f4f569d853e02f4c4b98bb3331368baee5af2b4a8718ace0a42b21c6375168cb1b6f9eabd2dff045f09f5aeed681600c69f5
|
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
|
-
|
12
|
+
# Examples
|
10
13
|
|
11
|
-
|
14
|
+
## Creating an OptStruct
|
12
15
|
|
13
16
|
```ruby
|
14
|
-
|
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
|
-
|
17
|
-
# => argument error
|
27
|
+
## Using an OptStruct
|
18
28
|
|
19
|
-
|
20
|
-
|
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
|
-
|
23
|
-
# => #<MyClass>
|
47
|
+
# Documentation
|
24
48
|
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
-
|
53
|
+
The following are functionally equivalent
|
33
54
|
|
34
|
-
|
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
|
-
|
69
|
+
`OptStruct` classes can safely have descendants with their own isolated options.
|
37
70
|
|
38
71
|
```ruby
|
39
|
-
class
|
40
|
-
required :
|
41
|
-
option :bar, default: "foo"
|
72
|
+
class AdminUser < User
|
73
|
+
required :token
|
42
74
|
end
|
43
75
|
|
44
|
-
|
45
|
-
# =>
|
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
|
-
|
48
|
-
# => #<
|
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
|
-
##
|
86
|
+
## Use As Mixin Module
|
59
87
|
|
60
|
-
|
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
|
-
|
64
|
-
include OptStruct
|
65
|
-
|
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
|
-
|
69
|
-
|
103
|
+
```ruby
|
104
|
+
Visitable = OptStruct.build { options :expected_at, :arrived_at, :departed_at }
|
70
105
|
|
71
|
-
|
72
|
-
|
106
|
+
class AuditLog
|
107
|
+
include Visitable
|
108
|
+
end
|
73
109
|
```
|
74
110
|
|
75
|
-
|
111
|
+
These example results in an `AuditLog` class with identical behavior, but no explicit `Visitable` module.
|
76
112
|
|
77
|
-
|
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
|
81
|
-
include
|
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
|
-
|
85
|
-
# => argument error
|
132
|
+
All of the examples in this section are functionally equivalent.
|
86
133
|
|
87
|
-
|
88
|
-
|
89
|
-
|
134
|
+
```ruby
|
135
|
+
class User < OptStruct.new
|
136
|
+
option :email
|
137
|
+
option :role, default: "member"
|
138
|
+
end
|
90
139
|
```
|
91
140
|
|
92
|
-
|
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
|
-
|
153
|
+
Passing a Hash to `.new` or `.build` is equivalent to passing the same hash to `options`
|
95
154
|
|
96
155
|
```ruby
|
97
|
-
|
98
|
-
|
99
|
-
option :last_name
|
156
|
+
User < OptStruct.new(email: nil, role: "member")
|
157
|
+
```
|
100
158
|
|
101
|
-
|
102
|
-
|
103
|
-
|
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
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
t.name
|
112
|
-
# => "Trish Smith"
|
168
|
+
```ruby
|
169
|
+
class User < OptStruct.new
|
170
|
+
options :email, role: -> { "member" }
|
171
|
+
end
|
172
|
+
```
|
113
173
|
|
114
|
-
|
115
|
-
|
116
|
-
|
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
|
-
|
119
|
-
|
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
|
-
|
299
|
+
options.fetch(:name) { "#{given_name} #{family_name}" }
|
124
300
|
end
|
125
301
|
end
|
302
|
+
```
|
303
|
+
|
304
|
+
## On Initialization
|
126
305
|
|
127
|
-
|
128
|
-
|
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
|
-
|
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
|
-
|
136
|
-
|
137
|
-
|
323
|
+
```ruby
|
324
|
+
class UserReportBuilder < OptStruct.new(:user)
|
325
|
+
attr_reader :report
|
326
|
+
init { @report = [] }
|
327
|
+
end
|
328
|
+
```
|
138
329
|
|
139
|
-
|
140
|
-
|
141
|
-
|
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.
|
@@ -17,8 +17,6 @@ module OptStruct
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def option_reader(*keys)
|
20
|
-
check_reserved_words(keys)
|
21
|
-
|
22
20
|
keys.each do |key|
|
23
21
|
define_method(key) do
|
24
22
|
if options.key?(key)
|
@@ -31,14 +29,13 @@ module OptStruct
|
|
31
29
|
end
|
32
30
|
|
33
31
|
def option_writer(*keys)
|
34
|
-
check_reserved_words(keys)
|
35
|
-
|
36
32
|
keys.each do |key|
|
37
33
|
define_method("#{key}=") { |value| options[key] = value }
|
38
34
|
end
|
39
35
|
end
|
40
36
|
|
41
37
|
def option_accessor(*keys)
|
38
|
+
check_reserved_words(keys)
|
42
39
|
option_reader *keys
|
43
40
|
option_writer *keys
|
44
41
|
end
|
@@ -66,6 +63,7 @@ module OptStruct
|
|
66
63
|
required(*arguments)
|
67
64
|
expected_arguments.concat(arguments)
|
68
65
|
end
|
66
|
+
alias_method :expect_argument, :expect_arguments
|
69
67
|
|
70
68
|
def expected_arguments
|
71
69
|
@expected_arguments ||= []
|
data/lib/opt_struct/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: opt_struct
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.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-
|
11
|
+
date: 2019-10-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|