dry-initializer 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +23 -0
  3. data/.gitignore +10 -0
  4. data/.rspec +4 -0
  5. data/.rubocop.yml +51 -0
  6. data/.travis.yml +28 -0
  7. data/CHANGELOG.md +883 -0
  8. data/Gemfile +29 -0
  9. data/Guardfile +5 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +90 -0
  12. data/Rakefile +8 -0
  13. data/benchmarks/compare_several_defaults.rb +82 -0
  14. data/benchmarks/plain_options.rb +63 -0
  15. data/benchmarks/plain_params.rb +84 -0
  16. data/benchmarks/with_coercion.rb +71 -0
  17. data/benchmarks/with_defaults.rb +66 -0
  18. data/benchmarks/with_defaults_and_coercion.rb +59 -0
  19. data/dry-initializer.gemspec +20 -0
  20. data/lib/dry-initializer.rb +1 -0
  21. data/lib/dry/initializer.rb +61 -0
  22. data/lib/dry/initializer/builders.rb +7 -0
  23. data/lib/dry/initializer/builders/attribute.rb +81 -0
  24. data/lib/dry/initializer/builders/initializer.rb +61 -0
  25. data/lib/dry/initializer/builders/reader.rb +50 -0
  26. data/lib/dry/initializer/builders/signature.rb +32 -0
  27. data/lib/dry/initializer/config.rb +184 -0
  28. data/lib/dry/initializer/definition.rb +65 -0
  29. data/lib/dry/initializer/dispatchers.rb +112 -0
  30. data/lib/dry/initializer/dispatchers/build_nested_type.rb +59 -0
  31. data/lib/dry/initializer/dispatchers/check_type.rb +43 -0
  32. data/lib/dry/initializer/dispatchers/prepare_default.rb +40 -0
  33. data/lib/dry/initializer/dispatchers/prepare_ivar.rb +12 -0
  34. data/lib/dry/initializer/dispatchers/prepare_optional.rb +13 -0
  35. data/lib/dry/initializer/dispatchers/prepare_reader.rb +30 -0
  36. data/lib/dry/initializer/dispatchers/prepare_source.rb +28 -0
  37. data/lib/dry/initializer/dispatchers/prepare_target.rb +44 -0
  38. data/lib/dry/initializer/dispatchers/unwrap_type.rb +22 -0
  39. data/lib/dry/initializer/dispatchers/wrap_type.rb +27 -0
  40. data/lib/dry/initializer/dsl.rb +43 -0
  41. data/lib/dry/initializer/mixin.rb +15 -0
  42. data/lib/dry/initializer/mixin/local.rb +19 -0
  43. data/lib/dry/initializer/mixin/root.rb +10 -0
  44. data/lib/dry/initializer/struct.rb +40 -0
  45. data/lib/dry/initializer/undefined.rb +2 -0
  46. data/lib/tasks/benchmark.rake +41 -0
  47. data/lib/tasks/profile.rake +78 -0
  48. data/spec/attributes_spec.rb +38 -0
  49. data/spec/coercion_of_nil_spec.rb +25 -0
  50. data/spec/custom_dispatchers_spec.rb +35 -0
  51. data/spec/custom_initializer_spec.rb +30 -0
  52. data/spec/default_values_spec.rb +83 -0
  53. data/spec/definition_spec.rb +111 -0
  54. data/spec/invalid_default_spec.rb +13 -0
  55. data/spec/list_type_spec.rb +32 -0
  56. data/spec/missed_default_spec.rb +14 -0
  57. data/spec/nested_type_spec.rb +44 -0
  58. data/spec/optional_spec.rb +71 -0
  59. data/spec/options_tolerance_spec.rb +11 -0
  60. data/spec/public_attributes_utility_spec.rb +22 -0
  61. data/spec/reader_spec.rb +87 -0
  62. data/spec/repetitive_definitions_spec.rb +69 -0
  63. data/spec/several_assignments_spec.rb +41 -0
  64. data/spec/spec_helper.rb +22 -0
  65. data/spec/subclassing_spec.rb +49 -0
  66. data/spec/type_argument_spec.rb +35 -0
  67. data/spec/type_constraint_spec.rb +78 -0
  68. data/spec/value_coercion_via_dry_types_spec.rb +29 -0
  69. metadata +189 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e17a987f9500b55fd7bf3c60caa5e8ce5c4c26cb78b94f339b62a63bdeb5a9e3
4
+ data.tar.gz: 362138efc7974312bf747f060c7af4d4117e6b8c7648849998c1aefb39d14b1e
5
+ SHA512:
6
+ metadata.gz: fc4416e43fce6418f4708f5d7fc6753096009e9f2087f334bc11a2edeb44b3f9099d6d53ad9969d7e38ffefa524031c0e95fd4c7cb277ff7fed1a9ff63ebad5b
7
+ data.tar.gz: a2745ea32279c9e6bd9a964b0cef5e876bf3f02afd604b4193e6223d629fc1ca6ee8fd22cf1543d652b9ff5548b3bb9a7fc28c7a06f4eeb04cf233b4ccf451de
@@ -0,0 +1,23 @@
1
+ ---
2
+ engines:
3
+ rubocop:
4
+ enabled: true
5
+ checks:
6
+ Rubocop/Style/FrozenStringLiteralComment:
7
+ enabled: false
8
+ Rubocop/Style/PercentLiteralDelimiters:
9
+ enabled: false
10
+ Rubocop/Lint/UnderscorePrefixedVariableName:
11
+ enabled: false
12
+ duplication:
13
+ enabled: true
14
+ config:
15
+ languages:
16
+ - ruby
17
+ exclude_paths:
18
+ - "benchmarks/**/*"
19
+ - "spec/**/*"
20
+ - "lib/tasks/**/*"
21
+ ratings:
22
+ paths:
23
+ - "lib/**/*"
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /*.gem
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
4
+ --warnings
@@ -0,0 +1,51 @@
1
+ ---
2
+ AllCops:
3
+ DisplayCopNames: true
4
+ DisplayStyleGuide: true
5
+ StyleGuideCopsOnly: true
6
+ TargetRubyVersion: 2.3
7
+ Exclude:
8
+ - lib/tasks/*.rake
9
+
10
+ Bundler/DuplicatedGem:
11
+ Enabled: false
12
+
13
+ Naming/FileName:
14
+ Exclude:
15
+ - lib/dry-initializer.rb
16
+
17
+ Style/CaseEquality:
18
+ Enabled: false
19
+
20
+ Style/ClassAndModuleChildren:
21
+ Enabled: false
22
+
23
+ Style/ClassVars:
24
+ Enabled: false
25
+
26
+ Style/Documentation:
27
+ Enabled: false
28
+
29
+ Style/DoubleNegation:
30
+ Enabled: false
31
+
32
+ Style/Lambda:
33
+ Exclude:
34
+ - spec/**/*.rb
35
+
36
+ Style/LambdaCall:
37
+ Enabled: false
38
+
39
+ Style/RescueModifier:
40
+ Exclude:
41
+ - spec/**/*.rb
42
+
43
+ Style/Semicolon:
44
+ Exclude:
45
+ - spec/**/*.rb
46
+
47
+ Style/StringLiterals:
48
+ EnforcedStyle: double_quotes
49
+
50
+ Style/StringLiteralsInInterpolation:
51
+ EnforcedStyle: double_quotes
@@ -0,0 +1,28 @@
1
+ ---
2
+ language: ruby
3
+ cache: bundler
4
+ bundler_args: --without benchmarks tools
5
+ script:
6
+ - bundle exec rake spec
7
+ rvm:
8
+ - 2.3.8
9
+ - 2.4.6
10
+ - 2.5.5
11
+ - 2.6.2
12
+ - jruby-9.2.7.0
13
+ - jruby-9000
14
+ - rbx-3
15
+ - ruby-head
16
+ - truffleruby
17
+ env:
18
+ global:
19
+ - JRUBY_OPTS='--dev -J-Xmx1024M'
20
+ matrix:
21
+ allow_failures:
22
+ - rvm: rbx-3
23
+ - rvm: ruby-head
24
+ - rvm: jruby-head
25
+ - rvm: truffleruby
26
+ include:
27
+ - rvm: jruby-head
28
+ before_install: gem install bundler --no-document
@@ -0,0 +1,883 @@
1
+ # Change Log
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](http://keepachangelog.com/)
6
+ and this project adheres to [Semantic Versioning](http://semver.org/).
7
+
8
+ ## [3.0.1] [2019-04-15]
9
+
10
+ ### Fixed
11
+
12
+ - Usage of underscored names of `option`-s and `param`-s (nepalez)
13
+
14
+ You can use any sequence of underscores except for in nested types.
15
+ In nested types single underscores can be used to split alphanumeric
16
+ parts only.
17
+
18
+ ```ruby
19
+ class Test
20
+ extend Dry::Initializer
21
+
22
+ # Proper usage
23
+ option :foo_bar do
24
+ option :__foo__, proc(&:to_s)
25
+ end
26
+ end
27
+
28
+ # Improper usage
29
+ option :__foo__ do
30
+ # ...
31
+ end
32
+
33
+ option :foo__bar do
34
+ # ...
35
+ end
36
+ end
37
+ ```
38
+
39
+ This restriction is necessary because we constantize option/param names
40
+ when defining nested structs.
41
+
42
+ ## [3.0.0] [2019-04-14]
43
+
44
+ ### Added
45
+
46
+ - Support of wrapped types/coercers (nepalez)
47
+
48
+ ```ruby
49
+ class Test
50
+ # Wrap type to the array
51
+ param :foo, [proc(&:to_s)]
52
+ end
53
+
54
+ # And the value will be wrapped as well
55
+ test = Test.new(42)
56
+ test.foo # => ["42"]
57
+ ```
58
+
59
+ - It works with several layers of nesting (nepalez)
60
+
61
+ ```ruby
62
+ class Test
63
+ # Wrap type to the array
64
+ param :foo, [[proc(&:to_s)]]
65
+ end
66
+
67
+ # And the value will be wrapped as well
68
+ test = Test.new(42)
69
+ test.foo # => [["42"]]
70
+ ```
71
+
72
+ - Support of nested types/coercers (nepalez)
73
+
74
+ ```ruby
75
+ class Test
76
+ param :foo do
77
+ option :bar do
78
+ option :baz, proc(&:to_s)
79
+ end
80
+ end
81
+ end
82
+
83
+ test = Test.new(bar: { "baz" => 42 })
84
+ test.foo.bar.baz # => "42"
85
+ ```
86
+
87
+ - Wrapped/nested combinations are supported as well (nepalez)
88
+
89
+ ```ruby
90
+ class Test
91
+ param :foo, [] do
92
+ option :bar, proc(&:to_s)
93
+ end
94
+ end
95
+
96
+ test = Test.new(bar: 42)
97
+ test.foo.first.bar # => "42"
98
+ ```
99
+
100
+ ## [2.7.0] Unreleazed
101
+
102
+ ### Fixed
103
+
104
+ - Roll back master to the state of [2.5.0].
105
+
106
+ Somehow distinction between `@default_null` and `@null` variables
107
+ in the `Dry::Initializer::Builders` broken the `rom` library.
108
+
109
+ The version [2.6.0] has been yanked on rubygems, so the master
110
+ was rolled back to the previous state until the reason for
111
+ the incompatibility become clear (bjeanes, nepalez)
112
+
113
+ ## [2.6.0] [2018-09-09] (YANKED)
114
+
115
+ ## [2.5.0] [2018-08-17]
116
+
117
+ ### Fixed
118
+
119
+ - `nil` coercion (belousovAV)
120
+
121
+ When default value is `nil` instead of `Dry::Initializer::UNDEFINED`,
122
+ the coercion should be applied to any value, including `nil`, because
123
+ we cannot distinct "undefined" `nil` from the "assigned" `nil` value.
124
+
125
+ ## [2.4.0] [2018-02-01]
126
+
127
+ ### Added
128
+ - Dispatchers for adding syntax sugar to `param` and `options` (nepalez)
129
+
130
+ ```ruby
131
+ # Converts `integer: true` to `type: proc(&:to_i)`
132
+ dispatcher = ->(op) { op[:integer] ? op.merge(type: proc(&:to_i)) : op }
133
+ # Register a dispatcher
134
+ Dry::Initializer::Dispatchers << dispatcher
135
+ # Use syntax sugar
136
+ class User
137
+ param :id, integer: true # same as param :id, proc(&:to_i)
138
+ end
139
+ ```
140
+
141
+ ## [2.3.0] [2017-09-19]
142
+
143
+ ### Added
144
+ - Type coercer can take second argument for the initialized instance (nepalez)
145
+ This allows to wrap assigned value to the object that refers back
146
+ to the initializer instance. More verbose example:
147
+
148
+ ```ruby
149
+ class Location < String
150
+ attr_reader :parameter # refers back to its parameter
151
+
152
+ def initialize(name, parameter)
153
+ super(name)
154
+ @parameter = parameter
155
+ end
156
+ end
157
+
158
+ class Parameter
159
+ extend Dry::Initializer
160
+ param :name
161
+ param :location, ->(value, param) { Location.new(value, param) }
162
+ end
163
+
164
+ offset = Parameter.new "offset", location: "query"
165
+ offset.name # => "offset"
166
+ offset.location # => "query"
167
+ offset.location.parameter == offset # true
168
+ ```
169
+
170
+ ## [2.2.0] [2017-09-13]
171
+
172
+ ### Added
173
+ - Option `:desc` for option/param to add a description (nepalez)
174
+
175
+ - Methods `Definition#inch` and `Config#inch` to inspect definitions (nepalez)
176
+
177
+ ```ruby
178
+ class User
179
+ extend Dry::Initializer
180
+ option :name, proc(&:to_s), optional: true, desc: "User name"
181
+ option :email, optional: true, desc: "user email"
182
+ end
183
+
184
+ User.dry_initializer.inch
185
+ # @!method initialize(*, **options)
186
+ # Initializes an instance of User
187
+ # @option [Object] :name (optional) User name
188
+ # @option [Object] :email (optional) User email
189
+ # @return [User]
190
+ ```
191
+
192
+ ## [2.1.0] [2017-09-11]
193
+
194
+ ### Added
195
+ - Method `#options` to param/option definition (nepalez)
196
+
197
+ ```ruby
198
+ class User
199
+ extend Dry::Initializer
200
+ option :name, proc(&:to_s), optional: true
201
+ option :email, optional: true
202
+ end
203
+
204
+ User.dry_initializer.options.map do |option|
205
+ [option.source, option.options]
206
+ end
207
+ # => [
208
+ # [:name, { type: proc(&:to_s), as: :name, optional: true }],
209
+ # [:email, { as: :email, optional: true }]
210
+ # ]
211
+ ```
212
+
213
+ This method can be helpful for replicating params/options
214
+ in another class without inheritance.
215
+
216
+ ## [2.0.0] [2017-08-28]
217
+
218
+ The gem has been rewritten under the hood keeping its documented
219
+ interface about the same (see "Deleted" section below for the only
220
+ backward-incompatible change).
221
+
222
+ The main achievement of this version is fixing an edge case where
223
+ change in params/options of superclass wasn't reflected in its
224
+ previously declared subclasses.
225
+
226
+ Thanks to @solnic for the idea of class-level container,
227
+ and to @gzigzigzeo for persuading me to do this refactoring.
228
+
229
+ ### Deleted
230
+ - Undocumented variable `@__option__` which was the main reason for refactoring
231
+ (gzigzigzeo, nepalez).
232
+
233
+ ### Added
234
+ - Class method `.dry_initializer` -- a container for `.params` and `.options`
235
+ `.definitions` along with the `.null` setting (either `nil` or `UNDEFINED`)
236
+ used for unassigned values (nepalez)
237
+
238
+ - `.dry_initializer.attributes` method takes an instance of the same class
239
+ and returns the hash of assigned options. This provide the same
240
+ functionality as previously used instance variable `@__options__` (nepalez)
241
+
242
+ ```ruby
243
+ object.class.dry_initializer.attributes(object)
244
+ ```
245
+
246
+ When you use "Dry::Initializer.define -> { ... }" syntax,
247
+ the class method `.dry_initializer` is not defined. To access attributes
248
+ you should use private instance method `#__dry_initializer_config__` instead:
249
+
250
+ ```ruby
251
+ object.send(:__dry_initializer_config__).attributes(object)
252
+ ```
253
+
254
+ Both methods `.dry_initializer` and `#__dry_initializer_config__` refer
255
+ to the same object.
256
+
257
+ - `.dry_initializer.public_attributes`. This method works differently:
258
+ it looks through (possibly reloaded) readers instead of variables
259
+ (gzigzigzeo, nepalez)
260
+
261
+ ```ruby
262
+ object.class.dry_initializer.public_attributes(object)
263
+ ```
264
+
265
+ You can use the same trick as above mutatis mutandis.
266
+
267
+ ### Fixed
268
+ - Definition order dependency bug (nepalez)
269
+
270
+ I've found out that if you provided a subclass and then changed params
271
+ or options of its superclass, these changes woudn't be reflected in
272
+ subclasses until you change any of it params/options as well.
273
+
274
+ Now this bug is fixed: every time you call `param` or `option` at
275
+ any class, the gem scans through all its descendants to the very bottom
276
+ of the tree, and reloads their defintitions.
277
+
278
+ Being done in load time, the rebuilt makes no effect on runtime performance.
279
+
280
+ - Possible misbehavior when you define param and option with the same name (nepalez)
281
+
282
+ Doing this will provide `option :name` only, not both:
283
+
284
+ ```ruby
285
+ param :name
286
+ option :name
287
+ ```
288
+
289
+ - Attempt to redefine param/option of superclass with option/param in
290
+ its subclass will cause an exception because it would break
291
+ Liskov substitute principle with unexpected behaviour (nepalez)
292
+
293
+ No, you can do neither these definitions, nor vice versa:
294
+
295
+ ```ruby
296
+ class Foo
297
+ extend Dry::Intitializer
298
+ param :name
299
+ end
300
+
301
+ class Bar < Foo
302
+ option :name
303
+ end
304
+ ```
305
+
306
+ - When you reloading previously defined param of superclass, the gem
307
+ will check all its descendands for whether all required positional params
308
+ goes before optional ones (nepalez)
309
+
310
+ ```ruby
311
+ class Foo
312
+ param :name
313
+ # Foo: def initializer(name)
314
+ end
315
+
316
+ class Bar
317
+ param :email
318
+ # Bar: def initializer(name, email)
319
+ end
320
+
321
+ class Foo
322
+ # This raises SyntaxError because in Bar this would cause wrong definition
323
+ # Foo: def initializer(name = nil)
324
+ # Bar: def initializer(name = nil, email)
325
+ param :name, optional: true
326
+ end
327
+ ```
328
+
329
+ ### Changed
330
+ - Under the hood I've separated param/option settings declaration (a container
331
+ with param/option settings) from code builders for initializer and readers
332
+ (nepalez)
333
+
334
+ You can check both the code for the `__initializer__`:
335
+
336
+ ```ruby
337
+ class Foo
338
+ extend Dry::Initializer
339
+ # ...
340
+ end
341
+
342
+ Foo.dry_initializer.code
343
+ ```
344
+
345
+ and readers:
346
+
347
+ ```ruby
348
+ Foo.dry_initializer.params.map(&:code)
349
+ Foo.dry_initializer.options.map(&:code)
350
+
351
+ # or
352
+
353
+ Foo.dry_initializer.definitions.values.map(&:code)
354
+ ```
355
+
356
+ You can also check settings for every param and option using methods
357
+ `dry_initializer.params`, `dry_initializer.options` (lists), or
358
+ `dry_initializer.definitions` (hash).
359
+
360
+ You can check null value via `.dry_initializer.null` which is different
361
+ for `Dry::Initializer` and `Dry::Initializer[undefined: false]` modules.
362
+
363
+ - Optimized the code for `__initializer__`-s (the method where all magics occurs)
364
+ (nepalez)
365
+
366
+ Benchmarks remained about the same:
367
+
368
+ ```shell
369
+ rake benchmark
370
+ ```
371
+
372
+ ```
373
+ Benchmark for instantiation with plain params
374
+ value_struct: 4317196.9 i/s
375
+ plain Ruby: 4129803.9 i/s - 1.05x slower
376
+ dry-initializer: 1710702.1 i/s - 2.52x slower
377
+ concord: 1372630.4 i/s - 3.15x slower
378
+ values: 601651.8 i/s - 7.18x slower
379
+ attr_extras: 535599.5 i/s - 8.06x slower
380
+ ```
381
+
382
+ ```
383
+ Benchmark for instantiation with plain options
384
+ plain Ruby: 1769174.1 i/s
385
+ dry-initializer: 636634.1 i/s - 2.78x slower
386
+ kwattr: 423296.5 i/s - 4.18x slower
387
+ anima: 399415.0 i/s - 4.43x slower
388
+ ```
389
+
390
+ ```
391
+ Benchmark for instantiation with coercion
392
+ plain Ruby: 1565501.0 i/s
393
+ fast_attributes: 569952.9 i/s - 2.75x slower
394
+ dry-initializer: 461122.1 i/s - 3.39x slower
395
+ virtus: 138074.8 i/s - 11.34x slower
396
+ ```
397
+
398
+ ```
399
+ Benchmark for instantiation with default values
400
+ plain Ruby: 3402455.4 i/s
401
+ kwattr: 586206.5 i/s - 5.80x slower
402
+ dry-initializer: 528482.2 i/s - 6.44x slower
403
+ active_attr: 298697.7 i/s - 11.39x slower
404
+ ```
405
+
406
+ ```
407
+ Benchmark for instantiation with type constraints and default values
408
+ plain Ruby: 2881696.1 i/s
409
+ dry-initializer: 470815.1 i/s - 6.12x slower
410
+ virtus: 180272.6 i/s - 15.99x slower
411
+ ```
412
+
413
+ ## [1.4.1] [2017-04-05]
414
+
415
+ ### Fixed
416
+ - Warning about redefined `#initialize` in case the method reloaded in a klass
417
+ that extends the module (nepalez, sergey-chechaev)
418
+
419
+ ### Changed
420
+ - Rename `Dry::Initializer::DSL` -> `Dry::Initializer::ClassDSL` (nepalez)
421
+ - Add `Dry::Initializer::InstanceDSL` (nepalez)
422
+
423
+ ### Deprecated
424
+ - `Dry::Initializer::Mixin`. In latest version this was an alias for
425
+ `Dry::Initializer` that kept for backward compatibility for early versions of the gem.
426
+
427
+ This story will come to the end in `v2.1.0`.
428
+
429
+ ## [1.4.0] [2017-03-08]
430
+
431
+ ### Changed (backward-incompatible)
432
+ - The `@__options__` hash now collects all assigned attributes,
433
+ collected via `#option` (as before), and `#param` (nepalez)
434
+
435
+ ## [1.3.0] [2017-03-05]
436
+
437
+ ### Added
438
+ - No-undefined configuration of the initializer (nepalez, flash-gordon)
439
+
440
+ You can either extend or include module `Dry::Initializer` with additional option
441
+ `[undefined: false]`. This time `nil` will be assigned instead of
442
+ `Dry::Initializer::UNDEFINED`. Readers becomes faster because there is no need
443
+ to chech whether a variable was defined or not. At the same time the initializer
444
+ doesn't distinct cases when a variable was set to `nil` explicitly, and when it wasn's set at all:
445
+
446
+ class Foo # old behavior
447
+ extend Dry::Initializer
448
+ param :qux, optional: true
449
+ end
450
+
451
+ class Bar # new behavior
452
+ extend Dry::Initializer[undefined: false]
453
+ param :qux, optional: true
454
+ end
455
+
456
+ Foo.new.instance_variable_get(:@qux) # => Dry::Initializer::UNDEFINED
457
+ Bar.new.instance_variable_get(:@qux) # => nil
458
+
459
+ ### Changed
460
+ - Fixed method definitions for performance at the load time (nepalez, flash-gordon)
461
+
462
+ ## [1.2.0] [2017-03-05]
463
+
464
+ ### Fixed
465
+ - The `@__options__` variable collects renamed options after default values and coercions were applied (nepalez)
466
+
467
+ ## [1.1.3] [2017-03-01]
468
+
469
+ ### Added
470
+ - Support for lambdas as default values (nepalez, gzigzigzeo)
471
+
472
+ ## [1.1.2] [2017-02-06]
473
+
474
+ ### Changed
475
+ - Remove previously defined methods before redefining them (flash-gordon)
476
+
477
+ ## [1.1.1] [2017-02-04]
478
+
479
+ ### Fixed
480
+ - `@__options__` collects defined options only (nepalez)
481
+
482
+ ## [1.1.0] [2017-01-28]
483
+
484
+ ### Added
485
+ - enhancement via `Dry::Initializer::Attribute.dispatchers` registry (nepalez)
486
+
487
+ ```ruby
488
+ # Register dispatcher for `:string` option
489
+ Dry::Initializer::Attribute.dispatchers << ->(string: nil, **op) do
490
+ string ? op.merge(type: proc(&:to_s)) : op
491
+ end
492
+
493
+ # Now you can use the `:string` key for `param` and `option`
494
+ class User
495
+ extend Dry::Initializer
496
+ param :name, string: true
497
+ end
498
+
499
+ User.new(:Andy).name # => "Andy"
500
+ ```
501
+
502
+ ### Changed
503
+ - optimize assignments for performance (nepalez)
504
+
505
+ ## [1.0.0] [2017-01-22]
506
+
507
+ In this version the code has been rewritten for simplicity
508
+
509
+ ### Changed
510
+ - [BREAKING] when `param` or `option` was not defined, the corresponding **variable** is set to `Dry::Initializer::UNDEFINED`, but the **reader** (when defined) will return `nil` (nepalez)
511
+
512
+ - `Dry::Initializer` and `Dry::Initializer::Mixin` became aliases (nepalez)
513
+
514
+ ### Added
515
+ - support for reloading `param` and `option` definitions (nepalez)
516
+
517
+ class User
518
+ extend Dry::Initializer
519
+ param :name
520
+ param :phone, optional: true
521
+ end
522
+
523
+ User.new # => Boom!
524
+
525
+ class Admin < User
526
+ param :name, default: proc { 'Merlin' }
527
+ end
528
+
529
+ # order of the param not changed
530
+ Admin.new.name # => "Merlin"
531
+
532
+ - support for assignment of attributes via several options (nepalez)
533
+
534
+ class User
535
+ extend Dry::Initializer
536
+ option :phone
537
+ option :number, as: :phone
538
+ end
539
+
540
+ # Both ways provide the same result
541
+ User.new(phone: '1234567890').phone # => '1234567890'
542
+ User.new(number: '1234567890').phone # => '1234567890'
543
+
544
+ ## [0.11.0] [2017-01-02]
545
+
546
+ ### Added
547
+ - Support of reloading `#initializer` with `super` (nepalez)
548
+
549
+ ### Internal
550
+ - Refactor the way [#initializer] method is (re)defined (nepalez)
551
+
552
+ When you extend class with `Dry::Initializer::Mixin`, the initializer is
553
+ defined not "inside" the class per se, but inside the included module. The
554
+ reference to that module is stored as class-level `__initializer_mixin__`.
555
+
556
+ Mixin method [#initialize] calls another private method [#__initialize__].
557
+ It is this method to be reloaded every time you envoke a helper
558
+ `option` or `product`.
559
+
560
+ When new subclass is inherited, new mixin is added to chain of accessors,
561
+ but this time it does reload `__initialize__` only, not the `initialize`.
562
+ That is how you can safely reload initializer using `super`, but at the same
563
+ time use the whole power of dry-initializer DSL both in parent class and its
564
+ subclasses.
565
+
566
+ The whole stack of accessors looks like the following:
567
+ - Parent class mixin: `initialize` --> `__initialize__`
568
+ ^
569
+ - Parent class: `initialize`
570
+ - Subclass mixin: ^ `__initialize__`
571
+ - Subclass: `initialize`
572
+
573
+ See specification `spec/custom_initializer_spec.rb` to see how this works.
574
+
575
+ ## [0.10.2] [2016-12-31]
576
+
577
+ ### Added
578
+ - Support of Ruby 2.4 (flas-gordon)
579
+
580
+ ### Internal
581
+ - Code clearance for ROM integration (flash-gordon)
582
+
583
+ ## [0.10.1] [2016-12-27]
584
+
585
+ ### Fixed
586
+ - Wrong arity when there were no options and the last param had a default (nolith)
587
+
588
+ ## [0.10.0] [2016-11-20]
589
+
590
+ ### Deleted (BREAKING CHANGE!)
591
+ - Deprecated method DSL#using (nepalez)
592
+
593
+ ## [0.9.3] [2016-11-20]
594
+
595
+ ### Deprecated
596
+ - After discussion in [PR #17]: https://github.com/dry-rb/dry-initializer/pull/17)
597
+ (many thanks to @sahal2080 and @hrom512 for starting that issue and PR),
598
+ the method `using` is deprecated and will be removed from v0.10.0 (nepalez)
599
+
600
+ ### Fixed
601
+ - Support of weird option names (nepalez)
602
+
603
+ ```ruby
604
+ option :"First name", as: :first_name
605
+ ```
606
+
607
+ ## [0.9.2] [2016-11-10]
608
+
609
+ ### Fixed
610
+ - Validation of attributes (params and options) (nepalez)
611
+
612
+ ## [0.9.1] [2016-11-06]
613
+
614
+ ### Added
615
+ - Support for renaming an option during initialization (nepalez)
616
+
617
+ option :name, as: :username # to take :name option and create :username attribute
618
+
619
+ ## [0.9.0] [2016-11-06]
620
+
621
+ ### Added
622
+ - The method `#initialize` is defined when a class extended the module (nepalez)
623
+
624
+ In previous versions the method was defined only by `param` and `option` calls.
625
+
626
+ ### Breaking Changes
627
+ - The initializer accepts any option (but skips unknown) from the very beginning (nepalez)
628
+
629
+ ### Deleted
630
+ - Deprecated methods `tolerant_to_unknown_options` and `intolerant_to_unknown_options` (nepalez)
631
+
632
+ ### Internal
633
+ - Refactor scope (`using`) to support methods renaming and aliasing (nepalez)
634
+
635
+ ## [0.8.1] [2016-11-05]
636
+
637
+ ### Added
638
+ - Support for `dry-struct`ish syntax for constraints (type as a second parameter) (nepalez)
639
+
640
+ option :name, Dry::Types['strict.string']
641
+
642
+ ## [0.8.0] [2016-11-05]
643
+
644
+ In this version we switched from key arguments to ** to support special keys:
645
+
646
+ option :end
647
+ option :begin
648
+
649
+ In previous versions this was translated to
650
+
651
+ def initialize(end:, begin:)
652
+ @end = end # BOOM! SyntaxError!
653
+ @begin = begin # Potential BOOM (unreached)
654
+ end
655
+
656
+ Now the assignment is imlemented like this:
657
+
658
+ def initialize(**__options__)
659
+ @end = __options__.fetch(:end)
660
+ @begin = __options__.fetch(:begin)
661
+ end
662
+
663
+ As a side effect of the change the initializer becomes tolerant
664
+ to any unknown option if, and only if some `option` was set explicitly.
665
+
666
+ Methods `tolerant_to_unknown_options` and `intolerant_to_unknown_options`
667
+ are deprecated and will be removed in the next version of the gem.
668
+
669
+ ### Added
670
+ - support for special options like `option :end`, `option :begin` etc. (nepalez)
671
+
672
+ ### Changed
673
+ - switched from key arguments to serialized hash argument in the initializer (nepalez)
674
+
675
+ ### Breaking Changes
676
+ - the initializer becomes tolerant to unknown options when any `option` was set,
677
+ ignoring `intolerant_to_unknown_options` helper.
678
+
679
+ - the initializer becomes intolerant to options when no `option` was set,
680
+ ignoring `tolerant_to_unknown_options` helper.
681
+
682
+ ### Deprecated
683
+ - `tolerant_to_unknown_options`
684
+ - `intolerant_to_unknown_options`
685
+
686
+ ## [0.7.0] [2016-10-11]
687
+
688
+ ### Added
689
+ - Shared settings with `#using` method (nepalez)
690
+
691
+ ## [0.6.0] [2016-10-09]
692
+
693
+ ### Added
694
+ - Support for private and protected readers in the `reader:` option (jmgarnier)
695
+
696
+ ## [0.5.0] [2016-08-21]
697
+
698
+ ### Added
699
+ - Allow `optional` attribute to be left undefined (nepalez)
700
+
701
+ ## [0.4.0] [2016-05-28]
702
+
703
+ ### Deleted (backward-incompatible changes)
704
+ - Support of modules and case equality as type constraints (nepalez)
705
+
706
+ ## [0.3.3] [2016-05-28]
707
+
708
+ - Add deprecation warnings about modules and case equality as type constraints (nepalez)
709
+
710
+ ## [0.3.2] [2016-05-25]
711
+
712
+ ### Fixed
713
+ - Add explicit requirement for ruby 'set' (rickenharp)
714
+
715
+ ## [0.3.1] [2016-05-22]
716
+
717
+ ### Added
718
+ - Support for tolerance to unknown options (nepalez)
719
+
720
+ ## [0.3.0] [2016-05-19]
721
+
722
+ Breaks interface for adding new plugins. Register new plugin via:
723
+
724
+ ```
725
+ def self.extended(klass)
726
+ klass.register_initializer_plugin NewPlugin
727
+ end
728
+ ```
729
+
730
+ instead of:
731
+
732
+ ```
733
+ def self.extended(klass)
734
+ klass.initializer_builder.register NewPlugin
735
+ end
736
+ ```
737
+
738
+ While the private method ##initializer_builder is still accessible
739
+ its method #register doesn't mutate the builder instance.
740
+
741
+ ### Changed (backward-incompatible changes)
742
+ - Made Mixin##initializer_builder method private (nepalez)
743
+ - Add Mixin#register_initializer_plugin(plugin) method (nepalez)
744
+
745
+ ### Fixed
746
+ - Prevent plugin's registry from polluting superclass (nepalez)
747
+
748
+ ### Changed
749
+ - Make all instances (Builder and Signature) immutable (nepalez)
750
+ - Decouple mixin from a builder to prevent pollution (nepalez)
751
+ - Ensure default value block can use private variables (jeremyf)
752
+
753
+ ## [0.2.1] [2016-05-19]
754
+
755
+ ### Fixed
756
+ - Fix polluting superclass with declarations from subclass (nepalez)
757
+
758
+ ### Changed
759
+ - Make all instances (Builder and Signature) immutable (nepalez)
760
+ - Decouple mixin from a builder to prevent pollution (nepalez)
761
+ - Ensure default value block can use private variables (jeremyf)
762
+
763
+ ## [0.2.0] [2016-05-16]
764
+
765
+ The gem internals has been rewritten heavily to make the gem pluggable and fix
766
+ bugs in "container style". Type constraints were extracted to a plugin
767
+ that should be added explicitly.
768
+
769
+ Small extensions were added to type constraints to support constraint by any
770
+ object, and apply value coercion via `dry-types`.
771
+
772
+ Default assignments became slower (while plain type constraint are not)!
773
+
774
+ ### Changed (backward-incompatible changes)
775
+ - Make dry-types constraint to coerce variables (nepalez)
776
+
777
+ ```ruby
778
+ # This will coerce `name: :foo` to `"foo"`
779
+ option :name, type: Dry::Types::Coercible::String
780
+ ```
781
+
782
+ - Stop supporing proc type constraint (nepalez)
783
+
784
+ ```ruby
785
+ option :name, type: ->(v) { String === v } # this does NOT work any more
786
+ ```
787
+
788
+ later it will be implemented via coercion plugin (not added by default):
789
+
790
+ ```ruby
791
+ require 'dry/initializer/coercion'
792
+
793
+ class MyClass
794
+ extend Dry::Initializer::Mixin
795
+ extend Dry::Initializer::Coercion
796
+
797
+ option :name, coercer: ->(v) { (String === v) ? v.to_sym : fail }
798
+ end
799
+ ```
800
+
801
+ ### Added
802
+ - Support type constraint via every object's case equality (nepalez)
803
+
804
+ ```ruby
805
+ option :name, type: /foo/
806
+ option :name, type: (1...14)
807
+ ```
808
+
809
+ - Support defaults and type constraints for the "container" syntax (nepalez)
810
+ - Support adding extensions via plugin system (nepalez)
811
+
812
+ ### Internal
813
+ - Private method `##__after_initialize__` is added by the `Mixin` along with `#initialize` (nepalez)
814
+
815
+ The previous implementation required defaults and types to be stored in the class method `.initializer_builder`.
816
+ That made "container" syntax to support neither defaults nor types.
817
+
818
+ Now the `#initializer` is still defined via `instance_eval(code)` for efficiency. Some operations
819
+ (like default assignments, coercions, dry-type constraints etc.) cannot be implemented in this way.
820
+ They are made inside `##__after_initialize__` callback, that is biult via `default_method(&block)`
821
+ using instance evals.
822
+
823
+ ## [0.1.1] [2016-04-28]
824
+
825
+ ### Added
826
+ - `include Dry::Initializer.define -> do ... end` syntax (flash-gordon)
827
+
828
+ ## [0.1.0] [2016-04-26]
829
+
830
+ Class DSL splitted to mixin and container versions (thanks to @AMHOL for the idea).
831
+ Backward compatibility is broken.
832
+
833
+ ### Changed (backward-incompatible changes)
834
+ - Use `extend Dry::Initializer::Mixin` instead of `extend Dry::Initializer` (nepalez)
835
+
836
+ ### Added
837
+ - Use `include Dry::Initializer.define(&block)` as an alternative to extending the class (nepalez)
838
+
839
+ ## [0.0.1] [2016-04-09]
840
+
841
+ First public release
842
+
843
+ [0.1.0]: https://github.com/dry-rb/dry-initializer/compare/v0.0.1...v0.1.0
844
+ [0.1.1]: https://github.com/dry-rb/dry-initializer/compare/v0.1.0...v0.1.1
845
+ [0.2.0]: https://github.com/dry-rb/dry-initializer/compare/v0.1.1...v0.2.0
846
+ [0.2.1]: https://github.com/dry-rb/dry-initializer/compare/v0.2.0...v0.2.1
847
+ [0.2.1]: https://github.com/dry-rb/dry-initializer/compare/v0.2.0...v0.2.1
848
+ [0.3.0]: https://github.com/dry-rb/dry-initializer/compare/v0.2.1...v0.3.0
849
+ [0.3.1]: https://github.com/dry-rb/dry-initializer/compare/v0.3.0...v0.3.1
850
+ [0.3.2]: https://github.com/dry-rb/dry-initializer/compare/v0.3.1...v0.3.2
851
+ [0.3.3]: https://github.com/dry-rb/dry-initializer/compare/v0.3.2...v0.3.3
852
+ [0.4.0]: https://github.com/dry-rb/dry-initializer/compare/v0.3.3...v0.4.0
853
+ [0.5.0]: https://github.com/dry-rb/dry-initializer/compare/v0.4.0...v0.5.0
854
+ [0.6.0]: https://github.com/dry-rb/dry-initializer/compare/v0.5.0...v0.6.0
855
+ [0.7.0]: https://github.com/dry-rb/dry-initializer/compare/v0.6.0...v0.7.0
856
+ [0.8.0]: https://github.com/dry-rb/dry-initializer/compare/v0.7.0...v0.8.0
857
+ [0.8.1]: https://github.com/dry-rb/dry-initializer/compare/v0.8.0...v0.8.1
858
+ [0.9.0]: https://github.com/dry-rb/dry-initializer/compare/v0.8.1...v0.9.0
859
+ [0.9.1]: https://github.com/dry-rb/dry-initializer/compare/v0.9.0...v0.9.1
860
+ [0.9.2]: https://github.com/dry-rb/dry-initializer/compare/v0.9.1...v0.9.2
861
+ [0.9.3]: https://github.com/dry-rb/dry-initializer/compare/v0.9.2...v0.9.3
862
+ [0.10.0]: https://github.com/dry-rb/dry-initializer/compare/v0.9.3...v0.10.0
863
+ [0.10.1]: https://github.com/dry-rb/dry-initializer/compare/v0.10.0...v0.10.1
864
+ [0.10.2]: https://github.com/dry-rb/dry-initializer/compare/v0.10.1...v0.10.2
865
+ [0.11.0]: https://github.com/dry-rb/dry-initializer/compare/v0.10.2...v0.11.0
866
+ [1.0.0]: https://github.com/dry-rb/dry-initializer/compare/v0.11.0...v1.0.0
867
+ [1.1.0]: https://github.com/dry-rb/dry-initializer/compare/v1.0.0...v1.1.0
868
+ [1.1.1]: https://github.com/dry-rb/dry-initializer/compare/v1.1.0...v1.1.1
869
+ [1.1.2]: https://github.com/dry-rb/dry-initializer/compare/v1.1.1...v1.1.2
870
+ [1.1.3]: https://github.com/dry-rb/dry-initializer/compare/v1.1.2...v1.1.3
871
+ [1.2.0]: https://github.com/dry-rb/dry-initializer/compare/v1.1.3...v1.2.0
872
+ [1.3.0]: https://github.com/dry-rb/dry-initializer/compare/v1.2.0...v1.3.0
873
+ [1.4.0]: https://github.com/dry-rb/dry-initializer/compare/v1.3.0...v1.4.0
874
+ [1.4.1]: https://github.com/dry-rb/dry-initializer/compare/v1.4.0...v1.4.1
875
+ [2.0.0]: https://github.com/dry-rb/dry-initializer/compare/v1.4.1...v2.0.0
876
+ [2.1.0]: https://github.com/dry-rb/dry-initializer/compare/v2.0.0...v2.1.0
877
+ [2.2.0]: https://github.com/dry-rb/dry-initializer/compare/v2.1.0...v2.2.0
878
+ [2.3.0]: https://github.com/dry-rb/dry-initializer/compare/v2.2.0...v2.3.0
879
+ [2.4.0]: https://github.com/dry-rb/dry-initializer/compare/v2.3.0...v2.4.0
880
+ [2.6.0]: https://github.com/dry-rb/dry-initializer/compare/v2.4.0...v2.5.0
881
+ [2.6.0]: https://github.com/dry-rb/dry-initializer/compare/v2.5.0...v2.6.0
882
+ [3.0.0]: https://github.com/dry-rb/dry-initializer/compare/v2.5.0...v3.0.0
883
+ [3.0.1]: https://github.com/dry-rb/dry-initializer/compare/v3.0.0...v3.0.1