dry-struct 0.2.1 → 0.3.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
  SHA1:
3
- metadata.gz: 0b2abda38b30796edc16f515274eb6c0be35a528
4
- data.tar.gz: 0114e855f8892fcfb1598410da854f52e61188ae
3
+ metadata.gz: 0cc9fef4b32f2b57bac889a7f5ca759795b650dd
4
+ data.tar.gz: be8ff9b280bcc8d0afdccf28d39453b7060e13d3
5
5
  SHA512:
6
- metadata.gz: eb01e9ed8ec7a914957c385733e0ea1894621309721527e5de11c1855d36fc2f0b615d5387474947cb65da30b52b70b5cc33701170ffaa987a2a53481352731c
7
- data.tar.gz: e1403ea9f6f014e7d0fb31f64019dc179f87175c9fbe0afcb42551ec6b7cb5b09a9b73df5b7d4d33702a5d8684a7213648f35903f906b0a5628d84b2553ed37f
6
+ metadata.gz: f8c76b9683ad51451060a3c388741598aedcac78946b0f979c8928fef1a3e6f4300c211438f3d3ca6490c23525730d051ffb7c2a808fd5e1b322474e91b20e9a
7
+ data.tar.gz: 0d8dc1bc47204f422ee104d60ee732e7bb276e3441ec255303f3a794f2718be0eb63bea2beda822c08a40a1a04a864d097171c4becaede57d7ffa701794a17fd
@@ -5,16 +5,21 @@ bundler_args: --without benchmarks tools
5
5
  script:
6
6
  - bundle exec rake spec
7
7
  after_success:
8
- - '[ "$TRAVIS_RUBY_VERSION" = "2.3.1" ] && [ "$TRAVIS_BRANCH" = "master" ] && bundle exec codeclimate-test-reporter'
8
+ - '[ -d coverage ] && bundle exec codeclimate-test-reporter'
9
9
  rvm:
10
10
  - 2.1.10
11
- - 2.2.5
12
- - 2.3.1
11
+ - 2.2.7
12
+ - 2.3.3
13
+ - 2.4.1
13
14
  - rbx-3
14
- - jruby-9.1.5.0
15
+ - jruby-9.1.8.0
15
16
  env:
16
17
  global:
18
+ - COVERAGE=true
17
19
  - JRUBY_OPTS='--dev -J-Xmx1024M'
20
+ matrix:
21
+ allow_failures:
22
+ - rvm: rbx-3
18
23
  notifications:
19
24
  email: false
20
25
  webhooks:
@@ -0,0 +1,4 @@
1
+ --title 'dry-struct'
2
+ --markup markdown
3
+ --readme README.md
4
+ lib/**/*.rb
@@ -1,9 +1,28 @@
1
+ master
2
+
3
+ ## Added
4
+
5
+ * `Dry::Struct#new` method to return new instance with applied changeset (Kukunin)
6
+
7
+ ## Fixed
8
+
9
+ * `.[]` and `.call` does not coerce subclass to superclass anymore (Kukunin)
10
+ * Raise ArgumentError when attribute type is a string and no value provided is for `new` (GustavoCaso)
11
+
12
+ ## Changed
13
+
14
+ * `.new` without arguments doesn't use nil as an input for non-default types anymore (flash-gordon)
15
+
16
+ [Compare v0.2.1...master](https://github.com/dry-rb/dry-struct/compare/v0.2.1...master)
17
+
1
18
  # v0.2.1 2017-02-27
2
19
 
3
20
  ## Fixed
4
21
 
5
22
  * Fixed `Dry::Struct::Value` which appeared to be broken in the last release (flash-gordon)
6
23
 
24
+ [Compare v0.2.0...v0.2.1](https://github.com/dry-rb/dry-struct/compare/v0.2.0...v0.2.1)
25
+
7
26
  # v0.2.0 2016-02-26
8
27
 
9
28
  ## Changed
data/Rakefile CHANGED
@@ -4,3 +4,7 @@ require 'rspec/core/rake_task'
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
6
  task :default => :spec
7
+
8
+ require 'yard'
9
+ require 'yard/rake/yardoc_task'
10
+ YARD::Rake::YardocTask.new(:doc)
@@ -1,5 +1,3 @@
1
- # coding: utf-8
2
-
3
1
  lib = File.expand_path('../lib', __FILE__)
4
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
3
  require 'dry/struct/version'
@@ -31,9 +29,11 @@ Gem::Specification.new do |spec|
31
29
  spec.add_runtime_dependency 'dry-equalizer', '~> 0.2'
32
30
  spec.add_runtime_dependency 'dry-configurable', '~> 0.1'
33
31
  spec.add_runtime_dependency 'dry-types', '~> 0.9', '>= 0.9.0'
32
+ spec.add_runtime_dependency 'dry-core', '~> 0.3'
34
33
  spec.add_runtime_dependency 'ice_nine', '~> 0.11'
35
34
 
36
35
  spec.add_development_dependency 'bundler', '~> 1.6'
37
36
  spec.add_development_dependency 'rake', '~> 11.0'
38
37
  spec.add_development_dependency 'rspec', '~> 3.3'
38
+ spec.add_development_dependency 'yard', '~> 0.9.5'
39
39
  end
@@ -6,25 +6,230 @@ require 'dry/struct/class_interface'
6
6
  require 'dry/struct/hashify'
7
7
 
8
8
  module Dry
9
+ # Typed {Struct} with virtus-like DSL for defining schema.
10
+ #
11
+ # ### Differences between dry-struct and virtus
12
+ #
13
+ # {Struct} look somewhat similar to [Virtus][] but there are few significant differences:
14
+ #
15
+ # * {Struct}s don't provide attribute writers and are meant to be used
16
+ # as "data objects" exclusively.
17
+ # * Handling of attribute values is provided by standalone type objects from
18
+ # [`dry-types`][].
19
+ # * Handling of attribute hashes is provided by standalone hash schemas from
20
+ # [`dry-types`][], which means there are different types of constructors in
21
+ # {Struct} (see {Dry::Struct::ClassInterface#constructor_type})
22
+ # * Struct classes quack like [`dry-types`][], which means you can use them
23
+ # in hash schemas, as array members or sum them
24
+ #
25
+ # {Struct} class can specify a constructor type, which uses [hash schemas][]
26
+ # to handle attributes in `.new` method.
27
+ # See {ClassInterface#new} for constructor types descriptions and examples.
28
+ #
29
+ # [`dry-types`]: https://github.com/dry-rb/dry-types
30
+ # [Virtus]: https://github.com/solnic/virtus
31
+ # [hash schemas]: http://dry-rb.org/gems/dry-types/hash-schemas
32
+ #
33
+ # @example
34
+ # require 'dry-struct'
35
+ #
36
+ # module Types
37
+ # include Dry::Types.module
38
+ # end
39
+ #
40
+ # class Book < Dry::Struct
41
+ # attribute :title, Types::Strict::String
42
+ # attribute :subtitle, Types::Strict::String.optional
43
+ # end
44
+ #
45
+ # rom_n_roda = Book.new(
46
+ # title: 'Web Development with ROM and Roda',
47
+ # subtitle: nil
48
+ # )
49
+ # rom_n_roda.title #=> 'Web Development with ROM and Roda'
50
+ # rom_n_roda.subtitle #=> nil
51
+ #
52
+ # refactoring = Book.new(
53
+ # title: 'Refactoring',
54
+ # subtitle: 'Improving the Design of Existing Code'
55
+ # )
56
+ # refactoring.title #=> 'Refactoring'
57
+ # refactoring.subtitle #=> 'Improving the Design of Existing Code'
9
58
  class Struct
10
59
  extend ClassInterface
11
60
 
12
- constructor_type(:permissive)
61
+ # {Dry::Types::Hash} subclass with specific behaviour defined for
62
+ # @return [Dry::Types::Hash]
63
+ # @see #constructor_type
64
+ defines :input
65
+ input Types['coercible.hash']
13
66
 
67
+ # Sets or retrieves {#constructor} type as a symbol
68
+ #
69
+ # @note All examples below assume that you have defined {Struct} with
70
+ # following attributes and explicitly call only {#constructor_type}:
71
+ #
72
+ # ```ruby
73
+ # class User < Dry::Struct
74
+ # attribute :name, Types::Strict::String.default('John Doe')
75
+ # attribute :age, Types::Strict::Int
76
+ # end
77
+ # ```
78
+ #
79
+ # ### Common constructor types include:
80
+ #
81
+ # * `:permissive` - the default constructor type, useful for defining
82
+ # {Struct}s that are instantiated using data from the database
83
+ # (i.e. results of a database query), where you expect *all defined
84
+ # attributes to be present* and it's OK to ignore other keys
85
+ # (i.e. keys used for joining, that are not relevant from your domain
86
+ # {Struct}s point of view). Default values **are not used** otherwise
87
+ # you wouldn't notice missing data.
88
+ # * `:schema` - missing keys will result in setting them using default
89
+ # values, unexpected keys will be ignored.
90
+ # * `:strict` - useful when you *do not expect keys other than the ones
91
+ # you specified as attributes* in the input hash
92
+ # * `:strict_with_defaults` - same as `:strict` but you are OK that some
93
+ # values may be nil and you want defaults to be set
94
+ #
95
+ # To feel the difference between constructor types, look into examples.
96
+ # Each of them provide the same attributes' definitions,
97
+ # different constructor type, and 4 cases of given input:
98
+ #
99
+ # 1. Input omits a key for a value that does not have a default
100
+ # 2. Input omits a key for a value that has a default
101
+ # 3. Input contains nil for a value that specifies a default
102
+ # 4. Input includes a key that was not specified in the schema
103
+ #
104
+ # @note Don’t use `:weak` and `:symbolized` as {#constructor_type},
105
+ # and instead use [`dry-validation`][] to process and validate
106
+ # attributes, otherwise your struct will behave as a data validator
107
+ # which raises exceptions on invalid input (assuming your attributes
108
+ # types are strict)
109
+ # [`dry-validation`]: https://github.com/dry-rb/dry-validation
110
+ #
111
+ # @example `:permissive` constructor
112
+ # class User < Dry::Struct
113
+ # constructor_type :permissive
114
+ # end
115
+ #
116
+ # User.new(name: "Jane")
117
+ # #=> Dry::Struct::Error: [User.new] :age is missing in Hash input
118
+ # User.new(age: 31)
119
+ # #=> Dry::Struct::Error: [User.new] :name is missing in Hash input
120
+ # User.new(name: nil, age: 31)
121
+ # #=> #<User name="John Doe" age=31>
122
+ # User.new(name: "Jane", age: 31, unexpected: "attribute")
123
+ # #=> #<User name="Jane" age=31>
124
+ #
125
+ # @example `:schema` constructor
126
+ # class User < Dry::Struct
127
+ # constructor_type :schema
128
+ # end
129
+ #
130
+ # User.new(name: "Jane") #=> #<User name="Jane" age=nil>
131
+ # User.new(age: 31) #=> #<User name="John Doe" age=31>
132
+ # User.new(name: nil, age: 31) #=> #<User name="John Doe" age=31>
133
+ # User.new(name: "Jane", age: 31, unexpected: "attribute")
134
+ # #=> #<User name="Jane" age=31>
135
+ #
136
+ # @example `:strict` constructor
137
+ # class User < Dry::Struct
138
+ # constructor_type :strict
139
+ # end
140
+ #
141
+ # User.new(name: "Jane")
142
+ # #=> Dry::Struct::Error: [User.new] :age is missing in Hash input
143
+ # User.new(age: 31)
144
+ # #=> Dry::Struct::Error: [User.new] :name is missing in Hash input
145
+ # User.new(name: nil, age: 31)
146
+ # #=> Dry::Struct::Error: [User.new] nil (NilClass) has invalid type for :name
147
+ # User.new(name: "Jane", age: 31, unexpected: "attribute")
148
+ # #=> Dry::Struct::Error: [User.new] unexpected keys [:unexpected] in Hash input
149
+ #
150
+ # @example `:strict_with_defaults` constructor
151
+ # class User < Dry::Struct
152
+ # constructor_type :strict_with_defaults
153
+ # end
154
+ #
155
+ # User.new(name: "Jane")
156
+ # #=> Dry::Struct::Error: [User.new] :age is missing in Hash input
157
+ # User.new(age: 31)
158
+ # #=> #<User name="John Doe" age=31>
159
+ # User.new(name: nil, age: 31)
160
+ # #=> Dry::Struct::Error: [User.new] nil (NilClass) has invalid type for :name
161
+ # User.new(name: "Jane", age: 31, unexpected: "attribute")
162
+ # #=> Dry::Struct::Error: [User.new] unexpected keys [:unexpected] in Hash input
163
+ #
164
+ # @see http://dry-rb.org/gems/dry-types/hash-schemas
165
+ #
166
+ # @overload constructor_type(type)
167
+ # Sets the constructor type for {Struct}
168
+ # @param [Symbol] type one of constructor types, see above
169
+ # @return [Symbol]
170
+ #
171
+ # @overload constructor_type
172
+ # Returns the constructor type for {Struct}
173
+ # @return [Symbol] (:strict)
174
+ defines :constructor_type
175
+ constructor_type :permissive
176
+
177
+ # @return [Dry::Equalizer]
178
+ defines :equalizer
179
+
180
+ # @param [Hash, #each] attributes
14
181
  def initialize(attributes)
15
182
  attributes.each { |key, value| instance_variable_set("@#{key}", value) }
16
183
  end
17
184
 
185
+ # Retrieves value of previously defined attribute by its' `name`
186
+ #
187
+ # @param [String] name
188
+ # @return [Object]
189
+ #
190
+ # @example
191
+ # class Book < Dry::Struct
192
+ # attribute :title, Types::Strict::String
193
+ # attribute :subtitle, Types::Strict::String.optional
194
+ # end
195
+ #
196
+ # rom_n_roda = Book.new(
197
+ # title: 'Web Development with ROM and Roda',
198
+ # subtitle: nil
199
+ # )
200
+ # rom_n_roda[:title] #=> 'Web Development with ROM and Roda'
201
+ # rom_n_roda[:subtitle] #=> nil
18
202
  def [](name)
19
203
  public_send(name)
20
204
  end
21
205
 
206
+ # Converts the {Dry::Struct} to a hash with keys representing
207
+ # each attribute (as symbols) and their corresponding values
208
+ #
209
+ # @return [Hash{Symbol => Object}]
210
+ #
211
+ # @example
212
+ # class Book < Dry::Struct
213
+ # attribute :title, Types::Strict::String
214
+ # attribute :subtitle, Types::Strict::String.optional
215
+ # end
216
+ #
217
+ # rom_n_roda = Book.new(
218
+ # title: 'Web Development with ROM and Roda',
219
+ # subtitle: nil
220
+ # )
221
+ # rom_n_roda.to_hash
222
+ # #=> {title: 'Web Development with ROM and Roda', subtitle: nil}
22
223
  def to_hash
23
224
  self.class.schema.keys.each_with_object({}) do |key, result|
24
225
  result[key] = Hashify[self[key]]
25
226
  end
26
227
  end
27
228
  alias_method :to_h, :to_hash
229
+
230
+ def new(changeset)
231
+ self.class[to_hash.merge(changeset)]
232
+ end
28
233
  end
29
234
  end
30
235
 
@@ -1,48 +1,78 @@
1
+ require 'dry/core/class_attributes'
2
+ require 'dry/equalizer'
3
+
1
4
  require 'dry/struct/errors'
2
5
 
3
6
  module Dry
4
7
  class Struct
8
+ # Class-level interface of {Struct} and {Value}
5
9
  module ClassInterface
6
- include Dry::Types::Builder
7
-
8
- attr_accessor :constructor
10
+ include Core::ClassAttributes
9
11
 
10
- attr_accessor :equalizer
11
-
12
- attr_writer :constructor_type
13
-
14
- protected :constructor=, :equalizer=, :constructor_type=
12
+ include Dry::Types::Builder
15
13
 
14
+ # @param [Module] base
16
15
  def self.extended(base)
17
- base.instance_variable_set(:@schema, {})
16
+ base.instance_variable_set(:@schema, EMPTY_HASH)
18
17
  end
19
18
 
19
+ # @param [Class] klass
20
20
  def inherited(klass)
21
21
  super
22
22
 
23
- klass.instance_variable_set(:@schema, {})
24
- klass.equalizer = Equalizer.new(*schema.keys)
25
- klass.constructor_type = constructor_type
23
+ klass.instance_variable_set(:@schema, EMPTY_HASH)
24
+ klass.equalizer Equalizer.new(*schema.keys)
26
25
  klass.send(:include, klass.equalizer)
27
26
 
28
- unless klass == Value
29
- klass.constructor = Types['coercible.hash']
30
- end
31
-
32
- klass.attributes({}) unless equal?(Struct)
33
- end
34
-
27
+ klass.attributes(EMPTY_HASH) unless equal?(Struct)
28
+ end
29
+
30
+ # Adds an attribute for this {Struct} with given `name` and `type`
31
+ # and modifies {.schema} accordingly.
32
+ #
33
+ # @param [Symbol] name name of the defined attribute
34
+ # @param [Dry::Types::Definition] type
35
+ # @return [Dry::Struct]
36
+ # @raise [RepeatedAttributeError] when trying to define attribute with the
37
+ # same name as previously defined one
38
+ #
39
+ # @example
40
+ # class Language < Dry::Struct
41
+ # attribute :name, Types::String
42
+ # end
43
+ #
44
+ # Language.schema
45
+ # #=> {name: #<Dry::Types::Definition primitive=String options={}>}
46
+ #
47
+ # ruby = Language.new(name: 'Ruby')
48
+ # ruby.name #=> 'Ruby'
35
49
  def attribute(name, type)
36
50
  attributes(name => type)
37
51
  end
38
52
 
53
+ # @param [Hash{Symbol => Dry::Types::Definition}] new_schema
54
+ # @return [Dry::Struct]
55
+ # @raise [RepeatedAttributeError] when trying to define attribute with the
56
+ # same name as previously defined one
57
+ # @see #attribute
58
+ # @example
59
+ # class Book1 < Dry::Struct
60
+ # attributes(
61
+ # title: Types::String,
62
+ # author: Types::String
63
+ # )
64
+ # end
65
+ #
66
+ # Book.schema
67
+ # #=> {title: #<Dry::Types::Definition primitive=String options={}>,
68
+ # # author: #<Dry::Types::Definition primitive=String options={}>}
39
69
  def attributes(new_schema)
40
70
  check_schema_duplication(new_schema)
41
71
 
42
72
  prev_schema = schema
43
73
 
44
74
  @schema = prev_schema.merge(new_schema)
45
- @constructor = Types['coercible.hash'].public_send(constructor_type, schema)
75
+ input Types['coercible.hash'].public_send(constructor_type, schema)
46
76
 
47
77
  attr_reader(*new_schema.keys)
48
78
  equalizer.instance_variable_get('@keys').concat(new_schema.keys)
@@ -50,6 +80,9 @@ module Dry
50
80
  self
51
81
  end
52
82
 
83
+ # @param [Hash{Symbol => Dry::Types::Definition, Dry::Struct}] new_schema
84
+ # @raise [RepeatedAttributeError] when trying to define attribute with the
85
+ # same name as previously defined one
53
86
  def check_schema_duplication(new_schema)
54
87
  shared_keys = new_schema.keys & (schema.keys - superclass.schema.keys)
55
88
 
@@ -57,37 +90,60 @@ module Dry
57
90
  end
58
91
  private :check_schema_duplication
59
92
 
60
- def constructor_type(type = nil)
61
- if type
62
- @constructor_type = type
63
- else
64
- @constructor_type || :strict
65
- end
66
- end
67
-
93
+ # @return [Hash{Symbol => Dry::Types::Definition, Dry::Struct}]
68
94
  def schema
69
- super_schema = superclass.respond_to?(:schema) ? superclass.schema : {}
95
+ super_schema = superclass.respond_to?(:schema) ? superclass.schema : EMPTY_HASH
70
96
  super_schema.merge(@schema)
71
97
  end
72
98
 
99
+ # @param [Hash{Symbol => Object},Dry::Struct] attributes
100
+ # @raise [Struct::Error] if the given attributes don't conform {#schema}
101
+ # with given {#constructor_type}
73
102
  def new(attributes = default_attributes)
74
103
  if attributes.instance_of?(self)
75
104
  attributes
76
105
  else
77
- super(constructor[attributes])
106
+ super(input[attributes])
78
107
  end
79
108
  rescue Types::SchemaError, Types::MissingKeyError, Types::UnknownKeysError => error
80
109
  raise Struct::Error, "[#{self}.new] #{error}"
81
110
  end
82
- alias_method :call, :new
83
- alias_method :[], :new
84
111
 
112
+ # Calls type constructor. The behavior is identical to `.new` but returns
113
+ # returns the input back if it's a subclass of the struct.
114
+ #
115
+ # @param [Hash{Symbol => Object},Dry::Struct] attributes
116
+ # @return [Dry::Struct]
117
+ def call(attributes = default_attributes)
118
+ return attributes if attributes.is_a?(self)
119
+ new(attributes)
120
+ end
121
+ alias_method :[], :call
122
+
123
+ # Retrieves default attributes from defined {.schema}.
124
+ # Used in a {Struct} constructor if no attributes provided to {.new}
125
+ #
126
+ # @return [Hash{Symbol => Object}]
85
127
  def default_attributes
128
+ check_invalid_schema_keys
86
129
  schema.each_with_object({}) { |(name, type), result|
87
- result[name] = type.default? ? type.evaluate : type[nil]
130
+ result[name] = type.evaluate if type.default?
88
131
  }
89
132
  end
90
133
 
134
+ def check_invalid_schema_keys
135
+ invalid_keys = schema.select { |name, type| type.instance_of?(String) }
136
+ raise ArgumentError, argument_error_msg(invalid_keys.keys) if invalid_keys.any?
137
+ end
138
+
139
+ def argument_error_msg(keys)
140
+ "Invaild argument for #{keys.join(', ')}"
141
+ end
142
+
143
+ # @param [Hash{Symbol => Object}] input
144
+ # @yieldparam [Dry::Types::Result::Failure] failure
145
+ # @yieldreturn [Dry::Types::ResultResult]
146
+ # @return [Dry::Types::Result]
91
147
  def try(input)
92
148
  Types::Result::Success.new(self[input])
93
149
  rescue Struct::Error => e
@@ -95,33 +151,49 @@ module Dry
95
151
  block_given? ? yield(failure) : failure
96
152
  end
97
153
 
154
+ # @param [({Symbol => Object})] args
155
+ # @return [Dry::Types::Result::Success]
98
156
  def success(*args)
99
157
  result(Types::Result::Success, *args)
100
158
  end
101
159
 
160
+ # @param [({Symbol => Object})] args
161
+ # @return [Dry::Types::Result::Failure]
102
162
  def failure(*args)
103
163
  result(Types::Result::Failure, *args)
104
164
  end
105
165
 
166
+ # @param [Class] klass
167
+ # @param [({Symbol => Object})] args
106
168
  def result(klass, *args)
107
169
  klass.new(*args)
108
170
  end
109
171
 
172
+ # @return [false]
110
173
  def default?
111
174
  false
112
175
  end
113
176
 
177
+ # @param [Object, Dry::Struct] value
178
+ # @return [Boolean]
114
179
  def valid?(value)
115
180
  self === value
116
181
  end
117
182
 
183
+ # @return [true]
118
184
  def constrained?
119
185
  true
120
186
  end
121
187
 
188
+ # @return [self]
122
189
  def primitive
123
190
  self
124
191
  end
192
+
193
+ # @return [false]
194
+ def optional?
195
+ false
196
+ end
125
197
  end
126
198
  end
127
199
  end
@@ -4,9 +4,13 @@ module Dry
4
4
 
5
5
  setting :namespace, self
6
6
 
7
+ # Raised when given input doesn't conform schema and constructor type
7
8
  Error = Class.new(TypeError)
8
9
 
10
+ # Raised when defining duplicate attributes
9
11
  class RepeatedAttributeError < ArgumentError
12
+ # @param [Symbol] key
13
+ # attribute name that is the same as previously defined one
10
14
  def initialize(key)
11
15
  super("Attribute :#{key} has already been defined")
12
16
  end
@@ -1,8 +1,10 @@
1
- # Converts value to hash recursively
2
-
3
1
  module Dry
4
2
  class Struct
3
+ # Helper for {Struct#to_hash} implementation
5
4
  module Hashify
5
+ # Converts value to hash recursively
6
+ # @param [#to_hash, #map, Object] value
7
+ # @return [Hash, Array]
6
8
  def self.[](value)
7
9
  if value.respond_to?(:to_hash)
8
10
  value.to_hash
@@ -2,7 +2,27 @@ require 'ice_nine'
2
2
 
3
3
  module Dry
4
4
  class Struct
5
+ # {Value} objects behave like {Struct}s but *deeply frozen*
6
+ # using [`ice_nine`](https://github.com/dkubb/ice_nine)
7
+ #
8
+ # @example
9
+ # class Location < Dry::Struct::Value
10
+ # attribute :lat, Types::Strict::Float
11
+ # attribute :lng, Types::Strict::Float
12
+ # end
13
+ #
14
+ # loc1 = Location.new(lat: 1.23, lng: 4.56)
15
+ # loc2 = Location.new(lat: 1.23, lng: 4.56)
16
+ #
17
+ # loc1.frozen? #=> true
18
+ # loc2.frozen? #=> true
19
+ # loc1 == loc2 #=> true
20
+ #
21
+ # @see https://github.com/dkubb/ice_nine
5
22
  class Value < self
23
+ # @param (see ClassInterface#new)
24
+ # @return [Value]
25
+ # @see https://github.com/dkubb/ice_nine
6
26
  def self.new(*)
7
27
  IceNine.deep_freeze(super)
8
28
  end
@@ -1,5 +1,5 @@
1
1
  module Dry
2
2
  class Struct
3
- VERSION = '0.2.1'.freeze
3
+ VERSION = '0.3.0'.freeze
4
4
  end
5
5
  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.2.1
4
+ version: 0.3.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: 2017-02-27 00:00:00.000000000 Z
11
+ date: 2017-05-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-equalizer
@@ -58,6 +58,20 @@ dependencies:
58
58
  - - ">="
59
59
  - !ruby/object:Gem::Version
60
60
  version: 0.9.0
61
+ - !ruby/object:Gem::Dependency
62
+ name: dry-core
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.3'
68
+ type: :runtime
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '0.3'
61
75
  - !ruby/object:Gem::Dependency
62
76
  name: ice_nine
63
77
  requirement: !ruby/object:Gem::Requirement
@@ -114,6 +128,20 @@ dependencies:
114
128
  - - "~>"
115
129
  - !ruby/object:Gem::Version
116
130
  version: '3.3'
131
+ - !ruby/object:Gem::Dependency
132
+ name: yard
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: 0.9.5
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: 0.9.5
117
145
  description: Typed structs and value objects.
118
146
  email:
119
147
  - piotr.solnica@gmail.com
@@ -124,6 +152,7 @@ files:
124
152
  - ".gitignore"
125
153
  - ".rspec"
126
154
  - ".travis.yml"
155
+ - ".yardopts"
127
156
  - CHANGELOG.md
128
157
  - Gemfile
129
158
  - LICENSE
@@ -162,7 +191,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
162
191
  version: '0'
163
192
  requirements: []
164
193
  rubyforge_project:
165
- rubygems_version: 2.6.10
194
+ rubygems_version: 2.6.11
166
195
  signing_key:
167
196
  specification_version: 4
168
197
  summary: Typed structs and value objects.