value_semantics 2.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 235fbd4782ce1bc943f12688408c20a5b6893e7b57882c63b725953115de74d3
4
- data.tar.gz: 6adbdbcc36d8093fd323fb2d734557025df4b0dc9983e6ba343690a229dfed40
3
+ metadata.gz: 17ee24bc8ffd744493e9957a4cb7ee0071f0c33aa8560b3ed36689575b9c0ab1
4
+ data.tar.gz: 05da8d260966f9257578f0ebf3891ba447b51032709bb75b39bdacba3e081961
5
5
  SHA512:
6
- metadata.gz: 179eed3a45a75b8f6546c99fd4fdcd2cb987885a4b00f36caa359a3e7b19854dd0401d3b6f3c4cd5b108256492be94d068dfa1765eb6a6844b9ccd610212cffe
7
- data.tar.gz: c9eefd7b84e63507513c2d3b88469730c346899a2a7ae109bc1159f2ed764d71102a7dbbd9e7ae0ea9d2caad1ee82c1c6b740c69eb508caa50571687d0f51c75
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
- Basic Usage
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
- The curly bracket syntax used with `ValueSemantics.for_attributes` is, unfortunately,
68
- mandatory due to Ruby's precedence rules.
69
- The `do`/`end` syntax will not work unless you surround the whole thing with parenthesis.
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\d\d\d-\d\d-\d\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:
@@ -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
- NOT_SPECIFIED = Object.new.freeze
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:, default_value:, validator:, coercer:)
84
+ def initialize(name:, default_generator:, validator:, coercer:)
83
85
  @name = name.to_sym
84
- @default_value = default_value
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 default_specified?
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, validator=Anything, default: Attribute::NOT_SPECIFIED, coerce: nil)
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
- default_value: default,
183
+ default_generator: generator,
177
184
  coercer: coerce
178
185
  )
179
186
  end
@@ -1,3 +1,3 @@
1
1
  module ValueSemantics
2
- VERSION = "2.0.1"
2
+ VERSION = "2.1.0"
3
3
  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.1
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-10-15 00:00:00.000000000 Z
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
- rubyforge_project:
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.