value_semantics 2.0.1 → 2.1.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 +62 -38
- data/lib/value_semantics.rb +28 -21
- data/lib/value_semantics/version.rb +1 -1
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 17ee24bc8ffd744493e9957a4cb7ee0071f0c33aa8560b3ed36689575b9c0ab1
|
4
|
+
data.tar.gz: 05da8d260966f9257578f0ebf3891ba447b51032709bb75b39bdacba3e081961
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4a4da5150969146f5fd8cb06a17f8e43b1c897cd40f228cdddd7b2a8367fdbbceeacc66843668bf74a48575d72f752d3f24fd101c2ac7e946335688a1eec95a3
|
7
|
+
data.tar.gz: c0e71a37be6df7bcafa4833f7b7cea4304468818a4391e020770c6f85e16edaf68a56c84c572f3bd26399aa85b2dc149abb48660d82750a656d746d87da5a16a
|
data/README.md
CHANGED
@@ -16,8 +16,42 @@ Invalid or missing attributes cause an exception intended for developers,
|
|
16
16
|
not an error message intended for the user.
|
17
17
|
|
18
18
|
|
19
|
-
|
20
|
-
|
19
|
+
Defining and Creating Value Objects
|
20
|
+
-----------------------------------
|
21
|
+
|
22
|
+
```ruby
|
23
|
+
class Person
|
24
|
+
include ValueSemantics.for_attributes {
|
25
|
+
name String, default: "Anon Emous"
|
26
|
+
birthday Either(Date, nil), coerce: true
|
27
|
+
}
|
28
|
+
|
29
|
+
def self.coerce_birthday(value)
|
30
|
+
if value.is_a?(String)
|
31
|
+
Date.parse(value)
|
32
|
+
else
|
33
|
+
value
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
Person.new(name: "Tom", birthday: "2020-12-25")
|
39
|
+
#=> #<Person name="Tom" birthday=#<Date: 2020-12-25 ((2459209j,0s,0n),+0s,2299161j)>>
|
40
|
+
|
41
|
+
Person.new(birthday: Date.today)
|
42
|
+
#=> #<Person name="Anon Emous" birthday=#<Date: 2018-09-04 ((2458366j,0s,0n),+0s,2299161j)>>
|
43
|
+
|
44
|
+
Person.new(birthday: nil)
|
45
|
+
#=> #<Person name="Anon Emous" birthday=nil>
|
46
|
+
```
|
47
|
+
|
48
|
+
The curly bracket syntax used with `ValueSemantics.for_attributes` is, unfortunately,
|
49
|
+
mandatory due to Ruby's precedence rules.
|
50
|
+
The `do`/`end` syntax will not work unless you surround the whole thing with parenthesis.
|
51
|
+
|
52
|
+
|
53
|
+
Using Value Objects
|
54
|
+
-------------------
|
21
55
|
|
22
56
|
```ruby
|
23
57
|
require 'value_semantics'
|
@@ -64,9 +98,31 @@ tom.eql?(other_tom) #=> true
|
|
64
98
|
tom.hash == other_tom.hash #=> true
|
65
99
|
```
|
66
100
|
|
67
|
-
|
68
|
-
|
69
|
-
|
101
|
+
|
102
|
+
Defaults
|
103
|
+
--------
|
104
|
+
|
105
|
+
Defaults can be specified in one of two ways:
|
106
|
+
the `:default` option, or the `default_generator` option.
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
class Cat
|
110
|
+
include ValueSemantics.for_attributes {
|
111
|
+
paws Integer, default: 4
|
112
|
+
born_at Time, default_generator: ->{ Time.now }
|
113
|
+
}
|
114
|
+
end
|
115
|
+
|
116
|
+
Cat.new
|
117
|
+
#=> #<Cat paws=4 born_at=2018-12-21 18:42:01 +1100>
|
118
|
+
```
|
119
|
+
|
120
|
+
The `default` option is a single value.
|
121
|
+
|
122
|
+
The `default_generator` option is a callable object, which returns a default value.
|
123
|
+
In the example above, `default_generator` is a lambda that returns the current time.
|
124
|
+
|
125
|
+
Only one of these options can be used per attribute.
|
70
126
|
|
71
127
|
|
72
128
|
Validation (Types)
|
@@ -83,7 +139,7 @@ Anything that you can use in a `case`/`when` expression will work.
|
|
83
139
|
class Person
|
84
140
|
include ValueSemantics.for_attributes {
|
85
141
|
name String
|
86
|
-
birthday /\d
|
142
|
+
birthday /\d{4}-\d{2}-\d{2}/
|
87
143
|
}
|
88
144
|
end
|
89
145
|
|
@@ -243,34 +299,6 @@ For example, the default value could be a string,
|
|
243
299
|
which would then be coerced into an `IPAddr` object.
|
244
300
|
|
245
301
|
|
246
|
-
## All Together
|
247
|
-
|
248
|
-
```ruby
|
249
|
-
class Person
|
250
|
-
include ValueSemantics.for_attributes {
|
251
|
-
name String, default: "Anon Emous"
|
252
|
-
birthday Either(Date, nil), coerce: true
|
253
|
-
}
|
254
|
-
|
255
|
-
def self.coerce_birthday(value)
|
256
|
-
if value.is_a?(String)
|
257
|
-
Date.parse(value)
|
258
|
-
else
|
259
|
-
value
|
260
|
-
end
|
261
|
-
end
|
262
|
-
end
|
263
|
-
|
264
|
-
Person.new(name: "Tom", birthday: "2020-12-25")
|
265
|
-
#=> #<Person name="Tom" birthday=#<Date: 2020-12-25 ((2459209j,0s,0n),+0s,2299161j)>>
|
266
|
-
|
267
|
-
Person.new(birthday: Date.today)
|
268
|
-
#=> #<Person name="Anon Emous" birthday=#<Date: 2018-09-04 ((2458366j,0s,0n),+0s,2299161j)>>
|
269
|
-
|
270
|
-
Person.new(birthday: nil)
|
271
|
-
#=> #<Person name="Anon Emous" birthday=nil>
|
272
|
-
```
|
273
|
-
|
274
302
|
## Installation
|
275
303
|
|
276
304
|
Add this line to your application's Gemfile:
|
@@ -288,10 +316,6 @@ Or install it yourself as:
|
|
288
316
|
$ gem install value_semantics
|
289
317
|
|
290
318
|
|
291
|
-
## TODO
|
292
|
-
|
293
|
-
- Allow defaults to be generated by calling a method (e.g. to get the current time)
|
294
|
-
|
295
319
|
## Contributing
|
296
320
|
|
297
321
|
Bug reports and pull requests are welcome on GitHub at:
|
data/lib/value_semantics.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
module ValueSemantics
|
2
|
+
NOT_SPECIFIED = Object.new.freeze
|
3
|
+
|
2
4
|
def self.for_attributes(&block)
|
3
5
|
attributes = DSL.run(&block)
|
4
6
|
generate_module(attributes.freeze)
|
@@ -75,13 +77,13 @@ module ValueSemantics
|
|
75
77
|
end
|
76
78
|
|
77
79
|
class Attribute
|
78
|
-
|
80
|
+
NO_DEFAULT_GENERATOR = ->{ raise "Attribute does not have a default value" }
|
79
81
|
|
80
|
-
attr_reader :name, :validator, :coercer
|
82
|
+
attr_reader :name, :validator, :coercer, :default_generator
|
81
83
|
|
82
|
-
def initialize(name:,
|
84
|
+
def initialize(name:, default_generator:, validator:, coercer:)
|
83
85
|
@name = name.to_sym
|
84
|
-
@
|
86
|
+
@default_generator = default_generator
|
85
87
|
@validator = validator
|
86
88
|
@coercer = coercer
|
87
89
|
freeze
|
@@ -89,10 +91,10 @@ module ValueSemantics
|
|
89
91
|
|
90
92
|
def determine_from!(attr_hash, klass)
|
91
93
|
raw_value = attr_hash.fetch(name) do
|
92
|
-
if
|
93
|
-
default_value
|
94
|
-
else
|
94
|
+
if default_generator.equal?(NO_DEFAULT_GENERATOR)
|
95
95
|
raise ArgumentError, "Value missing for attribute '#{name}'"
|
96
|
+
else
|
97
|
+
default_generator.call
|
96
98
|
end
|
97
99
|
end
|
98
100
|
|
@@ -115,18 +117,6 @@ module ValueSemantics
|
|
115
117
|
end
|
116
118
|
end
|
117
119
|
|
118
|
-
def default_specified?
|
119
|
-
!@default_value.equal?(NOT_SPECIFIED)
|
120
|
-
end
|
121
|
-
|
122
|
-
def default_value
|
123
|
-
if default_specified?
|
124
|
-
@default_value
|
125
|
-
else
|
126
|
-
fail "Attribute does not have a default value"
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
120
|
def validate?(value)
|
131
121
|
validator === value
|
132
122
|
end
|
@@ -169,11 +159,28 @@ module ValueSemantics
|
|
169
159
|
ArrayOf.new(element_validator)
|
170
160
|
end
|
171
161
|
|
172
|
-
def def_attr(attr_name,
|
162
|
+
def def_attr(attr_name,
|
163
|
+
validator=Anything,
|
164
|
+
default: NOT_SPECIFIED,
|
165
|
+
default_generator: nil,
|
166
|
+
coerce: nil
|
167
|
+
)
|
168
|
+
generator = begin
|
169
|
+
if default_generator && !default.equal?(NOT_SPECIFIED)
|
170
|
+
raise ArgumentError, "Attribute '#{attr_name}' can not have both a default, and a default_generator"
|
171
|
+
elsif default_generator
|
172
|
+
default_generator
|
173
|
+
elsif !default.equal?(NOT_SPECIFIED)
|
174
|
+
->{ default }
|
175
|
+
else
|
176
|
+
Attribute::NO_DEFAULT_GENERATOR
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
173
180
|
__attributes << Attribute.new(
|
174
181
|
name: attr_name,
|
175
182
|
validator: validator,
|
176
|
-
|
183
|
+
default_generator: generator,
|
177
184
|
coercer: coerce
|
178
185
|
)
|
179
186
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: value_semantics
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tom Dalling
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-12-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -121,8 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
121
121
|
- !ruby/object:Gem::Version
|
122
122
|
version: '0'
|
123
123
|
requirements: []
|
124
|
-
|
125
|
-
rubygems_version: 2.7.7
|
124
|
+
rubygems_version: 3.0.0
|
126
125
|
signing_key:
|
127
126
|
specification_version: 4
|
128
127
|
summary: Create value classes quickly, with all the proper conventions.
|