u-attributes 2.0.0 → 2.3.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: 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: []