dry-struct 0.6.0 → 0.7.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: 33cd05dd51a3310d7ede685ae1ce8631a0955d7563afca7633a7625b9b34dd17
4
- data.tar.gz: 182c58865ccb3e74595aa22d6c9730ce8ca0e078d1b6ecb5e8b69e026ce6409d
3
+ metadata.gz: 2ea6fc5c14bec80d877a1a04549fa897c48fb6d028e9a35c86f05d59fff4cf65
4
+ data.tar.gz: 38062528ba03fb91e4d0c4ca95dc526e1db4703a71b0095a5d99d43b1112a015
5
5
  SHA512:
6
- metadata.gz: 3f5e4f6609b93e80ae775ab556812cc872883eaf423a74ae2e5191d2bef6907152653fa7fe441bb9aab945b7cf9f4ec62dd4d359ffcb88f4e976b8b53fb42ac8
7
- data.tar.gz: 4b76e94533ff98f1c1d9d427967a5c19af3d58484a5e785c7eac15d50211d70e4718d35572bb6e89fbafeb9b9f092a73f68e517244676f28d4c7e4854f1a055a
6
+ metadata.gz: d29117cb09adefbdca14a7ed0ce5ed3e0b3cf22d5167ae30ab143bad0771c0d02782f0386f3995a4aa1625303a759894473ddc668e67d4a04bd93a818458bdc2
7
+ data.tar.gz: 5e1f4cdba2ceb5325c90fa454701daac5d82b410a4b6742eda3bfb467038aee16fd5796a8d0819cc8c510471590ffc42fb9fe3f4b04e9f3bf11d359b8b850a4d
@@ -4,20 +4,21 @@ sudo: required
4
4
  bundler_args: --without benchmarks tools
5
5
  script:
6
6
  - bundle exec rake spec
7
- before_install:
8
- - gem update --system
9
7
  after_success:
10
8
  - '[ -d coverage ] && bundle exec codeclimate-test-reporter'
11
9
  rvm:
12
- - 2.2.10
13
- - 2.3.7
14
- - 2.4.4
15
- - 2.5.1
16
- - jruby-9.2.0.0
10
+ - 2.4.5
11
+ - 2.5.3
12
+ - 2.6.1
13
+ - jruby-9.2.6.0
14
+ - truffleruby
17
15
  env:
18
16
  global:
19
17
  - COVERAGE=true
20
18
  - JRUBY_OPTS='--dev -J-Xmx1024M'
19
+ matrix:
20
+ allow_failures:
21
+ - rvm: truffleruby
21
22
  notifications:
22
23
  email: false
23
24
  webhooks:
@@ -1,8 +1,37 @@
1
+ # 0.7.0 2019-03-22
2
+
3
+ ## Changed
4
+
5
+ * [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.
6
+ New API:
7
+ ```ruby
8
+ User.schema.each do |key|
9
+ puts "Key name: #{ key.name }"
10
+ puts "Key type: #{ key.type }"
11
+ end
12
+ ```
13
+ To get a type by its name use `.key`:
14
+ ```ruby
15
+ User.schema.key(:id) # => #<Dry::Types::Hash::Key ...>
16
+ ```
17
+ * [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:
18
+ ```ruby
19
+ class StructWithOptionalKeys < Dry::Struct
20
+ transform_types { |key| key.required(false) }
21
+ # or simply
22
+ transform_types(&:omittable)
23
+ end
24
+ ```
25
+ * `Dry::Stuct#new` is now more efficient for partial updates (flash-gordon)
26
+ * Ruby 2.3 is EOL and not officially supported. It may work but we don't test it.
27
+
28
+ [Compare v0.6.0...v0.7.0](https://github.com/dry-rb/dry-struct/compare/v0.6.0...v0.7.0)
29
+
1
30
  # v0.6.0 2018-10-24
2
31
 
3
- ## BREAKING CHANGES
32
+ ## Changed
4
33
 
5
- * `Struct.attribute?` in the old sense is deprecated, use `has_attribute?` as a replacement
34
+ * [BREAKING] `Struct.attribute?` in the old sense is deprecated, use `has_attribute?` as a replacement
6
35
 
7
36
  ## Added
8
37
 
data/Gemfile CHANGED
@@ -1,9 +1,8 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gemspec
3
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
4
 
5
- gem 'dry-types', git: 'https://github.com/dry-rb/dry-types'
6
- gem 'dry-inflector', git: 'https://github.com/dry-rb/dry-inflector'
5
+ gemspec
7
6
 
8
7
  group :test do
9
8
  gem 'codeclimate-test-reporter', platform: :mri, require: false
@@ -12,10 +11,8 @@ group :test do
12
11
  end
13
12
 
14
13
  group :tools do
14
+ gem 'pry'
15
15
  gem 'pry-byebug', platform: :mri
16
- gem 'pry', platform: :jruby
17
- gem 'mutant'
18
- gem 'mutant-rspec'
19
16
  end
20
17
 
21
18
  group :benchmarks do
@@ -17,7 +17,7 @@ class ARUser < ActiveRecord::Base
17
17
  end
18
18
 
19
19
  module Types
20
- include Dry::Types.module
20
+ include Dry.Types
21
21
  end
22
22
 
23
23
  class DryStructUser < Dry::Struct
@@ -6,7 +6,7 @@ require 'dry/struct'
6
6
  require 'irb'
7
7
 
8
8
  module Types
9
- include Dry::Types.module
9
+ include Dry.Types
10
10
  end
11
11
 
12
12
  binding.irb
@@ -27,13 +27,14 @@ Gem::Specification.new do |spec|
27
27
  spec.bindir = 'exe'
28
28
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
29
29
  spec.require_paths = ['lib']
30
+ spec.required_ruby_version = ">= 2.3.0"
30
31
 
31
32
  spec.add_runtime_dependency 'dry-equalizer', '~> 0.2'
32
- spec.add_runtime_dependency 'dry-types', '~> 0.13'
33
+ spec.add_runtime_dependency 'dry-types', '~> 0.15'
33
34
  spec.add_runtime_dependency 'dry-core', '~> 0.4', '>= 0.4.3'
34
35
  spec.add_runtime_dependency 'ice_nine', '~> 0.11'
35
36
 
36
- spec.add_development_dependency 'bundler', '~> 1.6'
37
+ spec.add_development_dependency 'bundler'
37
38
  spec.add_development_dependency 'rake', '~> 11.0'
38
39
  spec.add_development_dependency 'rspec', '~> 3.3'
39
40
  spec.add_development_dependency 'yard', '~> 0.9.5'
@@ -16,7 +16,7 @@ module Dry
16
16
  # require 'dry-struct'
17
17
  #
18
18
  # module Types
19
- # include Dry::Types.module
19
+ # include Dry.Types
20
20
  # end
21
21
  #
22
22
  # Person = Dry.Struct(name: Types::Strict::String, age: Types::Strict::Int)
@@ -60,12 +60,12 @@ module Dry
60
60
  # require 'dry-struct'
61
61
  #
62
62
  # module Types
63
- # include Dry::Types.module
63
+ # include Dry.Types
64
64
  # end
65
65
  #
66
66
  # class Book < Dry::Struct
67
- # attribute :title, Types::Strict::String
68
- # attribute :subtitle, Types::Strict::String.optional
67
+ # attribute :title, Types::String
68
+ # attribute :subtitle, Types::String.optional
69
69
  # end
70
70
  #
71
71
  # rom_n_roda = Book.new(
@@ -90,8 +90,8 @@ module Dry
90
90
 
91
91
  # {Dry::Types::Hash::Schema} subclass with specific behaviour defined for
92
92
  # @return [Dry::Types::Hash::Schema]
93
- defines :input
94
- input Types['coercible.hash'].schema(EMPTY_HASH)
93
+ defines :schema
94
+ schema Types['coercible.hash'].schema(EMPTY_HASH)
95
95
 
96
96
  @meta = EMPTY_HASH
97
97
 
@@ -143,8 +143,8 @@ module Dry
143
143
  # rom_n_roda.to_hash
144
144
  # #=> {title: 'Web Development with ROM and Roda', subtitle: nil}
145
145
  def to_hash
146
- self.class.schema.keys.each_with_object({}) do |key, result|
147
- result[key] = Hashify[self[key]] if attributes.key?(key)
146
+ self.class.schema.each_with_object({}) do |key, result|
147
+ result[key.name] = Hashify[self[key.name]] if attributes.key?(key.name)
148
148
  end
149
149
  end
150
150
  alias_method :to_h, :to_hash
@@ -170,7 +170,10 @@ module Dry
170
170
  # rom_n_roda.new(subtitle: '3rd edition')
171
171
  # #=> #<Book title="Web Development with ROM and Roda" subtitle="3rd edition">
172
172
  def new(changeset)
173
- self.class[__attributes__.merge(changeset)]
173
+ new_attributes = self.class.schema.apply(changeset, skip_missing: true, resolve_defaults: false)
174
+ self.class.load(__attributes__.merge(new_attributes))
175
+ rescue Types::SchemaError, Types::MissingKeyError, Types::UnknownKeysError => error
176
+ raise Struct::Error, "[#{self}.new] #{error}"
174
177
  end
175
178
  alias_method :__new__, :new
176
179
 
@@ -34,7 +34,7 @@ module Dry
34
34
  # and modifies {.schema} accordingly.
35
35
  #
36
36
  # @param [Symbol] name name of the defined attribute
37
- # @param [Dry::Types::Definition, nil] type or superclass of nested type
37
+ # @param [Dry::Types::Type, nil] type or superclass of nested type
38
38
  # @return [Dry::Struct]
39
39
  # @yield
40
40
  # If a block is given, it will be evaluated in the context of
@@ -53,10 +53,7 @@ module Dry
53
53
  # end
54
54
  #
55
55
  # Language.schema
56
- # #=> {
57
- # :name=>#<Dry::Types::Definition primitive=String options={} meta={}>,
58
- # :details=>Language::Details
59
- # }
56
+ # # => #<Dry::Types[Constructor<Schema<keys={name: Nominal<String> details: Language::Details}> fn=Kernel.Hash>]>
60
57
  #
61
58
  # ruby = Language.new(name: 'Ruby', details: { type: 'OO' })
62
59
  # ruby.name #=> 'Ruby'
@@ -74,11 +71,11 @@ module Dry
74
71
  # end
75
72
  #
76
73
  # Language.schema
77
- # #=> {
78
- # :name=>#<Dry::Types::Definition primitive=String options={} meta={}>,
79
- # :versions=>#<Dry::Types::Array::Member primitive=Array options={:member=>#<Dry::Types::Definition primitive=String options={} meta={}>} meta={}>,
80
- # :celebrities=>#<Dry::Types::Array::Member primitive=Array options={:member=>Language::Celebrity} meta={}>
81
- # }
74
+ # => #<Dry::Types[Constructor<Schema<keys={
75
+ # name: Nominal<String>
76
+ # versions: Array<Nominal<String>>
77
+ # celebrities: Array<Language::Celebrity>
78
+ # }> fn=Kernel.Hash>]>
82
79
  #
83
80
  # ruby = Language.new(
84
81
  # name: 'Ruby',
@@ -114,7 +111,7 @@ module Dry
114
111
  # User.new(name: 'John') # => #<User name="John">
115
112
  #
116
113
  # @param [Symbol] name name of the defined attribute
117
- # @param [Dry::Types::Definition, nil] type or superclass of nested type
114
+ # @param [Dry::Types::Type, nil] type or superclass of nested type
118
115
  # @return [Dry::Struct]
119
116
  #
120
117
  def attribute?(*args, &block)
@@ -129,11 +126,11 @@ module Dry
129
126
  else
130
127
  name, type = args
131
128
 
132
- attribute(name, build_type(name, type, &block).meta(omittable: true))
129
+ attribute(:"#{ name }?", build_type(name, type, &block))
133
130
  end
134
131
  end
135
132
 
136
- # @param [Hash{Symbol => Dry::Types::Definition}] new_schema
133
+ # @param [Hash{Symbol => Dry::Types::Type}] new_schema
137
134
  # @return [Dry::Struct]
138
135
  # @raise [RepeatedAttributeError] when trying to define attribute with the
139
136
  # same name as previously defined one
@@ -147,14 +144,17 @@ module Dry
147
144
  # end
148
145
  #
149
146
  # Book.schema
150
- # #=> {title: #<Dry::Types::Definition primitive=String options={}>,
151
- # # author: #<Dry::Types::Definition primitive=String options={}>}
147
+ # # => #<Dry::Types[Constructor<Schema<keys={
148
+ # # title: Nominal<String>
149
+ # # author: Nominal<String>
150
+ # # }> fn=Kernel.Hash>]>
152
151
  def attributes(new_schema)
153
- check_schema_duplication(new_schema)
152
+ keys = new_schema.keys.map { |k| k.to_s.chomp('?').to_sym }
153
+ check_schema_duplication(keys)
154
154
 
155
- input input.schema(new_schema)
155
+ schema schema.schema(new_schema)
156
156
 
157
- new_schema.each_key do |key|
157
+ keys.each do |key|
158
158
  next if instance_methods.include?(key)
159
159
  class_eval(<<-RUBY)
160
160
  def #{ key }
@@ -165,9 +165,11 @@ module Dry
165
165
 
166
166
  @attribute_names = nil
167
167
 
168
- descendants.
169
- select { |d| d.superclass == self }.
170
- each { |d| d.attributes(new_schema.reject { |k, _| d.schema.key?(k) }) }
168
+ direct_descendants = descendants.select { |d| d.superclass == self }
169
+ direct_descendants.each do |d|
170
+ inherited_attrs = new_schema.reject { |k, _| d.has_attribute?(k.to_s.chomp('?').to_sym) }
171
+ d.attributes(inherited_attrs)
172
+ end
171
173
 
172
174
  self
173
175
  end
@@ -183,10 +185,10 @@ module Dry
183
185
  # attribute :title, Types::Strict::String
184
186
  # end
185
187
  #
186
- # Book.schema[:title].meta # => { struct: :Book }
188
+ # Book.schema.key(:title).meta # => { struct: :Book }
187
189
  #
188
190
  def transform_types(proc = nil, &block)
189
- input input.with_type_transform(proc || block)
191
+ schema schema.with_type_transform(proc || block)
190
192
  end
191
193
 
192
194
  # Add an arbitrary transformation for input hash keys.
@@ -203,16 +205,18 @@ module Dry
203
205
  # Book.new('title' => "The Old Man and the Sea")
204
206
  # # => #<Book title="The Old Man and the Sea">
205
207
  def transform_keys(proc = nil, &block)
206
- input input.with_key_transform(proc || block)
208
+ schema schema.with_key_transform(proc || block)
207
209
  end
208
210
 
209
- # @param [Hash{Symbol => Dry::Types::Definition, Dry::Struct}] new_schema
211
+ # @param [Hash{Symbol => Dry::Types::Type, Dry::Struct}] new_schema
210
212
  # @raise [RepeatedAttributeError] when trying to define attribute with the
211
213
  # same name as previously defined one
212
- def check_schema_duplication(new_schema)
213
- shared_keys = new_schema.keys & (schema.keys - superclass.schema.keys)
214
+ def check_schema_duplication(new_keys)
215
+ overlapping_keys = new_keys & (attribute_names - superclass.attribute_names)
214
216
 
215
- raise RepeatedAttributeError, shared_keys.first if shared_keys.any?
217
+ if overlapping_keys.any?
218
+ raise RepeatedAttributeError, overlapping_keys.first
219
+ end
216
220
  end
217
221
  private :check_schema_duplication
218
222
 
@@ -222,12 +226,19 @@ module Dry
222
226
  if attributes.instance_of?(self)
223
227
  attributes
224
228
  else
225
- super(input[attributes])
229
+ super(schema[attributes])
226
230
  end
227
231
  rescue Types::SchemaError, Types::MissingKeyError, Types::UnknownKeysError => error
228
232
  raise Struct::Error, "[#{self}.new] #{error}"
229
233
  end
230
234
 
235
+ # @api private
236
+ def load(attributes)
237
+ struct = allocate
238
+ struct.send(:initialize, attributes)
239
+ struct
240
+ end
241
+
231
242
  # Calls type constructor. The behavior is identical to `.new` but returns
232
243
  # the input back if it's a subclass of the struct.
233
244
  #
@@ -321,16 +332,11 @@ module Dry
321
332
  schema.key?(key)
322
333
  end
323
334
 
324
- # @return [Hash{Symbol => Dry::Types::Definition, Dry::Struct}]
325
- def schema
326
- input.member_types
327
- end
328
-
329
335
  # Gets the list of attribute names
330
336
  #
331
337
  # @return [Array<Symbol>]
332
338
  def attribute_names
333
- @attribute_names ||= schema.keys
339
+ @attribute_names ||= schema.map(&:name)
334
340
  end
335
341
 
336
342
  # @return [{Symbol => Object}]
@@ -367,15 +373,15 @@ module Dry
367
373
  #
368
374
  # @return [Hash{Symbol => Object}]
369
375
  def default_attributes(default_schema = schema)
370
- default_schema.each_with_object({}) do |(name, type), result|
371
- result[name] = default_attributes(type.schema) if struct?(type)
376
+ default_schema.each_with_object({}) do |key, result|
377
+ result[key.name] = default_attributes(key.schema) if struct?(key.type)
372
378
  end
373
379
  end
374
380
  private :default_attributes
375
381
 
376
382
  # Checks if the given type is a Dry::Struct
377
383
  #
378
- # @param [Dry::Types::Definition, Dry::Struct] type
384
+ # @param [Dry::Types::Type] type
379
385
  # @return [Boolean]
380
386
  def struct?(type)
381
387
  type.is_a?(Class) && type <= Struct
@@ -73,7 +73,7 @@ module Dry
73
73
  member
74
74
  end
75
75
 
76
- def visit_definition(*)
76
+ def visit_nominal(*)
77
77
  default_superclass
78
78
  end
79
79
 
@@ -1,6 +1,6 @@
1
1
  module Dry
2
2
  class Struct
3
3
  # @private
4
- VERSION = '0.6.0'.freeze
4
+ VERSION = '0.7.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: 0.6.0
4
+ version: 0.7.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: 2018-10-24 00:00:00.000000000 Z
11
+ date: 2019-03-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-equalizer
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0.13'
33
+ version: '0.15'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0.13'
40
+ version: '0.15'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: dry-core
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -76,16 +76,16 @@ dependencies:
76
76
  name: bundler
77
77
  requirement: !ruby/object:Gem::Requirement
78
78
  requirements:
79
- - - "~>"
79
+ - - ">="
80
80
  - !ruby/object:Gem::Version
81
- version: '1.6'
81
+ version: '0'
82
82
  type: :development
83
83
  prerelease: false
84
84
  version_requirements: !ruby/object:Gem::Requirement
85
85
  requirements:
86
- - - "~>"
86
+ - - ">="
87
87
  - !ruby/object:Gem::Version
88
- version: '1.6'
88
+ version: '0'
89
89
  - !ruby/object:Gem::Dependency
90
90
  name: rake
91
91
  requirement: !ruby/object:Gem::Requirement
@@ -178,15 +178,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
178
178
  requirements:
179
179
  - - ">="
180
180
  - !ruby/object:Gem::Version
181
- version: '0'
181
+ version: 2.3.0
182
182
  required_rubygems_version: !ruby/object:Gem::Requirement
183
183
  requirements:
184
184
  - - ">="
185
185
  - !ruby/object:Gem::Version
186
186
  version: '0'
187
187
  requirements: []
188
- rubyforge_project:
189
- rubygems_version: 2.7.6
188
+ rubygems_version: 3.0.1
190
189
  signing_key:
191
190
  specification_version: 4
192
191
  summary: Typed structs and value objects.