u-attributes 1.0.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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 +334 -261
- 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 +19 -12
- metadata +47 -11
- 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: bc53b00949ac8d0d8d8572443936ba537a637569be77e0b105a0b98f2d1ae9d0
|
4
|
+
data.tar.gz: 94f42f6da3c2cba68f6565e1bb413b5692bd700baa077555cd421e4aeb82e2ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e5617a5fd34458de945dae2874bed24013e682871f19b70063e734156f2528e8b249e615632f90c978c203abf876560de9717940f9b81e48271f84a956456199
|
7
|
+
data.tar.gz: 50b19483c9e8f88169101306be67a34e4e8c6a26358b187fd119b41ca26e83d461619d6a149bfc898ab7b1208c6af6791b58acf039a3d92d0e8f912e007232b9
|
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,74 +1,79 @@
|
|
1
|
+

|
1
2
|
[](https://rubygems.org/gems/u-attributes)
|
2
|
-
[](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
|
-
This gem allows
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
- [
|
18
|
-
- [
|
19
|
-
- [
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
- [
|
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
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
40
|
+
- [Strict mode](#strict-mode)
|
41
|
+
- [Development](#development)
|
42
|
+
- [Contributing](#contributing)
|
43
|
+
- [License](#license)
|
44
|
+
- [Code of Conduct](#code-of-conduct)
|
31
45
|
|
32
|
-
|
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
|
-
|
54
|
+
# Compatibility
|
41
55
|
|
42
|
-
|
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
|
-
|
61
|
+
> **Note**: The activemodel is an optional dependency, this module [can be enabled](#activemodelvalidation-extension) to validate the attributes.
|
45
62
|
|
46
|
-
|
63
|
+
[⬆️ Back to Top](#table-of-contents-)
|
47
64
|
|
48
|
-
|
65
|
+
# Usage
|
49
66
|
|
50
|
-
|
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
|
-
|
81
|
-
|
85
|
+
person.age # 21
|
86
|
+
person.name # John Doe
|
82
87
|
|
83
|
-
# By design
|
84
|
-
# If you try to call a setter
|
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=
|
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
|
-
#
|
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
|
-
|
109
|
-
|
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
|
-
|
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
|
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
|
-
|
135
|
-
person.attribute!('foo') { |value| puts value } # NameError (undefined attribute `foo)
|
136
|
-
```
|
143
|
+
### `Micro::Attributes#attribute!`
|
137
144
|
|
138
|
-
|
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
|
-
|
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
|
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
|
-
|
157
|
-
|
172
|
+
person.name # nil
|
173
|
+
person.age # 32
|
158
174
|
```
|
159
175
|
|
160
|
-
|
161
|
-
|
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.
|
186
|
+
include Micro::Attributes.with(:initialize)
|
166
187
|
|
167
|
-
|
188
|
+
attribute :age
|
189
|
+
attribute :name, default: 'John Doe'
|
168
190
|
end
|
169
191
|
|
170
192
|
person = Person.new(age: 18)
|
171
193
|
|
172
|
-
|
173
|
-
|
194
|
+
person.age # 18
|
195
|
+
person.name # John Doe
|
196
|
+
```
|
174
197
|
|
175
|
-
|
176
|
-
|
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
|
-
|
186
|
-
|
187
|
-
|
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
|
-
|
198
|
-
|
199
|
-
|
217
|
+
other_person.age # 32
|
218
|
+
other_person.name # Serradura
|
219
|
+
other_person.equal?(person) # false
|
220
|
+
```
|
200
221
|
|
201
|
-
|
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
|
-
#
|
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
|
-
|
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.
|
265
|
+
include Micro::Attributes.with(initialize: :strict)
|
214
266
|
|
215
|
-
|
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
|
-
|
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
|
-
|
226
|
-
|
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
|
-
|
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.
|
293
|
+
include Micro::Attributes.with(:initialize)
|
237
294
|
|
238
|
-
|
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
|
-
|
248
|
-
|
249
|
-
|
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
|
-
|
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
|
-
|
264
|
-
|
323
|
+
alfa_person.name # 'Alfa'
|
324
|
+
alfa_person.age # nil
|
265
325
|
|
266
326
|
class SubSubclass < Subclass
|
267
|
-
|
327
|
+
attribute! :age, default: 0
|
328
|
+
attribute! :name, default: 'Beta'
|
268
329
|
end
|
269
330
|
|
270
331
|
beta_person = SubSubclass.new({})
|
271
332
|
|
272
|
-
|
273
|
-
|
333
|
+
beta_person.name # 'Beta'
|
334
|
+
beta_person.age # 0
|
274
335
|
```
|
275
336
|
|
276
|
-
|
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
|
-
|
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
|
-
|
357
|
+
Person.attributes # ['name', 'age']
|
294
358
|
|
295
359
|
#---------------#
|
296
360
|
# .attribute?() #
|
297
361
|
#---------------#
|
298
362
|
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
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
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
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
|
-
|
322
|
-
|
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
|
-
|
400
|
+
[⬆️ Back to Top](#table-of-contents-)
|
326
401
|
|
327
|
-
|
402
|
+
# Built-in extensions
|
328
403
|
|
329
|
-
|
404
|
+
You can use the method `Micro::Attributes.with()` to combine and require only the features that better fit your needs.
|
330
405
|
|
331
|
-
|
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
|
-
|
408
|
+
## Picking specific features
|
337
409
|
|
338
|
-
|
339
|
-
include Micro::Attributes.feature(:diff)
|
410
|
+
### `Micro::Attributes.with`
|
340
411
|
|
341
|
-
|
342
|
-
|
412
|
+
```ruby
|
413
|
+
Micro::Attributes.with(:initialize)
|
343
414
|
|
344
|
-
|
345
|
-
self.attributes = options
|
346
|
-
end
|
347
|
-
end
|
415
|
+
Micro::Attributes.with(initialize: :strict)
|
348
416
|
|
349
|
-
|
350
|
-
# ---
|
417
|
+
Micro::Attributes.with(:diff, :initialize)
|
351
418
|
|
352
|
-
|
353
|
-
include Micro::Attributes.features
|
419
|
+
Micro::Attributes.with(:diff, initialize: :strict)
|
354
420
|
|
355
|
-
|
356
|
-
end
|
421
|
+
Micro::Attributes.with(:activemodel_validations)
|
357
422
|
|
358
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
388
|
-
|
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
|
-
|
397
|
-
|
448
|
+
```ruby
|
449
|
+
Micro::Attributes.with_all_features
|
398
450
|
```
|
399
451
|
|
400
|
-
|
452
|
+
[⬆️ Back to Top](#table-of-contents-)
|
453
|
+
|
454
|
+
## Extensions
|
401
455
|
|
402
|
-
|
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
|
-
|
407
|
-
|
408
|
-
|
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
|
-
|
419
|
-
|
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
|
-
|
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
|
-
|
509
|
+
attribute :id
|
510
|
+
attribute :state, default: 'sleeping'
|
435
511
|
end
|
436
512
|
|
437
513
|
job = Job.new(id: SecureRandom.uuid())
|
438
514
|
|
439
|
-
|
440
|
-
|
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
|
-
|
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
|
-
|
453
|
-
|
454
|
-
|
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
|
-
|
535
|
+
job_changes.changed? # true
|
460
536
|
|
461
|
-
|
537
|
+
job_changes.changed?(:id) # false
|
462
538
|
|
463
|
-
|
464
|
-
|
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
|
-
|
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.
|
553
|
+
2. Add methods to build new instances when some data was assigned.
|
476
554
|
|
477
555
|
```ruby
|
478
556
|
class Job
|
479
|
-
|
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
|
-
|
490
|
-
|
564
|
+
job.id # nil
|
565
|
+
job.state # nil
|
491
566
|
|
492
567
|
job = Job.new(id: 1, state: 'sleeping')
|
493
568
|
|
494
|
-
|
495
|
-
|
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
|
-
|
508
|
-
|
509
|
-
|
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
|
-
|
520
|
-
|
521
|
-
|
594
|
+
other_job.id # 2
|
595
|
+
other_job.state # killed
|
596
|
+
other_job.equal?(job) # false
|
522
597
|
```
|
523
598
|
|
524
|
-
|
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
|
-
|
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
|
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
|
-
|
555
|
-
|
628
|
+
job.id # nil
|
629
|
+
job.state # nil
|
556
630
|
|
557
631
|
job = Job.new(id: 1, state: 'sleeping')
|
558
632
|
|
559
|
-
|
560
|
-
|
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
|
-
#
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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/
|
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).
|