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