u-attributes 1.2.0 → 2.2.0

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