u-attributes 2.0.0 → 2.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
  SHA256:
3
- metadata.gz: ac1586077d89b91d2e851ce3f59819d0c474bc809fe9d64eff5d5627bd78a967
4
- data.tar.gz: 7246180e49313f3b9038e3bfdd858356dce65a80c7c94aa6173a15afbea9ab90
3
+ metadata.gz: 2f90839cb67a0501e1ef44a5f06a324103063c87e929549377f885504bda5d8a
4
+ data.tar.gz: fd77058f7e514644a8363d460ad33ad76885648e32b5d9b02e22e592ffbf3ffe
5
5
  SHA512:
6
- metadata.gz: 1ce2864d28e95139a1fc3e3a08ca08ea2c771eec936b5ff21567fb3f0bae792bd406a4b905c7c5346b7f53de3972a6f01ce4629e50e786935186ae6f93614956
7
- data.tar.gz: 28076be013bc9038497297186994d23df2b39b16fb46b2a6c82d59520f3f5de84a3c1f1f89196a6dc073677f0fa339a707e1a2cb10591e9aabfc19dab80036b6
6
+ metadata.gz: 27401e952c4f5611ec5443df546d4c2c67f0af7aa40606a8e0f128ae37dc167747d0ba4c94008d993d65c886d4e1d66c6ef3472556ecfa353903779f51b4fe23
7
+ data.tar.gz: d05025bd7442681e14fa8f970fb166898ec9af4a344b8912e10c57f559830833bebe05ccacea85332ef9c88437adabc60152967f8b0b38d01f38f1cc6a7c1257
@@ -1,7 +1,5 @@
1
1
  language: ruby
2
2
 
3
- sudo: false
4
-
5
3
  rvm:
6
4
  - 2.2.2
7
5
  - 2.3.0
@@ -9,6 +7,7 @@ rvm:
9
7
  - 2.5.0
10
8
  - 2.6.0
11
9
  - 2.7.0
10
+ - truffleruby-head
12
11
 
13
12
  cache: bundler
14
13
 
data/Gemfile CHANGED
@@ -1,5 +1,7 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ gem 'u-case', '~> 4.0'
4
+
3
5
  activemodel_version = ENV.fetch('ACTIVEMODEL_VERSION', '6.1')
4
6
 
5
7
  activemodel = case activemodel_version
@@ -18,9 +20,16 @@ if activemodel_version < '6.1'
18
20
  gem 'activesupport', activemodel, require: false
19
21
  end
20
22
 
23
+ simplecov_version =
24
+ case RUBY_VERSION
25
+ when /\A2.[23]/ then '~> 0.17.1'
26
+ when /\A2.4/ then '~> 0.18.5'
27
+ else '~> 0.19'
28
+ end
29
+
21
30
  group :test do
22
31
  gem 'minitest', activemodel_version < '4.1' ? '~> 4.2' : '~> 5.0'
23
- gem 'simplecov', require: false
32
+ gem 'simplecov', simplecov_version, require: false
24
33
  end
25
34
 
26
35
  # Specify your gem's dependencies in u-attributes.gemspec
data/README.md CHANGED
@@ -1,21 +1,41 @@
1
- ![Ruby](https://img.shields.io/badge/ruby-2.2+-ruby.svg?colorA=99004d&colorB=cc0066)
2
- [![Gem](https://img.shields.io/gem/v/u-attributes.svg?style=flat-square)](https://rubygems.org/gems/u-attributes)
3
- [![Build Status](https://travis-ci.com/serradura/u-attributes.svg?branch=master)](https://travis-ci.com/serradura/u-attributes)
4
- [![Maintainability](https://api.codeclimate.com/v1/badges/b562e6b877a9edf4dbf6/maintainability)](https://codeclimate.com/github/serradura/u-attributes/maintainability)
5
- [![Test Coverage](https://api.codeclimate.com/v1/badges/b562e6b877a9edf4dbf6/test_coverage)](https://codeclimate.com/github/serradura/u-attributes/test_coverage)
1
+ <p align="center">
2
+ <img src="./assets/u-attributes_logo_v1.png" alt='Create "immutable" objects. No setters, just getters!'>
6
3
 
7
- μ-attributes (Micro::Attributes) <!-- omit in toc -->
8
- ================================
4
+ <p align="center"><i>Create "immutable" objects. No setters, just getters!</i></p>
5
+ <br>
6
+ </p>
9
7
 
10
- This gem allows defining read-only attributes, that is, your objects will have only getters to access their attributes data.
8
+ <p align="center">
9
+ <img src="https://img.shields.io/badge/ruby-2.2+-ruby.svg?colorA=99004d&colorB=cc0066" alt="Ruby">
11
10
 
12
- ## Table of contents <!-- omit in toc -->
13
- - [Required Ruby version](#required-ruby-version)
11
+ <a href="https://rubygems.org/gems/u-attributes">
12
+ <img alt="Gem" src="https://img.shields.io/gem/v/u-attributes.svg?style=flat-square">
13
+ </a>
14
+
15
+ <a href="https://travis-ci.com/serradura/u-attributes">
16
+ <img alt="Build Status" src="https://travis-ci.com/serradura/u-attributes.svg?branch=main">
17
+ </a>
18
+
19
+ <a href="https://codeclimate.com/github/serradura/u-attributes/maintainability">
20
+ <img alt="Maintainability" src="https://api.codeclimate.com/v1/badges/b562e6b877a9edf4dbf6/maintainability">
21
+ </a>
22
+
23
+ <a href="https://codeclimate.com/github/serradura/u-attributes/test_coverage">
24
+ <img alt="Test Coverage" src="https://api.codeclimate.com/v1/badges/b562e6b877a9edf4dbf6/test_coverage">
25
+ </a>
26
+ </p>
27
+
28
+ This gem allows you to define "immutable" objects, and your objects will have only getters and no setters.
29
+ So, if you change [[1](#with_attribute)] [[2](#with_attributes)] some object attribute, you will have a new object instance. That is, you transform the object instead of modifying it.
30
+
31
+ # Table of contents <!-- omit in toc -->
14
32
  - [Installation](#installation)
15
33
  - [Compatibility](#compatibility)
16
34
  - [Usage](#usage)
17
35
  - [How to define attributes?](#how-to-define-attributes)
18
36
  - [`Micro::Attributes#attributes=`](#microattributesattributes)
37
+ - [How to extract attributes from an object or hash?](#how-to-extract-attributes-from-an-object-or-hash)
38
+ - [Is it possible to define an attribute as required?](#is-it-possible-to-define-an-attribute-as-required)
19
39
  - [`Micro::Attributes#attribute`](#microattributesattribute)
20
40
  - [`Micro::Attributes#attribute!`](#microattributesattribute-1)
21
41
  - [How to define multiple attributes?](#how-to-define-multiple-attributes)
@@ -23,26 +43,36 @@ This gem allows defining read-only attributes, that is, your objects will have o
23
43
  - [`#with_attribute()`](#with_attribute)
24
44
  - [`#with_attributes()`](#with_attributes)
25
45
  - [Defining default values to the attributes](#defining-default-values-to-the-attributes)
26
- - [Strict initializer](#strict-initializer)
46
+ - [The strict initializer](#the-strict-initializer)
27
47
  - [Is it possible to inherit the attributes?](#is-it-possible-to-inherit-the-attributes)
28
- - [.attribute!()](#attribute)
48
+ - [`.attribute!()`](#attribute)
29
49
  - [How to query the attributes?](#how-to-query-the-attributes)
50
+ - [`.attributes`](#attributes)
51
+ - [`.attribute?()`](#attribute-1)
52
+ - [`#attribute?()`](#attribute-2)
53
+ - [`#attributes()`](#attributes-1)
54
+ - [`#attributes(keys_as:)`](#attributeskeys_as)
55
+ - [`#attributes(*names)`](#attributesnames)
56
+ - [`#attributes([names])`](#attributesnames-1)
57
+ - [`#attributes(with:, without)`](#attributeswith-without)
58
+ - [`#defined_attributes`](#defined_attributes)
30
59
  - [Built-in extensions](#built-in-extensions)
31
- - [ActiveModel::Validations extension](#activemodelvalidations-extension)
32
- - [Attribute options](#attribute-options)
33
- - [Diff extension](#diff-extension)
34
- - [Initialize extension](#initialize-extension)
35
- - [Strict initialize mode](#strict-initialize-mode)
60
+ - [Picking specific features](#picking-specific-features)
61
+ - [`Micro::Attributes.with`](#microattributeswith)
62
+ - [`Micro::Attributes.without`](#microattributeswithout)
63
+ - [Picking all the features](#picking-all-the-features)
64
+ - [Extensions](#extensions)
65
+ - [`ActiveModel::Validation` extension](#activemodelvalidation-extension)
66
+ - [`.attribute()` options](#attribute-options)
67
+ - [Diff extension](#diff-extension)
68
+ - [Initialize extension](#initialize-extension)
69
+ - [Strict mode](#strict-mode)
36
70
  - [Development](#development)
37
71
  - [Contributing](#contributing)
38
72
  - [License](#license)
39
73
  - [Code of Conduct](#code-of-conduct)
40
74
 
41
- ## Required Ruby version
42
-
43
- > \>= 2.2.0
44
-
45
- ## Installation
75
+ # Installation
46
76
 
47
77
  Add this line to your application's Gemfile and `bundle install`:
48
78
 
@@ -50,25 +80,29 @@ Add this line to your application's Gemfile and `bundle install`:
50
80
  gem 'u-attributes'
51
81
  ```
52
82
 
53
- ## Compatibility
83
+ # Compatibility
54
84
 
55
85
  | u-attributes | branch | ruby | activemodel |
56
86
  | -------------- | ------- | -------- | ------------- |
57
- | 2.0.0 | master | >= 2.2.0 | >= 3.2, < 6.1 |
87
+ | 2.3.0 | main | >= 2.2.0 | >= 3.2, < 6.1 |
58
88
  | 1.2.0 | v1.x | >= 2.2.0 | >= 3.2, < 6.1 |
59
89
 
60
- ## Usage
90
+ > **Note**: The activemodel is an optional dependency, this module [can be enabled](#activemodelvalidation-extension) to validate the attributes.
61
91
 
62
- ### How to define attributes?
92
+ [⬆️ Back to Top](#table-of-contents-)
63
93
 
64
- ```ruby
65
- # By default you must to define the class constructor.
94
+ # Usage
95
+
96
+ ## How to define attributes?
66
97
 
98
+ By default, you must define the class constructor.
99
+
100
+ ```ruby
67
101
  class Person
68
102
  include Micro::Attributes
69
103
 
70
- attribute :name
71
104
  attribute :age
105
+ attribute :name
72
106
 
73
107
  def initialize(name: 'John Doe', age:)
74
108
  @name, @age = name, age
@@ -77,8 +111,8 @@ end
77
111
 
78
112
  person = Person.new(age: 21)
79
113
 
80
- person.name # John Doe
81
114
  person.age # 21
115
+ person.name # John Doe
82
116
 
83
117
  # By design the attributes are always exposed as reader methods (getters).
84
118
  # If you try to call a setter you will see a NoMethodError.
@@ -87,7 +121,9 @@ person.age # 21
87
121
  # NoMethodError (undefined method `name=' for #<Person:0x0000... @name='John Doe', @age=21>)
88
122
  ```
89
123
 
90
- #### `Micro::Attributes#attributes=`
124
+ [⬆️ Back to Top](#table-of-contents-)
125
+
126
+ ### `Micro::Attributes#attributes=`
91
127
 
92
128
  This is a protected method to make easier the assignment in a constructor. e.g.
93
129
 
@@ -95,8 +131,8 @@ This is a protected method to make easier the assignment in a constructor. e.g.
95
131
  class Person
96
132
  include Micro::Attributes
97
133
 
98
- attribute :name, default: 'John Doe'
99
134
  attribute :age
135
+ attribute :name, default: 'John Doe'
100
136
 
101
137
  def initialize(options)
102
138
  self.attributes = options
@@ -105,19 +141,81 @@ end
105
141
 
106
142
  person = Person.new(age: 20)
107
143
 
144
+ person.age # 20
108
145
  person.name # John Doe
146
+ ```
147
+
148
+ #### How to extract attributes from an object or hash?
149
+
150
+ You can extract attributes using the `extract_attributes_from` method, it will try to fetch attributes from the
151
+ object using either the `object[attribute_key]` accessor or the reader method `object.attribute_key`.
152
+
153
+ ```ruby
154
+ class Person
155
+ include Micro::Attributes
156
+
157
+ attribute :age
158
+ attribute :name, default: 'John Doe'
159
+
160
+ def initialize(user:)
161
+ self.attributes = extract_attributes_from(user)
162
+ end
163
+ end
164
+
165
+ # extracting from an object
166
+
167
+ class User
168
+ attr_accessor :age, :name
169
+ end
170
+
171
+ user = User.new
172
+ user.age = 20
173
+
174
+ person = Person.new(user: user)
175
+
109
176
  person.age # 20
177
+ person.name # John Doe
178
+
179
+ # extracting from a hash
180
+
181
+ another_person = Person.new(user: { age: 55, name: 'Julia Not Roberts' })
182
+
183
+ another_person.age # 55
184
+ another_person.name # Julia Not Roberts
185
+ ```
186
+
187
+ #### Is it possible to define an attribute as required?
188
+
189
+ You only need to use the `required: true` option.
190
+
191
+ But to this work, you need to assign the attributes using the [`#attributes=`](#microattributesattributes) method or the extensions: [initialize](#initialize-extension), [activemodel_validations](#activemodelvalidation-extension).
192
+
193
+ ```ruby
194
+ class Person
195
+ include Micro::Attributes
196
+
197
+ attribute :age
198
+ attribute :name, required: true
199
+
200
+ def initialize(attributes)
201
+ self.attributes = attributes
202
+ end
203
+ end
204
+
205
+ Person.new(age: 32) # ArgumentError (missing keyword: :name)
110
206
  ```
111
207
 
112
- #### `Micro::Attributes#attribute`
208
+ [⬆️ Back to Top](#table-of-contents-)
209
+
210
+ ### `Micro::Attributes#attribute`
113
211
 
114
212
  Use this method with a valid attribute name to get its value.
115
213
 
116
214
  ```ruby
117
215
  person = Person.new(age: 20)
118
216
 
119
- person.attribute(:name) # John Doe
120
217
  person.attribute('age') # 20
218
+ person.attribute(:name) # John Doe
121
219
  person.attribute('foo') # nil
122
220
  ```
123
221
 
@@ -129,17 +227,21 @@ person.attribute('age') { |value| puts value } # 20
129
227
  person.attribute('foo') { |value| puts value } # !! Nothing happened, because of the attribute doesn't exist.
130
228
  ```
131
229
 
132
- #### `Micro::Attributes#attribute!`
230
+ [⬆️ Back to Top](#table-of-contents-)
231
+
232
+ ### `Micro::Attributes#attribute!`
133
233
 
134
234
  Works like the `#attribute` method, but it will raise an exception when the attribute doesn't exist.
135
235
 
136
236
  ```ruby
137
- person.attribute!('foo') # NameError (undefined attribute `foo)
237
+ person.attribute!('foo') # NameError (undefined attribute `foo)
138
238
 
139
239
  person.attribute!('foo') { |value| value } # NameError (undefined attribute `foo)
140
240
  ```
141
241
 
142
- ### How to define multiple attributes?
242
+ [⬆️ Back to Top](#table-of-contents-)
243
+
244
+ ## How to define multiple attributes?
143
245
 
144
246
  Use `.attributes` with a list of attribute names.
145
247
 
@@ -162,7 +264,9 @@ person.age # 32
162
264
 
163
265
  > **Note:** This method can't define default values. To do this, use the `#attribute()` method.
164
266
 
165
- ### `Micro::Attributes.with(:initialize)`
267
+ [⬆️ Back to Top](#table-of-contents-)
268
+
269
+ ## `Micro::Attributes.with(:initialize)`
166
270
 
167
271
  Use `Micro::Attributes.with(:initialize)` to define a constructor to assign the attributes. e.g.
168
272
 
@@ -170,47 +274,49 @@ Use `Micro::Attributes.with(:initialize)` to define a constructor to assign the
170
274
  class Person
171
275
  include Micro::Attributes.with(:initialize)
172
276
 
173
- attribute :age
277
+ attribute :age, required: true
174
278
  attribute :name, default: 'John Doe'
175
279
  end
176
280
 
177
281
  person = Person.new(age: 18)
178
282
 
179
- person.name # John Doe
180
283
  person.age # 18
284
+ person.name # John Doe
181
285
  ```
182
286
 
183
287
  This extension enables two methods for your objects.
184
288
  The `#with_attribute()` and `#with_attributes()`.
185
289
 
186
- #### `#with_attribute()`
290
+ ### `#with_attribute()`
187
291
 
188
292
  ```ruby
189
293
  another_person = person.with_attribute(:age, 21)
190
294
 
191
- another_person.name # John Doe
192
295
  another_person.age # 21
296
+ another_person.name # John Doe
193
297
  another_person.equal?(person) # false
194
298
  ```
195
299
 
196
- #### `#with_attributes()`
300
+ ### `#with_attributes()`
197
301
 
198
302
  Use it to assign multiple attributes
199
303
  ```ruby
200
304
  other_person = person.with_attributes(name: 'Serradura', age: 32)
201
305
 
202
- other_person.name # Serradura
203
306
  other_person.age # 32
307
+ other_person.name # Serradura
204
308
  other_person.equal?(person) # false
205
309
  ```
206
310
 
207
311
  If you pass a value different of a Hash, a Kind::Error will be raised.
208
312
 
209
313
  ```ruby
210
- Person.new(1) # Kind::Error (1 must be a Hash)
314
+ Person.new(1) # Kind::Error (1 expected to be a kind of Hash)
211
315
  ```
212
316
 
213
- ### Defining default values to the attributes
317
+ [⬆️ Back to Top](#table-of-contents-)
318
+
319
+ ## Defining default values to the attributes
214
320
 
215
321
  To do this, you only need make use of the `default:` keyword. e.g.
216
322
 
@@ -223,10 +329,9 @@ class Person
223
329
  end
224
330
  ```
225
331
 
226
- There are 3 different strategies to define default values.
332
+ There are two different strategies to define default values.
227
333
  1. Pass a regular object, like in the previous example.
228
334
  2. Pass a `proc`/`lambda`, and if it has an argument you will receive the attribute value to do something before assign it.
229
- 3. Pass a **callable**, that is, a `class`, `module` or `instance` which responds to the `call` method. The behavior will be like the previous item (`proc`/`lambda`).
230
335
 
231
336
  ```ruby
232
337
  class Person
@@ -237,9 +342,13 @@ class Person
237
342
  end
238
343
  ```
239
344
 
240
- ### Strict initializer
345
+ [⬆️ Back to Top](#table-of-contents-)
346
+
347
+ ## The strict initializer
348
+
349
+ Use `.with(initialize: :strict)` to forbids an instantiation without all the attribute keywords.
241
350
 
242
- Use `.with(initialize: :strict)` to forbids an instantiation without all the attribute keywords. e.g.
351
+ In other words, it is equivalent to you define all the attributes using the [`required: true` option](#is-it-possible-to-define-an-attribute-as-required).
243
352
 
244
353
  ```ruby
245
354
  class StrictPerson
@@ -257,13 +366,15 @@ An attribute with a default value can be omitted.
257
366
  ``` ruby
258
367
  person_without_age = StrictPerson.new(age: nil)
259
368
 
260
- person_without_age.name # 'John Doe'
261
369
  person_without_age.age # nil
370
+ person_without_age.name # 'John Doe'
262
371
  ```
263
372
 
264
373
  > **Note:** Except for this validation the `.with(initialize: :strict)` method will works in the same ways of `.with(:initialize)`.
265
374
 
266
- ### Is it possible to inherit the attributes?
375
+ [⬆️ Back to Top](#table-of-contents-)
376
+
377
+ ## Is it possible to inherit the attributes?
267
378
 
268
379
  Yes. e.g.
269
380
 
@@ -286,7 +397,9 @@ instance.respond_to?(:age) # true
286
397
  instance.respond_to?(:foo) # true
287
398
  ```
288
399
 
289
- #### .attribute!()
400
+ [⬆️ Back to Top](#table-of-contents-)
401
+
402
+ ### `.attribute!()`
290
403
 
291
404
  This method allows us to redefine the attributes default data that was defined in the parent class. e.g.
292
405
 
@@ -311,137 +424,217 @@ beta_person.name # 'Beta'
311
424
  beta_person.age # 0
312
425
  ```
313
426
 
314
- ### How to query the attributes?
427
+ [⬆️ Back to Top](#table-of-contents-)
428
+
429
+ ## How to query the attributes?
430
+
431
+ All of the methods that will be explained can be used with any of the built-in extensions.
432
+
433
+ **PS:** We will use the class below for all of the next examples.
315
434
 
316
435
  ```ruby
317
436
  class Person
318
437
  include Micro::Attributes
319
438
 
320
439
  attribute :age
321
- attribute :name, default: 'John Doe'
440
+ attribute :first_name, default: 'John'
441
+ attribute :last_name, default: 'Doe'
322
442
 
323
443
  def initialize(options)
324
444
  self.attributes = options
325
445
  end
446
+
447
+ def name
448
+ "#{first_name} #{last_name}"
449
+ end
326
450
  end
451
+ ```
452
+
453
+ ### `.attributes`
454
+
455
+ Listing all the class attributes.
456
+
457
+ ```ruby
458
+ Person.attributes # ["age", "first_name", "last_name"]
459
+ ```
327
460
 
328
- #---------------#
329
- # .attributes() #
330
- #---------------#
461
+ ### `.attribute?()`
331
462
 
332
- Person.attributes # ['name', 'age']
463
+ Checking the existence of some attribute.
333
464
 
334
- #---------------#
335
- # .attribute?() #
336
- #---------------#
465
+ ```ruby
466
+ Person.attribute?(:first_name) # true
467
+ Person.attribute?('first_name') # true
337
468
 
338
- Person.attribute?(:name) # true
339
- Person.attribute?('name') # true
340
469
  Person.attribute?('foo') # false
341
470
  Person.attribute?(:foo) # false
471
+ ```
342
472
 
343
- # ---
473
+ ### `#attribute?()`
344
474
 
345
- person = Person.new(age: 20)
475
+ Checking the existence of some attribute in an instance.
346
476
 
347
- #---------------#
348
- # #attribute?() #
349
- #---------------#
477
+ ```ruby
478
+ person = Person.new(age: 20)
350
479
 
351
480
  person.attribute?(:name) # true
352
481
  person.attribute?('name') # true
482
+
353
483
  person.attribute?('foo') # false
354
484
  person.attribute?(:foo) # false
485
+ ```
486
+
487
+ ### `#attributes()`
488
+
489
+ Fetching all the attributes with their values.
490
+
491
+ ```ruby
492
+ person1 = Person.new(age: 20)
493
+ person1.attributes # {"age"=>20, "first_name"=>"John", "last_name"=>"Doe"}
494
+
495
+ person2 = Person.new(first_name: 'Rodrigo', last_name: 'Rodrigues')
496
+ person2.attributes # {"age"=>nil, "first_name"=>"Rodrigo", "last_name"=>"Rodrigues"}
497
+ ```
498
+
499
+ #### `#attributes(keys_as:)`
500
+
501
+ Use the `keys_as:` option with `Symbol` or `String` to transform the attributes hash keys.
502
+
503
+ ```ruby
504
+ person1 = Person.new(age: 20)
505
+ person1.attributes(keys_as: Symbol) # {:age=>20, :first_name=>"John", :last_name=>"Doe"}
506
+
507
+ person2 = Person.new(first_name: 'Rodrigo', last_name: 'Rodrigues')
508
+ person2.attributes(keys_as: String) # {"age"=>nil, "first_name"=>"Rodrigo", "last_name"=>"Rodrigues"}
509
+ ```
510
+
511
+ #### `#attributes(*names)`
512
+
513
+ Slices the attributes to include only the given keys (in their types).
514
+
515
+ ```ruby
516
+ person = Person.new(age: 20)
517
+
518
+ person.attributes(:age) # {:age => 20}
519
+ person.attributes(:age, :first_name) # {:age => 20, :first_name => "John"}
520
+ person.attributes('age', 'last_name') # {"age" => 20, "last_name" => "Doe"}
521
+
522
+ person.attributes(:age, 'last_name') # {:age => 20, "last_name" => "Doe"}
523
+
524
+ # You could also use the keys_as: option to ensure the same type for all of the hash keys.
525
+
526
+ person.attributes(:age, 'last_name', keys_as: Symbol) # {:age=>20, :last_name=>"Doe"}
527
+ ```
528
+
529
+ #### `#attributes([names])`
530
+
531
+ As the previous example, this methods accepts a list of keys to slice the attributes.
532
+
533
+ ```ruby
534
+ person = Person.new(age: 20)
535
+
536
+ person.attributes([:age]) # {:age => 20}
537
+ person.attributes([:age, :first_name]) # {:age => 20, :first_name => "John"}
538
+ person.attributes(['age', 'last_name']) # {"age" => 20, "last_name" => "Doe"}
539
+
540
+ person.attributes([:age, 'last_name']) # {:age => 20, "last_name" => "Doe"}
541
+
542
+ # You could also use the keys_as: option to ensure the same type for all of the hash keys.
543
+
544
+ person.attributes([:age, 'last_name'], keys_as: Symbol) # {:age=>20, :last_name=>"Doe"}
545
+ ```
546
+
547
+ #### `#attributes(with:, without)`
548
+
549
+ Use the `with:` option to include any method value of the instance inside of the hash, and,
550
+ you can use the `without:` option to exclude one or more attribute keys from the final hash.
551
+
552
+ ```ruby
553
+ person = Person.new(age: 20)
355
554
 
356
- #---------------#
357
- # #attributes() #
358
- #---------------#
555
+ person.attributes(without: :age) # {"first_name"=>"John", "last_name"=>"Doe"}
556
+ person.attributes(without: [:age, :last_name]) # {"first_name"=>"John"}
359
557
 
360
- person.attributes # {'age'=>20, 'name'=>'John Doe'}
361
- Person.new(name: 'John').attributes # {'age'=>nil, 'name'=>'John'}
558
+ person.attributes(with: [:name], without: [:first_name, :last_name]) # {"age"=>20, "name"=>"John Doe"}
362
559
 
363
- #---------------------#
364
- # #attributes(*names) #
365
- #---------------------#
560
+ # To achieves the same output of the previous example, use the attribute names to slice only them.
366
561
 
367
- # Slices the attributes to include only the given keys.
368
- # Returns a hash containing the given keys (in their types).
562
+ person.attributes(:age, with: [:name]) # {:age=>20, "name"=>"John Doe"}
369
563
 
370
- person.attributes(:age) # {age: 20}
371
- person.attributes(:age, :name) # {age: 20, name: 'John Doe'}
372
- person.attributes('age', 'name') # {'age'=>20, 'name'=>'John Doe'}
564
+ # You could also use the keys_as: option to ensure the same type for all of the hash keys.
565
+
566
+ person.attributes(:age, with: [:name], keys_as: Symbol) # {:age=>20, :name=>"John Doe"}
373
567
  ```
374
568
 
375
- ## Built-in extensions
569
+ ### `#defined_attributes`
570
+
571
+ Listing all the available attributes.
572
+
573
+ ```ruby
574
+ person = Person.new(age: 20)
575
+
576
+ person.defined_attributes # ["age", "first_name", "last_name"]
577
+ ```
578
+
579
+ [⬆️ Back to Top](#table-of-contents-)
580
+
581
+ # Built-in extensions
376
582
 
377
583
  You can use the method `Micro::Attributes.with()` to combine and require only the features that better fit your needs.
378
584
 
379
585
  But, if you desire except one or more features, use the `Micro::Attributes.without()` method.
380
586
 
587
+ ## Picking specific features
588
+
589
+ ### `Micro::Attributes.with`
590
+
381
591
  ```ruby
382
- #===========================#
383
- # Loading specific features #
384
- #===========================#
592
+ Micro::Attributes.with(:initialize)
385
593
 
386
- class Job
387
- include Micro::Attributes.with(:diff)
594
+ Micro::Attributes.with(initialize: :strict)
388
595
 
389
- attribute :id
390
- attribute :state, default: 'sleeping'
596
+ Micro::Attributes.with(:diff, :initialize)
391
597
 
392
- def initialize(options)
393
- self.attributes = options
394
- end
395
- end
598
+ Micro::Attributes.with(:diff, initialize: :strict)
396
599
 
397
- #======================#
398
- # Loading all features #
399
- # --- #
400
- #======================#
600
+ Micro::Attributes.with(:activemodel_validations)
401
601
 
402
- class Job
403
- include Micro::Attributes.with_all_features
602
+ Micro::Attributes.with(:activemodel_validations, :diff)
404
603
 
405
- attribute :id
406
- attribute :state, default: 'sleeping'
407
- end
604
+ Micro::Attributes.with(:activemodel_validations, :diff, initialize: :strict)
605
+ ```
408
606
 
409
- #----------------------------------------------------------------------------#
410
- # Using the .with() method alias and adding the strict initialize extension. #
411
- #----------------------------------------------------------------------------#
412
- class Job
413
- include Micro::Attributes.with(:diff, initialize: :strict)
607
+ The method `Micro::Attributes.with()` will raise an exception if no arguments/features were declared.
414
608
 
415
- attribute :id
416
- attribute :state, default: 'sleeping'
609
+ ```ruby
610
+ class Job
611
+ include Micro::Attributes.with() # ArgumentError (Invalid feature name! Available options: :activemodel_validations, :diff, :initialize)
417
612
  end
613
+ ```
418
614
 
419
- # Note:
420
- # The method `Micro::Attributes.with()` will raise an exception if no arguments/features were declared.
421
- #
422
- # class Job
423
- # include Micro::Attributes.with() # ArgumentError (Invalid feature name! Available options: diff, initialize, activemodel_validations)
424
- # end
615
+ ### `Micro::Attributes.without`
425
616
 
426
- #=====================================#
427
- # Loading except one or more features #
428
- # ----- #
429
- #=====================================#
617
+ Picking *except* one or more features
430
618
 
431
- class Job
432
- include Micro::Attributes.without(:diff)
619
+ ```ruby
620
+ Micro::Attributes.without(:diff) # will load :activemodel_validations and initialize: :strict
433
621
 
434
- attribute :id
435
- attribute :state, default: 'sleeping'
436
- end
622
+ Micro::Attributes.without(initialize: :strict) # will load :activemodel_validations and :diff
623
+ ```
437
624
 
438
- # Note:
439
- # The method `Micro::Attributes.without()` returns `Micro::Attributes` if all features extensions were used.
625
+ ## Picking all the features
626
+
627
+ ```ruby
628
+ Micro::Attributes.with_all_features
440
629
  ```
441
630
 
442
- ### ActiveModel::Validations extension
631
+ [⬆️ Back to Top](#table-of-contents-)
632
+
633
+ ## Extensions
443
634
 
444
- If your application uses ActiveModel as a dependency (like a regular Rails app). You will be enabled to use the `actimodel_validations` extension.
635
+ ### `ActiveModel::Validation` extension
636
+
637
+ If your application uses ActiveModel as a dependency (like a regular Rails app). You will be enabled to use the `activemodel_validations` extension.
445
638
 
446
639
  ```ruby
447
640
  class Job
@@ -461,7 +654,7 @@ job.id # 1
461
654
  job.state # 'sleeping'
462
655
  ```
463
656
 
464
- #### Attribute options
657
+ #### `.attribute()` options
465
658
 
466
659
  You can use the `validate` or `validates` options to define your attributes. e.g.
467
660
 
@@ -472,7 +665,7 @@ class Job
472
665
  attribute :id, validates: { presence: true }
473
666
  attribute :state, validate: :must_be_a_filled_string
474
667
 
475
- def must_be_a_string
668
+ def must_be_a_filled_string
476
669
  return if state.is_a?(String) && state.present?
477
670
 
478
671
  errors.add(:state, 'must be a filled string')
@@ -480,6 +673,8 @@ class Job
480
673
  end
481
674
  ```
482
675
 
676
+ [⬆️ Back to Top](#table-of-contents-)
677
+
483
678
  ### Diff extension
484
679
 
485
680
  Provides a way to track changes in your object attributes.
@@ -529,6 +724,8 @@ job_changes.changed?(:state, from: 'sleeping', to: 'running') # true
529
724
  job_changes.differences # {'state'=> {'from' => 'sleeping', 'to' => 'running'}}
530
725
  ```
531
726
 
727
+ [⬆️ Back to Top](#table-of-contents-)
728
+
532
729
  ### Initialize extension
533
730
 
534
731
  1. Creates a constructor to assign the attributes.
@@ -578,7 +775,9 @@ other_job.state # killed
578
775
  other_job.equal?(job) # false
579
776
  ```
580
777
 
581
- ### Strict initialize mode
778
+ [⬆️ Back to Top](#table-of-contents-)
779
+
780
+ #### Strict mode
582
781
 
583
782
  1. Creates a constructor to assign the attributes.
584
783
  2. Adds methods to build new instances when some data was assigned.
@@ -616,20 +815,22 @@ job.state # 'sleeping'
616
815
 
617
816
  > **Note**: This extension works like the `initialize` extension. So, look at its section to understand all of the other features.
618
817
 
619
- ## Development
818
+ [⬆️ Back to Top](#table-of-contents-)
819
+
820
+ # Development
620
821
 
621
822
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
622
823
 
623
824
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
624
825
 
625
- ## Contributing
826
+ # Contributing
626
827
 
627
828
  Bug reports and pull requests are welcome on GitHub at https://github.com/serradura/u-attributes. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
628
829
 
629
- ## License
830
+ # License
630
831
 
631
832
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
632
833
 
633
- ## Code of Conduct
834
+ # Code of Conduct
634
835
 
635
- Everyone interacting in the Micro::Attributes project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/serradura/u-attributes/blob/master/CODE_OF_CONDUCT.md).
836
+ Everyone interacting in the Micro::Attributes project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/serradura/u-attributes/blob/main/CODE_OF_CONDUCT.md).
@@ -14,7 +14,8 @@ module Micro
14
14
  base.extend(::Micro::Attributes.const_get(:Macros))
15
15
 
16
16
  base.class_eval do
17
- private_class_method :__attributes, :__attribute_set, :__attribute_reader
17
+ private_class_method :__attributes, :__attribute_reader
18
+ private_class_method :__attribute_assign, :__attributes_data_to_assign
18
19
  end
19
20
 
20
21
  def base.inherited(subclass)
@@ -54,48 +55,92 @@ module Micro
54
55
  raise NameError, "undefined attribute `#{name}"
55
56
  end
56
57
 
58
+ def defined_attributes
59
+ @defined_attributes ||= self.class.attributes
60
+ end
61
+
57
62
  def attributes(*names)
58
63
  return __attributes if names.empty?
59
64
 
60
- names.each_with_object({}) do |name, memo|
61
- memo[name] = attribute(name) if attribute?(name)
65
+ options = names.last.is_a?(Hash) ? names.pop : Kind::Empty::HASH
66
+
67
+ names.flatten!
68
+
69
+ without_option = Array(options.fetch(:without, Kind::Empty::ARRAY))
70
+
71
+ keys = names.empty? ? defined_attributes - without_option.map(&:to_s) : names - without_option
72
+
73
+ data = keys.each_with_object({}) { |key, memo| memo[key] = attribute(key) if attribute?(key) }
74
+
75
+ with_option = Array(options.fetch(:with, Kind::Empty::ARRAY))
76
+
77
+ unless with_option.empty?
78
+ extra = with_option.each_with_object({}) { |key, memo| memo[key.to_s] = public_send(key) }
79
+
80
+ data.merge!(extra)
62
81
  end
82
+
83
+ Utils::Hashes.keys_as(options[:keys_as], data)
63
84
  end
64
85
 
65
86
  protected
66
87
 
67
88
  def attributes=(arg)
68
- hash = Utils.stringify_hash_keys(arg)
89
+ hash = Utils::Hashes.stringify_keys(arg)
90
+
91
+ __attributes_missing!(hash)
69
92
 
70
- __attributes_set(hash, self.class.__attributes_data__)
93
+ __attributes_assign(hash)
71
94
  end
72
95
 
73
96
  private
74
97
 
98
+ def extract_attributes_from(other)
99
+ Utils::ExtractAttribute.from(other, keys: defined_attributes)
100
+ end
101
+
75
102
  def __attributes
76
103
  @__attributes ||= {}
77
104
  end
78
105
 
79
- def __attribute_set(name, value)
80
- __attributes[name] = instance_variable_set("@#{name}", value) if attribute?(name)
106
+ FetchValueToAssign = -> (value, default) do
107
+ if default.is_a?(Proc)
108
+ default.arity > 0 ? default.call(value) : default.call
109
+ else
110
+ value.nil? ? default : value
111
+ end
81
112
  end
82
113
 
83
- def __attributes_set(hash, att_data)
84
- att_data.each do |key, default|
85
- value = hash[key]
86
-
87
- final_value =
88
- if default.respond_to?(:call)
89
- callable = default.is_a?(Proc) ? default : default.method(:call)
90
- callable.arity > 0 ? callable.call(value) : callable.call
91
- else
92
- value || default
93
- end
94
-
95
- __attribute_set(key, final_value)
114
+ def __attributes_assign(hash)
115
+ self.class.__attributes_data__.each do |name, default|
116
+ __attribute_assign(name, FetchValueToAssign.(hash[name], default)) if attribute?(name)
96
117
  end
97
118
 
98
119
  __attributes.freeze
99
120
  end
121
+
122
+ def __attribute_assign(name, value)
123
+ __attributes[name] = instance_variable_set("@#{name}", value)
124
+ end
125
+
126
+ MISSING_KEYWORD = 'missing keyword'.freeze
127
+ MISSING_KEYWORDS = 'missing keywords'.freeze
128
+
129
+ def __attributes_missing!(hash)
130
+ required_keys = self.class.__attributes_required__
131
+
132
+ return if required_keys.empty?
133
+
134
+ missing_keys = required_keys.map { |name| ":#{name}" if !hash.key?(name) }
135
+ missing_keys.compact!
136
+
137
+ return if missing_keys.empty?
138
+
139
+ label = missing_keys.size == 1 ? MISSING_KEYWORD : MISSING_KEYWORDS
140
+
141
+ raise ArgumentError, "#{label}: #{missing_keys.join(', ')}"
142
+ end
143
+
144
+ private_constant :FetchValueToAssign, :MISSING_KEYWORD, :MISSING_KEYWORDS
100
145
  end
101
146
  end
@@ -14,11 +14,11 @@ module Micro::Attributes
14
14
  end
15
15
 
16
16
  module ClassMethods
17
- def __call_after_attribute_set__(attr_name, options)
17
+ def __call_after_attribute_assign__(attr_name, options)
18
18
  validate, validates = options.values_at(:validate, :validates)
19
19
 
20
20
  self.validate(validate) if validate
21
- self.validates(attr_name, validates) if validates
21
+ self.validates(attr_name, validates.dup) if validates
22
22
  end
23
23
  end
24
24
 
@@ -4,35 +4,15 @@ module Micro::Attributes
4
4
  module Features
5
5
  module Initialize
6
6
  module Strict
7
- MISSING_KEYWORD = 'missing keyword'.freeze
8
- MISSING_KEYWORDS = 'missing keywords'.freeze
9
-
10
- protected def attributes=(arg)
11
- arg_hash = Utils.stringify_hash_keys(arg)
12
- att_data = self.class.__attributes_data__
13
-
14
- attributes_missing!(ref: att_data, arg: arg_hash)
15
-
16
- __attributes_set(arg_hash, att_data)
17
- end
18
-
19
- private def attributes_missing!(ref:, arg:)
20
- missing_keys = attributes_missing(ref, arg)
21
-
22
- return if missing_keys.empty?
23
-
24
- label = missing_keys.size == 1 ? MISSING_KEYWORD : MISSING_KEYWORDS
25
-
26
- raise ArgumentError, "#{label}: #{missing_keys.join(', ')}"
27
- end
28
-
29
- private def attributes_missing(ref, arg)
30
- ref.each_with_object([]) do |(key, val), memo|
31
- memo << ":#{key}" if val.nil? && !arg.has_key?(key)
7
+ module ClassMethods
8
+ def attributes_are_all_required?
9
+ true
32
10
  end
33
11
  end
34
12
 
35
- private_constant :MISSING_KEYWORD, :MISSING_KEYWORDS
13
+ def self.included(base)
14
+ base.send(:extend, ClassMethods)
15
+ end
36
16
  end
37
17
  end
38
18
  end
@@ -3,10 +3,33 @@
3
3
  module Micro
4
4
  module Attributes
5
5
  module Macros
6
+ def attributes_are_all_required?
7
+ false
8
+ end
9
+
10
+ # NOTE: can't be renamed! It is used by u-case v4.
6
11
  def __attributes_data__
7
12
  @__attributes_data__ ||= {}
8
13
  end
9
14
 
15
+ def __attributes_required__
16
+ @__attributes_required__ ||= Set.new
17
+ end
18
+
19
+ def __attributes_required_add(name, is_required, hasnt_default)
20
+ if is_required || (attributes_are_all_required? && hasnt_default)
21
+ __attributes_required__.add(name)
22
+ end
23
+
24
+ nil
25
+ end
26
+
27
+ def __attributes_data_to_assign(name, options)
28
+ hasnt_default = !options.key?(:default)
29
+
30
+ hasnt_default ? __attributes_required_add(name, options[:required], hasnt_default) : options[:default]
31
+ end
32
+
10
33
  def __attributes
11
34
  @__attributes ||= Set.new
12
35
  end
@@ -17,20 +40,24 @@ module Micro
17
40
  attr_reader(name)
18
41
  end
19
42
 
20
- def __attribute_set(key, can_overwrite, options)
43
+ def __attribute_assign(key, can_overwrite, options)
21
44
  name = key.to_s
22
45
  has_attribute = attribute?(name)
23
46
 
24
47
  __attribute_reader(name) unless has_attribute
25
- __attributes_data__[name] = options[:default] if can_overwrite || !has_attribute
26
48
 
27
- __call_after_attribute_set__(name, options)
49
+ __attributes_data__[name] = __attributes_data_to_assign(name, options) if can_overwrite || !has_attribute
50
+
51
+ __call_after_attribute_assign__(name, options)
28
52
  end
29
53
 
30
- def __call_after_attribute_set__(attr_name, options); end
54
+ def __call_after_attribute_assign__(attr_name, options); end
31
55
 
56
+ # NOTE: can't be renamed! It is used by u-case v4.
32
57
  def __attributes_set_after_inherit__(arg)
33
- arg.each { |key, val| __attribute_set(key, true, default: val) }
58
+ arg.each do |key, val|
59
+ __attribute_assign(key, true, val ? { default: val } : {})
60
+ end
34
61
  end
35
62
 
36
63
  def attribute?(name)
@@ -38,27 +65,32 @@ module Micro
38
65
  end
39
66
 
40
67
  def attribute(name, options = Kind::Empty::HASH)
41
- __attribute_set(name, false, options)
68
+ __attribute_assign(name, false, options)
42
69
  end
43
70
 
44
71
  def attributes(*args)
45
72
  return __attributes.to_a if args.empty?
46
73
 
47
74
  args.flatten!
75
+
76
+ options =
77
+ args.size > 1 && args.last.is_a?(::Hash) ? args.pop : Kind::Empty::HASH
78
+
48
79
  args.each do |arg|
49
80
  if arg.is_a?(String) || arg.is_a?(Symbol)
50
- __attribute_set(arg, false, Kind::Empty::HASH)
81
+ __attribute_assign(arg, false, options)
51
82
  else
52
83
  raise Kind::Error.new('String/Symbol'.freeze, arg)
53
84
  end
54
85
  end
55
86
  end
56
87
 
88
+ # NOTE: can't be renamed! It is used by u-case v4.
57
89
  module ForSubclasses
58
90
  WRONG_NUMBER_OF_ARGS = 'wrong number of arguments (given 0, expected 1 or more)'.freeze
59
91
 
60
92
  def attribute!(name, options = Kind::Empty::HASH)
61
- __attribute_set(name, true, options)
93
+ __attribute_assign(name, true, options)
62
94
  end
63
95
 
64
96
  private_constant :WRONG_NUMBER_OF_ARGS
@@ -1,17 +1,52 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Micro
4
- module Attributes
5
- module Utils
6
- def self.stringify_hash_keys(arg)
3
+ module Micro::Attributes
4
+ module Utils
5
+ module Hashes
6
+ def self.stringify_keys(arg)
7
7
  hash = Kind::Of.(::Hash, arg)
8
8
 
9
9
  return hash if hash.empty?
10
+ return hash.transform_keys(&:to_s) if hash.respond_to?(:transform_keys)
10
11
 
11
- if hash.respond_to?(:transform_keys)
12
- hash.transform_keys { |key| key.to_s }
13
- else
14
- hash.each_with_object({}) { |(key, val), memo| memo[key.to_s] = val }
12
+ hash.each_with_object({}) { |(key, val), memo| memo[key.to_s] = val }
13
+ end
14
+
15
+ def self.symbolize_keys(arg)
16
+ hash = Kind::Of.(::Hash, arg)
17
+
18
+ return hash if hash.empty?
19
+ return hash.transform_keys(&:to_sym) if hash.respond_to?(:transform_keys)
20
+
21
+ hash.each_with_object({}) { |(key, val), memo| memo[key.to_sym] = val }
22
+ end
23
+
24
+ def self.keys_as(type, hash)
25
+ return Kind::Of.(::Hash, hash) unless type
26
+
27
+ return symbolize_keys(hash) if type == Symbol
28
+ return stringify_keys(hash) if type == String
29
+
30
+ raise ArgumentError, 'first argument must be the class String or Symbol'.freeze
31
+ end
32
+
33
+ def self.get(hash, key)
34
+ value = hash[key.to_s]
35
+
36
+ value.nil? ? hash[key.to_sym] : value
37
+ end
38
+ end
39
+
40
+ module ExtractAttribute
41
+ def self.call(object, key:)
42
+ return object.public_send(key) if object.respond_to?(key)
43
+
44
+ Hashes.get(object, key) if object.respond_to?(:[])
45
+ end
46
+
47
+ def self.from(object, keys:)
48
+ Kind::Of.(::Array, keys).each_with_object({}) do |key, memo|
49
+ memo[key] = call(object, key: key)
15
50
  end
16
51
  end
17
52
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Micro
4
4
  module Attributes
5
- VERSION = '2.0.0'.freeze
5
+ VERSION = '2.3.0'.freeze
6
6
  end
7
7
  end
@@ -27,7 +27,7 @@ module Micro
27
27
 
28
28
  module ActiveModelValidations
29
29
  def self.included(base)
30
- base.send(:include, ::Micro::Attributes)
30
+ base.send(:include, Initialize)
31
31
  base.send(:include, ::Micro::Attributes::Features::ActiveModelValidations)
32
32
  end
33
33
  end
@@ -9,8 +9,11 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ['Rodrigo Serradura']
10
10
  spec.email = ['rodrigo.serradura@gmail.com']
11
11
 
12
- spec.summary = %q{Define read-only attributes}
13
- spec.description = %q{This gem allows defining read-only attributes, that is, your objects will have only getters to access their attributes data.}
12
+ spec.summary = %q{Create "immutable" objects. No setters, just getters!}
13
+ spec.description =
14
+ "This gem allows you to define \"immutable\" objects, and your objects will have only getters and no setters. "\
15
+ "So, if you change some object attribute, you will have a new object instance. " \
16
+ "That is, you transform the object instead of modifying it."
14
17
  spec.homepage = 'https://github.com/serradura/u-attributes'
15
18
  spec.license = 'MIT'
16
19
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: u-attributes
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Serradura
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-08-20 00:00:00.000000000 Z
11
+ date: 2020-09-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: kind
@@ -58,8 +58,10 @@ dependencies:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
60
  version: '13.0'
61
- description: This gem allows defining read-only attributes, that is, your objects
62
- will have only getters to access their attributes data.
61
+ description: This gem allows you to define "immutable" objects, and your objects will
62
+ have only getters and no setters. So, if you change some object attribute, you will
63
+ have a new object instance. That is, you transform the object instead of modifying
64
+ it.
63
65
  email:
64
66
  - rodrigo.serradura@gmail.com
65
67
  executables: []
@@ -74,6 +76,7 @@ files:
74
76
  - LICENSE.txt
75
77
  - README.md
76
78
  - Rakefile
79
+ - assets/u-attributes_logo_v1.png
77
80
  - bin/console
78
81
  - bin/setup
79
82
  - lib/micro/attributes.rb
@@ -112,5 +115,5 @@ requirements: []
112
115
  rubygems_version: 3.0.6
113
116
  signing_key:
114
117
  specification_version: 4
115
- summary: Define read-only attributes
118
+ summary: Create "immutable" objects. No setters, just getters!
116
119
  test_files: []