deco_lite 0.3.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|