deco_lite 0.3.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/Gemfile.lock +4 -4
- data/README.md +49 -47
- data/lib/deco_lite/field_conflictable.rb +0 -2
- data/lib/deco_lite/fields_auto_attr_accessable.rb +2 -4
- data/lib/deco_lite/hash_loadable.rb +4 -1
- data/lib/deco_lite/model.rb +41 -10
- data/lib/deco_lite/options.rb +0 -4
- data/lib/deco_lite/options_defaultable.rb +1 -4
- data/lib/deco_lite/options_validatable.rb +1 -13
- data/lib/deco_lite/version.rb +1 -1
- data/lib/deco_lite.rb +0 -1
- metadata +3 -5
- data/lib/deco_lite/field_requireable.rb +0 -37
- data/lib/deco_lite/required_fields_optionable.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 00c4b0e5c7169ef19c7f3f43ddc01a13b542f542f6f42d8b84e14e648ea1dea1
|
4
|
+
data.tar.gz: a092f920b44a0c0274e0b39e7a4674f8ebc00ae81893a135dcfde274acf3ccd0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 817c072e966c9ce994c10cc7113f1c6dbbf8653e9ca84af330903fc7f82187f3df4e995516871085a77f9de7c89eac2d0ba2f1a1a67dfbf5f8821406d7652c4e
|
7
|
+
data.tar.gz: 8878462c837f7e6e312ce0d9f6184ee3cab4c4cc564dd2d8aa49d1ac8954129e3d5ae9f0dde89ef9b3faffa4dd817d2e81065397163182c67414ee5266fae118
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
### 1.0.0
|
2
|
+
* Breaking changes
|
3
|
+
* Removed `FieldRequireable` and moved this code to the `Model` class because determination of required fields loaded is now determined in the `Model` class, rather than checking the existance of model attributes.
|
4
|
+
* Removed `RequiredFieldsOptionable` since determination of whether or not required fields were loaded no longer depends on the existance of model attributes, this option was removed. `Model#required_fields` model attributes can now be created unconditionally, and still determine whether or not reqired fields were loaded or not.
|
5
|
+
* Since `FieldRequireable` has been removed, along with `#required_fields`, the `Model#required_fields` can now be overridden or manipulated to designate required fields. Required fields will cause validtion to fail if those fields are not loaded into the model like before.
|
6
|
+
* The reason for the aforementioned changes is that the whole paradigm to validate "required fields" (i.e. fields required when loading a Hash) based on model attributes introduced complexity/awkwardness in using the `DecoLite::Model` class, as well as complexity in the `DecoLite` codebase. These changes makes things simpler and cleaner.
|
7
|
+
* Remove deprecated `DecoLite::Model#load`. Use `DecoLite::Model#load!` instead.
|
8
|
+
* Changes
|
9
|
+
* Update README.md file accordingly.
|
10
|
+
|
1
11
|
### 0.3.2
|
2
12
|
* Changes
|
3
13
|
* Refactor FieldAssignable to remove call to FieldCreatable#create_field_accessor as this breaks single responsibility rule; which, in this case, makes sense to remove. FieldCreatable#create_field_accessor can be called wherever creation of a attr_accessor is needed.
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
deco_lite (0.
|
4
|
+
deco_lite (1.0.0)
|
5
5
|
activemodel (~> 7.0, >= 7.0.3.1)
|
6
6
|
activesupport (~> 7.0, >= 7.0.3.1)
|
7
7
|
immutable_struct_ex (~> 0.2.0)
|
@@ -25,7 +25,7 @@ GEM
|
|
25
25
|
docile (1.4.0)
|
26
26
|
i18n (1.12.0)
|
27
27
|
concurrent-ruby (~> 1.0)
|
28
|
-
immutable_struct_ex (0.2.
|
28
|
+
immutable_struct_ex (0.2.3)
|
29
29
|
json (2.6.2)
|
30
30
|
kwalify (0.7.2)
|
31
31
|
mad_flatter (1.0.1.pre.beta)
|
@@ -63,7 +63,7 @@ GEM
|
|
63
63
|
diff-lcs (>= 1.2.0, < 2.0)
|
64
64
|
rspec-support (~> 3.11.0)
|
65
65
|
rspec-support (3.11.0)
|
66
|
-
rubocop (1.35.
|
66
|
+
rubocop (1.35.1)
|
67
67
|
json (~> 2.3)
|
68
68
|
parallel (~> 1.10)
|
69
69
|
parser (>= 3.1.2.1)
|
@@ -107,4 +107,4 @@ DEPENDENCIES
|
|
107
107
|
simplecov (~> 0.21.2)
|
108
108
|
|
109
109
|
BUNDLED WITH
|
110
|
-
2.
|
110
|
+
2.3.22
|
data/README.md
CHANGED
@@ -13,13 +13,9 @@
|
|
13
13
|
|
14
14
|
## Introduction
|
15
15
|
|
16
|
-
|
16
|
+
_Deco_ is a little gem that allows you to use the provided `DecoLite::Model` class (`include ActiveModel::Model`) to dynamically create Decorator class objects. Inherit from `DecoLite::Model` to create your own unique classes with custom functionality. A `DecoLite::Model` includes `ActiveModel::Model`, so validation can be applied using [ActiveModel validation helpers](https://api.rubyonrails.org/v6.1.3/classes/ActiveModel/Validations/HelperMethods.html) you are familiar with; or, you can roll your own - just like any other ActiveModel.
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
_Deco_ is a little gem that allows you to use the provided `DecoLite::Model` class (`include ActiveModel::Model`) to create Decorator classes which can be instantiated and used. Inherit from `DecoLite::Model` to create your own unique classes with custom functionality. A `DecoLite::Model` includes `ActiveModel::Model`, so validation can be applied using [ActiveModel validation helpers](https://api.rubyonrails.org/v6.1.3/classes/ActiveModel/Validations/HelperMethods.html) you are familiar with; or, you can roll your own - just like any other ActiveModel.
|
21
|
-
|
22
|
-
A `DecoLite::Model` will allow you to consume a Ruby Hash that you supply via the initializer (`DecoLite::Model#new`) or via the `DecoLite::Model#load!` method. Your supplied Ruby Hashes are used to create `attr_accessor` attributes (_"fields"_) on the model. Each attribute created, is then assigned its value from the Hash loaded.
|
18
|
+
A `DecoLite::Model` will allow you to consume a Ruby Hash that you supply via the initializer (`DecoLite::Model#new`) or via the `DecoLite::Model#load!` method. Your supplied Ruby Hashes are used to create `attr_accessor` attributes (_"fields"_) on the model. Each attribute created, is then assigned its value from the Hash loaded. Any number of hashes can be consumed using the `DecoLite::Model#load!` method.
|
23
19
|
|
24
20
|
`attr_accessor` names created are _mangled_ to include namespacing. This creates unique attribute names for nested Hashes that may include non-unique keys. For example:
|
25
21
|
|
@@ -34,7 +30,7 @@ family = {
|
|
34
30
|
}
|
35
31
|
}
|
36
32
|
```
|
37
|
-
Given the above example, DecoLite will produce the following `attr_accessors` on the `DecoLite::Model` object
|
33
|
+
Given the above example, DecoLite will produce the following `attr_accessors` on the `DecoLite::Model` object and assign the values:
|
38
34
|
|
39
35
|
```ruby
|
40
36
|
# Or DecoLite::Model.new.load!(hash: family)
|
@@ -53,17 +49,21 @@ model.wife_age #=> 30
|
|
53
49
|
model.respond_to? :wife_age= #=> true
|
54
50
|
```
|
55
51
|
|
56
|
-
`DecoLite::Model#load!` can be called _multiple times_, on the same model, with different Hashes. This could potentially cause `attr_accessor` name clashes. In order to ensure unique `attr_accessor` names, a _"namespace"_ may be _explicitly_ provided to ensure uniqueness.
|
52
|
+
`DecoLite::Model#load!` can be called _multiple times_, on the same model, with different Hashes. This could potentially cause `attr_accessor` name clashes. In order to ensure unique `attr_accessor` names, a _"namespace"_ may be _explicitly_ provided to ensure uniqueness.
|
53
|
+
|
54
|
+
For example, **continuing from the previous example;** if we were to call `DecoLite::Model#load!` a _second time_ with the following Hash, this would potentially produce `attr_accessor` name clashes:
|
57
55
|
|
58
56
|
```ruby
|
59
57
|
grandpa = {
|
60
58
|
name: 'Henry Doe',
|
61
59
|
age: 85,
|
62
60
|
}
|
63
|
-
# The :name and :age Hash keys above will produce :name/:name= and :age/:age= attr_accessors
|
61
|
+
# The :name and :age Hash keys above will produce :name/:name= and :age/:age= attr_accessors
|
62
|
+
# and clash because these were already added to the model when "John Doe" was loaded with
|
63
|
+
# the first call to DecoLite::Model.new(hash: family).
|
64
64
|
```
|
65
65
|
|
66
|
-
However, passing a `
|
66
|
+
However, passing a `:namespace` option (for example `namespace: :grandpa`) to the `DecoLite::Model#load!` method, would produce the following `attr_accessors`, ensuring their uniqueness:
|
67
67
|
|
68
68
|
```ruby
|
69
69
|
model.load!(hash: grandpa, options: { namespace: :grandpa })
|
@@ -88,6 +88,11 @@ model.respond_to? :wife_name= #=> true
|
|
88
88
|
model.wife_age #=> 30
|
89
89
|
model.respond_to? :wife_age= #=> true
|
90
90
|
```
|
91
|
+
|
92
|
+
### For more examples and usage
|
93
|
+
|
94
|
+
For more examples and usage, see the [Examples and usage](#examples-and-usage) and [Mode examples and usage](#more-examples-and-usage) sections; there is also an "I want to..." section with examples you might encounter when using `DecoLite`.
|
95
|
+
|
91
96
|
## Use cases
|
92
97
|
|
93
98
|
### General
|
@@ -187,7 +192,7 @@ model.wife_info_address #=> 1 street, boonton, nj 07005
|
|
187
192
|
|
188
193
|
#### Add validators to my model
|
189
194
|
|
190
|
-
Simply add your `ActiveModel` validators just like you would any other `ActiveModel::Model` validator. However, be aware that (currently), any attribute (field) having an _explicit validation_ associated with it, will automatically cause an `attr_accessor` to be created for that field; this is to avoid `NoMethodErrors` when calling a validation method on the model (e.g. `#valid?`, `#validate`, etc.)
|
195
|
+
Simply add your `ActiveModel` validators just like you would any other `ActiveModel::Model` validator. However, be aware that (currently), any attribute (field) having an _explicit validation_ associated with it, will automatically cause an `attr_accessor` to be created for that field; this is to avoid `NoMethodErrors` when calling a validation method on the model (e.g. `#valid?`, `#validate`, etc.) *before* the data is loaded to create the associated `attr_accessors`:
|
191
196
|
|
192
197
|
```ruby
|
193
198
|
class Model < DecoLite::Model
|
@@ -212,13 +217,11 @@ model.validate
|
|
212
217
|
|
213
218
|
#### Validate whether or not certain fields were loaded
|
214
219
|
|
215
|
-
To be clear, this
|
220
|
+
To be clear, this example does not validate the _data_ associated with the fields loaded; rather, this example validates whether or not the _fields themselves_ were loaded into your model, and as a result, `attr_accessors` created. If you only want to validate the _data_ loaded into your model, simply add `ActiveModel` validation, just like you would any other `ActiveModel` model, see the [Add validators to my model](#add-validators-to-my-model) section.
|
216
221
|
|
217
|
-
If you want to validate whether or not particular _fields_ were added to your model as attributes (
|
222
|
+
If you want to validate whether or not particular _fields_ were added to your model as attributes (`attr_accessor`), as a result of `#load!`ing data into your model, you need to add the required field names to the `DecoLite::Model#required_fields` attribute, or use inheritance:
|
218
223
|
- Create a `DecoLite::Model` subclass.
|
219
|
-
- Override the `DecoLite::Model#required_fields` method
|
220
|
-
- Use the `required_fields: nil` option when instantiating your model object.
|
221
|
-
- DO NOT add `ActiveModel` validators that _explicitly_ reference any field returned from `DecoLite::Model#required_fields`; this will cause `attr_accessors` to be created for these fields; consequently, `DecoLite::FieldRequireable#validate_required_fields` will _never_ return any errors because these fields will exist as attributes on your model. In other words, do not add `validates :first, :last, :address, presence: true` to your model if you need to validate whether or not the data you load into your model included fields :first, :last and :address.
|
224
|
+
- Override the `DecoLite::Model#required_fields` method and return an Array of field names represented by `Symbols` you want to validate.
|
222
225
|
|
223
226
|
For example:
|
224
227
|
|
@@ -228,22 +231,34 @@ class Model < DecoLite::Model
|
|
228
231
|
validates :age, numericality: { only_integer: true }, allow_blank: true
|
229
232
|
|
230
233
|
def required_fields
|
231
|
-
# We want to ensure
|
232
|
-
%i
|
234
|
+
# We want to ensure these fields were included as Hash keys during loading.
|
235
|
+
%i[first last address]
|
233
236
|
end
|
234
237
|
end
|
235
238
|
|
236
|
-
|
237
|
-
# attr_accessors for fields returned from DecoLite::Model#required_fields, so we
|
238
|
-
# need to set this option to nil (i.e. required_fields: nil).
|
239
|
-
model = Model.new(options: { required_fields: nil })
|
239
|
+
model = Model.new
|
240
240
|
|
241
241
|
model.validate
|
242
242
|
#=> false
|
243
243
|
model.errors.full_messages
|
244
244
|
#=> ["First field is missing", "Last field is missing", "Address field is missing"]
|
245
245
|
|
246
|
-
|
246
|
+
# If we load data that includes :first, :last, and :address Hash keys even with
|
247
|
+
# nil data, our ":<field> field is missing" errors go away; in this scenario,
|
248
|
+
# we're validating the presence of the FIELDS, not the data associated with
|
249
|
+
# these fields!
|
250
|
+
model.load!(hash: { first: nil, last: nil, address: nil })
|
251
|
+
model.validate
|
252
|
+
#=> true
|
253
|
+
model.errors.full_messages
|
254
|
+
#=> []
|
255
|
+
|
256
|
+
user = {
|
257
|
+
first: 'John',
|
258
|
+
last: 'Doe',
|
259
|
+
address: '123 anystreet, anytown, nj 01234',
|
260
|
+
age: 'x'
|
261
|
+
}
|
247
262
|
model.load!(hash: user)
|
248
263
|
model.validate
|
249
264
|
#=> false
|
@@ -254,34 +269,19 @@ model.errors.full_messages
|
|
254
269
|
|
255
270
|
If you simply want to validate the _data_ loaded into your model, simply add `ActiveModel` validation, just like you would any other `ActiveModel` model, see the [Add validators to my model](#add-validators-to-my-model) section.
|
256
271
|
|
257
|
-
If you want to validate whether or not particular fields were loaded _and_ field data associated with these same fields, you
|
272
|
+
If you want to validate whether or not particular fields were loaded _and_ field data associated with these same fields, you simply need to add the required fields and any other validation(s).
|
258
273
|
|
259
274
|
For example:
|
260
275
|
|
261
276
|
```ruby
|
262
277
|
class Model < DecoLite::Model
|
263
|
-
|
264
|
-
%i(first last address age)
|
265
|
-
end
|
266
|
-
|
267
|
-
def validate_required_fields
|
268
|
-
super
|
269
|
-
|
270
|
-
first = self.try(:first)
|
271
|
-
errors.add(:first, "can't be blank") if first.nil?
|
278
|
+
validates :first, :last, :address, :age, presence: true
|
272
279
|
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
address = self.try(:address)
|
277
|
-
errors.add(:address, "can't be blank") if address.nil?
|
278
|
-
|
279
|
-
age = self.try(:age)
|
280
|
-
errors.add(:age, "can't be blank") if age.nil?
|
281
|
-
errors.add(:age, 'is not a number') unless /\d+/ =~ age
|
280
|
+
def required_fields
|
281
|
+
%i[first last address age]
|
282
282
|
end
|
283
283
|
end
|
284
|
-
model = Model.new
|
284
|
+
model = Model.new
|
285
285
|
|
286
286
|
model.validate
|
287
287
|
#=> false
|
@@ -311,24 +311,26 @@ class JustBecauseYouCanDoesntMeanYouShould < DecoLite::Model
|
|
311
311
|
def initialize(options: {})
|
312
312
|
super
|
313
313
|
|
314
|
-
@field_names = %i
|
314
|
+
@field_names = %i[existing_field]
|
315
315
|
end
|
316
316
|
end
|
317
317
|
```
|
318
318
|
|
319
|
-
However, the above is unnecessary as this can be easily accomplished using `DecoLite::Model#load!`:
|
319
|
+
However, the above is unnecessary as this can be easily accomplished by passing a `Hash` to the initializer or by using `DecoLite::Model#load!`:
|
320
|
+
|
320
321
|
```ruby
|
321
|
-
model = Class.new(DecoLite::Model).new
|
322
|
+
model = Class.new(DecoLite::Model).new(hash:{ existing_field: :value })
|
322
323
|
|
323
324
|
model.field_names
|
324
325
|
#=> [:existing_field]
|
325
326
|
|
326
327
|
model.existing_field
|
327
|
-
#=> :
|
328
|
+
#=> :value
|
328
329
|
|
329
330
|
model.respond_to? :existing_field=
|
330
331
|
#=> true
|
331
332
|
```
|
333
|
+
|
332
334
|
## Installation
|
333
335
|
|
334
336
|
Add this line to your application's Gemfile:
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'field_name_namespaceable'
|
4
|
-
require_relative 'field_requireable'
|
5
4
|
require_relative 'fields_optionable'
|
6
5
|
|
7
6
|
module DecoLite
|
@@ -10,7 +9,6 @@ module DecoLite
|
|
10
9
|
module FieldConflictable
|
11
10
|
include FieldNameNamespaceable
|
12
11
|
include FieldsOptionable
|
13
|
-
include FieldRequireable
|
14
12
|
|
15
13
|
def validate_field_conflicts!(field_name:, options:)
|
16
14
|
return unless field_conflict?(field_name: field_name, options: options)
|
@@ -10,14 +10,12 @@ module DecoLite
|
|
10
10
|
auto_attr_accessors.present?
|
11
11
|
end
|
12
12
|
|
13
|
-
# This method returns a Hash of fields that are implicitly defined
|
14
|
-
# through ActiveModel validators
|
15
|
-
# #required_fields Array.
|
13
|
+
# This method returns a Hash of fields that are implicitly defined
|
14
|
+
# through ActiveModel validators.
|
16
15
|
def auto_attr_accessors
|
17
16
|
return @auto_attr_accessors.dup if defined?(@auto_attr_accessors)
|
18
17
|
|
19
18
|
@auto_attr_accessors = self.class.validators.map(&:attributes)
|
20
|
-
@auto_attr_accessors.concat(required_fields) if options.required_fields_auto?
|
21
19
|
@auto_attr_accessors = auto_attr_accessors_assign
|
22
20
|
end
|
23
21
|
|
@@ -21,7 +21,10 @@ module DecoLite
|
|
21
21
|
load_service.execute(hash: hash, options: load_service_options).tap do |service_hash|
|
22
22
|
service_hash.each_pair do |field_name, value|
|
23
23
|
create_field_accessor field_name: field_name, options: deco_lite_options
|
24
|
-
|
24
|
+
unless field_names.include? field_name
|
25
|
+
yield field_name if block_given?
|
26
|
+
field_names << field_name
|
27
|
+
end
|
25
28
|
set_field_value(field_name: field_name, value: value, options: deco_lite_options)
|
26
29
|
end
|
27
30
|
end
|
data/lib/deco_lite/model.rb
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
require 'active_model'
|
4
4
|
require_relative 'field_assignable'
|
5
5
|
require_relative 'field_names_persistable'
|
6
|
-
require_relative 'field_requireable'
|
7
6
|
require_relative 'fields_auto_attr_accessable'
|
8
7
|
require_relative 'hash_loadable'
|
9
8
|
require_relative 'hashable'
|
@@ -17,14 +16,13 @@ module DecoLite
|
|
17
16
|
include ActiveModel::Model
|
18
17
|
include FieldAssignable
|
19
18
|
include FieldNamesPersistable
|
20
|
-
include FieldRequireable
|
21
19
|
include FieldsAutoloadable
|
22
20
|
include HashLoadable
|
23
21
|
include Hashable
|
24
22
|
include ModelNameable
|
25
23
|
include Optionable
|
26
24
|
|
27
|
-
|
25
|
+
MISSING_REQUIRED_FIELD_ERROR_TYPE = :missing_required_field
|
28
26
|
|
29
27
|
def initialize(hash: {}, options: {})
|
30
28
|
# Accept whatever options are sent, but make sure
|
@@ -36,27 +34,60 @@ module DecoLite
|
|
36
34
|
|
37
35
|
hash ||= {}
|
38
36
|
|
39
|
-
|
40
|
-
|
37
|
+
load_hash!(hash: hash, options: options) if hash.present?
|
38
|
+
|
39
|
+
load_hash!(hash: auto_attr_accessors, options: options, add_loaded_fields: false) if auto_attr_accessors?
|
40
|
+
end
|
41
|
+
|
42
|
+
validate :validate_required_fields
|
43
|
+
|
44
|
+
# Returns field names that will be used to validate whether or not
|
45
|
+
# these fields were loaded from hashes upon construction (#new) or
|
46
|
+
# via #load!.
|
47
|
+
#
|
48
|
+
# You must override this method if you want to return field names that
|
49
|
+
# are required to be present.
|
50
|
+
def required_fields
|
51
|
+
@required_fields ||= %i[]
|
41
52
|
end
|
42
53
|
|
43
54
|
def load!(hash:, options: {})
|
55
|
+
load_hash! hash: hash, options: options
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
attr_writer :required_fields
|
61
|
+
|
62
|
+
def loaded_fields
|
63
|
+
@loaded_fields ||= []
|
64
|
+
end
|
65
|
+
|
66
|
+
def load_hash!(hash:, options: {}, add_loaded_fields: true)
|
44
67
|
# Merge options into the default options passed through the
|
45
68
|
# constructor; these will override any options passed in when
|
46
69
|
# this object was created, allowing us to retain any defaut
|
47
70
|
# options while loading, but also provide option customization
|
48
71
|
# of options when needed.
|
49
72
|
options = Options.with_defaults(options, defaults: self.options)
|
50
|
-
load_hash(hash: hash, deco_lite_options: options)
|
73
|
+
load_hash(hash: hash, deco_lite_options: options) do |loaded_field|
|
74
|
+
loaded_fields << loaded_field if add_loaded_fields
|
75
|
+
end
|
51
76
|
|
52
77
|
self
|
53
78
|
end
|
54
79
|
|
55
|
-
|
56
|
-
|
57
|
-
|
80
|
+
# Validator for field names. This validator simply checks to make
|
81
|
+
# sure that the field was created, which can only occur if:
|
82
|
+
# A) The field was defined on the model explicitly (e.g. attr_accessor :field).
|
83
|
+
# B) The field was created as a result of loading data dynamically.
|
84
|
+
def validate_required_fields
|
85
|
+
required_fields.each do |field_name|
|
86
|
+
next if loaded_fields.include? field_name
|
58
87
|
|
59
|
-
|
88
|
+
errors.add(field_name, 'field is missing',
|
89
|
+
type: self.class::MISSING_REQUIRED_FIELD_ERROR_TYPE)
|
90
|
+
end
|
60
91
|
end
|
61
92
|
end
|
62
93
|
end
|
data/lib/deco_lite/options.rb
CHANGED
@@ -2,19 +2,16 @@
|
|
2
2
|
|
3
3
|
require_relative 'fields_optionable'
|
4
4
|
require_relative 'namespace_optionable'
|
5
|
-
require_relative 'required_fields_optionable'
|
6
5
|
|
7
6
|
module DecoLite
|
8
7
|
# Defines default options and their optionn values.
|
9
8
|
module OptionsDefaultable
|
10
9
|
include DecoLite::FieldsOptionable
|
11
10
|
include DecoLite::NamespaceOptionable
|
12
|
-
include DecoLite::RequiredFieldsOptionable
|
13
11
|
|
14
12
|
DEFAULT_OPTIONS = {
|
15
13
|
OPTION_FIELDS => OPTION_FIELDS_DEFAULT,
|
16
|
-
OPTION_NAMESPACE => OPTION_NAMESPACE_DEFAULT
|
17
|
-
OPTION_REQUIRED_FIELDS => OPTION_REQUIRED_FIELDS_DEFAULT
|
14
|
+
OPTION_NAMESPACE => OPTION_NAMESPACE_DEFAULT
|
18
15
|
}.freeze
|
19
16
|
end
|
20
17
|
end
|
@@ -2,16 +2,14 @@
|
|
2
2
|
|
3
3
|
require_relative 'fields_optionable'
|
4
4
|
require_relative 'namespace_optionable'
|
5
|
-
require_relative 'required_fields_optionable'
|
6
5
|
|
7
6
|
module DecoLite
|
8
7
|
# Methods to validate options.
|
9
8
|
module OptionsValidatable
|
10
9
|
include DecoLite::FieldsOptionable
|
11
10
|
include DecoLite::NamespaceOptionable
|
12
|
-
include DecoLite::RequiredFieldsOptionable
|
13
11
|
|
14
|
-
OPTIONS = [OPTION_FIELDS, OPTION_NAMESPACE
|
12
|
+
OPTIONS = [OPTION_FIELDS, OPTION_NAMESPACE].freeze
|
15
13
|
|
16
14
|
def validate_options!(options:)
|
17
15
|
raise ArgumentError, 'options is not a Hash' unless options.is_a? Hash
|
@@ -21,7 +19,6 @@ module DecoLite
|
|
21
19
|
validate_option_keys! options: options
|
22
20
|
validate_option_fields! fields: options[OPTION_FIELDS]
|
23
21
|
validate_option_namespace! namespace: options[OPTION_NAMESPACE]
|
24
|
-
validate_option_required_fields! required_fields: options[OPTION_REQUIRED_FIELDS]
|
25
22
|
end
|
26
23
|
|
27
24
|
def validate_options_present!(options:)
|
@@ -48,14 +45,5 @@ module DecoLite
|
|
48
45
|
raise ArgumentError, 'option :namespace value or type is invalid. A Symbol was expected, ' \
|
49
46
|
"but '#{namespace}' (#{namespace.class}) was received."
|
50
47
|
end
|
51
|
-
|
52
|
-
def validate_option_required_fields!(required_fields:)
|
53
|
-
# :required_fields is optional.
|
54
|
-
return if required_fields.blank? || OPTION_REQUIRED_FIELDS_VALUES.include?(required_fields)
|
55
|
-
|
56
|
-
raise ArgumentError,
|
57
|
-
"option :fields_required value or type is invalid. #{OPTION_REQUIRED_FIELDS_VALUES} (Symbol) " \
|
58
|
-
"was expected, but '#{required_fields}' (#{required_fields.class}) was received."
|
59
|
-
end
|
60
48
|
end
|
61
49
|
end
|
data/lib/deco_lite/version.rb
CHANGED
data/lib/deco_lite.rb
CHANGED
@@ -5,7 +5,6 @@ require_relative 'deco_lite/field_conflictable'
|
|
5
5
|
require_relative 'deco_lite/field_creatable'
|
6
6
|
require_relative 'deco_lite/field_name_namespaceable'
|
7
7
|
require_relative 'deco_lite/field_names_persistable'
|
8
|
-
require_relative 'deco_lite/field_requireable'
|
9
8
|
require_relative 'deco_lite/field_retrievable'
|
10
9
|
require_relative 'deco_lite/fields_optionable'
|
11
10
|
require_relative 'deco_lite/hash_loadable'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: deco_lite
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gene M. Angelo, Jr.
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-09-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -263,7 +263,6 @@ files:
|
|
263
263
|
- lib/deco_lite/field_creatable.rb
|
264
264
|
- lib/deco_lite/field_name_namespaceable.rb
|
265
265
|
- lib/deco_lite/field_names_persistable.rb
|
266
|
-
- lib/deco_lite/field_requireable.rb
|
267
266
|
- lib/deco_lite/field_retrievable.rb
|
268
267
|
- lib/deco_lite/field_validatable.rb
|
269
268
|
- lib/deco_lite/fields_auto_attr_accessable.rb
|
@@ -277,7 +276,6 @@ files:
|
|
277
276
|
- lib/deco_lite/options.rb
|
278
277
|
- lib/deco_lite/options_defaultable.rb
|
279
278
|
- lib/deco_lite/options_validatable.rb
|
280
|
-
- lib/deco_lite/required_fields_optionable.rb
|
281
279
|
- lib/deco_lite/version.rb
|
282
280
|
homepage: https://github.com/gangelo/deco_lite
|
283
281
|
licenses:
|
@@ -298,7 +296,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
298
296
|
- !ruby/object:Gem::Version
|
299
297
|
version: '0'
|
300
298
|
requirements: []
|
301
|
-
rubygems_version: 3.
|
299
|
+
rubygems_version: 3.3.22
|
302
300
|
signing_key:
|
303
301
|
specification_version: 4
|
304
302
|
summary: Dynamically creates an active model from a Hash.
|
@@ -1,37 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module DecoLite
|
4
|
-
# Provides methods to manage fields that must be defined from
|
5
|
-
# the dynamically loaded data.
|
6
|
-
module FieldRequireable
|
7
|
-
MISSING_REQUIRED_FIELD_ERROR_TYPE = :missing_required_field
|
8
|
-
|
9
|
-
# Returns field names that will be used to validate the presence of
|
10
|
-
# dynamically created fields from loaded objects.
|
11
|
-
#
|
12
|
-
# You must override this method if you want to return field names that
|
13
|
-
# are required to be present. You may simply return a static array, but
|
14
|
-
# if you want to dynamically manipulate this field, return a variable.
|
15
|
-
def required_fields
|
16
|
-
# @required_fields ||= []
|
17
|
-
[]
|
18
|
-
end
|
19
|
-
|
20
|
-
# Validator for field names. This validator simply checks to make
|
21
|
-
# sure that the field was created, which can only occur if:
|
22
|
-
# A) The field was defined on the model explicitly (e.g. attr_accessor :field).
|
23
|
-
# B) The field was created as a result of loading data dynamically.
|
24
|
-
def validate_required_fields
|
25
|
-
required_fields.each do |field_name|
|
26
|
-
next if required_field_exist? field_name: field_name
|
27
|
-
|
28
|
-
errors.add(field_name, 'field is missing', type: MISSING_REQUIRED_FIELD_ERROR_TYPE)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
# :reek:ManualDispatch - method added dynamically; this is the best way to check.
|
33
|
-
def required_field_exist?(field_name:)
|
34
|
-
respond_to?(field_name) && respond_to?("#{field_name}=".to_sym)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module DecoLite
|
4
|
-
# Defines the fields option hash key and acceptable hash key values.
|
5
|
-
module RequiredFieldsOptionable
|
6
|
-
# The option hash key for this option.
|
7
|
-
OPTION_REQUIRED_FIELDS = :required_fields
|
8
|
-
# The valid option values for this option key.
|
9
|
-
OPTION_REQUIRED_FIELDS_AUTO = :auto
|
10
|
-
# The default value for this option.
|
11
|
-
OPTION_REQUIRED_FIELDS_DEFAULT = OPTION_REQUIRED_FIELDS_AUTO
|
12
|
-
# The valid option key values for this option.
|
13
|
-
OPTION_REQUIRED_FIELDS_VALUES = [OPTION_REQUIRED_FIELDS_AUTO].freeze
|
14
|
-
end
|
15
|
-
end
|