dry-struct 1.0.0 → 1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b8e3499eadacef74b5203366275c7f68c77ad138e950ac4e5f0a02635bc118f9
4
- data.tar.gz: a227f8eb3d8beb3b1388bbe1d447055076938611929ed0a41610253feae64741
3
+ metadata.gz: 9e2aaf63cecd9545e3b19c763209b1dc4d2a2c1dec53e2050cf02e5e9c29d97a
4
+ data.tar.gz: b7464679f32b3d861bd63b31a24926a8806158bef3d2b99fc5199aa8c7f0db78
5
5
  SHA512:
6
- metadata.gz: 1c721723926e9edaf876e0e96e209fbdd33b882a9f92b2b670aeb3af26901c9c42d262a7446e811706c4fe02a3eb4bf76895747c2cac084ad20023952f480879
7
- data.tar.gz: a0dba3a720335d985feea9004a0b1ec1f8bb8625a4b6947a22acb6ab68bb0f4fb23aea38c8122570475ed332dc408330391d1572707216007f302e853e8a6e66
6
+ metadata.gz: 2003c10114e1aa03a19f21c68a1f3e3d684d2e4500cd22782c261ec935491668e56ce4916cd98ee1a9fbb64222dcf2f1061c9d6f90360bac351f0cd352c935a3
7
+ data.tar.gz: 45d7b302ba157d73ae9d5039675dc6153b8b68b0739626d3db799bd8255be8ef3a2528db6b370ca6f4fde7dec2cc597950b710b3f84bcbee3cc889d9b444f664
@@ -0,0 +1,10 @@
1
+ ---
2
+ name: "⚠️ Please don't ask for support via issues"
3
+ about: See CONTRIBUTING.md for more information
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+
@@ -0,0 +1,34 @@
1
+ ---
2
+ name: "\U0001F41B Bug report"
3
+ about: See CONTRIBUTING.md for more information
4
+ title: ''
5
+ labels: bug
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Before you submit this: WE ONLY ACCEPT BUG REPORTS AND FEATURE REQUESTS**
11
+
12
+ For more information see [our contribution guidelines](https://github.com/rom-rb/rom/blob/master/CONTRIBUTING.md)
13
+
14
+ **Before you report**
15
+
16
+ :warning: If you have a problem related to a schema, please **report it under [dry-schema issues](https://github.com/dry-rb/dry-schema/issues/new?assignees=&labels=bug&template=---bug-report.md&title=)** instead.
17
+
18
+ **Describe the bug**
19
+
20
+ A clear and concise description of what the bug is.
21
+
22
+ **To Reproduce**
23
+
24
+ Provide detailed steps to reproduce, an executable script would be best.
25
+
26
+ **Expected behavior**
27
+
28
+ A clear and concise description of what you expected to happen.
29
+
30
+ **Your environment**
31
+
32
+ - Affects my production application: **YES/NO**
33
+ - Ruby version: ...
34
+ - OS: ...
@@ -0,0 +1,18 @@
1
+ ---
2
+ name: "\U0001F6E0 Feature request"
3
+ about: See CONTRIBUTING.md for more information
4
+ title: ''
5
+ labels: feature
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ Summary of what the feature is supposed to do.
11
+
12
+ ## Examples
13
+
14
+ Code examples showing how the feature could be used.
15
+
16
+ ## Resources
17
+
18
+ Additional information, like a link to the discussion forum thread where the feature was discussed etc.
@@ -3,9 +3,9 @@ dist: trusty
3
3
  sudo: required
4
4
  bundler_args: --without benchmarks tools
5
5
  script:
6
- - bundle exec rake spec
6
+ - bundle exec rspec spec
7
7
  after_success:
8
- - '[ -d coverage ] && bundle exec codeclimate-test-reporter'
8
+ - "[ -d coverage ] && bundle exec codeclimate-test-reporter"
9
9
  rvm:
10
10
  - 2.4.5
11
11
  - 2.5.5
@@ -17,13 +17,9 @@ env:
17
17
  - COVERAGE=true
18
18
  - JRUBY_OPTS='--dev -J-Xmx1024M'
19
19
  matrix:
20
+ include:
21
+ - rvm: 2.7
20
22
  allow_failures:
21
23
  - rvm: truffleruby
22
24
  notifications:
23
25
  email: false
24
- webhooks:
25
- urls:
26
- - https://webhooks.gitter.im/e/19098b4253a72c9796db
27
- on_success: change # options: [always|never|change] default: always
28
- on_failure: always # options: [always|never|change] default: always
29
- on_start: false # default: false
@@ -1,12 +1,35 @@
1
+ # 1.1.0 2019-10-07
2
+
3
+ ## Added
4
+
5
+ - Experimental support for pattern matching :tada: (flash-gordon)
6
+
7
+ ```ruby
8
+ User = Dry.Struct(name: 'string', email: 'string')
9
+
10
+ user = User.new(name: 'John Doe', email: 'john@acme.org')
11
+
12
+ case user
13
+ in User({ name: 'John Doe', email: })
14
+ puts email
15
+ else
16
+ puts 'Not John'
17
+ end
18
+ ```
19
+
20
+ See more examples in the [specs](https://github.com/dry-rb/dry-struct/blob/956fff208296731c40f1fea04b36106ea01b56d0/spec/dry/struct/pattern_matching_spec.rb).
21
+
22
+ [Compare v1.0.0...v1.1.0](https://github.com/dry-rb/dry-struct/compare/v1.0.0...v1.1.0)
23
+
1
24
  # 1.0.0 2019-04-23
2
25
 
3
26
  ## Changed
4
27
 
5
- * `valid?` and `===` behave differently, `===` works the same way `Class#===` does and `valid?` checks if the value _can be_ coerced to the struct (flash-gordon)
28
+ - `valid?` and `===` behave differently, `===` works the same way `Class#===` does and `valid?` checks if the value _can be_ coerced to the struct (flash-gordon)
6
29
 
7
30
  ## Added
8
31
 
9
- * `Struct.call` now accepts an optional block that will be called on failed coercion. This behavior is consistent with dry-types 1.0. Note that `.new` doesn't take a block (flash-gordon)
32
+ - `Struct.call` now accepts an optional block that will be called on failed coercion. This behavior is consistent with dry-types 1.0. Note that `.new` doesn't take a block (flash-gordon)
10
33
  ```ruby
11
34
  User = Dry::Struct(name: 'string')
12
35
  User.(1) { :oh_no }
@@ -19,7 +42,7 @@
19
42
 
20
43
  ## Changed
21
44
 
22
- * [BREAKING] `Struct.input` was renamed `Struct.schema`, hence `Struct.schema` returns an instance of `Dry::Types::Hash::Schema` rather than a `Hash`. Schemas are also implementing `Enumerable` but they iterate over key types.
45
+ - [BREAKING] `Struct.input` was renamed `Struct.schema`, hence `Struct.schema` returns an instance of `Dry::Types::Hash::Schema` rather than a `Hash`. Schemas are also implementing `Enumerable` but they iterate over key types.
23
46
  New API:
24
47
  ```ruby
25
48
  User.schema.each do |key|
@@ -31,7 +54,7 @@
31
54
  ```ruby
32
55
  User.schema.key(:id) # => #<Dry::Types::Hash::Key ...>
33
56
  ```
34
- * [BREAKING] `transform_types` now passes one argument to the block, an instance of the `Key` type. Combined with the new API from dry-types it simplifies declaring omittable keys:
57
+ - [BREAKING] `transform_types` now passes one argument to the block, an instance of the `Key` type. Combined with the new API from dry-types it simplifies declaring omittable keys:
35
58
  ```ruby
36
59
  class StructWithOptionalKeys < Dry::Struct
37
60
  transform_types { |key| key.required(false) }
@@ -39,8 +62,8 @@
39
62
  transform_types(&:omittable)
40
63
  end
41
64
  ```
42
- * `Dry::Stuct#new` is now more efficient for partial updates (flash-gordon)
43
- * Ruby 2.3 is EOL and not officially supported. It may work but we don't test it.
65
+ - `Dry::Stuct#new` is now more efficient for partial updates (flash-gordon)
66
+ - Ruby 2.3 is EOL and not officially supported. It may work but we don't test it.
44
67
 
45
68
  [Compare v0.6.0...v0.7.0](https://github.com/dry-rb/dry-struct/compare/v0.6.0...v0.7.0)
46
69
 
@@ -48,11 +71,11 @@
48
71
 
49
72
  ## Changed
50
73
 
51
- * [BREAKING] `Struct.attribute?` in the old sense is deprecated, use `has_attribute?` as a replacement
74
+ - [BREAKING] `Struct.attribute?` in the old sense is deprecated, use `has_attribute?` as a replacement
52
75
 
53
76
  ## Added
54
77
 
55
- * `Struct.attribute?` is an easy way to define omittable attributes (flash-gordon):
78
+ - `Struct.attribute?` is an easy way to define omittable attributes (flash-gordon):
56
79
 
57
80
  ```ruby
58
81
  class User < Dry::Struct
@@ -64,7 +87,7 @@
64
87
 
65
88
  ## Fixed
66
89
 
67
- * `Struct#to_h` recursively converts hash values to hashes, this was done to be consistent with current behavior for arrays (oeoeaio + ZimbiX)
90
+ - `Struct#to_h` recursively converts hash values to hashes, this was done to be consistent with current behavior for arrays (oeoeaio + ZimbiX)
68
91
 
69
92
  [Compare v0.5.1...v0.6.0](https://github.com/dry-rb/dry-struct/compare/v0.5.1...v0.6.0)
70
93
 
@@ -72,11 +95,11 @@
72
95
 
73
96
  ## Fixed
74
97
 
75
- * Constant resolution is now restricted to the current module when structs are automatically defined using the block syntax. This shouldn't break any existing code (piktur)
98
+ - Constant resolution is now restricted to the current module when structs are automatically defined using the block syntax. This shouldn't break any existing code (piktur)
76
99
 
77
100
  ## Added
78
101
 
79
- * Pretty print extension (ojab)
102
+ - Pretty print extension (ojab)
80
103
  ```ruby
81
104
  Dry::Struct.load_extensions(:pretty_print)
82
105
  PP.pp(user)
@@ -92,14 +115,14 @@
92
115
 
93
116
  ## BREAKING CHANGES
94
117
 
95
- * `constructor_type` was removed, use `transform_types` and `transform_keys` as a replacement (see below)
96
- * Default types are evaluated _only_ on missing values. Again, use `tranform_types` as a work around for `nil`s
97
- * Values are now stored within a single instance variable names `@attributes`, this sped up struct creation and improved support for reserved attribute names such as `hash`, they don't get a getter but still can be read via `#[]`
98
- * Ruby 2.3 is a minimal supported version
118
+ - `constructor_type` was removed, use `transform_types` and `transform_keys` as a replacement (see below)
119
+ - Default types are evaluated _only_ on missing values. Again, use `tranform_types` as a work around for `nil`s
120
+ - Values are now stored within a single instance variable names `@attributes`, this sped up struct creation and improved support for reserved attribute names such as `hash`, they don't get a getter but still can be read via `#[]`
121
+ - Ruby 2.3 is a minimal supported version
99
122
 
100
123
  ## Added
101
124
 
102
- * `Dry::Struct.transform_types` accepts a block which is yielded on every type to add. Since types are `dry-types`' objects that come with a robust DSL it's rather simple to restore the behavior of `constructor_type`. See https://github.com/dry-rb/dry-struct/pull/64 for details (flash-gordon)
125
+ - `Dry::Struct.transform_types` accepts a block which is yielded on every type to add. Since types are `dry-types`' objects that come with a robust DSL it's rather simple to restore the behavior of `constructor_type`. See https://github.com/dry-rb/dry-struct/pull/64 for details (flash-gordon)
103
126
 
104
127
  Example: evaluate defaults on `nil` values
105
128
 
@@ -111,9 +134,9 @@
111
134
  end
112
135
  ```
113
136
 
114
- * `Data::Struct.transform_keys` accepts a block/proc that transforms keys of input hashes. The most obvious usage is simbolization but arbitrary transformations are allowed (flash-gordon)
137
+ - `Data::Struct.transform_keys` accepts a block/proc that transforms keys of input hashes. The most obvious usage is simbolization but arbitrary transformations are allowed (flash-gordon)
115
138
 
116
- * `Dry.Struct` builds a struct by a hash of attribute names and types (citizen428)
139
+ - `Dry.Struct` builds a struct by a hash of attribute names and types (citizen428)
117
140
 
118
141
  ```ruby
119
142
  User = Dry::Struct(name: 'strict.string') do
@@ -121,7 +144,7 @@
121
144
  end
122
145
  ```
123
146
 
124
- * Support for `Struct.meta`, note that `.meta` returns a _new class_ (flash-gordon)
147
+ - Support for `Struct.meta`, note that `.meta` returns a _new class_ (flash-gordon)
125
148
 
126
149
  ```ruby
127
150
  class User < Dry::Struct
@@ -133,7 +156,7 @@
133
156
  User.new(name: 'Jade').class == UserWithMeta.new(name: 'Jade').class # => false
134
157
  ```
135
158
 
136
- * `Struct.attribute` yields a block with definition for nested structs. It defines a nested constant for the new struct and supports arrays (AMHOL + flash-gordon)
159
+ - `Struct.attribute` yields a block with definition for nested structs. It defines a nested constant for the new struct and supports arrays (AMHOL + flash-gordon)
137
160
 
138
161
  ```ruby
139
162
  class User < Dry::Struct
@@ -153,8 +176,8 @@
153
176
 
154
177
  ## Fixed
155
178
 
156
- * Adding a new attribute invalidates `attribute_names` (flash-gordon)
157
- * Struct classes track subclasses and define attributes in them, now it doesn't matter whether you define attributes first and _then_ subclass or vice versa. Note this can lead to memory leaks in Rails environment when struct classes are reloaded (flash-gordon)
179
+ - Adding a new attribute invalidates `attribute_names` (flash-gordon)
180
+ - Struct classes track subclasses and define attributes in them, now it doesn't matter whether you define attributes first and _then_ subclass or vice versa. Note this can lead to memory leaks in Rails environment when struct classes are reloaded (flash-gordon)
158
181
 
159
182
  [Compare v0.4.0...v0.5.0](https://github.com/dry-rb/dry-struct/compare/v0.4.0...v0.5.0)
160
183
 
@@ -162,13 +185,13 @@
162
185
 
163
186
  ## Changed
164
187
 
165
- * Attribute readers don't override existing instance methods (solnic)
166
- * `Struct#new` uses raw attributes instead of method calls, this makes the behavior consistent with the change above (flash-gordon)
167
- * `constructor_type` now actively rejects `:weak` and `:symbolized` values (GustavoCaso)
188
+ - Attribute readers don't override existing instance methods (solnic)
189
+ - `Struct#new` uses raw attributes instead of method calls, this makes the behavior consistent with the change above (flash-gordon)
190
+ - `constructor_type` now actively rejects `:weak` and `:symbolized` values (GustavoCaso)
168
191
 
169
192
  ## Fixed
170
193
 
171
- * `Struct#new` doesn't call `.to_hash` recursively (flash-gordon)
194
+ - `Struct#new` doesn't call `.to_hash` recursively (flash-gordon)
172
195
 
173
196
  [Compare v0.3.1...v0.4.0](https://github.com/dry-rb/dry-struct/compare/v0.3.1...v0.4.0)
174
197
 
@@ -176,9 +199,9 @@
176
199
 
177
200
  ## Added
178
201
 
179
- * `Struct.constructor` that makes dry-struct more aligned with dry-types; now you can have a struct with a custom constructor that will be called _before_ calling the `new` method (v-kolesnikov)
180
- * `Struct.attribute?` and `Struct.attribute_names` for introspecting struct attributes (flash-gordon)
181
- * `Struct#__new__` is a safe-to-use-in-gems alias for `Struct#new` (flash-gordon)
202
+ - `Struct.constructor` that makes dry-struct more aligned with dry-types; now you can have a struct with a custom constructor that will be called _before_ calling the `new` method (v-kolesnikov)
203
+ - `Struct.attribute?` and `Struct.attribute_names` for introspecting struct attributes (flash-gordon)
204
+ - `Struct#__new__` is a safe-to-use-in-gems alias for `Struct#new` (flash-gordon)
182
205
 
183
206
  [Compare v0.3.0...v0.3.1](https://github.com/dry-rb/dry-struct/compare/v0.3.0...v0.3.1)
184
207
 
@@ -186,16 +209,16 @@
186
209
 
187
210
  ## Added
188
211
 
189
- * `Dry::Struct#new` method to return new instance with applied changeset (Kukunin)
212
+ - `Dry::Struct#new` method to return new instance with applied changeset (Kukunin)
190
213
 
191
214
  ## Fixed
192
215
 
193
- * `.[]` and `.call` does not coerce subclass to superclass anymore (Kukunin)
194
- * Raise ArgumentError when attribute type is a string and no value provided is for `new` (GustavoCaso)
216
+ - `.[]` and `.call` does not coerce subclass to superclass anymore (Kukunin)
217
+ - Raise ArgumentError when attribute type is a string and no value provided is for `new` (GustavoCaso)
195
218
 
196
219
  ## Changed
197
220
 
198
- * `.new` without arguments doesn't use nil as an input for non-default types anymore (flash-gordon)
221
+ - `.new` without arguments doesn't use nil as an input for non-default types anymore (flash-gordon)
199
222
 
200
223
  [Compare v0.2.1...v0.3.0](https://github.com/dry-rb/dry-struct/compare/v0.2.1...v0.3.0)
201
224
 
@@ -203,7 +226,7 @@
203
226
 
204
227
  ## Fixed
205
228
 
206
- * Fixed `Dry::Struct::Value` which appeared to be broken in the last release (flash-gordon)
229
+ - Fixed `Dry::Struct::Value` which appeared to be broken in the last release (flash-gordon)
207
230
 
208
231
  [Compare v0.2.0...v0.2.1](https://github.com/dry-rb/dry-struct/compare/v0.2.0...v0.2.1)
209
232
 
@@ -211,7 +234,7 @@
211
234
 
212
235
  ## Changed
213
236
 
214
- * Struct attributes can be overridden in a subclass (flash-gordon)
237
+ - Struct attributes can be overridden in a subclass (flash-gordon)
215
238
 
216
239
  [Compare v0.1.1...v0.2.0](https://github.com/dry-rb/dry-struct/compare/v0.1.1...v0.2.0)
217
240
 
@@ -219,7 +242,7 @@
219
242
 
220
243
  ## Fixed
221
244
 
222
- * Make `Dry::Struct` act as a constrained type. This fixes the behavior of sum types containing structs (flash-gordon)
245
+ - Make `Dry::Struct` act as a constrained type. This fixes the behavior of sum types containing structs (flash-gordon)
223
246
 
224
247
  [Compare v0.1.0...v0.1.1](https://github.com/dry-rb/dry-struct/compare/v0.1.0...v0.1.1)
225
248
 
@@ -227,13 +250,13 @@
227
250
 
228
251
  ## Added
229
252
 
230
- * `:strict_with_defaults` constructor type (backus)
253
+ - `:strict_with_defaults` constructor type (backus)
231
254
 
232
255
  ## Changed
233
256
 
234
- * [BREAKING] `:strict` was renamed to `:permissive` as it ignores missing keys (backus)
235
- * [BREAKING] `:strict` now raises on unexpected keys (backus)
236
- * Structs no longer auto-register themselves in the types container as they implement `Type` interface and we don't have to wrap them in `Type::Definition` (flash-gordon)
257
+ - [BREAKING] `:strict` was renamed to `:permissive` as it ignores missing keys (backus)
258
+ - [BREAKING] `:strict` now raises on unexpected keys (backus)
259
+ - Structs no longer auto-register themselves in the types container as they implement `Type` interface and we don't have to wrap them in `Type::Definition` (flash-gordon)
237
260
 
238
261
  [Compare v0.0.1...v0.1.0](https://github.com/dry-rb/dry-struct/compare/v0.0.1...v0.1.0)
239
262
 
data/Gemfile CHANGED
@@ -6,6 +6,7 @@ gemspec
6
6
 
7
7
  group :test do
8
8
  gem 'codeclimate-test-reporter', platform: :mri, require: false
9
+ gem 'dry-monads'
9
10
  gem 'simplecov', require: false
10
11
  gem 'warning'
11
12
  end
@@ -6,7 +6,7 @@ require 'dry/struct'
6
6
  require 'irb'
7
7
 
8
8
  module Types
9
- include Dry.Types
9
+ include Dry.Types()
10
10
  end
11
11
 
12
12
  binding.irb
@@ -0,0 +1,101 @@
1
+ ---
2
+ title: Introduction
3
+ layout: gem-single
4
+ type: gem
5
+ name: dry-struct
6
+ sections:
7
+ - nested-structs
8
+ - recipes
9
+ ---
10
+
11
+ `dry-struct` is a gem built on top of `dry-types` which provides virtus-like DSL for defining typed struct classes.
12
+
13
+ ### Basic Usage
14
+
15
+ You can define struct objects which will have readers for specified attributes using a simple dsl:
16
+
17
+ ``` ruby
18
+ require 'dry-struct'
19
+
20
+ module Types
21
+ include Dry.Types()
22
+ end
23
+
24
+ class User < Dry::Struct
25
+ attribute :name, Types::String.optional
26
+ attribute :age, Types::Coercible::Integer
27
+ end
28
+
29
+ user = User.new(name: nil, age: '21')
30
+
31
+ user.name # nil
32
+ user.age # 21
33
+
34
+ user = User.new(name: 'Jane', age: '21')
35
+
36
+ user.name # => "Jane"
37
+ user.age # => 21
38
+ ```
39
+
40
+ ### Value
41
+
42
+ You can define value objects which will behave like structs but will be *deeply frozen*:
43
+
44
+ ``` ruby
45
+ class Location < Dry::Struct::Value
46
+ attribute :lat, Types::Float
47
+ attribute :lng, Types::Float
48
+ end
49
+
50
+ loc1 = Location.new(lat: 1.23, lng: 4.56)
51
+ loc2 = Location.new(lat: 1.23, lng: 4.56)
52
+
53
+ loc1.frozen? # true
54
+ loc2.frozen? # true
55
+
56
+ loc1 == loc2
57
+ # true
58
+ ```
59
+
60
+ ### Hash Schemas
61
+
62
+ `Dry::Struct` out of the box uses [hash schemas](/gems/dry-types/hash-schemas) from `dry-types` for processing input hashes. `with_type_transform` and `with_key_transform` are exposed as `transform_types` and `transform_keys`:
63
+
64
+ ```ruby
65
+ class User < Dry::Struct
66
+ transform_keys(&:to_sym)
67
+
68
+ attribute :name, Types::String.optional
69
+ attribute :age, Types::Coercible::Integer
70
+ end
71
+
72
+ User.new('name' => 'Jane', 'age' => '21')
73
+ # => #<User name="Jane" age=21>
74
+ ```
75
+
76
+ This plays nicely with inheritance, you can define a base struct for symbolizing input and then reuse it:
77
+
78
+ ```ruby
79
+ class SymbolizeStruct < Dry::Struct
80
+ transform_keys(&:to_sym)
81
+ end
82
+
83
+ class User < SymbolizeStruct
84
+ attribute :name, Types::String.optional
85
+ attribute :age, Types::Coercible::Integer
86
+ end
87
+ ```
88
+
89
+ ### Validating data with dry-struct
90
+
91
+ Please don't. Structs are meant to work with valid input, it cannot generate error messages good enough for displaying them for a user etc. Use [`dry-validation`](/gems/dry-validation) for validating incoming data and then pass its output to structs.
92
+
93
+ ### Differences between dry-struct and virtus
94
+
95
+ `dry-struct` look somewhat similar to Virtus but there are few significant differences:
96
+
97
+ * Structs don't provide attribute writers and are meant to be used as "data objects" exclusively
98
+ * Handling of attribute values is provided by standalone type objects from `dry-types`, which gives you way more powerful features
99
+ * Handling of attribute hashes is provided by standalone hash schemas from `dry-types`, which means there are different types of constructors in `dry-struct`
100
+ * Structs are not designed as swiss-army knifes, specific constructor types are used depending on the use case
101
+ * Struct classes quack like `dry-types`, which means you can use them in hash schemas, as array members or sum them
@@ -0,0 +1,49 @@
1
+ ---
2
+ title: Nested Structs
3
+ layout: gem-single
4
+ name: dry-struct
5
+ ---
6
+
7
+ The DSL allows to define nested structs by passing a block to `attribute`:
8
+
9
+ ```ruby
10
+ class User < Dry::Struct
11
+ attribute :name, Types::String
12
+ attribute :address do
13
+ attribute :city, Types::String
14
+ attribute :street, Types::String
15
+ end
16
+ end
17
+
18
+ User.new(name: 'Jane', address: { city: 'London', street: 'Oxford' })
19
+ # => #<User name="Jane" address=#<User::Address city="London" street="Oxford">>
20
+
21
+ # constants for nested structs are automatically defined
22
+ User::Address
23
+ # => User::Address
24
+ ```
25
+
26
+ By default, new struct classes uses `Dry::Struct` as a base class (`Dry::Struct::Value` for values). You can explicitly pass a different class:
27
+
28
+ ```ruby
29
+ class User < Dry::Struct
30
+ attribute :address, MyStruct do
31
+ # ...
32
+ end
33
+ end
34
+ ```
35
+
36
+ It is even possible to define an array of struct:
37
+
38
+ ```ruby
39
+ class User < Dry::Struct
40
+ attribute :addresses, Types::Array do
41
+ attribute :city, Types::String
42
+ attribute :street, Types::String
43
+ end
44
+ end
45
+
46
+ # constants are still there!
47
+ User::Address
48
+ # => User::Address
49
+ ```
@@ -0,0 +1,143 @@
1
+ ---
2
+ title: Recipes
3
+ layout: gem-single
4
+ name: dry-struct
5
+ ---
6
+
7
+ ### Symbolize input keys
8
+
9
+ ```ruby
10
+ require 'dry-struct'
11
+
12
+ module Types
13
+ include Dry.Types()
14
+ end
15
+
16
+ class User < Dry::Struct
17
+ transform_keys(&:to_sym)
18
+
19
+ attribute :name, Types::String
20
+ end
21
+
22
+ User.new('name' => 'Jane')
23
+ # => #<User name="Jane">
24
+ ```
25
+
26
+ ### Tolerance to extra keys
27
+
28
+ Structs ignore extra keys by default. This can be changed by replacing the constructor.
29
+
30
+ ```ruby
31
+ class User < Dry::Struct
32
+ # This does the trick
33
+ schema schema.strict
34
+
35
+ attribute :name, Types::String
36
+ end
37
+
38
+ User.new(name: 'Jane', age: 21)
39
+ # => Dry::Struct::Error ([User.new] unexpected keys [:age] in Hash input)
40
+ ```
41
+
42
+ ### Tolerance to missing keys
43
+
44
+ You can mark certain keys as optional by calling `attribute?`.
45
+
46
+ ```ruby
47
+ class User < Dry::Struct
48
+ attribute :name, Types::String
49
+ attribute? :age, Types::Integer
50
+ end
51
+
52
+ user = User.new(name: 'Jane')
53
+ # => #<User name="Jane" age=nil>
54
+ user.age
55
+ # => nil
56
+ ```
57
+
58
+ In the example above `nil` violates the type constraint so be careful with `attribute?`.
59
+
60
+ ### Default values
61
+
62
+ Instead of violating constraints you can assign default values to attributes:
63
+
64
+ ```ruby
65
+ class User < Dry::Struct
66
+ attribute :name, Types::String
67
+ attribute :age, Types::Integer.default(18)
68
+ end
69
+
70
+ User.new(name: 'Jane')
71
+ # => #<User name="Jane" age=18>
72
+ ```
73
+
74
+ ### Resolving default values on `nil`
75
+
76
+ `nil` as a value isn't replaced with a default value for default types. You may use `transform_types` to turn all types into constructors which map `nil` to `Dry::Types::Undefined` which in order triggers default values.
77
+
78
+ ```ruby
79
+ class User < Dry::Struct
80
+ transform_types do |type|
81
+ if type.default?
82
+ type.constructor do |value|
83
+ value.nil? ? Dry::Types::Undefined : value
84
+ end
85
+ else
86
+ type
87
+ end
88
+ end
89
+
90
+ attribute :name, Types::String
91
+ attribute :age, Types::Integer.default(18)
92
+ end
93
+
94
+ User.new(name: 'Jane')
95
+ # => #<User name="Jane" age=18>
96
+ User.new(name: 'Jane', age: nil)
97
+ # => #<User name="Jane" age=18>
98
+ ```
99
+
100
+ ### Creating a custom struct class
101
+
102
+ You can combine examples from this page to create a custom-purposed base struct class and the reuse it your application or gem
103
+
104
+ ```ruby
105
+ class MyStruct < Dry::Struct
106
+ # throw an error when unknown keys provided
107
+ schema schema.strict
108
+
109
+ # convert string keys to symbols
110
+ transform_keys(&:to_sym)
111
+
112
+ # resolve default types on nil
113
+ transform_types do |type|
114
+ if type.default?
115
+ type.constructor do |value|
116
+ value.nil? ? Dry::Types::Undefined : value
117
+ end
118
+ else
119
+ type
120
+ end
121
+ end
122
+ end
123
+ ```
124
+
125
+ ### Set default value for a nested hash
126
+
127
+ ```ruby
128
+ class Foo < Dry::Struct
129
+ attribute :bar do
130
+ attribute :nested, Types::Integer
131
+ end
132
+ end
133
+ ```
134
+
135
+ ```ruby
136
+ class Foo < Dry::Struct
137
+ class Bar < Dry::Struct
138
+ attribute :nested, Types::Integer
139
+ end
140
+
141
+ attribute :bar, Bar.default { Bar.new(nested: 1) }
142
+ end
143
+ ```
@@ -16,21 +16,21 @@ module Dry
16
16
  # require 'dry-struct'
17
17
  #
18
18
  # module Types
19
- # include Dry.Types
19
+ # include Dry.Types()
20
20
  # end
21
21
  #
22
- # Person = Dry.Struct(name: Types::Strict::String, age: Types::Strict::Int)
22
+ # Person = Dry.Struct(name: Types::String, age: Types::Integer)
23
23
  # matz = Person.new(name: "Matz", age: 52)
24
24
  # matz.name #=> "Matz"
25
25
  # matz.age #=> 52
26
26
  #
27
- # Test = Dry.Struct(expected: Types::Strict::String) { input(input.strict) }
27
+ # Test = Dry.Struct(expected: Types::String) { schema(schema.strict) }
28
28
  # Test[expected: "foo", unexpected: "bar"]
29
29
  # #=> Dry::Struct::Error: [Test.new] unexpected keys [:unexpected] in Hash input
30
30
  def self.Struct(attributes = Dry::Core::Constants::EMPTY_HASH, &block)
31
31
  Class.new(Dry::Struct) do
32
32
  attributes.each { |a, type| attribute a, type }
33
- instance_eval(&block) if block
33
+ module_eval(&block) if block
34
34
  end
35
35
  end
36
36
 
@@ -60,7 +60,7 @@ module Dry
60
60
  # require 'dry-struct'
61
61
  #
62
62
  # module Types
63
- # include Dry.Types
63
+ # include Dry.Types()
64
64
  # end
65
65
  #
66
66
  # class Book < Dry::Struct
@@ -111,8 +111,8 @@ module Dry
111
111
  #
112
112
  # @example
113
113
  # class Book < Dry::Struct
114
- # attribute :title, Types::Strict::String
115
- # attribute :subtitle, Types::Strict::String.optional
114
+ # attribute :title, Types::String
115
+ # attribute :subtitle, Types::String.optional
116
116
  # end
117
117
  #
118
118
  # rom_n_roda = Book.new(
@@ -132,8 +132,8 @@ module Dry
132
132
  #
133
133
  # @example
134
134
  # class Book < Dry::Struct
135
- # attribute :title, Types::Strict::String
136
- # attribute :subtitle, Types::Strict::String.optional
135
+ # attribute :title, Types::String
136
+ # attribute :subtitle, Types::String.optional
137
137
  # end
138
138
  #
139
139
  # rom_n_roda = Book.new(
@@ -157,8 +157,8 @@ module Dry
157
157
  #
158
158
  # @example
159
159
  # class Book < Dry::Struct
160
- # attribute :title, Types::Strict::String
161
- # attribute :subtitle, Types::Strict::String.optional
160
+ # attribute :title, Types::String
161
+ # attribute :subtitle, Types::String.optional
162
162
  # end
163
163
  #
164
164
  # rom_n_roda = Book.new(
@@ -183,6 +183,15 @@ module Dry
183
183
  attrs = klass.attribute_names.map { |key| " #{key}=#{@attributes[key].inspect}" }.join
184
184
  "#<#{ klass.name || klass.inspect }#{ attrs }>"
185
185
  end
186
+
187
+ if RUBY_VERSION >= '2.7'
188
+ # Pattern matching support
189
+ #
190
+ # @api private
191
+ def deconstruct
192
+ [attributes]
193
+ end
194
+ end
186
195
  end
187
196
  end
188
197
 
@@ -53,7 +53,7 @@ module Dry
53
53
  # end
54
54
  #
55
55
  # Language.schema
56
- # # => #<Dry::Types[Constructor<Schema<keys={name: Nominal<String> details: Language::Details}> fn=Kernel.Hash>]>
56
+ # # => #<Dry::Types[Constructor<Schema<keys={name: Constrained<Nominal<String> rule=[type?(String)]> details: Language::Details}> fn=Kernel.Hash>]>
57
57
  #
58
58
  # ruby = Language.new(name: 'Ruby', details: { type: 'OO' })
59
59
  # ruby.name #=> 'Ruby'
@@ -72,9 +72,9 @@ module Dry
72
72
  #
73
73
  # Language.schema
74
74
  # => #<Dry::Types[Constructor<Schema<keys={
75
- # name: Nominal<String>
76
- # versions: Array<Nominal<String>>
77
- # celebrities: Array<Language::Celebrity>
75
+ # name: Constrained<Nominal<String> rule=[type?(String)]>
76
+ # versions: Constrained<Array<Constrained<Nominal<String> rule=[type?(String)]>> rule=[type?(Array)]>
77
+ # celebrities: Constrained<Array<Language::Celebrity> rule=[type?(Array)]>
78
78
  # }> fn=Kernel.Hash>]>
79
79
  #
80
80
  # ruby = Language.new(
@@ -104,11 +104,11 @@ module Dry
104
104
  #
105
105
  # @example
106
106
  # class User < Dry::Struct
107
- # attribute :name, Types::Strict::String
108
- # attribute? :email, Types::Strict::String
107
+ # attribute :name, Types::String
108
+ # attribute? :email, Types::String
109
109
  # end
110
110
  #
111
- # User.new(name: 'John') # => #<User name="John">
111
+ # User.new(name: 'John') # => #<User name="John" email=nil>
112
112
  #
113
113
  # @param [Symbol] name name of the defined attribute
114
114
  # @param [Dry::Types::Type, nil] type or superclass of nested type
@@ -145,8 +145,8 @@ module Dry
145
145
  #
146
146
  # Book.schema
147
147
  # # => #<Dry::Types[Constructor<Schema<keys={
148
- # # title: Nominal<String>
149
- # # author: Nominal<String>
148
+ # # title: Constrained<Nominal<String> rule=[type?(String)]>
149
+ # # author: Constrained<Nominal<String> rule=[type?(String)]>
150
150
  # # }> fn=Kernel.Hash>]>
151
151
  def attributes(new_schema)
152
152
  keys = new_schema.keys.map { |k| k.to_s.chomp('?').to_sym }
@@ -182,7 +182,7 @@ module Dry
182
182
  # class Book < Dry::Struct
183
183
  # transform_types { |t| t.meta(struct: :Book) }
184
184
  #
185
- # attribute :title, Types::Strict::String
185
+ # attribute :title, Types::String
186
186
  # end
187
187
  #
188
188
  # Book.schema.key(:title).meta # => { struct: :Book }
@@ -199,7 +199,7 @@ module Dry
199
199
  # class Book < Dry::Struct
200
200
  # transform_keys(&:to_sym)
201
201
  #
202
- # attribute :title, Types::Strict::String
202
+ # attribute :title, Types::String
203
203
  # end
204
204
  #
205
205
  # Book.new('title' => "The Old Man and the Sea")
@@ -272,10 +272,10 @@ module Dry
272
272
  # @yieldreturn [Dry::Types::ResultResult]
273
273
  # @return [Dry::Types::Result]
274
274
  def try(input)
275
- Types::Result::Success.new(self[input])
275
+ success(self[input])
276
276
  rescue Struct::Error => e
277
- failure = Types::Result::Failure.new(input, e.message)
278
- block_given? ? yield(failure) : failure
277
+ failure_result = failure(input, e.message)
278
+ block_given? ? yield(failure_result) : failure_result
279
279
  end
280
280
 
281
281
  # @param [Hash{Symbol => Object},Dry::Struct] input
@@ -7,11 +7,7 @@ module Dry
7
7
  # @return [Hash, Array]
8
8
  def self.[](value)
9
9
  if value.respond_to?(:to_hash)
10
- if RUBY_VERSION >= '2.4'
11
- value.to_hash.transform_values { |v| self[v] }
12
- else
13
- value.to_hash.each_with_object({}) { |(k, v), h| h[k] = self[v] }
14
- end
10
+ value.to_hash.transform_values { |current| self[current] }
15
11
  elsif value.respond_to?(:to_ary)
16
12
  value.to_ary.map { |item| self[item] }
17
13
  else
@@ -7,8 +7,8 @@ module Dry
7
7
  #
8
8
  # @example
9
9
  # class Location < Dry::Struct::Value
10
- # attribute :lat, Types::Strict::Float
11
- # attribute :lng, Types::Strict::Float
10
+ # attribute :lat, Types::Float
11
+ # attribute :lng, Types::Float
12
12
  # end
13
13
  #
14
14
  # loc1 = Location.new(lat: 1.23, lng: 4.56)
@@ -1,6 +1,6 @@
1
1
  module Dry
2
2
  class Struct
3
3
  # @private
4
- VERSION = '1.0.0'.freeze
4
+ VERSION = '1.1.0'.freeze
5
5
  end
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dry-struct
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Solnica
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-04-23 00:00:00.000000000 Z
11
+ date: 2019-10-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-equalizer
@@ -135,6 +135,9 @@ executables: []
135
135
  extensions: []
136
136
  extra_rdoc_files: []
137
137
  files:
138
+ - ".github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md"
139
+ - ".github/ISSUE_TEMPLATE/---bug-report.md"
140
+ - ".github/ISSUE_TEMPLATE/---feature-request.md"
138
141
  - ".gitignore"
139
142
  - ".rspec"
140
143
  - ".travis.yml"
@@ -151,6 +154,9 @@ files:
151
154
  - benchmarks/setup.rb
152
155
  - bin/console
153
156
  - bin/setup
157
+ - docsite/source/index.html.md
158
+ - docsite/source/nested-structs.html.md
159
+ - docsite/source/recipes.html.md
154
160
  - dry-struct.gemspec
155
161
  - lib/dry-struct.rb
156
162
  - lib/dry/struct.rb
@@ -188,7 +194,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
188
194
  - !ruby/object:Gem::Version
189
195
  version: '0'
190
196
  requirements: []
191
- rubygems_version: 3.0.3
197
+ rubygems_version: 3.0.6
192
198
  signing_key:
193
199
  specification_version: 4
194
200
  summary: Typed structs and value objects.