u-attributes 1.0.1 → 2.0.1

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: 2748d97dc4a469f8bd54558d869b735095a44c58758d8ab48a372f400e525f2f
4
- data.tar.gz: c338340af08a1013d01c61ac657f6a326d2a375d0db6476f87e983a8c7d480ba
3
+ metadata.gz: bc53b00949ac8d0d8d8572443936ba537a637569be77e0b105a0b98f2d1ae9d0
4
+ data.tar.gz: 94f42f6da3c2cba68f6565e1bb413b5692bd700baa077555cd421e4aeb82e2ff
5
5
  SHA512:
6
- metadata.gz: 9aefaa228794f860a3859b4f7f9503d6de6a971ce1e584c5180be96567619cf070feaf0ba85b8bf44f46d636c943d74898ec14c21d410db7a1d3bc48db50dbec
7
- data.tar.gz: d3d30b8579e0ea4b710b219c4f6ebc609c603ebcb568fd190632c4dcd5d35543bb01ec68bcc191c022b793e25c97aced729d9be290c725f769e13804ad976330
6
+ metadata.gz: e5617a5fd34458de945dae2874bed24013e682871f19b70063e734156f2528e8b249e615632f90c978c203abf876560de9717940f9b81e48271f84a956456199
7
+ data.tar.gz: 50b19483c9e8f88169101306be67a34e4e8c6a26358b187fd119b41ca26e83d461619d6a149bfc898ab7b1208c6af6791b58acf039a3d92d0e8f912e007232b9
data/.gitignore CHANGED
@@ -6,3 +6,4 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ Gemfile.lock
data/.travis.sh CHANGED
@@ -20,5 +20,12 @@ ACTIVEMODEL_VERSION='5.0' bundle exec rake test
20
20
  ACTIVEMODEL_VERSION='5.1' bundle update
21
21
  ACTIVEMODEL_VERSION='5.1' bundle exec rake test
22
22
 
23
- ACTIVEMODEL_VERSION='5.2' bundle update
24
- ACTIVEMODEL_VERSION='5.2' bundle exec rake test
23
+ if [[ ! $ruby_v =~ '2.2.0' ]]; then
24
+ ACTIVEMODEL_VERSION='5.2' bundle update
25
+ ACTIVEMODEL_VERSION='5.2' bundle exec rake test
26
+ fi
27
+
28
+ if [[ $ruby_v =~ '2.5.' ]] || [[ $ruby_v =~ '2.6.' ]] || [[ $ruby_v =~ '2.7.' ]]; then
29
+ ACTIVEMODEL_VERSION='6.0' bundle update
30
+ ACTIVEMODEL_VERSION='6.0' bundle exec rake test
31
+ fi
@@ -8,6 +8,7 @@ rvm:
8
8
  - 2.4.0
9
9
  - 2.5.0
10
10
  - 2.6.0
11
+ - 2.7.0
11
12
 
12
13
  cache: bundler
13
14
 
@@ -20,9 +21,9 @@ install: bundle install --jobs=3 --retry=3
20
21
  before_script:
21
22
  - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
22
23
  - chmod +x ./cc-test-reporter
23
- - "./cc-test-reporter before-build"
24
+ - './cc-test-reporter before-build'
24
25
 
25
- script: "./.travis.sh"
26
+ script: './.travis.sh'
26
27
 
27
28
  after_success:
28
- - "./cc-test-reporter after-build -t simplecov"
29
+ - './cc-test-reporter after-build -t simplecov'
data/Gemfile CHANGED
@@ -10,7 +10,7 @@ activemodel = case activemodel_version
10
10
  when '5.0' then '5.0.7'
11
11
  when '5.1' then '5.1.7'
12
12
  when '5.2' then '5.2.3'
13
- when '6.0' then '6.0.0.rc1'
13
+ when '6.0' then '6.0.0'
14
14
  end
15
15
 
16
16
  if activemodel_version < '6.1'
@@ -3,7 +3,7 @@ The MIT License (MIT)
3
3
  Copyright (c) 2019 Rodrigo Serradura
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
6
+ of this software and associated documentation files (the 'Software'), to deal
7
7
  in the Software without restriction, including without limitation the rights
8
8
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
9
  copies of the Software, and to permit persons to whom the Software is
@@ -12,7 +12,7 @@ furnished to do so, subject to the following conditions:
12
12
  The above copyright notice and this permission notice shall be included in
13
13
  all copies or substantial portions of the Software.
14
14
 
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
16
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
17
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
data/README.md CHANGED
@@ -1,74 +1,79 @@
1
+ ![Ruby](https://img.shields.io/badge/ruby-2.2+-ruby.svg?colorA=99004d&colorB=cc0066)
1
2
  [![Gem](https://img.shields.io/gem/v/u-attributes.svg?style=flat-square)](https://rubygems.org/gems/u-attributes)
2
- [![Build Status](https://travis-ci.com/serradura/u-attributes.svg?branch=master)](https://travis-ci.com/serradura/u-attributes)
3
+ [![Build Status](https://travis-ci.com/serradura/u-attributes.svg?branch=main)](https://travis-ci.com/serradura/u-attributes)
3
4
  [![Maintainability](https://api.codeclimate.com/v1/badges/b562e6b877a9edf4dbf6/maintainability)](https://codeclimate.com/github/serradura/u-attributes/maintainability)
4
5
  [![Test Coverage](https://api.codeclimate.com/v1/badges/b562e6b877a9edf4dbf6/test_coverage)](https://codeclimate.com/github/serradura/u-attributes/test_coverage)
5
6
 
6
- μ-attributes (Micro::Attributes)
7
+ μ-attributes (Micro::Attributes) <!-- omit in toc -->
7
8
  ================================
8
9
 
9
- This gem allows defining read-only attributes, that is, your objects will have only getters to access their attributes data.
10
-
11
- ## Table of contents
12
- - [μ-attributes (Micro::Attributes)](#%ce%bc-attributes-microattributes)
13
- - [Table of contents](#table-of-contents)
14
- - [Installation](#installation)
15
- - [Usage](#usage)
16
- - [How to require?](#how-to-require)
17
- - [How to define attributes?](#how-to-define-attributes)
18
- - [How to define multiple attributes?](#how-to-define-multiple-attributes)
19
- - [How to define attributes with a constructor to assign them?](#how-to-define-attributes-with-a-constructor-to-assign-them)
20
- - [How to inherit the attributes?](#how-to-inherit-the-attributes)
21
- - [How to query the attributes?](#how-to-query-the-attributes)
22
- - [Built-in extensions](#built-in-extensions)
23
- - [ActiveModel::Validations extension](#activemodelvalidations-extension)
10
+ This gem allows you to define "immutable" objects, and your objects will have only getters and no setters.
11
+ 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.
12
+
13
+ ## Table of contents <!-- omit in toc -->
14
+ - [Installation](#installation)
15
+ - [Compatibility](#compatibility)
16
+ - [Usage](#usage)
17
+ - [How to define attributes?](#how-to-define-attributes)
18
+ - [`Micro::Attributes#attributes=`](#microattributesattributes)
19
+ - [`Micro::Attributes#attribute`](#microattributesattribute)
20
+ - [`Micro::Attributes#attribute!`](#microattributesattribute-1)
21
+ - [How to define multiple attributes?](#how-to-define-multiple-attributes)
22
+ - [`Micro::Attributes.with(:initialize)`](#microattributeswithinitialize)
23
+ - [`#with_attribute()`](#with_attribute)
24
+ - [`#with_attributes()`](#with_attributes)
25
+ - [Defining default values to the attributes](#defining-default-values-to-the-attributes)
26
+ - [The strict initializer](#the-strict-initializer)
27
+ - [Is it possible to inherit the attributes?](#is-it-possible-to-inherit-the-attributes)
28
+ - [`.attribute!()`](#attribute)
29
+ - [How to query the attributes?](#how-to-query-the-attributes)
30
+ - [Built-in extensions](#built-in-extensions)
31
+ - [Picking specific features](#picking-specific-features)
32
+ - [`Micro::Attributes.with`](#microattributeswith)
33
+ - [`Micro::Attributes.without`](#microattributeswithout)
34
+ - [Picking all the features](#picking-all-the-features)
35
+ - [Extensions](#extensions)
36
+ - [`ActiveModel::Validation` extension](#activemodelvalidation-extension)
37
+ - [`.attribute()` options](#attribute-options)
24
38
  - [Diff extension](#diff-extension)
25
39
  - [Initialize extension](#initialize-extension)
26
- - [Strict initialize extension](#strict-initialize-extension)
27
- - [Development](#development)
28
- - [Contributing](#contributing)
29
- - [License](#license)
30
- - [Code of Conduct](#code-of-conduct)
40
+ - [Strict mode](#strict-mode)
41
+ - [Development](#development)
42
+ - [Contributing](#contributing)
43
+ - [License](#license)
44
+ - [Code of Conduct](#code-of-conduct)
31
45
 
32
- ## Installation
46
+ # Installation
33
47
 
34
- Add this line to your application's Gemfile:
48
+ Add this line to your application's Gemfile and `bundle install`:
35
49
 
36
50
  ```ruby
37
51
  gem 'u-attributes'
38
52
  ```
39
53
 
40
- And then execute:
54
+ # Compatibility
41
55
 
42
- $ bundle
56
+ | u-attributes | branch | ruby | activemodel |
57
+ | -------------- | ------- | -------- | ------------- |
58
+ | 2.0.0 | main | >= 2.2.0 | >= 3.2, < 6.1 |
59
+ | 1.2.0 | v1.x | >= 2.2.0 | >= 3.2, < 6.1 |
43
60
 
44
- Or install it yourself as:
61
+ > **Note**: The activemodel is an optional dependency, this module [can be enabled](#activemodelvalidation-extension) to validate the attributes.
45
62
 
46
- $ gem install u-attributes
63
+ [⬆️ Back to Top](#table-of-contents-)
47
64
 
48
- ## Usage
65
+ # Usage
49
66
 
50
- ### How to require?
51
- ```ruby
52
- # Bundler will do it automatically, but if you desire to do a manual require.
53
- # Use one of the following options:
54
-
55
- require 'micro/attributes'
56
-
57
- # or
58
-
59
- require 'u-attributes'
60
- ```
67
+ ## How to define attributes?
61
68
 
62
- ### How to define attributes?
63
69
  ```ruby
64
-
65
70
  # By default you must to define the class constructor.
66
71
 
67
72
  class Person
68
73
  include Micro::Attributes
69
74
 
70
- attribute :name
71
75
  attribute :age
76
+ attribute :name
72
77
 
73
78
  def initialize(name: 'John Doe', age:)
74
79
  @name, @age = name, age
@@ -77,26 +82,28 @@ end
77
82
 
78
83
  person = Person.new(age: 21)
79
84
 
80
- puts person.name # John Doe
81
- puts person.age # 21
85
+ person.age # 21
86
+ person.name # John Doe
82
87
 
83
- # By design, the attributes expose only reader methods (getters).
84
- # If you try to call a setter, you will see a NoMethodError.
88
+ # By design the attributes are always exposed as reader methods (getters).
89
+ # If you try to call a setter you will see a NoMethodError.
85
90
  #
86
91
  # person.name = 'Rodrigo'
87
- # NoMethodError (undefined method `name=' for #<Person:0x0000... @name="John Doe", @age=21>)
92
+ # NoMethodError (undefined method `name=' for #<Person:0x0000... @name='John Doe', @age=21>)
93
+ ```
88
94
 
89
- #------------------#
90
- # self.attributes= #
91
- #------------------#
95
+ [⬆️ Back to Top](#table-of-contents-)
92
96
 
93
- # This protected method is added to make easier the assignment in a constructor.
97
+ ### `Micro::Attributes#attributes=`
94
98
 
99
+ This is a protected method to make easier the assignment in a constructor. e.g.
100
+
101
+ ```ruby
95
102
  class Person
96
103
  include Micro::Attributes
97
104
 
98
- attribute :name, 'John Doe' # .attribute() accepts a second arg as its default value
99
105
  attribute :age
106
+ attribute :name, default: 'John Doe'
100
107
 
101
108
  def initialize(options)
102
109
  self.attributes = options
@@ -105,46 +112,55 @@ end
105
112
 
106
113
  person = Person.new(age: 20)
107
114
 
108
- puts person.name # John Doe
109
- puts person.age # 20
115
+ person.age # 20
116
+ person.name # John Doe
117
+ ```
110
118
 
111
- #--------------#
112
- # #attribute() #
113
- #--------------#
114
- #
115
- # Use the #attribute() method with a valid attribute name to get its value
119
+ [⬆️ Back to Top](#table-of-contents-)
116
120
 
117
- puts person.attribute(:name) # John Doe
118
- puts person.attribute('age') # 20
119
- puts person.attribute('foo') # nil
121
+ ### `Micro::Attributes#attribute`
120
122
 
121
- #
122
- # If you pass a block, it will be executed only if the attribute is valid.
123
+ Use this method with a valid attribute name to get its value.
123
124
 
125
+ ```ruby
126
+ person = Person.new(age: 20)
127
+
128
+ person.attribute('age') # 20
129
+ person.attribute(:name) # John Doe
130
+ person.attribute('foo') # nil
131
+ ```
132
+
133
+ If you pass a block, it will be executed only if the attribute was valid.
134
+
135
+ ```ruby
124
136
  person.attribute(:name) { |value| puts value } # John Doe
125
137
  person.attribute('age') { |value| puts value } # 20
126
- person.attribute('foo') { |value| puts value } # !! Nothing happened, because of the attribute not exists.
138
+ person.attribute('foo') { |value| puts value } # !! Nothing happened, because of the attribute doesn't exist.
139
+ ```
127
140
 
128
- #---------------#
129
- # #attribute!() #
130
- #---------------#
131
- #
132
- # Works like the #attribute() method, but will raise an exception when the attribute not exist.
141
+ [⬆️ Back to Top](#table-of-contents-)
133
142
 
134
- puts person.attribute!('foo') # NameError (undefined attribute `foo)
135
- person.attribute!('foo') { |value| puts value } # NameError (undefined attribute `foo)
136
- ```
143
+ ### `Micro::Attributes#attribute!`
137
144
 
138
- ### How to define multiple attributes?
145
+ Works like the `#attribute` method, but it will raise an exception when the attribute doesn't exist.
139
146
 
140
147
  ```ruby
148
+ person.attribute!('foo') # NameError (undefined attribute `foo)
141
149
 
142
- # Use .attributes with a list of attribute names.
150
+ person.attribute!('foo') { |value| value } # NameError (undefined attribute `foo)
151
+ ```
143
152
 
153
+ [⬆️ Back to Top](#table-of-contents-)
154
+
155
+ ## How to define multiple attributes?
156
+
157
+ Use `.attributes` with a list of attribute names.
158
+
159
+ ```ruby
144
160
  class Person
145
161
  include Micro::Attributes
146
162
 
147
- attributes :age, name: 'John Doe' # Use a hash to define attributes with default values
163
+ attributes :age, :name
148
164
 
149
165
  def initialize(options)
150
166
  self.attributes = options
@@ -153,89 +169,131 @@ end
153
169
 
154
170
  person = Person.new(age: 32)
155
171
 
156
- puts person.name # 'John Doe'
157
- puts person.age # 32
172
+ person.name # nil
173
+ person.age # 32
158
174
  ```
159
175
 
160
- ### How to define attributes with a constructor to assign them?
161
- A: Use `Micro::Attributes.to_initialize`
176
+ > **Note:** This method can't define default values. To do this, use the `#attribute()` method.
177
+
178
+ [⬆️ Back to Top](#table-of-contents-)
179
+
180
+ ## `Micro::Attributes.with(:initialize)`
181
+
182
+ Use `Micro::Attributes.with(:initialize)` to define a constructor to assign the attributes. e.g.
162
183
 
163
184
  ```ruby
164
185
  class Person
165
- include Micro::Attributes.to_initialize
186
+ include Micro::Attributes.with(:initialize)
166
187
 
167
- attributes :age, name: 'John Doe'
188
+ attribute :age
189
+ attribute :name, default: 'John Doe'
168
190
  end
169
191
 
170
192
  person = Person.new(age: 18)
171
193
 
172
- puts person.name # John Doe
173
- puts person.age # 18
194
+ person.age # 18
195
+ person.name # John Doe
196
+ ```
174
197
 
175
- ##############################################
176
- # Assigning new values to get a new instance #
177
- ##############################################
198
+ This extension enables two methods for your objects.
199
+ The `#with_attribute()` and `#with_attributes()`.
178
200
 
179
- #-------------------#
180
- # #with_attribute() #
181
- #-------------------#
201
+ ### `#with_attribute()`
182
202
 
203
+ ```ruby
183
204
  another_person = person.with_attribute(:age, 21)
184
205
 
185
- puts another_person.name # John Doe
186
- puts another_person.age # 21
187
- puts another_person.equal?(person) # false
206
+ another_person.age # 21
207
+ another_person.name # John Doe
208
+ another_person.equal?(person) # false
209
+ ```
188
210
 
189
- #--------------------#
190
- # #with_attributes() #
191
- #--------------------#
192
- #
193
- # Use it to assign multiple attributes
211
+ ### `#with_attributes()`
194
212
 
213
+ Use it to assign multiple attributes
214
+ ```ruby
195
215
  other_person = person.with_attributes(name: 'Serradura', age: 32)
196
216
 
197
- puts other_person.name # Serradura
198
- puts other_person.age # 32
199
- puts other_person.equal?(person) # false
217
+ other_person.age # 32
218
+ other_person.name # Serradura
219
+ other_person.equal?(person) # false
220
+ ```
200
221
 
201
- # If you pass a value different of a Hash, an ArgumentError will be raised.
202
- #
203
- # Person.new(1)
204
- # ArgumentError (argument must be a Hash)
222
+ If you pass a value different of a Hash, a Kind::Error will be raised.
205
223
 
206
- #--------------------#
207
- # Strict initializer #
208
- #--------------------#
224
+ ```ruby
225
+ Person.new(1) # Kind::Error (1 expected to be a kind of Hash)
226
+ ```
227
+
228
+ [⬆️ Back to Top](#table-of-contents-)
229
+
230
+ ## Defining default values to the attributes
231
+
232
+ To do this, you only need make use of the `default:` keyword. e.g.
233
+
234
+ ```ruby
235
+ class Person
236
+ include Micro::Attributes.with(:initialize)
237
+
238
+ attribute :age
239
+ attribute :name, default: 'John Doe'
240
+ end
241
+ ```
242
+
243
+ There are 3 different strategies to define default values.
244
+ 1. Pass a regular object, like in the previous example.
245
+ 2. Pass a `proc`/`lambda`, and if it has an argument you will receive the attribute value to do something before assign it.
246
+ 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`).
247
+
248
+ ```ruby
249
+ class Person
250
+ include Micro::Attributes.with(:initialize)
251
+
252
+ attribute :age, default: -> age { age&.to_i }
253
+ attribute :name, default: -> name { String(name || 'John Doe').strip }
254
+ end
255
+ ```
256
+
257
+ [⬆️ Back to Top](#table-of-contents-)
209
258
 
210
- # Use .to_initialize! to forbids an instantiation without all keywords.
259
+ ## The strict initializer
211
260
 
261
+ Use `.with(initialize: :strict)` to forbids an instantiation without all the attribute keywords. e.g.
262
+
263
+ ```ruby
212
264
  class StrictPerson
213
- include Micro::Attributes.to_initialize!
265
+ include Micro::Attributes.with(initialize: :strict)
214
266
 
215
- attributes :age, name: 'John Doe'
267
+ attribute :age
268
+ attribute :name, default: 'John Doe'
216
269
  end
217
270
 
218
- StrictPerson.new({})
271
+ StrictPerson.new({}) # ArgumentError (missing keyword: :age)
272
+ ```
219
273
 
220
- # The code above will raise:
221
- # ArgumentError (missing keyword: :age)
274
+ An attribute with a default value can be omitted.
222
275
 
276
+ ``` ruby
223
277
  person_without_age = StrictPerson.new(age: nil)
224
278
 
225
- p person_without_age.name # "John Doe"
226
- p person_without_age.age # nil
227
-
228
- # Except for this validation when initializing,
229
- # the `to_initialize!` method will works in the same ways of `to_initialize`.
279
+ person_without_age.age # nil
280
+ person_without_age.name # 'John Doe'
230
281
  ```
231
282
 
232
- ### How to inherit the attributes?
283
+ > **Note:** Except for this validation the `.with(initialize: :strict)` method will works in the same ways of `.with(:initialize)`.
284
+
285
+ [⬆️ Back to Top](#table-of-contents-)
286
+
287
+ ## Is it possible to inherit the attributes?
288
+
289
+ Yes. e.g.
233
290
 
234
291
  ```ruby
235
292
  class Person
236
- include Micro::Attributes.to_initialize
293
+ include Micro::Attributes.with(:initialize)
237
294
 
238
- attributes :age, name: 'John Doe'
295
+ attribute :age
296
+ attribute :name, default: 'John Doe'
239
297
  end
240
298
 
241
299
  class Subclass < Person # Will preserve the parent class attributes
@@ -244,42 +302,48 @@ end
244
302
 
245
303
  instance = Subclass.new({})
246
304
 
247
- puts instance.name # John Doe
248
- puts instance.respond_to?(:age) # true
249
- puts instance.respond_to?(:foo) # true
305
+ instance.name # John Doe
306
+ instance.respond_to?(:age) # true
307
+ instance.respond_to?(:foo) # true
308
+ ```
309
+
310
+ [⬆️ Back to Top](#table-of-contents-)
250
311
 
251
- #---------------------------------#
252
- # .attribute!() or .attributes!() #
253
- #---------------------------------#
312
+ ### `.attribute!()`
254
313
 
255
- # The methods above allow redefining the attributes default data
314
+ This method allows us to redefine the attributes default data that was defined in the parent class. e.g.
256
315
 
316
+ ```ruby
257
317
  class AnotherSubclass < Person
258
- attribute! :name, 'Alfa'
318
+ attribute! :name, default: 'Alfa'
259
319
  end
260
320
 
261
321
  alfa_person = AnotherSubclass.new({})
262
322
 
263
- p alfa_person.name # "Alfa"
264
- p alfa_person.age # nil
323
+ alfa_person.name # 'Alfa'
324
+ alfa_person.age # nil
265
325
 
266
326
  class SubSubclass < Subclass
267
- attributes! name: 'Beta', age: 0
327
+ attribute! :age, default: 0
328
+ attribute! :name, default: 'Beta'
268
329
  end
269
330
 
270
331
  beta_person = SubSubclass.new({})
271
332
 
272
- p beta_person.name # "Beta"
273
- p beta_person.age # 0
333
+ beta_person.name # 'Beta'
334
+ beta_person.age # 0
274
335
  ```
275
336
 
276
- ### How to query the attributes?
337
+ [⬆️ Back to Top](#table-of-contents-)
338
+
339
+ ## How to query the attributes?
277
340
 
278
341
  ```ruby
279
342
  class Person
280
343
  include Micro::Attributes
281
344
 
282
- attributes :age, name: 'John Doe'
345
+ attribute :age
346
+ attribute :name, default: 'John Doe'
283
347
 
284
348
  def initialize(options)
285
349
  self.attributes = options
@@ -290,16 +354,16 @@ end
290
354
  # .attributes() #
291
355
  #---------------#
292
356
 
293
- p Person.attributes # ["name", "age"]
357
+ Person.attributes # ['name', 'age']
294
358
 
295
359
  #---------------#
296
360
  # .attribute?() #
297
361
  #---------------#
298
362
 
299
- puts Person.attribute?(:name) # true
300
- puts Person.attribute?('name') # true
301
- puts Person.attribute?('foo') # false
302
- puts Person.attribute?(:foo) # false
363
+ Person.attribute?(:name) # true
364
+ Person.attribute?('name') # true
365
+ Person.attribute?('foo') # false
366
+ Person.attribute?(:foo) # false
303
367
 
304
368
  # ---
305
369
 
@@ -309,105 +373,97 @@ person = Person.new(age: 20)
309
373
  # #attribute?() #
310
374
  #---------------#
311
375
 
312
- puts person.attribute?(:name) # true
313
- puts person.attribute?('name') # true
314
- puts person.attribute?('foo') # false
315
- puts person.attribute?(:foo) # false
376
+ person.attribute?(:name) # true
377
+ person.attribute?('name') # true
378
+ person.attribute?('foo') # false
379
+ person.attribute?(:foo) # false
316
380
 
317
381
  #---------------#
318
382
  # #attributes() #
319
383
  #---------------#
320
384
 
321
- p person.attributes # {"age"=>20, "name"=>"John Doe"}
322
- p Person.new(name: 'John').attributes # {"age"=>nil, "name"=>"John"}
385
+ person.attributes # {'age'=>20, 'name'=>'John Doe'}
386
+ Person.new(name: 'John').attributes # {'age'=>nil, 'name'=>'John'}
387
+
388
+ #---------------------#
389
+ # #attributes(*names) #
390
+ #---------------------#
391
+
392
+ # Slices the attributes to include only the given keys.
393
+ # Returns a hash containing the given keys (in their types).
394
+
395
+ person.attributes(:age) # {age: 20}
396
+ person.attributes(:age, :name) # {age: 20, name: 'John Doe'}
397
+ person.attributes('age', 'name') # {'age'=>20, 'name'=>'John Doe'}
323
398
  ```
324
399
 
325
- ## Built-in extensions
400
+ [⬆️ Back to Top](#table-of-contents-)
326
401
 
327
- You can use the method `Micro::Attributes.features()` or `Micro::Attributes.with()` to combine and require only the features that better fit your needs.
402
+ # Built-in extensions
328
403
 
329
- Note: If you desire require only one feature, use the `Micro::Attributes.feature()` method.
404
+ You can use the method `Micro::Attributes.with()` to combine and require only the features that better fit your needs.
330
405
 
331
- ```ruby
332
- #----------------------------------#
333
- # Via Micro::Attributes.features() #
334
- #----------------------------------#
406
+ But, if you desire except one or more features, use the `Micro::Attributes.without()` method.
335
407
 
336
- # Loading specific features
408
+ ## Picking specific features
337
409
 
338
- class Job
339
- include Micro::Attributes.feature(:diff)
410
+ ### `Micro::Attributes.with`
340
411
 
341
- attribute :id
342
- attribute :state, 'sleeping'
412
+ ```ruby
413
+ Micro::Attributes.with(:initialize)
343
414
 
344
- def initialize(options)
345
- self.attributes = options
346
- end
347
- end
415
+ Micro::Attributes.with(initialize: :strict)
348
416
 
349
- # Loading all features
350
- # ---
417
+ Micro::Attributes.with(:diff, :initialize)
351
418
 
352
- class Job
353
- include Micro::Attributes.features
419
+ Micro::Attributes.with(:diff, initialize: :strict)
354
420
 
355
- attributes :id, state: 'sleeping'
356
- end
421
+ Micro::Attributes.with(:activemodel_validations)
357
422
 
358
- # Note:
359
- # If `Micro::Attributes.features()` be invoked without arguments, a module with all features will be returned.
423
+ Micro::Attributes.with(:activemodel_validations, :diff)
360
424
 
361
- #----------------------------------------------------------------------------#
362
- # Using the .with() method alias and adding the strict initialize extension. #
363
- #----------------------------------------------------------------------------#
364
- class Job
365
- include Micro::Attributes.with(:strict_initialize, :diff)
425
+ Micro::Attributes.with(:activemodel_validations, :diff, initialize: :strict)
426
+ ```
366
427
 
367
- attributes :id, state: 'sleeping'
428
+ The method `Micro::Attributes.with()` will raise an exception if no arguments/features were declared.
429
+
430
+ ```ruby
431
+ class Job
432
+ include Micro::Attributes.with() # ArgumentError (Invalid feature name! Available options: :activemodel_validations, :diff, :initialize)
368
433
  end
434
+ ```
369
435
 
370
- # Note:
371
- # The method `Micro::Attributes.with()` will raise an exception if no arguments/features were declared.
372
- #
373
- # class Job
374
- # include Micro::Attributes.with() # ArgumentError (Invalid feature name! Available options: diff, initialize, activemodel_validations)
375
- # end
436
+ ### `Micro::Attributes.without`
376
437
 
377
- #===================================#
378
- # Alternatives to the methods above #
379
- #===================================#
438
+ Picking *except* one or more features
380
439
 
381
- #---------------------------------------#
382
- # Via Micro::Attributes.to_initialize() #
383
- #---------------------------------------#
384
- class Job
385
- include Micro::Attributes.to_initialize(diff: true, activemodel_validations: true)
440
+ ```ruby
441
+ Micro::Attributes.without(:diff) # will load :activemodel_validations and initialize: :strict
386
442
 
387
- # Same of `include Micro::Attributes.with(:initialize, :diff, :activemodel_validations)`
388
- end
443
+ Micro::Attributes.without(initialize: :strict) # will load :activemodel_validations and :diff
444
+ ```
389
445
 
390
- #----------------------------------------#
391
- # Via Micro::Attributes.to_initialize!() #
392
- #----------------------------------------#
393
- class Job
394
- include Micro::Attributes.to_initialize!(diff: false, activemodel_validations: true)
446
+ ## Picking all the features
395
447
 
396
- # Same of `include Micro::Attributes.with(:strict_initialize, :activemodel_validations)`
397
- end
448
+ ```ruby
449
+ Micro::Attributes.with_all_features
398
450
  ```
399
451
 
400
- ### ActiveModel::Validations extension
452
+ [⬆️ Back to Top](#table-of-contents-)
453
+
454
+ ## Extensions
401
455
 
402
- If your application uses ActiveModel as a dependency (like a regular Rails app). You will be enabled to use the `actimodel_validations` extension.
456
+ ### `ActiveModel::Validation` extension
457
+
458
+ If your application uses ActiveModel as a dependency (like a regular Rails app). You will be enabled to use the `activemodel_validations` extension.
403
459
 
404
460
  ```ruby
405
461
  class Job
406
- # include Micro::Attributes.with(:initialize, :activemodel_validations)
407
- # include Micro::Attributes.features(:initialize, :activemodel_validations)
408
- include Micro::Attributes.to_initialize(activemodel_validations: true)
462
+ include Micro::Attributes.with(:activemodel_validations)
463
+
464
+ attribute :id
465
+ attribute :state, default: 'sleeping'
409
466
 
410
- attributes :id, state: 'sleeping'
411
467
  validates! :id, :state, presence: true
412
468
  end
413
469
 
@@ -415,10 +471,31 @@ Job.new({}) # ActiveModel::StrictValidationFailed (Id can't be blank)
415
471
 
416
472
  job = Job.new(id: 1)
417
473
 
418
- p job.id # 1
419
- p job.state # "sleeping"
474
+ job.id # 1
475
+ job.state # 'sleeping'
476
+ ```
477
+
478
+ #### `.attribute()` options
479
+
480
+ You can use the `validate` or `validates` options to define your attributes. e.g.
481
+
482
+ ```ruby
483
+ class Job
484
+ include Micro::Attributes.with(:activemodel_validations)
485
+
486
+ attribute :id, validates: { presence: true }
487
+ attribute :state, validate: :must_be_a_filled_string
488
+
489
+ def must_be_a_filled_string
490
+ return if state.is_a?(String) && state.present?
491
+
492
+ errors.add(:state, 'must be a filled string')
493
+ end
494
+ end
420
495
  ```
421
496
 
497
+ [⬆️ Back to Top](#table-of-contents-)
498
+
422
499
  ### Diff extension
423
500
 
424
501
  Provides a way to track changes in your object attributes.
@@ -427,21 +504,20 @@ Provides a way to track changes in your object attributes.
427
504
  require 'securerandom'
428
505
 
429
506
  class Job
430
- # include Micro::Attributes.with(:initialize, :diff)
431
- # include Micro::Attributes.to_initialize(diff: true)
432
- include Micro::Attributes.features(:initialize, :diff)
507
+ include Micro::Attributes.with(:initialize, :diff)
433
508
 
434
- attributes :id, state: 'sleeping'
509
+ attribute :id
510
+ attribute :state, default: 'sleeping'
435
511
  end
436
512
 
437
513
  job = Job.new(id: SecureRandom.uuid())
438
514
 
439
- p job.id # A random UUID generated from SecureRandom.uuid(). e.g: "e68bcc74-b91c-45c2-a904-12f1298cc60e"
440
- p job.state # "sleeping"
515
+ job.id # A random UUID generated from SecureRandom.uuid(). e.g: 'e68bcc74-b91c-45c2-a904-12f1298cc60e'
516
+ job.state # 'sleeping'
441
517
 
442
518
  job_running = job.with_attribute(:state, 'running')
443
519
 
444
- p job_running.state # "running"
520
+ job_running.state # 'running'
445
521
 
446
522
  job_changes = job.diff_attributes(job_running)
447
523
 
@@ -449,50 +525,49 @@ job_changes = job.diff_attributes(job_running)
449
525
  # #present?, #blank?, #empty? #
450
526
  #-----------------------------#
451
527
 
452
- p job_changes.present? # true
453
- p job_changes.blank? # false
454
- p job_changes.empty? # false
528
+ job_changes.present? # true
529
+ job_changes.blank? # false
530
+ job_changes.empty? # false
455
531
 
456
532
  #-----------#
457
533
  # #changed? #
458
534
  #-----------#
459
- p job_changes.changed? # true
535
+ job_changes.changed? # true
460
536
 
461
- p job_changes.changed?(:id) # false
537
+ job_changes.changed?(:id) # false
462
538
 
463
- p job_changes.changed?(:state) # true
464
- p job_changes.changed?(:state, from: 'sleeping', to: 'running') # true
539
+ job_changes.changed?(:state) # true
540
+ job_changes.changed?(:state, from: 'sleeping', to: 'running') # true
465
541
 
466
542
  #----------------#
467
543
  # #differences() #
468
544
  #----------------#
469
- p job_changes.differences # {"state"=> {"from" => "sleeping", "to" => "running"}}
545
+ job_changes.differences # {'state'=> {'from' => 'sleeping', 'to' => 'running'}}
470
546
  ```
471
547
 
548
+ [⬆️ Back to Top](#table-of-contents-)
549
+
472
550
  ### Initialize extension
473
551
 
474
552
  1. Creates a constructor to assign the attributes.
475
- 2. Adds methods to build new instances when some data was assigned.
553
+ 2. Add methods to build new instances when some data was assigned.
476
554
 
477
555
  ```ruby
478
556
  class Job
479
- # include Micro::Attributes.with(:initialize)
480
- # include Micro::Attributes.feature(:initialize)
481
- # include Micro::Attributes.features(:initialize)
482
- include Micro::Attributes.to_initialize
557
+ include Micro::Attributes.with(:initialize)
483
558
 
484
559
  attributes :id, :state
485
560
  end
486
561
 
487
562
  job_null = Job.new({})
488
563
 
489
- p job.id # nil
490
- p job.state # nil
564
+ job.id # nil
565
+ job.state # nil
491
566
 
492
567
  job = Job.new(id: 1, state: 'sleeping')
493
568
 
494
- p job.id # 1
495
- p job.state # "sleeping"
569
+ job.id # 1
570
+ job.state # 'sleeping'
496
571
 
497
572
  ##############################################
498
573
  # Assigning new values to get a new instance #
@@ -504,9 +579,9 @@ p job.state # "sleeping"
504
579
 
505
580
  new_job = job.with_attribute(:state, 'running')
506
581
 
507
- puts new_job.id # 1
508
- puts new_job.state # running
509
- puts new_job.equal?(job) # false
582
+ new_job.id # 1
583
+ new_job.state # running
584
+ new_job.equal?(job) # false
510
585
 
511
586
  #--------------------#
512
587
  # #with_attributes() #
@@ -516,12 +591,14 @@ puts new_job.equal?(job) # false
516
591
 
517
592
  other_job = job.with_attributes(id: 2, state: 'killed')
518
593
 
519
- puts other_job.id # 2
520
- puts other_job.state # killed
521
- puts other_job.equal?(job) # false
594
+ other_job.id # 2
595
+ other_job.state # killed
596
+ other_job.equal?(job) # false
522
597
  ```
523
598
 
524
- ### Strict initialize extension
599
+ [⬆️ Back to Top](#table-of-contents-)
600
+
601
+ #### Strict mode
525
602
 
526
603
  1. Creates a constructor to assign the attributes.
527
604
  2. Adds methods to build new instances when some data was assigned.
@@ -529,16 +606,13 @@ puts other_job.equal?(job) # false
529
606
 
530
607
  ```ruby
531
608
  class Job
532
- # include Micro::Attributes.with(:strict_initialize)
533
- # include Micro::Attributes.feature(:strict_initialize)
534
- # include Micro::Attributes.features(:strict_initialize)
535
- include Micro::Attributes.to_initialize!
609
+ include Micro::Attributes.with(initialize: :strict)
536
610
 
537
611
  attributes :id, :state
538
612
  end
539
- #----------------------------------------------------------------------------#
540
- # The strict_initialize extension will require all the keys when initialize. #
541
- #----------------------------------------------------------------------------#
613
+ #-----------------------------------------------------------------------#
614
+ # The strict initialize mode will require all the keys when initialize. #
615
+ #-----------------------------------------------------------------------#
542
616
 
543
617
  Job.new({})
544
618
 
@@ -551,34 +625,33 @@ Job.new({})
551
625
 
552
626
  job_null = Job.new(id: nil, state: nil)
553
627
 
554
- p job.id # nil
555
- p job.state # nil
628
+ job.id # nil
629
+ job.state # nil
556
630
 
557
631
  job = Job.new(id: 1, state: 'sleeping')
558
632
 
559
- p job.id # 1
560
- p job.state # "sleeping"
633
+ job.id # 1
634
+ job.state # 'sleeping'
635
+ ```
561
636
 
637
+ > **Note**: This extension works like the `initialize` extension. So, look at its section to understand all of the other features.
562
638
 
563
- # Note:
564
- # This extension works like the `initialize` extension.
565
- # So, look at its section to understand all the other features.
566
- ```
639
+ [⬆️ Back to Top](#table-of-contents-)
567
640
 
568
- ## Development
641
+ # Development
569
642
 
570
643
  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.
571
644
 
572
645
  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).
573
646
 
574
- ## Contributing
647
+ # Contributing
575
648
 
576
649
  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.
577
650
 
578
- ## License
651
+ # License
579
652
 
580
653
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
581
654
 
582
- ## Code of Conduct
655
+ # Code of Conduct
583
656
 
584
- 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).
657
+ 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).