u-attributes 1.1.0 → 2.1.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: a9b3e7bb30f3fc6eb465c26378b99146dc3762f191f8554220bad22fd760cd21
4
- data.tar.gz: 3c8de349ac5e966911877ca5c71f00ac5471f8b838323dd52b85a59e05b686a1
3
+ metadata.gz: d105f8057ad000b717972d8b2b5624d3f5d7608256f82402878ae0929c6462c7
4
+ data.tar.gz: 20f7b0132c445194fb3189b81c7c29c482a2fbeffdb1574444f833ede5619308
5
5
  SHA512:
6
- metadata.gz: 14fbd07d3cbc6e31293417dba289b8bd91e0c4c1de7d74692f9b5498f7e016dcb4463ef70f0e6ff94e703e6e87ea514bbbd4ad36cf566367d2785533578f2e0c
7
- data.tar.gz: ca9669124566003aad1d5cc520b9dcab7ec866a170b2561908a49eb8c236746352827e7fe65354584cd42120435ba7dde4723046dc6fc8d124491db000a885d1
6
+ metadata.gz: 7d5e1d415a6b94dfa9c90aad9f8700829696d1e1b2fa054d6bf62c9eac477f618604c59695b55c3a18dd2405b28c4af63c886057f2ef740cba747f19b2273706
7
+ data.tar.gz: 4493325bbeed30c82c13690ad35d87926a9913b3b33d18cc0ef2dedae8eca2e976fc43d2485bb65715b2cb6c51bc383dfd8b0c59b50885bd5099624b568455d6
data/.gitignore CHANGED
@@ -6,3 +6,4 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ Gemfile.lock
data/.travis.sh CHANGED
@@ -20,5 +20,12 @@ ACTIVEMODEL_VERSION='5.0' bundle exec rake test
20
20
  ACTIVEMODEL_VERSION='5.1' bundle update
21
21
  ACTIVEMODEL_VERSION='5.1' bundle exec rake test
22
22
 
23
- ACTIVEMODEL_VERSION='5.2' bundle update
24
- ACTIVEMODEL_VERSION='5.2' bundle exec rake test
23
+ if [[ ! $ruby_v =~ '2.2.0' ]]; then
24
+ ACTIVEMODEL_VERSION='5.2' bundle update
25
+ ACTIVEMODEL_VERSION='5.2' bundle exec rake test
26
+ fi
27
+
28
+ if [[ $ruby_v =~ '2.5.' ]] || [[ $ruby_v =~ '2.6.' ]] || [[ $ruby_v =~ '2.7.' ]]; then
29
+ ACTIVEMODEL_VERSION='6.0' bundle update
30
+ ACTIVEMODEL_VERSION='6.0' bundle exec rake test
31
+ fi
@@ -8,6 +8,7 @@ rvm:
8
8
  - 2.4.0
9
9
  - 2.5.0
10
10
  - 2.6.0
11
+ - 2.7.0
11
12
 
12
13
  cache: bundler
13
14
 
@@ -20,9 +21,9 @@ install: bundle install --jobs=3 --retry=3
20
21
  before_script:
21
22
  - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
22
23
  - chmod +x ./cc-test-reporter
23
- - "./cc-test-reporter before-build"
24
+ - './cc-test-reporter before-build'
24
25
 
25
- script: "./.travis.sh"
26
+ script: './.travis.sh'
26
27
 
27
28
  after_success:
28
- - "./cc-test-reporter after-build -t simplecov"
29
+ - './cc-test-reporter after-build -t simplecov'
data/Gemfile CHANGED
@@ -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,74 +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
- - [Installation](#installation)
15
- - [Usage](#usage)
16
- - [How to require?](#how-to-require)
17
- - [How to define attributes?](#how-to-define-attributes)
18
- - [How to define multiple attributes?](#how-to-define-multiple-attributes)
19
- - [How to define attributes with a constructor to assign them?](#how-to-define-attributes-with-a-constructor-to-assign-them)
20
- - [How to inherit the attributes?](#how-to-inherit-the-attributes)
21
- - [How to query the attributes?](#how-to-query-the-attributes)
22
- - [Built-in extensions](#built-in-extensions)
23
- - [ActiveModel::Validations extension](#activemodelvalidations-extension)
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)
24
58
  - [Diff extension](#diff-extension)
25
59
  - [Initialize extension](#initialize-extension)
26
- - [Strict initialize extension](#strict-initialize-extension)
27
- - [Development](#development)
28
- - [Contributing](#contributing)
29
- - [License](#license)
30
- - [Code of Conduct](#code-of-conduct)
60
+ - [Strict mode](#strict-mode)
61
+ - [Development](#development)
62
+ - [Contributing](#contributing)
63
+ - [License](#license)
64
+ - [Code of Conduct](#code-of-conduct)
31
65
 
32
- ## Installation
66
+ # Installation
33
67
 
34
- Add this line to your application's Gemfile:
68
+ Add this line to your application's Gemfile and `bundle install`:
35
69
 
36
70
  ```ruby
37
71
  gem 'u-attributes'
38
72
  ```
39
73
 
40
- And then execute:
74
+ # Compatibility
41
75
 
42
- $ bundle
76
+ | u-attributes | branch | ruby | activemodel |
77
+ | -------------- | ------- | -------- | ------------- |
78
+ | 2.1.0 | main | >= 2.2.0 | >= 3.2, < 6.1 |
79
+ | 1.2.0 | v1.x | >= 2.2.0 | >= 3.2, < 6.1 |
43
80
 
44
- Or install it yourself as:
81
+ > **Note**: The activemodel is an optional dependency, this module [can be enabled](#activemodelvalidation-extension) to validate the attributes.
45
82
 
46
- $ gem install u-attributes
83
+ [⬆️ Back to Top](#table-of-contents-)
47
84
 
48
- ## Usage
85
+ # Usage
49
86
 
50
- ### How to require?
51
- ```ruby
52
- # Bundler will do it automatically, but if you desire to do a manual require.
53
- # Use one of the following options:
54
-
55
- require 'micro/attributes'
56
-
57
- # or
87
+ ## How to define attributes?
58
88
 
59
- require 'u-attributes'
60
- ```
89
+ By default, you must define the class constructor.
61
90
 
62
- ### How to define attributes?
63
91
  ```ruby
64
-
65
- # By default you must to define the class constructor.
66
-
67
92
  class Person
68
93
  include Micro::Attributes
69
94
 
70
- attribute :name
71
95
  attribute :age
96
+ attribute :name
72
97
 
73
98
  def initialize(name: 'John Doe', age:)
74
99
  @name, @age = name, age
@@ -77,26 +102,28 @@ end
77
102
 
78
103
  person = Person.new(age: 21)
79
104
 
80
- puts person.name # John Doe
81
- puts person.age # 21
105
+ person.age # 21
106
+ person.name # John Doe
82
107
 
83
- # By design, the attributes expose only reader methods (getters).
84
- # 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.
85
110
  #
86
111
  # person.name = 'Rodrigo'
87
- # 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
+ ```
88
114
 
89
- #------------------#
90
- # self.attributes= #
91
- #------------------#
115
+ [⬆️ Back to Top](#table-of-contents-)
92
116
 
93
- # This protected method is added to make easier the assignment in a constructor.
117
+ ### `Micro::Attributes#attributes=`
94
118
 
119
+ This is a protected method to make easier the assignment in a constructor. e.g.
120
+
121
+ ```ruby
95
122
  class Person
96
123
  include Micro::Attributes
97
124
 
98
- attribute :name, 'John Doe' # .attribute() accepts a second arg as its default value
99
125
  attribute :age
126
+ attribute :name, default: 'John Doe'
100
127
 
101
128
  def initialize(options)
102
129
  self.attributes = options
@@ -105,46 +132,115 @@ end
105
132
 
106
133
  person = Person.new(age: 20)
107
134
 
108
- puts person.name # John Doe
109
- puts person.age # 20
135
+ person.age # 20
136
+ person.name # John Doe
137
+ ```
110
138
 
111
- #--------------#
112
- # #attribute() #
113
- #--------------#
114
- #
115
- # Use the #attribute() method with a valid attribute name to get its value
139
+ #### How to extract attributes from an object or hash?
116
140
 
117
- puts person.attribute(:name) # John Doe
118
- puts person.attribute('age') # 20
119
- 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`.
120
143
 
121
- #
122
- # 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'
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
123
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
124
216
  person.attribute(:name) { |value| puts value } # John Doe
125
217
  person.attribute('age') { |value| puts value } # 20
126
- 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
+ ```
127
220
 
128
- #---------------#
129
- # #attribute!() #
130
- #---------------#
131
- #
132
- # Works like the #attribute() method, but will raise an exception when the attribute not exist.
221
+ [⬆️ Back to Top](#table-of-contents-)
133
222
 
134
- puts person.attribute!('foo') # NameError (undefined attribute `foo)
135
- person.attribute!('foo') { |value| puts value } # NameError (undefined attribute `foo)
136
- ```
223
+ ### `Micro::Attributes#attribute!`
137
224
 
138
- ### How to define multiple attributes?
225
+ Works like the `#attribute` method, but it will raise an exception when the attribute doesn't exist.
139
226
 
140
227
  ```ruby
228
+ person.attribute!('foo') # NameError (undefined attribute `foo)
141
229
 
142
- # Use .attributes with a list of attribute names.
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?
143
236
 
237
+ Use `.attributes` with a list of attribute names.
238
+
239
+ ```ruby
144
240
  class Person
145
241
  include Micro::Attributes
146
242
 
147
- attributes :age, name: 'John Doe' # Use a hash to define attributes with default values
243
+ attributes :age, :name
148
244
 
149
245
  def initialize(options)
150
246
  self.attributes = options
@@ -153,89 +249,133 @@ end
153
249
 
154
250
  person = Person.new(age: 32)
155
251
 
156
- puts person.name # 'John Doe'
157
- puts person.age # 32
252
+ person.name # nil
253
+ person.age # 32
158
254
  ```
159
255
 
160
- ### How to define attributes with a constructor to assign them?
161
- 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.
162
263
 
163
264
  ```ruby
164
265
  class Person
165
- include Micro::Attributes.to_initialize
266
+ include Micro::Attributes.with(:initialize)
166
267
 
167
- attributes :age, name: 'John Doe'
268
+ attribute :age, required: true
269
+ attribute :name, default: 'John Doe'
168
270
  end
169
271
 
170
272
  person = Person.new(age: 18)
171
273
 
172
- puts person.name # John Doe
173
- puts person.age # 18
274
+ person.age # 18
275
+ person.name # John Doe
276
+ ```
174
277
 
175
- ##############################################
176
- # Assigning new values to get a new instance #
177
- ##############################################
278
+ This extension enables two methods for your objects.
279
+ The `#with_attribute()` and `#with_attributes()`.
178
280
 
179
- #-------------------#
180
- # #with_attribute() #
181
- #-------------------#
281
+ ### `#with_attribute()`
182
282
 
283
+ ```ruby
183
284
  another_person = person.with_attribute(:age, 21)
184
285
 
185
- puts another_person.name # John Doe
186
- puts another_person.age # 21
187
- puts another_person.equal?(person) # false
286
+ another_person.age # 21
287
+ another_person.name # John Doe
288
+ another_person.equal?(person) # false
289
+ ```
188
290
 
189
- #--------------------#
190
- # #with_attributes() #
191
- #--------------------#
192
- #
193
- # Use it to assign multiple attributes
291
+ ### `#with_attributes()`
194
292
 
293
+ Use it to assign multiple attributes
294
+ ```ruby
195
295
  other_person = person.with_attributes(name: 'Serradura', age: 32)
196
296
 
197
- puts other_person.name # Serradura
198
- puts other_person.age # 32
199
- puts other_person.equal?(person) # false
297
+ other_person.age # 32
298
+ other_person.name # Serradura
299
+ other_person.equal?(person) # false
300
+ ```
200
301
 
201
- # If you pass a value different of a Hash, an ArgumentError will be raised.
202
- #
203
- # Person.new(1)
204
- # ArgumentError (argument must be a Hash)
302
+ If you pass a value different of a Hash, a Kind::Error will be raised.
205
303
 
206
- #--------------------#
207
- # Strict initializer #
208
- #--------------------#
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 3 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
+ 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`).
327
+
328
+ ```ruby
329
+ class Person
330
+ include Micro::Attributes.with(:initialize)
331
+
332
+ attribute :age, default: -> age { age&.to_i }
333
+ attribute :name, default: -> name { String(name || 'John Doe').strip }
334
+ end
335
+ ```
336
+
337
+ [⬆️ Back to Top](#table-of-contents-)
338
+
339
+ ## The strict initializer
340
+
341
+ Use `.with(initialize: :strict)` to forbids an instantiation without all the attribute keywords.
209
342
 
210
- # Use .to_initialize! to forbids an instantiation without all keywords.
343
+ 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).
211
344
 
345
+ ```ruby
212
346
  class StrictPerson
213
- include Micro::Attributes.to_initialize!
347
+ include Micro::Attributes.with(initialize: :strict)
214
348
 
215
- attributes :age, name: 'John Doe'
349
+ attribute :age
350
+ attribute :name, default: 'John Doe'
216
351
  end
217
352
 
218
- StrictPerson.new({})
353
+ StrictPerson.new({}) # ArgumentError (missing keyword: :age)
354
+ ```
219
355
 
220
- # The code above will raise:
221
- # ArgumentError (missing keyword: :age)
356
+ An attribute with a default value can be omitted.
222
357
 
358
+ ``` ruby
223
359
  person_without_age = StrictPerson.new(age: nil)
224
360
 
225
- p person_without_age.name # "John Doe"
226
- p person_without_age.age # nil
227
-
228
- # Except for this validation when initializing,
229
- # the `to_initialize!` method will works in the same ways of `to_initialize`.
361
+ person_without_age.age # nil
362
+ person_without_age.name # 'John Doe'
230
363
  ```
231
364
 
232
- ### How to inherit the attributes?
365
+ > **Note:** Except for this validation the `.with(initialize: :strict)` method will works in the same ways of `.with(:initialize)`.
366
+
367
+ [⬆️ Back to Top](#table-of-contents-)
368
+
369
+ ## Is it possible to inherit the attributes?
370
+
371
+ Yes. e.g.
233
372
 
234
373
  ```ruby
235
374
  class Person
236
- include Micro::Attributes.to_initialize
375
+ include Micro::Attributes.with(:initialize)
237
376
 
238
- attributes :age, name: 'John Doe'
377
+ attribute :age
378
+ attribute :name, default: 'John Doe'
239
379
  end
240
380
 
241
381
  class Subclass < Person # Will preserve the parent class attributes
@@ -244,42 +384,48 @@ end
244
384
 
245
385
  instance = Subclass.new({})
246
386
 
247
- puts instance.name # John Doe
248
- puts instance.respond_to?(:age) # true
249
- puts instance.respond_to?(:foo) # true
387
+ instance.name # John Doe
388
+ instance.respond_to?(:age) # true
389
+ instance.respond_to?(:foo) # true
390
+ ```
391
+
392
+ [⬆️ Back to Top](#table-of-contents-)
250
393
 
251
- #---------------------------------#
252
- # .attribute!() or .attributes!() #
253
- #---------------------------------#
394
+ ### `.attribute!()`
254
395
 
255
- # The methods above allow redefining the attributes default data
396
+ This method allows us to redefine the attributes default data that was defined in the parent class. e.g.
256
397
 
398
+ ```ruby
257
399
  class AnotherSubclass < Person
258
- attribute! :name, 'Alfa'
400
+ attribute! :name, default: 'Alfa'
259
401
  end
260
402
 
261
403
  alfa_person = AnotherSubclass.new({})
262
404
 
263
- p alfa_person.name # "Alfa"
264
- p alfa_person.age # nil
405
+ alfa_person.name # 'Alfa'
406
+ alfa_person.age # nil
265
407
 
266
408
  class SubSubclass < Subclass
267
- attributes! name: 'Beta', age: 0
409
+ attribute! :age, default: 0
410
+ attribute! :name, default: 'Beta'
268
411
  end
269
412
 
270
413
  beta_person = SubSubclass.new({})
271
414
 
272
- p beta_person.name # "Beta"
273
- p beta_person.age # 0
415
+ beta_person.name # 'Beta'
416
+ beta_person.age # 0
274
417
  ```
275
418
 
276
- ### How to query the attributes?
419
+ [⬆️ Back to Top](#table-of-contents-)
420
+
421
+ ## How to query the attributes?
277
422
 
278
423
  ```ruby
279
424
  class Person
280
425
  include Micro::Attributes
281
426
 
282
- attributes :age, name: 'John Doe'
427
+ attribute :age
428
+ attribute :name, default: 'John Doe'
283
429
 
284
430
  def initialize(options)
285
431
  self.attributes = options
@@ -290,140 +436,122 @@ end
290
436
  # .attributes() #
291
437
  #---------------#
292
438
 
293
- p Person.attributes # ["name", "age"]
439
+ Person.attributes # ['name', 'age']
294
440
 
295
441
  #---------------#
296
442
  # .attribute?() #
297
443
  #---------------#
298
444
 
299
- puts Person.attribute?(:name) # true
300
- puts Person.attribute?('name') # true
301
- puts Person.attribute?('foo') # false
302
- puts Person.attribute?(:foo) # false
445
+ Person.attribute?(:name) # true
446
+ Person.attribute?('name') # true
447
+ Person.attribute?('foo') # false
448
+ Person.attribute?(:foo) # false
303
449
 
304
450
  # ---
305
451
 
306
452
  person = Person.new(age: 20)
307
453
 
454
+ #---------------------#
455
+ # #defined_attributes #
456
+ #---------------------#
457
+
458
+ person.defined_attributes # ['name', 'age']
459
+
308
460
  #---------------#
309
461
  # #attribute?() #
310
462
  #---------------#
311
463
 
312
- puts person.attribute?(:name) # true
313
- puts person.attribute?('name') # true
314
- puts person.attribute?('foo') # false
315
- puts person.attribute?(:foo) # false
464
+ person.attribute?(:name) # true
465
+ person.attribute?('name') # true
466
+ person.attribute?('foo') # false
467
+ person.attribute?(:foo) # false
316
468
 
317
469
  #---------------#
318
470
  # #attributes() #
319
471
  #---------------#
320
472
 
321
- p person.attributes # {"age"=>20, "name"=>"John Doe"}
322
- p Person.new(name: 'John').attributes # {"age"=>nil, "name"=>"John"}
323
- ```
473
+ person.attributes # {'age'=>20, 'name'=>'John Doe'}
474
+ Person.new(name: 'John').attributes # {'age'=>nil, 'name'=>'John'}
324
475
 
325
- ## Built-in extensions
476
+ #---------------------#
477
+ # #attributes(*names) #
478
+ #---------------------#
326
479
 
327
- You can use the method `Micro::Attributes.features()` or `Micro::Attributes.with()` to combine and require only the features that better fit your needs.
480
+ # Slices the attributes to include only the given keys.
481
+ # Returns a hash containing the given keys (in their types).
328
482
 
329
- But, if you desire...
330
- 1. only one feature, use the `Micro::Attributes.feature()` method.
331
- 2. except one or more features, use the `Micro::Attributes.without()` method.
483
+ person.attributes(:age) # {age: 20}
484
+ person.attributes(:age, :name) # {age: 20, name: 'John Doe'}
485
+ person.attributes('age', 'name') # {'age'=>20, 'name'=>'John Doe'}
486
+ ```
332
487
 
333
- ```ruby
334
- #===========================#
335
- # Loading specific features #
336
- #===========================#
488
+ [⬆️ Back to Top](#table-of-contents-)
337
489
 
338
- class Job
339
- include Micro::Attributes.feature(:diff)
490
+ # Built-in extensions
340
491
 
341
- attribute :id
342
- attribute :state, 'sleeping'
492
+ You can use the method `Micro::Attributes.with()` to combine and require only the features that better fit your needs.
343
493
 
344
- def initialize(options)
345
- self.attributes = options
346
- end
347
- end
494
+ But, if you desire except one or more features, use the `Micro::Attributes.without()` method.
348
495
 
349
- #======================#
350
- # Loading all features #
351
- # --- #
352
- #======================#
496
+ ## Picking specific features
353
497
 
354
- class Job
355
- include Micro::Attributes.features
498
+ ### `Micro::Attributes.with`
356
499
 
357
- attributes :id, state: 'sleeping'
358
- end
500
+ ```ruby
501
+ Micro::Attributes.with(:initialize)
359
502
 
360
- # Note:
361
- # If `Micro::Attributes.features()` be invoked without arguments, a module with all features will be returned.
503
+ Micro::Attributes.with(initialize: :strict)
362
504
 
363
- #----------------------------------------------------------------------------#
364
- # Using the .with() method alias and adding the strict initialize extension. #
365
- #----------------------------------------------------------------------------#
366
- class Job
367
- include Micro::Attributes.with(:strict_initialize, :diff)
505
+ Micro::Attributes.with(:diff, :initialize)
368
506
 
369
- attributes :id, state: 'sleeping'
370
- end
507
+ Micro::Attributes.with(:diff, initialize: :strict)
371
508
 
372
- # Note:
373
- # The method `Micro::Attributes.with()` will raise an exception if no arguments/features were declared.
374
- #
375
- # class Job
376
- # include Micro::Attributes.with() # ArgumentError (Invalid feature name! Available options: diff, initialize, activemodel_validations)
377
- # end
509
+ Micro::Attributes.with(:activemodel_validations)
378
510
 
379
- #===================================#
380
- # Alternatives to the methods above #
381
- #===================================#
511
+ Micro::Attributes.with(:activemodel_validations, :diff)
382
512
 
383
- #---------------------------------------#
384
- # Via Micro::Attributes.to_initialize() #
385
- #---------------------------------------#
386
- class Job
387
- include Micro::Attributes.to_initialize(diff: true, activemodel_validations: true)
513
+ Micro::Attributes.with(:activemodel_validations, :diff, initialize: :strict)
514
+ ```
388
515
 
389
- # Same of `include Micro::Attributes.with(:initialize, :diff, :activemodel_validations)`
390
- end
516
+ The method `Micro::Attributes.with()` will raise an exception if no arguments/features were declared.
391
517
 
392
- #----------------------------------------#
393
- # Via Micro::Attributes.to_initialize!() #
394
- #----------------------------------------#
518
+ ```ruby
395
519
  class Job
396
- include Micro::Attributes.to_initialize!(diff: false, activemodel_validations: true)
397
-
398
- # Same of `include Micro::Attributes.with(:strict_initialize, :activemodel_validations)`
520
+ include Micro::Attributes.with() # ArgumentError (Invalid feature name! Available options: :activemodel_validations, :diff, :initialize)
399
521
  end
522
+ ```
400
523
 
401
- #=====================================#
402
- # Loading except one or more features #
403
- # ----- #
404
- #=====================================#
524
+ ### `Micro::Attributes.without`
405
525
 
406
- class Job
407
- include Micro::Attributes.without(:diff)
526
+ Picking *except* one or more features
408
527
 
409
- attributes :id, state: 'sleeping'
410
- end
528
+ ```ruby
529
+ Micro::Attributes.without(:diff) # will load :activemodel_validations and initialize: :strict
530
+
531
+ Micro::Attributes.without(initialize: :strict) # will load :activemodel_validations and :diff
532
+ ```
411
533
 
412
- # Note:
413
- # The method `Micro::Attributes.without()` returns `Micro::Attributes` if all features extensions were used.
534
+ ## Picking all the features
535
+
536
+ ```ruby
537
+ Micro::Attributes.with_all_features
414
538
  ```
415
539
 
416
- ### ActiveModel::Validations extension
540
+ [⬆️ Back to Top](#table-of-contents-)
417
541
 
418
- If your application uses ActiveModel as a dependency (like a regular Rails app). You will be enabled to use the `actimodel_validations` extension.
542
+ ## Extensions
543
+
544
+ ### `ActiveModel::Validation` extension
545
+
546
+ If your application uses ActiveModel as a dependency (like a regular Rails app). You will be enabled to use the `activemodel_validations` extension.
419
547
 
420
548
  ```ruby
421
549
  class Job
422
- # include Micro::Attributes.with(:initialize, :activemodel_validations)
423
- # include Micro::Attributes.features(:initialize, :activemodel_validations)
424
- include Micro::Attributes.to_initialize(activemodel_validations: true)
550
+ include Micro::Attributes.with(:activemodel_validations)
551
+
552
+ attribute :id
553
+ attribute :state, default: 'sleeping'
425
554
 
426
- attributes :id, state: 'sleeping'
427
555
  validates! :id, :state, presence: true
428
556
  end
429
557
 
@@ -431,10 +559,31 @@ Job.new({}) # ActiveModel::StrictValidationFailed (Id can't be blank)
431
559
 
432
560
  job = Job.new(id: 1)
433
561
 
434
- p job.id # 1
435
- p job.state # "sleeping"
562
+ job.id # 1
563
+ job.state # 'sleeping'
564
+ ```
565
+
566
+ #### `.attribute()` options
567
+
568
+ You can use the `validate` or `validates` options to define your attributes. e.g.
569
+
570
+ ```ruby
571
+ class Job
572
+ include Micro::Attributes.with(:activemodel_validations)
573
+
574
+ attribute :id, validates: { presence: true }
575
+ attribute :state, validate: :must_be_a_filled_string
576
+
577
+ def must_be_a_filled_string
578
+ return if state.is_a?(String) && state.present?
579
+
580
+ errors.add(:state, 'must be a filled string')
581
+ end
582
+ end
436
583
  ```
437
584
 
585
+ [⬆️ Back to Top](#table-of-contents-)
586
+
438
587
  ### Diff extension
439
588
 
440
589
  Provides a way to track changes in your object attributes.
@@ -443,21 +592,20 @@ Provides a way to track changes in your object attributes.
443
592
  require 'securerandom'
444
593
 
445
594
  class Job
446
- # include Micro::Attributes.with(:initialize, :diff)
447
- # include Micro::Attributes.to_initialize(diff: true)
448
- include Micro::Attributes.features(:initialize, :diff)
595
+ include Micro::Attributes.with(:initialize, :diff)
449
596
 
450
- attributes :id, state: 'sleeping'
597
+ attribute :id
598
+ attribute :state, default: 'sleeping'
451
599
  end
452
600
 
453
601
  job = Job.new(id: SecureRandom.uuid())
454
602
 
455
- p job.id # A random UUID generated from SecureRandom.uuid(). e.g: "e68bcc74-b91c-45c2-a904-12f1298cc60e"
456
- p job.state # "sleeping"
603
+ job.id # A random UUID generated from SecureRandom.uuid(). e.g: 'e68bcc74-b91c-45c2-a904-12f1298cc60e'
604
+ job.state # 'sleeping'
457
605
 
458
606
  job_running = job.with_attribute(:state, 'running')
459
607
 
460
- p job_running.state # "running"
608
+ job_running.state # 'running'
461
609
 
462
610
  job_changes = job.diff_attributes(job_running)
463
611
 
@@ -465,50 +613,49 @@ job_changes = job.diff_attributes(job_running)
465
613
  # #present?, #blank?, #empty? #
466
614
  #-----------------------------#
467
615
 
468
- p job_changes.present? # true
469
- p job_changes.blank? # false
470
- p job_changes.empty? # false
616
+ job_changes.present? # true
617
+ job_changes.blank? # false
618
+ job_changes.empty? # false
471
619
 
472
620
  #-----------#
473
621
  # #changed? #
474
622
  #-----------#
475
- p job_changes.changed? # true
623
+ job_changes.changed? # true
476
624
 
477
- p job_changes.changed?(:id) # false
625
+ job_changes.changed?(:id) # false
478
626
 
479
- p job_changes.changed?(:state) # true
480
- p job_changes.changed?(:state, from: 'sleeping', to: 'running') # true
627
+ job_changes.changed?(:state) # true
628
+ job_changes.changed?(:state, from: 'sleeping', to: 'running') # true
481
629
 
482
630
  #----------------#
483
631
  # #differences() #
484
632
  #----------------#
485
- p job_changes.differences # {"state"=> {"from" => "sleeping", "to" => "running"}}
633
+ job_changes.differences # {'state'=> {'from' => 'sleeping', 'to' => 'running'}}
486
634
  ```
487
635
 
636
+ [⬆️ Back to Top](#table-of-contents-)
637
+
488
638
  ### Initialize extension
489
639
 
490
640
  1. Creates a constructor to assign the attributes.
491
- 2. Adds methods to build new instances when some data was assigned.
641
+ 2. Add methods to build new instances when some data was assigned.
492
642
 
493
643
  ```ruby
494
644
  class Job
495
- # include Micro::Attributes.with(:initialize)
496
- # include Micro::Attributes.feature(:initialize)
497
- # include Micro::Attributes.features(:initialize)
498
- include Micro::Attributes.to_initialize
645
+ include Micro::Attributes.with(:initialize)
499
646
 
500
647
  attributes :id, :state
501
648
  end
502
649
 
503
650
  job_null = Job.new({})
504
651
 
505
- p job.id # nil
506
- p job.state # nil
652
+ job.id # nil
653
+ job.state # nil
507
654
 
508
655
  job = Job.new(id: 1, state: 'sleeping')
509
656
 
510
- p job.id # 1
511
- p job.state # "sleeping"
657
+ job.id # 1
658
+ job.state # 'sleeping'
512
659
 
513
660
  ##############################################
514
661
  # Assigning new values to get a new instance #
@@ -520,9 +667,9 @@ p job.state # "sleeping"
520
667
 
521
668
  new_job = job.with_attribute(:state, 'running')
522
669
 
523
- puts new_job.id # 1
524
- puts new_job.state # running
525
- puts new_job.equal?(job) # false
670
+ new_job.id # 1
671
+ new_job.state # running
672
+ new_job.equal?(job) # false
526
673
 
527
674
  #--------------------#
528
675
  # #with_attributes() #
@@ -532,12 +679,14 @@ puts new_job.equal?(job) # false
532
679
 
533
680
  other_job = job.with_attributes(id: 2, state: 'killed')
534
681
 
535
- puts other_job.id # 2
536
- puts other_job.state # killed
537
- puts other_job.equal?(job) # false
682
+ other_job.id # 2
683
+ other_job.state # killed
684
+ other_job.equal?(job) # false
538
685
  ```
539
686
 
540
- ### Strict initialize extension
687
+ [⬆️ Back to Top](#table-of-contents-)
688
+
689
+ #### Strict mode
541
690
 
542
691
  1. Creates a constructor to assign the attributes.
543
692
  2. Adds methods to build new instances when some data was assigned.
@@ -545,16 +694,13 @@ puts other_job.equal?(job) # false
545
694
 
546
695
  ```ruby
547
696
  class Job
548
- # include Micro::Attributes.with(:strict_initialize)
549
- # include Micro::Attributes.feature(:strict_initialize)
550
- # include Micro::Attributes.features(:strict_initialize)
551
- include Micro::Attributes.to_initialize!
697
+ include Micro::Attributes.with(initialize: :strict)
552
698
 
553
699
  attributes :id, :state
554
700
  end
555
- #----------------------------------------------------------------------------#
556
- # The strict_initialize extension will require all the keys when initialize. #
557
- #----------------------------------------------------------------------------#
701
+ #-----------------------------------------------------------------------#
702
+ # The strict initialize mode will require all the keys when initialize. #
703
+ #-----------------------------------------------------------------------#
558
704
 
559
705
  Job.new({})
560
706
 
@@ -567,34 +713,33 @@ Job.new({})
567
713
 
568
714
  job_null = Job.new(id: nil, state: nil)
569
715
 
570
- p job.id # nil
571
- p job.state # nil
716
+ job.id # nil
717
+ job.state # nil
572
718
 
573
719
  job = Job.new(id: 1, state: 'sleeping')
574
720
 
575
- p job.id # 1
576
- p job.state # "sleeping"
721
+ job.id # 1
722
+ job.state # 'sleeping'
723
+ ```
577
724
 
725
+ > **Note**: This extension works like the `initialize` extension. So, look at its section to understand all of the other features.
578
726
 
579
- # Note:
580
- # This extension works like the `initialize` extension.
581
- # So, look at its section to understand all the other features.
582
- ```
727
+ [⬆️ Back to Top](#table-of-contents-)
583
728
 
584
- ## Development
729
+ # Development
585
730
 
586
731
  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.
587
732
 
588
733
  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).
589
734
 
590
- ## Contributing
735
+ # Contributing
591
736
 
592
737
  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.
593
738
 
594
- ## License
739
+ # License
595
740
 
596
741
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
597
742
 
598
- ## Code of Conduct
743
+ # Code of Conduct
599
744
 
600
- 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).
745
+ 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).