u-attributes 1.0.1 → 2.0.1

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: 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).