deco_lite 0.1.1 → 0.2.4
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 +19 -0
- data/Gemfile.lock +21 -17
- data/README.md +23 -0
- data/deco_lite.gemspec +1 -0
- data/lib/deco_lite/field_conflictable.rb +27 -6
- data/lib/deco_lite/field_creatable.rb +22 -1
- data/lib/deco_lite/field_names_persistable.rb +14 -0
- data/lib/deco_lite/field_validatable.rb +16 -0
- data/lib/deco_lite/hash_loadable.rb +20 -7
- data/lib/deco_lite/model.rb +18 -5
- data/lib/deco_lite/options.rb +1 -1
- data/lib/deco_lite/version.rb +1 -1
- data/lib/deco_lite.rb +17 -3
- metadata +18 -3
- data/lib/deco_lite/field_informable.rb +0 -82
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2d360b0607d72711264a19f807ad4367237f0e5e69efb385728ccbda66e72e05
|
4
|
+
data.tar.gz: f90e61477cc9dfa1ba8d19070c1490fcccfce413533436ed93b532d4933fad08
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 87d78e241cd8aada8c09c8adbcb20cdb5997e96ff4f17cc8dd3a494e973fd10f13fe73927edb604e2125209119bd881522c79995753768fc0f64d049632d1751
|
7
|
+
data.tar.gz: d4ae03338d636d82d6a67996cea9e2143a07b0319a9ad6f3a1f1e4a508b2f9a4262ec5cb7e68c8cb2b7d29a8f0e046413ad7f9470f4a20c6fa470c20d9e3c907
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,22 @@
|
|
1
|
+
### 0.2.4
|
2
|
+
* Changes
|
3
|
+
* Change DecoLite::Model#load to #load! as it alters the object, give deprecation warning when calling #load.
|
4
|
+
* FieldConflictable now expliticly prohibits loading fields that conflict with attributes that are native to the receiver. In other words, you cannot load fields with names like :to_s, :tap, :hash, etc.
|
5
|
+
* FieldCreatable now creates attr_accessors on the instance using #define_singleton_method, not at the class level (i.e. self.class.attr_accessor) (see bug fixes).
|
6
|
+
* bug fixes
|
7
|
+
* Fix bug that used self.class.attr_accessor in DecoLite::FieldCreatable to create attributes, which forced every object of that class subsequently created have the accessors created which caused field name conflicts across DecoLite::Model objects.
|
8
|
+
|
9
|
+
### 0.2.3
|
10
|
+
* Fix bug that added duplcate field names to Model#field_names.
|
11
|
+
|
12
|
+
### 0.2.2
|
13
|
+
* Fix bug requiring support codez in lib/deco_lite.rb.
|
14
|
+
|
15
|
+
### 0.2.1
|
16
|
+
* changes
|
17
|
+
* Add mad_flatter gem runtime dependency.
|
18
|
+
* Refactor to let mad_flatter handle the Hash flattening.
|
19
|
+
|
1
20
|
### 0.1.1
|
2
21
|
* changes
|
3
22
|
* Update gems and especially rake gem version to squash CVE-2020-8130, see https://github.com/advisories/GHSA-jppv-gw3r-w3q8.
|
data/Gemfile.lock
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
deco_lite (0.
|
4
|
+
deco_lite (0.2.4)
|
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)
|
8
|
+
mad_flatter (~> 1.0.0.pre.beta)
|
8
9
|
|
9
10
|
GEM
|
10
11
|
remote: https://rubygems.org/
|
@@ -24,20 +25,23 @@ GEM
|
|
24
25
|
docile (1.4.0)
|
25
26
|
i18n (1.12.0)
|
26
27
|
concurrent-ruby (~> 1.0)
|
27
|
-
immutable_struct_ex (0.2.
|
28
|
+
immutable_struct_ex (0.2.2)
|
28
29
|
json (2.6.2)
|
29
30
|
kwalify (0.7.2)
|
31
|
+
mad_flatter (1.0.1.pre.beta)
|
32
|
+
activesupport (~> 7.0, >= 7.0.3.1)
|
33
|
+
immutable_struct_ex (~> 0.2.0)
|
30
34
|
method_source (1.0.0)
|
31
|
-
minitest (5.16.
|
35
|
+
minitest (5.16.3)
|
32
36
|
parallel (1.22.1)
|
33
37
|
parser (3.1.2.1)
|
34
38
|
ast (~> 2.4.1)
|
35
|
-
pry (0.
|
39
|
+
pry (0.14.1)
|
36
40
|
coderay (~> 1.1)
|
37
41
|
method_source (~> 1.0)
|
38
|
-
pry-byebug (3.
|
42
|
+
pry-byebug (3.10.1)
|
39
43
|
byebug (~> 11.0)
|
40
|
-
pry (
|
44
|
+
pry (>= 0.13, < 0.15)
|
41
45
|
rainbow (3.1.1)
|
42
46
|
rake (13.0.6)
|
43
47
|
reek (6.1.1)
|
@@ -46,19 +50,19 @@ GEM
|
|
46
50
|
rainbow (>= 2.0, < 4.0)
|
47
51
|
regexp_parser (2.5.0)
|
48
52
|
rexml (3.2.5)
|
49
|
-
rspec (3.
|
50
|
-
rspec-core (~> 3.
|
51
|
-
rspec-expectations (~> 3.
|
52
|
-
rspec-mocks (~> 3.
|
53
|
-
rspec-core (3.
|
54
|
-
rspec-support (~> 3.
|
55
|
-
rspec-expectations (3.
|
53
|
+
rspec (3.11.0)
|
54
|
+
rspec-core (~> 3.11.0)
|
55
|
+
rspec-expectations (~> 3.11.0)
|
56
|
+
rspec-mocks (~> 3.11.0)
|
57
|
+
rspec-core (3.11.0)
|
58
|
+
rspec-support (~> 3.11.0)
|
59
|
+
rspec-expectations (3.11.0)
|
56
60
|
diff-lcs (>= 1.2.0, < 2.0)
|
57
|
-
rspec-support (~> 3.
|
58
|
-
rspec-mocks (3.
|
61
|
+
rspec-support (~> 3.11.0)
|
62
|
+
rspec-mocks (3.11.1)
|
59
63
|
diff-lcs (>= 1.2.0, < 2.0)
|
60
|
-
rspec-support (~> 3.
|
61
|
-
rspec-support (3.
|
64
|
+
rspec-support (~> 3.11.0)
|
65
|
+
rspec-support (3.11.0)
|
62
66
|
rubocop (1.35.0)
|
63
67
|
json (~> 2.3)
|
64
68
|
parallel (~> 1.10)
|
data/README.md
CHANGED
@@ -170,6 +170,29 @@ model.wife_info_age #=> 20
|
|
170
170
|
model.wife_info_address #=> 1 street, boonton, nj 07005
|
171
171
|
```
|
172
172
|
|
173
|
+
### Manually Defining Attributes
|
174
|
+
|
175
|
+
Manually defining attributes on your subclass is possible; however, you
|
176
|
+
must add your attr_reader name to the `DecoLite::Model@field_names` array, or an error will be reaised _if_ there are any conflicting field names being loaded
|
177
|
+
using `DecoLite::Model#load!`, regardless of setting the `{ fields: :merge }`
|
178
|
+
option. This is because DecoLite assumes any existing attributes not added to
|
179
|
+
the model via `load!`to be native to the object created, and therefore will not
|
180
|
+
allow you to create attr_accessors for existing attributes, as this can potentially be dangerous.
|
181
|
+
|
182
|
+
To avoid errors when manually defining attributes on the model that could potentially be in conflict with fields loaded using `DecoLite::Model#load!`, do the following:
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
class JustBecauseYouCanDoesntMeanYouShould < DecoLite::Model
|
186
|
+
attr_accessor :existing_field
|
187
|
+
|
188
|
+
def initialize(options: {})
|
189
|
+
super
|
190
|
+
|
191
|
+
@field_names = %i(existing_field)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
```
|
195
|
+
|
173
196
|
## Development
|
174
197
|
|
175
198
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/deco_lite.gemspec
CHANGED
@@ -36,6 +36,7 @@ Gem::Specification.new do |spec|
|
|
36
36
|
spec.add_runtime_dependency 'activemodel', '~> 7.0', '>= 7.0.3.1'
|
37
37
|
spec.add_runtime_dependency 'activesupport', '~> 7.0', '>= 7.0.3.1'
|
38
38
|
spec.add_runtime_dependency 'immutable_struct_ex', '~> 0.2.0'
|
39
|
+
spec.add_runtime_dependency 'mad_flatter', '~> 1.0.0.pre.beta'
|
39
40
|
spec.add_development_dependency 'bundler', '~> 2.2', '>= 2.2.17'
|
40
41
|
spec.add_development_dependency 'pry-byebug', '~> 3.9'
|
41
42
|
spec.add_development_dependency 'reek', '~> 6.1', '>= 6.1.1'
|
@@ -9,15 +9,36 @@ module DecoLite
|
|
9
9
|
include FieldsOptionable
|
10
10
|
|
11
11
|
def validate_field_conflicts!(field_name:, options:)
|
12
|
-
return unless
|
12
|
+
return unless field_conflict?(field_name: field_name, options: options)
|
13
13
|
|
14
|
-
raise "Field
|
15
|
-
|
16
|
-
|
14
|
+
raise "Field :#{field_name} conflicts with existing method(s) " \
|
15
|
+
":#{field_name} and/or :#{field_name}=; " \
|
16
|
+
'this will raise an error when loading using strict mode ' \
|
17
|
+
"(i.e. options: { #{OPTION_FIELDS}: :#{OPTION_FIELDS_STRICT} }) " \
|
18
|
+
'or if the method(s) are native to the object (e.g :to_s, :==, etc.).'
|
17
19
|
end
|
18
20
|
|
19
|
-
|
20
|
-
|
21
|
+
# This method returns true
|
22
|
+
def field_conflict?(field_name:, options:)
|
23
|
+
# If field_name was already added using Model#load, there is only a
|
24
|
+
# conflict if options.strict? is true.
|
25
|
+
if field_names_include?(field_name: field_name)
|
26
|
+
return options.strict?
|
27
|
+
end
|
28
|
+
|
29
|
+
# If we get here, we know that :field_name does not exist as an
|
30
|
+
# attribute on the model. If the attribute already exists on the
|
31
|
+
# model, this is a conflict because we cannot override an attribute
|
32
|
+
# that already exists on the model
|
33
|
+
attr_accessor_exist?(field_name: field_name)
|
34
|
+
end
|
35
|
+
|
36
|
+
def field_names_include?(field_name:)
|
37
|
+
field_names.include? field_name
|
38
|
+
end
|
39
|
+
|
40
|
+
def attr_accessor_exist?(field_name:)
|
41
|
+
respond_to?(field_name) || respond_to?(:"#{field_name}=")
|
21
42
|
end
|
22
43
|
end
|
23
44
|
end
|
@@ -1,11 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'field_conflictable'
|
4
|
+
require_relative 'field_validatable'
|
4
5
|
|
5
6
|
module DecoLite
|
6
7
|
# Takes an array of symbols and creates attr_accessors.
|
7
8
|
module FieldCreatable
|
8
9
|
include FieldConflictable
|
10
|
+
include FieldValidatable
|
9
11
|
|
10
12
|
def create_field_accessors(field_names:, options:)
|
11
13
|
return if field_names.blank?
|
@@ -16,9 +18,28 @@ module DecoLite
|
|
16
18
|
end
|
17
19
|
|
18
20
|
def create_field_accessor(field_name:, options:)
|
21
|
+
validate_field_name!(field_name: field_name, options: options)
|
19
22
|
validate_field_conflicts!(field_name: field_name, options: options)
|
20
23
|
|
21
|
-
|
24
|
+
# If we want to set a class-level attr_accessor
|
25
|
+
# self.class.attr_accessor(field_name) if field_name.present?
|
26
|
+
|
27
|
+
create_field_getter field_name: field_name, options: options
|
28
|
+
create_field_setter field_name: field_name, options: options
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def create_field_getter(field_name:, options:)
|
34
|
+
define_singleton_method(field_name) do
|
35
|
+
instance_variable_get "@#{field_name}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_field_setter(field_name:, options:)
|
40
|
+
define_singleton_method("#{field_name}=") do |value|
|
41
|
+
instance_variable_set "@#{field_name}", value
|
42
|
+
end
|
22
43
|
end
|
23
44
|
end
|
24
45
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DecoLite
|
4
|
+
# Takes an array of symbols and creates attr_accessors.
|
5
|
+
module FieldNamesPersistable
|
6
|
+
def field_names
|
7
|
+
@field_names ||= instance_variable_get(:@field_names) || []
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
attr_writer :field_names
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DecoLite
|
4
|
+
# Defines methods validate field (attribute) names.
|
5
|
+
module FieldValidatable
|
6
|
+
FIELD_NAME_REGEX = /\A(?:[a-z_]\w*[?!=]?|\[\]=?|<<|>>|\*\*|[!~+\*\/%&^|-]|[<>]=?|<=>|={2,3}|![=~]|=~)\z/i.freeze
|
7
|
+
|
8
|
+
module_function
|
9
|
+
|
10
|
+
def validate_field_name!(field_name:, options: nil)
|
11
|
+
unless field_name =~ FIELD_NAME_REGEX
|
12
|
+
raise "field_name '#{field_name}' is not a valid field name."
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -1,24 +1,37 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'mad_flatter'
|
3
4
|
require_relative 'field_assignable'
|
4
|
-
require_relative 'field_informable'
|
5
5
|
|
6
6
|
module DecoLite
|
7
7
|
# Provides methods to load and return information about a given hash.
|
8
8
|
module HashLoadable
|
9
9
|
include FieldAssignable
|
10
|
-
include FieldInformable
|
11
10
|
|
12
11
|
private
|
13
12
|
|
14
|
-
def load_hash(hash:,
|
13
|
+
def load_hash(hash:, deco_lite_options:)
|
15
14
|
raise ArgumentError, "Argument hash is not a Hash (#{hash.class})" unless hash.is_a? Hash
|
16
15
|
|
17
|
-
return if hash.blank?
|
16
|
+
return {} if hash.blank?
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
load_service_options = merge_with_load_service_options deco_lite_options: deco_lite_options
|
19
|
+
load_service.execute(hash: hash, options: load_service_options).tap do |h|
|
20
|
+
h.each_pair do |field_name, value|
|
21
|
+
create_field_accessor field_name: field_name, options: deco_lite_options
|
22
|
+
field_names << field_name unless field_names.include? field_name
|
23
|
+
set_field_value(field_name: field_name, value: value, options: deco_lite_options)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def load_service
|
29
|
+
@load_service ||= MadFlatter::Service.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def merge_with_load_service_options(deco_lite_options:)
|
33
|
+
load_service.options.to_h.merge \
|
34
|
+
deco_lite_options.to_h.slice(*MadFlatter::OptionsDefaultable::DEFAULT_OPTIONS.keys)
|
22
35
|
end
|
23
36
|
end
|
24
37
|
end
|
data/lib/deco_lite/model.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'active_model'
|
4
|
+
require_relative 'field_creatable'
|
4
5
|
require_relative 'field_requireable'
|
5
|
-
require_relative '
|
6
|
+
require_relative 'field_names_persistable'
|
6
7
|
require_relative 'hash_loadable'
|
8
|
+
require_relative 'hashable'
|
7
9
|
require_relative 'model_nameable'
|
8
10
|
require_relative 'optionable'
|
9
11
|
|
@@ -12,16 +14,19 @@ module DecoLite
|
|
12
14
|
# dynamic models that can be used as decorators.
|
13
15
|
class Model
|
14
16
|
include ActiveModel::Model
|
17
|
+
include FieldCreatable
|
18
|
+
include FieldNamesPersistable
|
15
19
|
include FieldRequireable
|
16
|
-
include Hashable
|
17
20
|
include HashLoadable
|
21
|
+
include Hashable
|
18
22
|
include ModelNameable
|
19
23
|
include Optionable
|
20
24
|
|
21
25
|
validate :validate_required_fields
|
22
26
|
|
23
27
|
def initialize(options: {})
|
24
|
-
@
|
28
|
+
@field_names = []
|
29
|
+
|
25
30
|
# Accept whatever options are sent, but make sure
|
26
31
|
# we have defaults set up. #options_with_defaults
|
27
32
|
# will merge options into OptionsDefaultable::DEFAULT_OPTIONS
|
@@ -30,16 +35,24 @@ module DecoLite
|
|
30
35
|
self.options = Options.with_defaults options
|
31
36
|
end
|
32
37
|
|
33
|
-
def load(hash:, options: {})
|
38
|
+
def load!(hash:, options: {})
|
34
39
|
# Merge options into the default options passed through the
|
35
40
|
# constructor; these will override any options passed in when
|
36
41
|
# this object was created, allowing us to retain any defaut
|
37
42
|
# options while loading, but also provide option customization
|
38
43
|
# of options when needed.
|
39
44
|
options = Options.with_defaults(options, defaults: self.options)
|
40
|
-
|
45
|
+
|
46
|
+
load_hash(hash: hash, deco_lite_options: options)
|
41
47
|
|
42
48
|
self
|
43
49
|
end
|
50
|
+
|
51
|
+
def load(hash:, options: {})
|
52
|
+
puts 'WARNING: DecoLite::Model#load will be deprecated in a future release;' \
|
53
|
+
' use DecoLite::Model#load! instead!'
|
54
|
+
|
55
|
+
load!(hash: hash, options: options)
|
56
|
+
end
|
44
57
|
end
|
45
58
|
end
|
data/lib/deco_lite/options.rb
CHANGED
data/lib/deco_lite/version.rb
CHANGED
data/lib/deco_lite.rb
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
require_relative 'deco_lite/field_assignable'
|
4
|
+
require_relative 'deco_lite/field_conflictable'
|
5
|
+
require_relative 'deco_lite/field_creatable'
|
6
|
+
require_relative 'deco_lite/field_names_persistable'
|
7
|
+
require_relative 'deco_lite/field_requireable'
|
8
|
+
require_relative 'deco_lite/field_retrievable'
|
9
|
+
require_relative 'deco_lite/fields_optionable'
|
10
|
+
require_relative 'deco_lite/hash_loadable'
|
11
|
+
require_relative 'deco_lite/hashable'
|
12
|
+
require_relative 'deco_lite/model'
|
13
|
+
require_relative 'deco_lite/model_nameable'
|
14
|
+
require_relative 'deco_lite/namespace_optionable'
|
15
|
+
require_relative 'deco_lite/optionable'
|
16
|
+
require_relative 'deco_lite/options'
|
17
|
+
require_relative 'deco_lite/options_defaultable'
|
18
|
+
require_relative 'deco_lite/options_validatable'
|
19
|
+
require_relative 'deco_lite/version'
|
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: 0.2.4
|
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-08-
|
11
|
+
date: 2022-08-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -64,6 +64,20 @@ dependencies:
|
|
64
64
|
- - "~>"
|
65
65
|
- !ruby/object:Gem::Version
|
66
66
|
version: 0.2.0
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: mad_flatter
|
69
|
+
requirement: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - "~>"
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: 1.0.0.pre.beta
|
74
|
+
type: :runtime
|
75
|
+
prerelease: false
|
76
|
+
version_requirements: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - "~>"
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: 1.0.0.pre.beta
|
67
81
|
- !ruby/object:Gem::Dependency
|
68
82
|
name: bundler
|
69
83
|
requirement: !ruby/object:Gem::Requirement
|
@@ -247,9 +261,10 @@ files:
|
|
247
261
|
- lib/deco_lite/field_assignable.rb
|
248
262
|
- lib/deco_lite/field_conflictable.rb
|
249
263
|
- lib/deco_lite/field_creatable.rb
|
250
|
-
- lib/deco_lite/
|
264
|
+
- lib/deco_lite/field_names_persistable.rb
|
251
265
|
- lib/deco_lite/field_requireable.rb
|
252
266
|
- lib/deco_lite/field_retrievable.rb
|
267
|
+
- lib/deco_lite/field_validatable.rb
|
253
268
|
- lib/deco_lite/fields_optionable.rb
|
254
269
|
- lib/deco_lite/hash_loadable.rb
|
255
270
|
- lib/deco_lite/hashable.rb
|
@@ -1,82 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module DecoLite
|
4
|
-
# Creates and returns a hash given the parameters that are used to
|
5
|
-
# dynamically create fields and assign values to a model.
|
6
|
-
module FieldInformable
|
7
|
-
# This method simply navigates the payload hash received and creates qualified
|
8
|
-
# hash key names that can be used to verify/map to our field names in this model.
|
9
|
-
# This can be used to qualify nested hash fields and saves us some headaches
|
10
|
-
# if there are nested field names with the same name:
|
11
|
-
#
|
12
|
-
# given:
|
13
|
-
#
|
14
|
-
# hash = {
|
15
|
-
# first_name: 'first_name',
|
16
|
-
# ...
|
17
|
-
# address: {
|
18
|
-
# street: '',
|
19
|
-
# ...
|
20
|
-
# }
|
21
|
-
# }
|
22
|
-
#
|
23
|
-
# get_field_info(hash: hash) #=>
|
24
|
-
#
|
25
|
-
# {
|
26
|
-
# :first_name=>{:field_name=>:first_name, :dig=>[]},
|
27
|
-
# ...
|
28
|
-
# :address_street=>{:field_name=>:street, :dig=>[:address]},
|
29
|
-
# ...
|
30
|
-
# }
|
31
|
-
#
|
32
|
-
# The generated, qualified field names expected to map to our model, because we named
|
33
|
-
# them as such.
|
34
|
-
#
|
35
|
-
# :field_name is the actual, unqualified field name found in the payload hash sent.
|
36
|
-
# :dig is the hash key by which :field_name can be found in the payload hash if need be -
|
37
|
-
# retained across recursive calls.
|
38
|
-
def get_field_info(hash:, namespace: nil, dig: [], field_info: {})
|
39
|
-
hash.each do |key, value|
|
40
|
-
if value.is_a? Hash
|
41
|
-
get_field_info hash: value,
|
42
|
-
namespace: namespace,
|
43
|
-
dig: dig << key,
|
44
|
-
field_info: field_info
|
45
|
-
dig.pop
|
46
|
-
else
|
47
|
-
set_field_info!(field_info: field_info,
|
48
|
-
key: key,
|
49
|
-
namespace: namespace,
|
50
|
-
dig: dig)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
field_info
|
55
|
-
end
|
56
|
-
|
57
|
-
def set_field_info!(field_info:, key:, namespace:, dig:)
|
58
|
-
field_key = [namespace, *dig, key].compact.join('_').to_sym
|
59
|
-
|
60
|
-
field_info[field_key] = {
|
61
|
-
field_name: key,
|
62
|
-
dig: dig.dup
|
63
|
-
}
|
64
|
-
end
|
65
|
-
|
66
|
-
def merge_field_info!(field_info:)
|
67
|
-
@field_info.merge!(field_info)
|
68
|
-
end
|
69
|
-
|
70
|
-
def field_names
|
71
|
-
field_info&.keys || []
|
72
|
-
end
|
73
|
-
|
74
|
-
attr_reader :field_info
|
75
|
-
|
76
|
-
private
|
77
|
-
|
78
|
-
attr_writer :field_info
|
79
|
-
|
80
|
-
module_function :get_field_info, :set_field_info!, :merge_field_info!
|
81
|
-
end
|
82
|
-
end
|