u-attributes 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.sh +9 -2
- data/.travis.yml +4 -3
- data/Gemfile +1 -1
- data/LICENSE.txt +2 -2
- data/README.md +285 -234
- data/Rakefile +5 -5
- data/bin/console +4 -4
- data/lib/micro/attributes.rb +58 -40
- data/lib/micro/attributes/diff.rb +52 -0
- data/lib/micro/attributes/features.rb +82 -17
- data/lib/micro/attributes/features/activemodel_validations.rb +17 -23
- data/lib/micro/attributes/features/diff.rb +2 -50
- data/lib/micro/attributes/features/initialize.rb +5 -0
- data/lib/micro/attributes/features/initialize/strict.rb +39 -0
- data/lib/micro/attributes/macros.rb +25 -23
- data/lib/micro/attributes/utils.rb +19 -0
- data/lib/micro/attributes/version.rb +1 -1
- data/lib/micro/attributes/with.rb +9 -13
- data/u-attributes.gemspec +14 -10
- metadata +42 -8
- data/Gemfile.lock +0 -29
- data/lib/micro/attributes/attributes_utils.rb +0 -21
- data/lib/micro/attributes/features/strict_initialize.rb +0 -43
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ac1586077d89b91d2e851ce3f59819d0c474bc809fe9d64eff5d5627bd78a967
|
4
|
+
data.tar.gz: 7246180e49313f3b9038e3bfdd858356dce65a80c7c94aa6173a15afbea9ab90
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1ce2864d28e95139a1fc3e3a08ca08ea2c771eec936b5ff21567fb3f0bae792bd406a4b905c7c5346b7f53de3972a6f01ce4629e50e786935186ae6f93614956
|
7
|
+
data.tar.gz: 28076be013bc9038497297186994d23df2b39b16fb46b2a6c82d59520f3f5de84a3c1f1f89196a6dc073677f0fa339a707e1a2cb10591e9aabfc19dab80036b6
|
data/.gitignore
CHANGED
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
|
-
|
24
|
-
ACTIVEMODEL_VERSION='5.2' bundle
|
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
|
data/.travis.yml
CHANGED
@@ -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
|
-
-
|
24
|
+
- './cc-test-reporter before-build'
|
24
25
|
|
25
|
-
script:
|
26
|
+
script: './.travis.sh'
|
26
27
|
|
27
28
|
after_success:
|
28
|
-
-
|
29
|
+
- './cc-test-reporter after-build -t simplecov'
|
data/Gemfile
CHANGED
data/LICENSE.txt
CHANGED
@@ -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
|
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
|
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,67 +1,67 @@
|
|
1
|
+

|
1
2
|
[](https://rubygems.org/gems/u-attributes)
|
2
3
|
[](https://travis-ci.com/serradura/u-attributes)
|
3
4
|
[](https://codeclimate.com/github/serradura/u-attributes/maintainability)
|
4
5
|
[](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
10
|
This gem allows defining read-only attributes, that is, your objects will have only getters to access their attributes data.
|
10
11
|
|
11
|
-
## Table of contents
|
12
|
-
- [
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
- [
|
18
|
-
- [
|
19
|
-
- [
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
- [
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
- [
|
29
|
-
|
30
|
-
- [
|
12
|
+
## Table of contents <!-- omit in toc -->
|
13
|
+
- [Required Ruby version](#required-ruby-version)
|
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
|
+
- [Strict initializer](#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
|
+
- [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)
|
36
|
+
- [Development](#development)
|
37
|
+
- [Contributing](#contributing)
|
38
|
+
- [License](#license)
|
39
|
+
- [Code of Conduct](#code-of-conduct)
|
40
|
+
|
41
|
+
## Required Ruby version
|
42
|
+
|
43
|
+
> \>= 2.2.0
|
31
44
|
|
32
45
|
## Installation
|
33
46
|
|
34
|
-
Add this line to your application's Gemfile
|
47
|
+
Add this line to your application's Gemfile and `bundle install`:
|
35
48
|
|
36
49
|
```ruby
|
37
50
|
gem 'u-attributes'
|
38
51
|
```
|
39
52
|
|
40
|
-
|
53
|
+
## Compatibility
|
41
54
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
$ gem install u-attributes
|
55
|
+
| u-attributes | branch | ruby | activemodel |
|
56
|
+
| -------------- | ------- | -------- | ------------- |
|
57
|
+
| 2.0.0 | master | >= 2.2.0 | >= 3.2, < 6.1 |
|
58
|
+
| 1.2.0 | v1.x | >= 2.2.0 | >= 3.2, < 6.1 |
|
47
59
|
|
48
60
|
## Usage
|
49
61
|
|
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
|
-
```
|
61
|
-
|
62
62
|
### How to define attributes?
|
63
|
-
```ruby
|
64
63
|
|
64
|
+
```ruby
|
65
65
|
# By default you must to define the class constructor.
|
66
66
|
|
67
67
|
class Person
|
@@ -77,25 +77,25 @@ end
|
|
77
77
|
|
78
78
|
person = Person.new(age: 21)
|
79
79
|
|
80
|
-
|
81
|
-
|
80
|
+
person.name # John Doe
|
81
|
+
person.age # 21
|
82
82
|
|
83
|
-
# By design
|
84
|
-
# If you try to call a setter
|
83
|
+
# By design the attributes are always exposed as reader methods (getters).
|
84
|
+
# If you try to call a setter you will see a NoMethodError.
|
85
85
|
#
|
86
86
|
# person.name = 'Rodrigo'
|
87
|
-
# NoMethodError (undefined method `name=' for #<Person:0x0000... @name=
|
87
|
+
# NoMethodError (undefined method `name=' for #<Person:0x0000... @name='John Doe', @age=21>)
|
88
|
+
```
|
88
89
|
|
89
|
-
|
90
|
-
# self.attributes= #
|
91
|
-
#------------------#
|
90
|
+
#### `Micro::Attributes#attributes=`
|
92
91
|
|
93
|
-
|
92
|
+
This is a protected method to make easier the assignment in a constructor. e.g.
|
94
93
|
|
94
|
+
```ruby
|
95
95
|
class Person
|
96
96
|
include Micro::Attributes
|
97
97
|
|
98
|
-
attribute :name, 'John Doe'
|
98
|
+
attribute :name, default: 'John Doe'
|
99
99
|
attribute :age
|
100
100
|
|
101
101
|
def initialize(options)
|
@@ -105,46 +105,49 @@ end
|
|
105
105
|
|
106
106
|
person = Person.new(age: 20)
|
107
107
|
|
108
|
-
|
109
|
-
|
108
|
+
person.name # John Doe
|
109
|
+
person.age # 20
|
110
|
+
```
|
110
111
|
|
111
|
-
|
112
|
-
# #attribute() #
|
113
|
-
#--------------#
|
114
|
-
#
|
115
|
-
# Use the #attribute() method with a valid attribute name to get its value
|
112
|
+
#### `Micro::Attributes#attribute`
|
116
113
|
|
117
|
-
|
118
|
-
puts person.attribute('age') # 20
|
119
|
-
puts person.attribute('foo') # nil
|
114
|
+
Use this method with a valid attribute name to get its value.
|
120
115
|
|
121
|
-
|
122
|
-
|
116
|
+
```ruby
|
117
|
+
person = Person.new(age: 20)
|
118
|
+
|
119
|
+
person.attribute(:name) # John Doe
|
120
|
+
person.attribute('age') # 20
|
121
|
+
person.attribute('foo') # nil
|
122
|
+
```
|
123
123
|
|
124
|
+
If you pass a block, it will be executed only if the attribute was valid.
|
125
|
+
|
126
|
+
```ruby
|
124
127
|
person.attribute(:name) { |value| puts value } # John Doe
|
125
128
|
person.attribute('age') { |value| puts value } # 20
|
126
|
-
person.attribute('foo') { |value| puts value } # !! Nothing happened, because of the attribute
|
129
|
+
person.attribute('foo') { |value| puts value } # !! Nothing happened, because of the attribute doesn't exist.
|
130
|
+
```
|
127
131
|
|
128
|
-
|
129
|
-
# #attribute!() #
|
130
|
-
#---------------#
|
131
|
-
#
|
132
|
-
# Works like the #attribute() method, but will raise an exception when the attribute not exist.
|
132
|
+
#### `Micro::Attributes#attribute!`
|
133
133
|
|
134
|
-
|
135
|
-
|
134
|
+
Works like the `#attribute` method, but it will raise an exception when the attribute doesn't exist.
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
person.attribute!('foo') # NameError (undefined attribute `foo)
|
138
|
+
|
139
|
+
person.attribute!('foo') { |value| value } # NameError (undefined attribute `foo)
|
136
140
|
```
|
137
141
|
|
138
142
|
### How to define multiple attributes?
|
139
143
|
|
140
|
-
|
141
|
-
|
142
|
-
# Use .attributes with a list of attribute names.
|
144
|
+
Use `.attributes` with a list of attribute names.
|
143
145
|
|
146
|
+
```ruby
|
144
147
|
class Person
|
145
148
|
include Micro::Attributes
|
146
149
|
|
147
|
-
attributes :age, name
|
150
|
+
attributes :age, :name
|
148
151
|
|
149
152
|
def initialize(options)
|
150
153
|
self.attributes = options
|
@@ -153,89 +156,123 @@ end
|
|
153
156
|
|
154
157
|
person = Person.new(age: 32)
|
155
158
|
|
156
|
-
|
157
|
-
|
159
|
+
person.name # nil
|
160
|
+
person.age # 32
|
158
161
|
```
|
159
162
|
|
160
|
-
|
161
|
-
|
163
|
+
> **Note:** This method can't define default values. To do this, use the `#attribute()` method.
|
164
|
+
|
165
|
+
### `Micro::Attributes.with(:initialize)`
|
166
|
+
|
167
|
+
Use `Micro::Attributes.with(:initialize)` to define a constructor to assign the attributes. e.g.
|
162
168
|
|
163
169
|
```ruby
|
164
170
|
class Person
|
165
|
-
include Micro::Attributes.
|
171
|
+
include Micro::Attributes.with(:initialize)
|
166
172
|
|
167
|
-
|
173
|
+
attribute :age
|
174
|
+
attribute :name, default: 'John Doe'
|
168
175
|
end
|
169
176
|
|
170
177
|
person = Person.new(age: 18)
|
171
178
|
|
172
|
-
|
173
|
-
|
179
|
+
person.name # John Doe
|
180
|
+
person.age # 18
|
181
|
+
```
|
174
182
|
|
175
|
-
|
176
|
-
|
177
|
-
##############################################
|
183
|
+
This extension enables two methods for your objects.
|
184
|
+
The `#with_attribute()` and `#with_attributes()`.
|
178
185
|
|
179
|
-
|
180
|
-
# #with_attribute() #
|
181
|
-
#-------------------#
|
186
|
+
#### `#with_attribute()`
|
182
187
|
|
188
|
+
```ruby
|
183
189
|
another_person = person.with_attribute(:age, 21)
|
184
190
|
|
185
|
-
|
186
|
-
|
187
|
-
|
191
|
+
another_person.name # John Doe
|
192
|
+
another_person.age # 21
|
193
|
+
another_person.equal?(person) # false
|
194
|
+
```
|
188
195
|
|
189
|
-
|
190
|
-
# #with_attributes() #
|
191
|
-
#--------------------#
|
192
|
-
#
|
193
|
-
# Use it to assign multiple attributes
|
196
|
+
#### `#with_attributes()`
|
194
197
|
|
198
|
+
Use it to assign multiple attributes
|
199
|
+
```ruby
|
195
200
|
other_person = person.with_attributes(name: 'Serradura', age: 32)
|
196
201
|
|
197
|
-
|
198
|
-
|
199
|
-
|
202
|
+
other_person.name # Serradura
|
203
|
+
other_person.age # 32
|
204
|
+
other_person.equal?(person) # false
|
205
|
+
```
|
200
206
|
|
201
|
-
|
202
|
-
#
|
203
|
-
# Person.new(1)
|
204
|
-
# ArgumentError (argument must be a Hash)
|
207
|
+
If you pass a value different of a Hash, a Kind::Error will be raised.
|
205
208
|
|
206
|
-
|
207
|
-
#
|
208
|
-
|
209
|
+
```ruby
|
210
|
+
Person.new(1) # Kind::Error (1 must be a Hash)
|
211
|
+
```
|
212
|
+
|
213
|
+
### Defining default values to the attributes
|
214
|
+
|
215
|
+
To do this, you only need make use of the `default:` keyword. e.g.
|
216
|
+
|
217
|
+
```ruby
|
218
|
+
class Person
|
219
|
+
include Micro::Attributes.with(:initialize)
|
220
|
+
|
221
|
+
attribute :age
|
222
|
+
attribute :name, default: 'John Doe'
|
223
|
+
end
|
224
|
+
```
|
225
|
+
|
226
|
+
There are 3 different strategies to define default values.
|
227
|
+
1. Pass a regular object, like in the previous example.
|
228
|
+
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`).
|
209
230
|
|
210
|
-
|
231
|
+
```ruby
|
232
|
+
class Person
|
233
|
+
include Micro::Attributes.with(:initialize)
|
211
234
|
|
235
|
+
attribute :age, default: -> age { age&.to_i }
|
236
|
+
attribute :name, default: -> name { String(name || 'John Doe').strip }
|
237
|
+
end
|
238
|
+
```
|
239
|
+
|
240
|
+
### Strict initializer
|
241
|
+
|
242
|
+
Use `.with(initialize: :strict)` to forbids an instantiation without all the attribute keywords. e.g.
|
243
|
+
|
244
|
+
```ruby
|
212
245
|
class StrictPerson
|
213
|
-
include Micro::Attributes.
|
246
|
+
include Micro::Attributes.with(initialize: :strict)
|
214
247
|
|
215
|
-
|
248
|
+
attribute :age
|
249
|
+
attribute :name, default: 'John Doe'
|
216
250
|
end
|
217
251
|
|
218
|
-
StrictPerson.new({})
|
252
|
+
StrictPerson.new({}) # ArgumentError (missing keyword: :age)
|
253
|
+
```
|
219
254
|
|
220
|
-
|
221
|
-
# ArgumentError (missing keyword: :age)
|
255
|
+
An attribute with a default value can be omitted.
|
222
256
|
|
257
|
+
``` ruby
|
223
258
|
person_without_age = StrictPerson.new(age: nil)
|
224
259
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
# Except for this validation when initializing,
|
229
|
-
# the `to_initialize!` method will works in the same ways of `to_initialize`.
|
260
|
+
person_without_age.name # 'John Doe'
|
261
|
+
person_without_age.age # nil
|
230
262
|
```
|
231
263
|
|
232
|
-
|
264
|
+
> **Note:** Except for this validation the `.with(initialize: :strict)` method will works in the same ways of `.with(:initialize)`.
|
265
|
+
|
266
|
+
### Is it possible to inherit the attributes?
|
267
|
+
|
268
|
+
Yes. e.g.
|
233
269
|
|
234
270
|
```ruby
|
235
271
|
class Person
|
236
|
-
include Micro::Attributes.
|
272
|
+
include Micro::Attributes.with(:initialize)
|
237
273
|
|
238
|
-
|
274
|
+
attribute :age
|
275
|
+
attribute :name, default: 'John Doe'
|
239
276
|
end
|
240
277
|
|
241
278
|
class Subclass < Person # Will preserve the parent class attributes
|
@@ -244,33 +281,34 @@ end
|
|
244
281
|
|
245
282
|
instance = Subclass.new({})
|
246
283
|
|
247
|
-
|
248
|
-
|
249
|
-
|
284
|
+
instance.name # John Doe
|
285
|
+
instance.respond_to?(:age) # true
|
286
|
+
instance.respond_to?(:foo) # true
|
287
|
+
```
|
250
288
|
|
251
|
-
|
252
|
-
# .attribute!() or .attributes!() #
|
253
|
-
#---------------------------------#
|
289
|
+
#### .attribute!()
|
254
290
|
|
255
|
-
|
291
|
+
This method allows us to redefine the attributes default data that was defined in the parent class. e.g.
|
256
292
|
|
293
|
+
```ruby
|
257
294
|
class AnotherSubclass < Person
|
258
|
-
attribute! :name, 'Alfa'
|
295
|
+
attribute! :name, default: 'Alfa'
|
259
296
|
end
|
260
297
|
|
261
298
|
alfa_person = AnotherSubclass.new({})
|
262
299
|
|
263
|
-
|
264
|
-
|
300
|
+
alfa_person.name # 'Alfa'
|
301
|
+
alfa_person.age # nil
|
265
302
|
|
266
303
|
class SubSubclass < Subclass
|
267
|
-
|
304
|
+
attribute! :age, default: 0
|
305
|
+
attribute! :name, default: 'Beta'
|
268
306
|
end
|
269
307
|
|
270
308
|
beta_person = SubSubclass.new({})
|
271
309
|
|
272
|
-
|
273
|
-
|
310
|
+
beta_person.name # 'Beta'
|
311
|
+
beta_person.age # 0
|
274
312
|
```
|
275
313
|
|
276
314
|
### How to query the attributes?
|
@@ -279,7 +317,8 @@ p beta_person.age # 0
|
|
279
317
|
class Person
|
280
318
|
include Micro::Attributes
|
281
319
|
|
282
|
-
|
320
|
+
attribute :age
|
321
|
+
attribute :name, default: 'John Doe'
|
283
322
|
|
284
323
|
def initialize(options)
|
285
324
|
self.attributes = options
|
@@ -290,16 +329,16 @@ end
|
|
290
329
|
# .attributes() #
|
291
330
|
#---------------#
|
292
331
|
|
293
|
-
|
332
|
+
Person.attributes # ['name', 'age']
|
294
333
|
|
295
334
|
#---------------#
|
296
335
|
# .attribute?() #
|
297
336
|
#---------------#
|
298
337
|
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
338
|
+
Person.attribute?(:name) # true
|
339
|
+
Person.attribute?('name') # true
|
340
|
+
Person.attribute?('foo') # false
|
341
|
+
Person.attribute?(:foo) # false
|
303
342
|
|
304
343
|
# ---
|
305
344
|
|
@@ -309,62 +348,72 @@ person = Person.new(age: 20)
|
|
309
348
|
# #attribute?() #
|
310
349
|
#---------------#
|
311
350
|
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
351
|
+
person.attribute?(:name) # true
|
352
|
+
person.attribute?('name') # true
|
353
|
+
person.attribute?('foo') # false
|
354
|
+
person.attribute?(:foo) # false
|
316
355
|
|
317
356
|
#---------------#
|
318
357
|
# #attributes() #
|
319
358
|
#---------------#
|
320
359
|
|
321
|
-
|
322
|
-
|
360
|
+
person.attributes # {'age'=>20, 'name'=>'John Doe'}
|
361
|
+
Person.new(name: 'John').attributes # {'age'=>nil, 'name'=>'John'}
|
362
|
+
|
363
|
+
#---------------------#
|
364
|
+
# #attributes(*names) #
|
365
|
+
#---------------------#
|
366
|
+
|
367
|
+
# Slices the attributes to include only the given keys.
|
368
|
+
# Returns a hash containing the given keys (in their types).
|
369
|
+
|
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'}
|
323
373
|
```
|
324
374
|
|
325
375
|
## Built-in extensions
|
326
376
|
|
327
|
-
You can use the method `Micro::Attributes.
|
377
|
+
You can use the method `Micro::Attributes.with()` to combine and require only the features that better fit your needs.
|
328
378
|
|
329
|
-
|
379
|
+
But, if you desire except one or more features, use the `Micro::Attributes.without()` method.
|
330
380
|
|
331
381
|
```ruby
|
332
|
-
|
333
|
-
#
|
334
|
-
|
335
|
-
|
336
|
-
# Loading specific features
|
382
|
+
#===========================#
|
383
|
+
# Loading specific features #
|
384
|
+
#===========================#
|
337
385
|
|
338
386
|
class Job
|
339
|
-
include Micro::Attributes.
|
387
|
+
include Micro::Attributes.with(:diff)
|
340
388
|
|
341
389
|
attribute :id
|
342
|
-
attribute :state, 'sleeping'
|
390
|
+
attribute :state, default: 'sleeping'
|
343
391
|
|
344
392
|
def initialize(options)
|
345
393
|
self.attributes = options
|
346
394
|
end
|
347
395
|
end
|
348
396
|
|
349
|
-
|
350
|
-
#
|
397
|
+
#======================#
|
398
|
+
# Loading all features #
|
399
|
+
# --- #
|
400
|
+
#======================#
|
351
401
|
|
352
402
|
class Job
|
353
|
-
include Micro::Attributes.
|
403
|
+
include Micro::Attributes.with_all_features
|
354
404
|
|
355
|
-
|
405
|
+
attribute :id
|
406
|
+
attribute :state, default: 'sleeping'
|
356
407
|
end
|
357
408
|
|
358
|
-
# Note:
|
359
|
-
# If `Micro::Attributes.features()` be invoked without arguments, a module with all features will be returned.
|
360
|
-
|
361
409
|
#----------------------------------------------------------------------------#
|
362
410
|
# Using the .with() method alias and adding the strict initialize extension. #
|
363
411
|
#----------------------------------------------------------------------------#
|
364
412
|
class Job
|
365
|
-
include Micro::Attributes.with(:
|
413
|
+
include Micro::Attributes.with(:diff, initialize: :strict)
|
366
414
|
|
367
|
-
|
415
|
+
attribute :id
|
416
|
+
attribute :state, default: 'sleeping'
|
368
417
|
end
|
369
418
|
|
370
419
|
# Note:
|
@@ -374,27 +423,20 @@ end
|
|
374
423
|
# include Micro::Attributes.with() # ArgumentError (Invalid feature name! Available options: diff, initialize, activemodel_validations)
|
375
424
|
# end
|
376
425
|
|
377
|
-
|
378
|
-
#
|
379
|
-
|
426
|
+
#=====================================#
|
427
|
+
# Loading except one or more features #
|
428
|
+
# ----- #
|
429
|
+
#=====================================#
|
380
430
|
|
381
|
-
#---------------------------------------#
|
382
|
-
# Via Micro::Attributes.to_initialize() #
|
383
|
-
#---------------------------------------#
|
384
431
|
class Job
|
385
|
-
include Micro::Attributes.
|
432
|
+
include Micro::Attributes.without(:diff)
|
386
433
|
|
387
|
-
|
434
|
+
attribute :id
|
435
|
+
attribute :state, default: 'sleeping'
|
388
436
|
end
|
389
437
|
|
390
|
-
|
391
|
-
#
|
392
|
-
#----------------------------------------#
|
393
|
-
class Job
|
394
|
-
include Micro::Attributes.to_initialize!(diff: false, activemodel_validations: true)
|
395
|
-
|
396
|
-
# Same of `include Micro::Attributes.with(:strict_initialize, :activemodel_validations)`
|
397
|
-
end
|
438
|
+
# Note:
|
439
|
+
# The method `Micro::Attributes.without()` returns `Micro::Attributes` if all features extensions were used.
|
398
440
|
```
|
399
441
|
|
400
442
|
### ActiveModel::Validations extension
|
@@ -403,11 +445,11 @@ If your application uses ActiveModel as a dependency (like a regular Rails app).
|
|
403
445
|
|
404
446
|
```ruby
|
405
447
|
class Job
|
406
|
-
|
407
|
-
|
408
|
-
|
448
|
+
include Micro::Attributes.with(:activemodel_validations)
|
449
|
+
|
450
|
+
attribute :id
|
451
|
+
attribute :state, default: 'sleeping'
|
409
452
|
|
410
|
-
attributes :id, state: 'sleeping'
|
411
453
|
validates! :id, :state, presence: true
|
412
454
|
end
|
413
455
|
|
@@ -415,8 +457,27 @@ Job.new({}) # ActiveModel::StrictValidationFailed (Id can't be blank)
|
|
415
457
|
|
416
458
|
job = Job.new(id: 1)
|
417
459
|
|
418
|
-
|
419
|
-
|
460
|
+
job.id # 1
|
461
|
+
job.state # 'sleeping'
|
462
|
+
```
|
463
|
+
|
464
|
+
#### Attribute options
|
465
|
+
|
466
|
+
You can use the `validate` or `validates` options to define your attributes. e.g.
|
467
|
+
|
468
|
+
```ruby
|
469
|
+
class Job
|
470
|
+
include Micro::Attributes.with(:activemodel_validations)
|
471
|
+
|
472
|
+
attribute :id, validates: { presence: true }
|
473
|
+
attribute :state, validate: :must_be_a_filled_string
|
474
|
+
|
475
|
+
def must_be_a_string
|
476
|
+
return if state.is_a?(String) && state.present?
|
477
|
+
|
478
|
+
errors.add(:state, 'must be a filled string')
|
479
|
+
end
|
480
|
+
end
|
420
481
|
```
|
421
482
|
|
422
483
|
### Diff extension
|
@@ -427,21 +488,20 @@ Provides a way to track changes in your object attributes.
|
|
427
488
|
require 'securerandom'
|
428
489
|
|
429
490
|
class Job
|
430
|
-
|
431
|
-
# include Micro::Attributes.to_initialize(diff: true)
|
432
|
-
include Micro::Attributes.features(:initialize, :diff)
|
491
|
+
include Micro::Attributes.with(:initialize, :diff)
|
433
492
|
|
434
|
-
|
493
|
+
attribute :id
|
494
|
+
attribute :state, default: 'sleeping'
|
435
495
|
end
|
436
496
|
|
437
497
|
job = Job.new(id: SecureRandom.uuid())
|
438
498
|
|
439
|
-
|
440
|
-
|
499
|
+
job.id # A random UUID generated from SecureRandom.uuid(). e.g: 'e68bcc74-b91c-45c2-a904-12f1298cc60e'
|
500
|
+
job.state # 'sleeping'
|
441
501
|
|
442
502
|
job_running = job.with_attribute(:state, 'running')
|
443
503
|
|
444
|
-
|
504
|
+
job_running.state # 'running'
|
445
505
|
|
446
506
|
job_changes = job.diff_attributes(job_running)
|
447
507
|
|
@@ -449,50 +509,47 @@ job_changes = job.diff_attributes(job_running)
|
|
449
509
|
# #present?, #blank?, #empty? #
|
450
510
|
#-----------------------------#
|
451
511
|
|
452
|
-
|
453
|
-
|
454
|
-
|
512
|
+
job_changes.present? # true
|
513
|
+
job_changes.blank? # false
|
514
|
+
job_changes.empty? # false
|
455
515
|
|
456
516
|
#-----------#
|
457
517
|
# #changed? #
|
458
518
|
#-----------#
|
459
|
-
|
519
|
+
job_changes.changed? # true
|
460
520
|
|
461
|
-
|
521
|
+
job_changes.changed?(:id) # false
|
462
522
|
|
463
|
-
|
464
|
-
|
523
|
+
job_changes.changed?(:state) # true
|
524
|
+
job_changes.changed?(:state, from: 'sleeping', to: 'running') # true
|
465
525
|
|
466
526
|
#----------------#
|
467
527
|
# #differences() #
|
468
528
|
#----------------#
|
469
|
-
|
529
|
+
job_changes.differences # {'state'=> {'from' => 'sleeping', 'to' => 'running'}}
|
470
530
|
```
|
471
531
|
|
472
532
|
### Initialize extension
|
473
533
|
|
474
534
|
1. Creates a constructor to assign the attributes.
|
475
|
-
2.
|
535
|
+
2. Add methods to build new instances when some data was assigned.
|
476
536
|
|
477
537
|
```ruby
|
478
538
|
class Job
|
479
|
-
|
480
|
-
# include Micro::Attributes.feature(:initialize)
|
481
|
-
# include Micro::Attributes.features(:initialize)
|
482
|
-
include Micro::Attributes.to_initialize
|
539
|
+
include Micro::Attributes.with(:initialize)
|
483
540
|
|
484
541
|
attributes :id, :state
|
485
542
|
end
|
486
543
|
|
487
544
|
job_null = Job.new({})
|
488
545
|
|
489
|
-
|
490
|
-
|
546
|
+
job.id # nil
|
547
|
+
job.state # nil
|
491
548
|
|
492
549
|
job = Job.new(id: 1, state: 'sleeping')
|
493
550
|
|
494
|
-
|
495
|
-
|
551
|
+
job.id # 1
|
552
|
+
job.state # 'sleeping'
|
496
553
|
|
497
554
|
##############################################
|
498
555
|
# Assigning new values to get a new instance #
|
@@ -504,9 +561,9 @@ p job.state # "sleeping"
|
|
504
561
|
|
505
562
|
new_job = job.with_attribute(:state, 'running')
|
506
563
|
|
507
|
-
|
508
|
-
|
509
|
-
|
564
|
+
new_job.id # 1
|
565
|
+
new_job.state # running
|
566
|
+
new_job.equal?(job) # false
|
510
567
|
|
511
568
|
#--------------------#
|
512
569
|
# #with_attributes() #
|
@@ -516,12 +573,12 @@ puts new_job.equal?(job) # false
|
|
516
573
|
|
517
574
|
other_job = job.with_attributes(id: 2, state: 'killed')
|
518
575
|
|
519
|
-
|
520
|
-
|
521
|
-
|
576
|
+
other_job.id # 2
|
577
|
+
other_job.state # killed
|
578
|
+
other_job.equal?(job) # false
|
522
579
|
```
|
523
580
|
|
524
|
-
### Strict initialize
|
581
|
+
### Strict initialize mode
|
525
582
|
|
526
583
|
1. Creates a constructor to assign the attributes.
|
527
584
|
2. Adds methods to build new instances when some data was assigned.
|
@@ -529,16 +586,13 @@ puts other_job.equal?(job) # false
|
|
529
586
|
|
530
587
|
```ruby
|
531
588
|
class Job
|
532
|
-
|
533
|
-
# include Micro::Attributes.feature(:strict_initialize)
|
534
|
-
# include Micro::Attributes.features(:strict_initialize)
|
535
|
-
include Micro::Attributes.to_initialize!
|
589
|
+
include Micro::Attributes.with(initialize: :strict)
|
536
590
|
|
537
591
|
attributes :id, :state
|
538
592
|
end
|
539
|
-
|
540
|
-
# The
|
541
|
-
|
593
|
+
#-----------------------------------------------------------------------#
|
594
|
+
# The strict initialize mode will require all the keys when initialize. #
|
595
|
+
#-----------------------------------------------------------------------#
|
542
596
|
|
543
597
|
Job.new({})
|
544
598
|
|
@@ -549,22 +603,19 @@ Job.new({})
|
|
549
603
|
# Samples passing some data #
|
550
604
|
#---------------------------#
|
551
605
|
|
552
|
-
job_null = Job.new(
|
606
|
+
job_null = Job.new(id: nil, state: nil)
|
553
607
|
|
554
|
-
|
555
|
-
|
608
|
+
job.id # nil
|
609
|
+
job.state # nil
|
556
610
|
|
557
611
|
job = Job.new(id: 1, state: 'sleeping')
|
558
612
|
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
# Note:
|
564
|
-
# This extension works like the `initialize` extension.
|
565
|
-
# So, look at its section to understand all the other features.
|
613
|
+
job.id # 1
|
614
|
+
job.state # 'sleeping'
|
566
615
|
```
|
567
616
|
|
617
|
+
> **Note**: This extension works like the `initialize` extension. So, look at its section to understand all of the other features.
|
618
|
+
|
568
619
|
## Development
|
569
620
|
|
570
621
|
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.
|