dry-struct 0.6.0 → 0.7.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: 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.