value_semantics 3.0.1 → 3.4.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: 3f845f722bb8928d4f16aa9ea89942c69ea5ad3cb42d87dfaa4b7969c9cb88a2
4
- data.tar.gz: dc6a772c529849b5e4928ae5f50b3d8cdc38a5e9700a32a2e8920115eaa2e93a
3
+ metadata.gz: e86c0e4467ff36d89870545718de723b0a0b62ff21dc50d30a3f6e1c0766c4c1
4
+ data.tar.gz: '011313548dfe90ee7b686bc91338b5e0f403ce2dd26f98fd607fdf16ef2c5ce7'
5
5
  SHA512:
6
- metadata.gz: 8f3303f40479363e698b7df06b2e0284813cae0080cd0bdb9e27508d93f1baedadc35116f7c6058c789c3cb1de8bd8ae880aadfdb7c719f54edc01735b6ea87a
7
- data.tar.gz: 3c4a07fab7ddab35fb127d29aa9c0eb17ef396c68f6d2e9a784218f6af7b356014280ecdb399a0f77a0575f4a9ceed0314676da99d29bf654ded719f72ed55c1
6
+ metadata.gz: ecd428749dd01e806df9bbd4a361084f1b066298d464fea3a04ff7f66214ba224b99b93c7902f513c1c4f89fc52690779f4a14bb2c7e78106a212456a8f2c14d
7
+ data.tar.gz: 86c31d6565594493891581dde7feac4f9a9a5c1ff39b86874fd1be74d6bf3827b9a3311369dcaf9a76d9c41ca03be9abc464736ac86d09d316125e9472534554
@@ -0,0 +1,44 @@
1
+ # Changelog
2
+
3
+ Notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [3.4.0] - 2020-08-01
9
+ ### Added
10
+ - Value objects can be instantiated from any object that responds to `#to_h`.
11
+ Previously attributes were required to be given as a `Hash`.
12
+
13
+ - Added monkey patching for super-convenient attribute definitions. This is
14
+ **not** available by default, and needs to be explicitly enabled with
15
+ `ValueSemantics.monkey_patch!` or `require 'value_semantics/monkey_patched'`.
16
+
17
+ ### Changed
18
+ - Improved exception messages for easier development experience
19
+
20
+ - Raises `ValueSemantics::InvalidValue` instead of `ArgumentError` when
21
+ attempting to initialize with an invalid value. `ValueSemantics::InvalidValue`
22
+ is a subclass of `ArgumentError`, so this change should be backward
23
+ compatible.
24
+
25
+ ## [3.3.0] - 2020-07-17
26
+ ### Added
27
+ - Added support for pattern matching in Ruby 2.7
28
+
29
+ ## [3.2.1] - 2020-07-11
30
+ ### Fixed
31
+ - Fix warnings new to Ruby 2.7 about keyword arguments
32
+
33
+ ## [3.2.0] - 2019-09-30
34
+ ### Added
35
+ - `ValueSemantics::Struct`, a convenience for creating a new class and mixing
36
+ in ValueSemantics in a single step.
37
+
38
+ ## [3.1.0] - 2019-06-30
39
+ ### Added
40
+ - Built-in PP support for value classes
41
+
42
+ ## [3.0.0] - 2019-01-27
43
+
44
+ First public release
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  [![Gem Version](https://badge.fury.io/rb/value_semantics.svg)](https://badge.fury.io/rb/value_semantics)
2
2
  [![Build Status](https://travis-ci.org/tomdalling/value_semantics.svg?branch=master)](https://travis-ci.org/tomdalling/value_semantics)
3
- ![Mutation Coverage](https://img.shields.io/badge/mutation%20coverage-100%25-brightgreen.svg)
3
+ ![Mutation Coverage](https://img.shields.io/badge/mutation%20coverage-to%20the%20MAX-brightgreen.svg)
4
4
 
5
5
  ValueSemantics
6
6
  ==============
@@ -15,6 +15,16 @@ These are intended for internal use, as opposed to validating user input like Ac
15
15
  Invalid or missing attributes cause an exception for developers,
16
16
  not an error message intended for application users.
17
17
 
18
+ See:
19
+
20
+ - The [announcement blog post][blog post] for some of the rationale behind the gem
21
+ - [RubyTapas episode #584][rubytapas] for an example usage scenario
22
+ - Some [discussion on Reddit][reddit]
23
+
24
+ [blog post]: https://www.rubypigeon.com/posts/value-semantics-gem-for-making-value-classes/
25
+ [rubytapas]: https://www.rubytapas.com/2019/07/09/from-hash-to-value-object/
26
+ [reddit]: https://www.reddit.com/r/ruby/comments/akz4fs/valuesemanticsa_gem_for_making_value_classes/
27
+
18
28
 
19
29
  Defining and Creating Value Objects
20
30
  -----------------------------------
@@ -45,9 +55,13 @@ Person.new(birthday: nil)
45
55
  #=> #<Person name="Anon Emous" birthday=nil>
46
56
  ```
47
57
 
48
- The curly bracket syntax used with `ValueSemantics.for_attributes` is, unfortunately,
49
- mandatory due to Ruby's precedence rules.
50
- The `do`/`end` syntax will not work unless you surround the whole thing with parenthesis.
58
+ Value objects are typically initialized with keyword arguments or a `Hash`, but
59
+ will accept any object that responds to `#to_h`.
60
+
61
+ The curly bracket syntax used with `ValueSemantics.for_attributes` is,
62
+ unfortunately, mandatory due to Ruby's precedence rules. For a shorter
63
+ alternative method that works better with `do`/`end`, see [Convenience (Monkey
64
+ Patch)](#convenience-monkey-patch) below.
51
65
 
52
66
 
53
67
  Using Value Objects
@@ -66,38 +80,70 @@ end
66
80
  tom = Person.new(name: 'Tom')
67
81
 
68
82
 
69
- #
70
83
  # Read-only attributes
71
- #
72
84
  tom.name #=> "Tom"
73
85
  tom.age #=> 31
74
86
 
75
87
 
76
- #
77
88
  # Convert to Hash
78
- #
79
89
  tom.to_h #=> { :name => "Tom", :age => 31 }
80
90
 
81
91
 
82
- #
83
92
  # Non-destructive updates
84
- #
85
93
  old_tom = tom.with(age: 99)
86
-
87
94
  old_tom #=> #<Person name="Tom" age=99>
88
95
  tom #=> #<Person name="Tom" age=31> (unchanged)
89
96
 
90
97
 
91
- #
92
98
  # Equality
93
- #
94
99
  other_tom = Person.new(name: 'Tom', age: 31)
95
-
96
100
  tom == other_tom #=> true
97
101
  tom.eql?(other_tom) #=> true
98
102
  tom.hash == other_tom.hash #=> true
103
+
104
+
105
+ # Ruby 2.7+ pattern matching
106
+ case tom
107
+ in name: "Tom", age:
108
+ puts age # outputs: 31
109
+ end
110
+ ```
111
+
112
+
113
+ Convenience (Monkey Patch)
114
+ --------------------------
115
+
116
+ There is a shorter way to define value attributes:
117
+
118
+ ```ruby
119
+ class Person
120
+ value_semantics do
121
+ name String
122
+ age Integer
123
+ end
124
+ end
99
125
  ```
100
126
 
127
+ **This is disabled by default**, to avoid polluting every class with an extra
128
+ class method.
129
+
130
+ This convenience method can be enabled in two ways:
131
+
132
+ 1. Add a `require:` option to your `Gemfile` like this:
133
+
134
+ ```ruby
135
+ gem 'value_semantics', '~> 3.3', require: 'value_semantics/monkey_patched'
136
+ ```
137
+
138
+ 2. Alternatively, you can call `ValueSemantics.monkey_patch!` somewhere early
139
+ in the boot sequence of your code -- at the top of your script, for example,
140
+ or `config/boot.rb` if it's a Rails project.
141
+
142
+ ```ruby
143
+ require 'value_semantics'
144
+ ValueSemantics.monkey_patch!
145
+ ```
146
+
101
147
 
102
148
  Defaults
103
149
  --------
@@ -145,13 +191,13 @@ end
145
191
 
146
192
  Person.new(name: 'Tom', ...) # works
147
193
  Person.new(name: 5, ...)
148
- #=> ArgumentError:
149
- #=> Value for attribute 'name' is not valid: 5
194
+ #=> ValueSemantics::InvalidValue:
195
+ #=> Attribute `Person#name` is invalid: 5
150
196
 
151
197
  Person.new(birthday: "1970-01-01", ...) # works
152
198
  Person.new(birthday: "hello", ...)
153
- #=> ArgumentError:
154
- #=> Value for attribute 'birthday' is not valid: "hello"
199
+ #=> ValueSemantics::InvalidValue:
200
+ #=> Attribute 'Person#birthday' is invalid: "hello"
155
201
  ```
156
202
 
157
203
 
@@ -206,8 +252,8 @@ end
206
252
 
207
253
  Person.new(age: 9) # works
208
254
  Person.new(age: 8)
209
- #=> ArgumentError:
210
- #=> Value for attribute 'age' is not valid: 8
255
+ #=> ValueSemantics::InvalidValue:
256
+ #=> Attribute 'Person#age' is invalid: 8
211
257
  ```
212
258
 
213
259
  Default attribute values also pass through validation.
@@ -219,46 +265,48 @@ Coercion
219
265
  Coercion allows non-standard or "convenience" values to be converted into
220
266
  proper, valid values, where possible.
221
267
 
222
- For example, an object with an `IPAddr` attribute may allow string values,
223
- which are then coerced into `IPAddr` objects.
268
+ For example, an object with an `Pathname` attribute may allow string values,
269
+ which are then coerced into `Pathname` objects.
224
270
 
225
271
  Using the option `coerce: true`,
226
272
  coercion happens through a custom class method called `coerce_#{attr}`,
227
273
  which takes the raw value as an argument, and returns the coerced value.
228
274
 
229
275
  ```ruby
230
- class Server
276
+ require 'pathname'
277
+
278
+ class Document
231
279
  include ValueSemantics.for_attributes {
232
- address IPAddr, coerce: true
280
+ path Pathname, coerce: true
233
281
  }
234
282
 
235
- def self.coerce_address(value)
283
+ def self.coerce_path(value)
236
284
  if value.is_a?(String)
237
- IPAddr.new(value)
285
+ Pathname.new(value)
238
286
  else
239
287
  value
240
288
  end
241
289
  end
242
290
  end
243
291
 
244
- Server.new(address: '127.0.0.1')
245
- #=> #<Server address=#<IPAddr: IPv4:127.0.0.1/255.255.255.255>>
292
+ Document.new(path: '~/Documents/whatever.doc')
293
+ #=> #<Document path=#<Pathname:~/Documents/whatever.doc>>
246
294
 
247
- Server.new(address: IPAddr.new('127.0.0.1'))
248
- #=> #<Server address=#<IPAddr: IPv4:127.0.0.1/255.255.255.255>>
295
+ Document.new(path: Pathname.new('~/Documents/whatever.doc'))
296
+ #=> #<Document path=#<Pathname:~/Documents/whatever.doc>>
249
297
 
250
- Server.new(address: 42)
251
- #=> ArgumentError:
252
- #=> Value for attribute 'address' is not valid: 42
298
+ Document.new(path: 42)
299
+ #=> ValueSemantics::InvalidValue:
300
+ #=> Attribute 'Document#path' is invalid: 42
253
301
  ```
254
302
 
255
303
  You can also use any callable object as a coercer.
256
304
  That means, you could use a lambda:
257
305
 
258
306
  ```ruby
259
- class Server
307
+ class Document
260
308
  include ValueSemantics.for_attributes {
261
- address IPAddr, coerce: ->(value) { IPAddr.new(value) }
309
+ path Pathname, coerce: ->(value) { Pathname.new(value) }
262
310
  }
263
311
  end
264
312
  ```
@@ -266,25 +314,25 @@ end
266
314
  Or a custom class:
267
315
 
268
316
  ```ruby
269
- class MyAddressCoercer
317
+ class MyPathCoercer
270
318
  def call(value)
271
- IPAddr.new(value)
319
+ Pathname.new(value)
272
320
  end
273
321
  end
274
322
 
275
- class Server
323
+ class Document
276
324
  include ValueSemantics.for_attributes {
277
- address IPAddr, coerce: MyAddressCoercer.new
325
+ path Pathname, coerce: MyPathCoercer.new
278
326
  }
279
327
  end
280
328
  ```
281
329
 
282
- Or reuse an existing class method:
330
+ Or reuse an existing method:
283
331
 
284
332
  ```ruby
285
- class Server
333
+ class Document
286
334
  include ValueSemantics.for_attributes {
287
- address IPAddr, coerce: IPAddr.method(:new)
335
+ path Pathname, coerce: Pathname.method(:new)
288
336
  }
289
337
  end
290
338
  ```
@@ -296,7 +344,22 @@ Another option is to raise an error within the coercion method.
296
344
 
297
345
  Default attribute values also pass through coercion.
298
346
  For example, the default value could be a string,
299
- which would then be coerced into an `IPAddr` object.
347
+ which would then be coerced into an `Pathname` object.
348
+
349
+
350
+ ## ValueSemantics::Struct
351
+
352
+ This is a convenience for making a new class and including ValueSemantics in
353
+ one step, similar to how `Struct` works from the Ruby standard library. For
354
+ example:
355
+
356
+ ```ruby
357
+ Cat = ValueSemantics::Struct.new do
358
+ name String, default: "Mittens"
359
+ end
360
+
361
+ Cat.new.name #=> "Mittens"
362
+ ```
300
363
 
301
364
 
302
365
  ## Installation
@@ -321,7 +384,15 @@ Or install it yourself as:
321
384
  Bug reports and pull requests are welcome on GitHub at:
322
385
  https://github.com/tomdalling/value_semantics
323
386
 
324
- Keep in mind that this gem aims to be as close to 100% backwards compatible as possible.
387
+ Keep in mind that this gem aims to be as close to 100% backwards compatible as
388
+ possible.
389
+
390
+ I'm happy to accept PRs that:
391
+
392
+ - Improve error messages for a better developer experience, especially those
393
+ that support a TDD workflow.
394
+ - Add new, helpful validators
395
+ - Implement automatic freezing of value objects (must be opt-in)
325
396
 
326
397
  ## License
327
398
 
@@ -3,6 +3,7 @@ module ValueSemantics
3
3
  class UnrecognizedAttributes < Error; end
4
4
  class NoDefaultValue < Error; end
5
5
  class MissingAttributes < Error; end
6
+ class InvalidValue < ArgumentError; end
6
7
 
7
8
  NOT_SPECIFIED = Object.new.freeze
8
9
 
@@ -43,6 +44,39 @@ module ValueSemantics
43
44
  end
44
45
  end
45
46
 
47
+ #
48
+ # Makes the `.value_semantics` convenience method available to all classes
49
+ #
50
+ # `.value_semantics` is a shortcut for `include ValueSemantics.for_attributes`.
51
+ # Instead of:
52
+ #
53
+ # class Person
54
+ # include ValueSemantics.for_attributes {
55
+ # name String
56
+ # }
57
+ # end
58
+ #
59
+ # You can just write:
60
+ #
61
+ # class Person
62
+ # value_semantics do
63
+ # name String
64
+ # end
65
+ # end
66
+ #
67
+ # Alternatively, you can `require 'value_semantics/monkey_patched'`, which
68
+ # will call this method automatically.
69
+ #
70
+ def self.monkey_patch!
71
+ Class.class_eval do
72
+ # @!visibility private
73
+ def value_semantics(&block)
74
+ include ValueSemantics.for_attributes(&block)
75
+ end
76
+ private :value_semantics
77
+ end
78
+ end
79
+
46
80
  #
47
81
  # All the class methods available on ValueSemantics classes
48
82
  #
@@ -55,6 +89,11 @@ module ValueSemantics
55
89
  # was included into this class.
56
90
  #
57
91
  def value_semantics
92
+ if block_given?
93
+ # caller is trying to use the monkey-patched Class method
94
+ raise "`#{self}` has already included ValueSemantics"
95
+ end
96
+
58
97
  self::VALUE_SEMANTICS_RECIPE__
59
98
  end
60
99
  end
@@ -64,15 +103,31 @@ module ValueSemantics
64
103
  #
65
104
  module InstanceMethods
66
105
  #
67
- # Creates a value object based on a Hash of attributes
106
+ # Creates a value object based on a hash of attributes
107
+ #
108
+ # @param attributes [#to_h] A hash of attribute values by name. Typically a
109
+ # `Hash`, but can be any object that responds to `#to_h`.
68
110
  #
69
- # @param given_attrs [Hash] a hash of attributes, with symbols for keys
70
- # @raise [UnrecognizedAttributes] if given_attrs contains keys that are not attributes
71
- # @raise [MissingAttributes] if given_attrs is missing any attributes that do not have defaults
72
- # @raise [ArgumentError] if any attribute values do no pass their validators
111
+ # @raise [UnrecognizedAttributes] if given_attrs contains keys that are not
112
+ # attributes
113
+ # @raise [MissingAttributes] if given_attrs is missing any attributes that
114
+ # do not have defaults
115
+ # @raise [InvalidValue] if any attribute values do no pass their validators
116
+ # @raise [TypeError] if the argument does not respond to `#to_h`
73
117
  #
74
- def initialize(given_attrs = {})
75
- remaining_attrs = given_attrs.dup
118
+ def initialize(attributes = nil)
119
+ attributes_hash =
120
+ if attributes.respond_to?(:to_h)
121
+ attributes.to_h
122
+ else
123
+ raise TypeError, <<-END_MESSAGE.strip.gsub(/\s+/, ' ')
124
+ Can not initialize a `#{self.class}` with a `#{attributes.class}`
125
+ object. This argument is typically a `Hash` of attributes, but can
126
+ be any object that responds to `#to_h`.
127
+ END_MESSAGE
128
+ end
129
+
130
+ remaining_attrs = attributes_hash.dup
76
131
 
77
132
  self.class.value_semantics.attributes.each do |attr|
78
133
  key, value = attr.determine_from!(remaining_attrs, self.class)
@@ -81,8 +136,14 @@ module ValueSemantics
81
136
  end
82
137
 
83
138
  unless remaining_attrs.empty?
84
- unrecognised = remaining_attrs.keys.map(&:inspect).join(', ')
85
- raise UnrecognizedAttributes, "Unrecognized attributes: #{unrecognised}"
139
+ raise(
140
+ UnrecognizedAttributes,
141
+ "`#{self.class}` does not define attributes: " +
142
+ remaining_attrs
143
+ .keys
144
+ .map { |k| '`' + k.inspect + '`' }
145
+ .join(', ')
146
+ )
86
147
  end
87
148
  end
88
149
 
@@ -139,6 +200,20 @@ module ValueSemantics
139
200
 
140
201
  "#<#{self.class} #{attrs}>"
141
202
  end
203
+
204
+ def pretty_print(pp)
205
+ pp.object_group(self) do
206
+ to_h.each do |attr, value|
207
+ pp.breakable
208
+ pp.text("#{attr}=")
209
+ pp.pp(value)
210
+ end
211
+ end
212
+ end
213
+
214
+ def deconstruct_keys(_)
215
+ to_h
216
+ end
142
217
  end
143
218
 
144
219
  #
@@ -169,7 +244,7 @@ module ValueSemantics
169
244
  coerce: nil)
170
245
  generator = begin
171
246
  if default_generator && !default.equal?(NOT_SPECIFIED)
172
- raise ArgumentError, "Attribute '#{name}' can not have both a :default and a :default_generator"
247
+ raise ArgumentError, "Attribute `#{name}` can not have both a `:default` and a `:default_generator`"
173
248
  elsif default_generator
174
249
  default_generator
175
250
  elsif !default.equal?(NOT_SPECIFIED)
@@ -190,7 +265,7 @@ module ValueSemantics
190
265
  def determine_from!(attr_hash, klass)
191
266
  raw_value = attr_hash.fetch(name) do
192
267
  if default_generator.equal?(NO_DEFAULT_GENERATOR)
193
- raise MissingAttributes, "Value missing for attribute '#{name}'"
268
+ raise MissingAttributes, "Attribute `#{klass}\##{name}` has no value"
194
269
  else
195
270
  default_generator.call
196
271
  end
@@ -201,7 +276,7 @@ module ValueSemantics
201
276
  if validate?(coerced_value)
202
277
  [name, coerced_value]
203
278
  else
204
- raise ArgumentError, "Value for attribute '#{name}' is not valid: #{coerced_value.inspect}"
279
+ raise InvalidValue, "Attribute `#{klass}\##{name}` is invalid: #{coerced_value.inspect}"
205
280
  end
206
281
  end
207
282
 
@@ -284,13 +359,13 @@ module ValueSemantics
284
359
  ArrayOf.new(element_validator)
285
360
  end
286
361
 
287
- def def_attr(*args)
288
- __attributes << Attribute.define(*args)
362
+ def def_attr(*args, **kwargs)
363
+ __attributes << Attribute.define(*args, **kwargs)
289
364
  end
290
365
 
291
- def method_missing(name, *args)
366
+ def method_missing(name, *args, **kwargs)
292
367
  if respond_to_missing?(name)
293
- def_attr(name, *args)
368
+ def_attr(name, *args, **kwargs)
294
369
  else
295
370
  super
296
371
  end
@@ -356,4 +431,22 @@ module ValueSemantics
356
431
  end
357
432
  end
358
433
 
434
+ #
435
+ # ValueSemantics equivalent of the Struct class from the Ruby standard
436
+ # library
437
+ #
438
+ class Struct
439
+ #
440
+ # Creates a new Class with ValueSemantics mixed in
441
+ #
442
+ # @yield a block containing ValueSemantics DSL
443
+ # @return [Class] the newly created class
444
+ #
445
+ def self.new(&block)
446
+ klass = Class.new
447
+ klass.include(ValueSemantics.for_attributes(&block))
448
+ klass
449
+ end
450
+ end
451
+
359
452
  end
@@ -0,0 +1,3 @@
1
+ require 'value_semantics'
2
+
3
+ ValueSemantics.monkey_patch!
@@ -1,3 +1,3 @@
1
1
  module ValueSemantics
2
- VERSION = "3.0.1"
2
+ VERSION = "3.4.0"
3
3
  end
metadata CHANGED
@@ -1,27 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: value_semantics
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.1
4
+ version: 3.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom Dalling
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2019-01-27 00:00:00.000000000 Z
11
+ date: 2020-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.15'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.15'
27
27
  - !ruby/object:Gem::Dependency
@@ -30,14 +30,28 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 3.7.0
33
+ version: '3.7'
34
34
  type: :development
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: 3.7.0
40
+ version: '3.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: super_diff
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: mutant-rspec
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -103,22 +117,20 @@ executables: []
103
117
  extensions: []
104
118
  extra_rdoc_files: []
105
119
  files:
106
- - ".gitignore"
107
- - ".rspec"
108
- - ".travis.yml"
109
- - Gemfile
120
+ - CHANGELOG.md
110
121
  - LICENSE.txt
111
122
  - README.md
112
- - bin/console
113
- - bin/setup
114
- - bin/test
115
123
  - lib/value_semantics.rb
124
+ - lib/value_semantics/monkey_patched.rb
116
125
  - lib/value_semantics/version.rb
117
- - value_semantics.gemspec
118
126
  homepage: https://github.com/tomdalling/value_semantics
119
127
  licenses:
120
128
  - MIT
121
- metadata: {}
129
+ metadata:
130
+ bug_tracker_uri: https://github.com/tomdalling/value_semantics/issues
131
+ changelog_uri: https://github.com/tomdalling/value_semantics/blob/master/CHANGELOG.md
132
+ documentation_uri: https://github.com/tomdalling/value_semantics/blob/v3.4.0/README.md
133
+ source_code_uri: https://github.com/tomdalling/value_semantics
122
134
  post_install_message:
123
135
  rdoc_options: []
124
136
  require_paths:
@@ -134,8 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
134
146
  - !ruby/object:Gem::Version
135
147
  version: '0'
136
148
  requirements: []
137
- rubyforge_project:
138
- rubygems_version: 2.7.7
149
+ rubygems_version: 3.0.8
139
150
  signing_key:
140
151
  specification_version: 4
141
152
  summary: Makes value classes, with lightweight validation and coercion.
data/.gitignore DELETED
@@ -1,12 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
10
-
11
- # rspec failure tracking
12
- .rspec_status
data/.rspec DELETED
@@ -1,2 +0,0 @@
1
- --format documentation
2
- --color
@@ -1,24 +0,0 @@
1
- language: ruby
2
- script: bin/test
3
-
4
- # test old Ruby versions WITHOUT mutation testing
5
- rvm:
6
- - 2.3.8
7
- - 2.4.5
8
- - 2.5.3
9
- env: MUTATION_TEST=false
10
-
11
- # test the latest Ruby version WITH mutation testing
12
- matrix:
13
- include:
14
- - rvm: 2.6.0
15
- env: MUTATION_TEST=true
16
-
17
- # deploy gem on tagged commits, on the latest Ruby version only
18
- deploy:
19
- provider: rubygems
20
- on:
21
- tags: true
22
- env: MUTATION_TEST=true
23
- api_key:
24
- secure: nL74QuUczEpA0qbhSBN2zjGdviWgKB3wR6vFvwervv1MZNWmwOQUYe99Oq9kPeyc8/x2MR/H6PQm5qbrk/WAfRede01WxlZ/EBUW+9CYGrxcBsGONx9IULO8A0I8/yN/YJHW2vjo3dfR66EwVsXTVWq8U63PRRcwJIyTqnIiUm2sxauMQoPRBbXG+pD9v/EJSn3ugpdtxp0lVYDn8LDKk5Ho4/wbpY4ML11XUJa9mz9CyR/GsAzdy5FTXaDMOwuWOVEx9cab7m4qPOBhmlJY4TrmooFpxTxRwChcvByjq1IboEd2M3RT5on7Q/xDTlHSOuT0OS8mnS2AocGT4a1gC+W/xOlghgEcN+xs2V5mfucR6+iUYlCy32uz1w3ey7T2X5xN4ubut09r1xLi7eu1NisAoAc+GOJ4TIxQNqkeRhY4X/fs8j7SMfOEMDr6pPxSLKZxgSvExt+IbdcZD/uQ7rTBQkadYCbc9MX5dHazBievmar3ZsFffbIf+n13FVDXsaPgRt7DlFM5dqGrEwVwt1jFRhdFuDCjkj4QWOLn7E1uY3XqgrqGvgUBlF8Znwc6qicW8zxV4SIWhqIzCOH6L9WIZGLHNq0remoCd9sq9Ter9av3jL+6UmZRRAr+JceeZfZmsYIXKomECzleM9FXMx7FXlpjJKOlf3JnrfeCTwI=
data/Gemfile DELETED
@@ -1,6 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
-
5
- # Specify your gem's dependencies in the gemspec
6
- gemspec
@@ -1,14 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "bundler/setup"
4
- require "value_semantics"
5
-
6
- # You can add fixtures and/or initialization code here to make experimenting
7
- # with your gem easier. You can also use a different console, if you like.
8
-
9
- # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
12
-
13
- require "irb"
14
- IRB.start(__FILE__)
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
data/bin/test DELETED
@@ -1,19 +0,0 @@
1
- #!/bin/bash
2
- set -ue
3
-
4
- MUTANT_PATTERN=${1:-ValueSemantics*}
5
-
6
- # if $MUTATION_TEST is false, just run RSpec
7
- if [[ "${MUTATION_TEST:-true}" == "false" ]] ; then
8
- bundle exec rspec
9
- else
10
- bundle exec mutant \
11
- --include lib \
12
- --require value_semantics \
13
- --use rspec "$MUTANT_PATTERN" \
14
- # Mutant 0.8.24 introduces new mutations that cause infinite recursion inside
15
- # #method_missing. These --ignore-subject lines prevent that from happening
16
- #--ignore-subject "ValueSemantics::DSL#method_missing" \
17
- #--ignore-subject "ValueSemantics::DSL#respond_to_missing?" \
18
- #--ignore-subject "ValueSemantics::DSL#def_attr" \
19
- fi
@@ -1,34 +0,0 @@
1
- # coding: utf-8
2
- lib = File.expand_path("../lib", __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require "value_semantics/version"
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = "value_semantics"
8
- spec.version = ValueSemantics::VERSION
9
- spec.authors = ["Tom Dalling"]
10
- spec.email = [["tom", "@", "tomdalling.com"].join]
11
-
12
- spec.summary = %q{Makes value classes, with lightweight validation and coercion.}
13
- spec.description = %q{
14
- Generates modules that provide conventional value semantics for a given set of attributes.
15
- The behaviour is similar to an immutable `Struct` class,
16
- plus extensible, lightweight validation and coercion.
17
- }
18
- spec.homepage = "https://github.com/tomdalling/value_semantics"
19
- spec.license = "MIT"
20
-
21
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
22
- f.match(%r{^(test|spec|features)/})
23
- end
24
- spec.bindir = "exe"
25
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
- spec.require_paths = ["lib"]
27
-
28
- spec.add_development_dependency "bundler", "~> 1.15"
29
- spec.add_development_dependency "rspec", "~> 3.7.0"
30
- spec.add_development_dependency "mutant-rspec"
31
- spec.add_development_dependency "yard"
32
- spec.add_development_dependency "byebug"
33
- spec.add_development_dependency "gem-release"
34
- end