mad_flatter 1.0.0.pre.alpha → 1.0.0.pre.beta
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/.reek.yml +18 -0
- data/.rubocop.yml +185 -5
- data/.ruby-version +1 -0
- data/Gemfile +1 -7
- data/Gemfile.lock +100 -0
- data/README.md +133 -6
- data/bin/console +6 -6
- data/lib/mad_flatter/hash_informable.rb +44 -0
- data/lib/mad_flatter/hash_loadable.rb +29 -0
- data/lib/mad_flatter/hash_retrievable.rb +13 -0
- data/lib/mad_flatter/metadata_optionable.rb +16 -0
- data/lib/mad_flatter/namespace_optionable.rb +11 -0
- data/lib/mad_flatter/optionable.rb +26 -0
- data/lib/mad_flatter/options.rb +37 -0
- data/lib/mad_flatter/options_defaultable.rb +17 -0
- data/lib/mad_flatter/options_validatable.rb +56 -0
- data/lib/mad_flatter/service.rb +32 -0
- data/lib/mad_flatter/version.rb +1 -1
- data/lib/mad_flatter.rb +4 -4
- metadata +170 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 996b96ff524cf36af3fc367f1de538beab06023345e7a45ba206b131d6106c08
|
4
|
+
data.tar.gz: 2d8672213da7902f712d4d40e14de3cc54305df6d19a53dfb0ed41a785fab335
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b41f5116f99ee102ef8a46b95930832aa94c06255b4df886a85177fa474f73d48dbdf799cc6f20b2c29baa84e354400e6f758da28f590a45a4837a42ed234445
|
7
|
+
data.tar.gz: fa5ce245cb03083dacc0ff8c5ba2318d8b1c89c20c157332ddcd611860de3ba6d431cac9d828a9cf3aeae0f65855ad3d894c7a991c4fc0d7444b436f4da4eab9
|
data/.reek.yml
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
exclude_paths:
|
2
|
+
- vendor
|
3
|
+
- spec
|
4
|
+
detectors:
|
5
|
+
# TooManyInstanceVariables:
|
6
|
+
# exclude:
|
7
|
+
# - "Class1"
|
8
|
+
# - "Class2"
|
9
|
+
# private methods do not have to depend on instance state
|
10
|
+
# https://github.com/troessner/reek/blob/master/docs/Utility-Function.md
|
11
|
+
UtilityFunction:
|
12
|
+
public_methods_only: true
|
13
|
+
# Check for variable name that doesn't communicate its intent well enough
|
14
|
+
# https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Variable-Name.md
|
15
|
+
UncommunicativeVariableName:
|
16
|
+
accept:
|
17
|
+
- /^_$/
|
18
|
+
- /^e$/
|
data/.rubocop.yml
CHANGED
@@ -1,13 +1,193 @@
|
|
1
|
+
require:
|
2
|
+
- rubocop-performance
|
3
|
+
- rubocop-rspec
|
4
|
+
|
1
5
|
AllCops:
|
2
|
-
TargetRubyVersion:
|
6
|
+
TargetRubyVersion: 3.0.1
|
7
|
+
NewCops: enable
|
8
|
+
Exclude:
|
9
|
+
- '.git/**/*'
|
10
|
+
- '.idea/**/*'
|
11
|
+
- 'init/*'
|
12
|
+
- 'Rakefile'
|
13
|
+
- '*.gemspec'
|
14
|
+
- 'spec/**/*'
|
15
|
+
- 'vendor/**/*'
|
16
|
+
- 'scratch.rb'
|
17
|
+
|
18
|
+
# Align the elements of a hash literal if they span more than one line.
|
19
|
+
Layout/HashAlignment:
|
20
|
+
EnforcedLastArgumentHashStyle: always_ignore
|
21
|
+
|
22
|
+
# Alignment of parameters in multi-line method definition.
|
23
|
+
# The `with_fixed_indentation` style aligns the following lines with one
|
24
|
+
# level of indentation relative to the start of the line with the method
|
25
|
+
# definition.
|
26
|
+
#
|
27
|
+
# def my_method(a,
|
28
|
+
# b)
|
29
|
+
Layout/ParameterAlignment:
|
30
|
+
EnforcedStyle: with_fixed_indentation
|
3
31
|
|
4
|
-
|
32
|
+
# Alignment of parameters in multi-line method call.
|
33
|
+
# The `with_fixed_indentation` style aligns the following lines with one
|
34
|
+
# level of indentation relative to the start of the line with the method call.
|
35
|
+
#
|
36
|
+
# my_method(a,
|
37
|
+
# b)
|
38
|
+
Layout/ArgumentAlignment:
|
39
|
+
EnforcedStyle: with_fixed_indentation
|
40
|
+
|
41
|
+
# a = case n
|
42
|
+
# when 0
|
43
|
+
# x * 2
|
44
|
+
# else
|
45
|
+
# y / 3
|
46
|
+
# end
|
47
|
+
Layout/CaseIndentation:
|
48
|
+
EnforcedStyle: end
|
49
|
+
|
50
|
+
# Enforces a configured order of definitions within a class body
|
51
|
+
Layout/ClassStructure:
|
52
|
+
Enabled: true
|
53
|
+
|
54
|
+
# Align `end` with the matching keyword or starting expression except for
|
55
|
+
# assignments, where it should be aligned with the LHS.
|
56
|
+
Layout/EndAlignment:
|
57
|
+
EnforcedStyleAlignWith: variable
|
58
|
+
AutoCorrect: true
|
59
|
+
|
60
|
+
# The `consistent` style enforces that the first element in an array
|
61
|
+
# literal where the opening bracket and the first element are on
|
62
|
+
# seprate lines is indented the same as an array literal which is not
|
63
|
+
# defined inside a method call.
|
64
|
+
Layout/FirstArrayElementIndentation:
|
65
|
+
EnforcedStyle: consistent
|
66
|
+
|
67
|
+
# The `consistent` style enforces that the first key in a hash
|
68
|
+
# literal where the opening brace and the first key are on
|
69
|
+
# seprate lines is indented the same as a hash literal which is not
|
70
|
+
# defined inside a method call.
|
71
|
+
Layout/FirstHashElementIndentation:
|
72
|
+
EnforcedStyle: consistent
|
73
|
+
|
74
|
+
# Indent multi-line methods instead of aligning with periods
|
75
|
+
Layout/MultilineMethodCallIndentation:
|
76
|
+
EnforcedStyle: indented
|
77
|
+
|
78
|
+
# Allow `debug` in tasks for now
|
79
|
+
Lint/Debugger:
|
80
|
+
Exclude:
|
81
|
+
- 'RakeFile'
|
82
|
+
|
83
|
+
# A calculated magnitude based on number of assignments, branches, and
|
84
|
+
# conditions.
|
85
|
+
# NOTE: This is temporarily disabled until we can eliminate existing Rubocop
|
86
|
+
# complaints
|
87
|
+
Metrics/AbcSize:
|
88
|
+
Enabled: false
|
89
|
+
|
90
|
+
# Avoid long blocks with many lines.
|
91
|
+
Metrics/BlockLength:
|
92
|
+
Exclude:
|
93
|
+
- 'RakeFile'
|
94
|
+
- 'db/seeds.rb'
|
95
|
+
- 'spec/**/*.rb'
|
96
|
+
|
97
|
+
# Avoid classes longer than 100 lines of code.
|
98
|
+
# NOTE: This is temporarily disabled until we can eliminate existing Rubocop
|
99
|
+
# complaints
|
100
|
+
Metrics/ClassLength:
|
101
|
+
Max: 200
|
102
|
+
Exclude:
|
103
|
+
- 'spec/**/*.rb'
|
104
|
+
|
105
|
+
# A complexity metric that is strongly correlated to the number of test cases
|
106
|
+
# needed to validate a method.
|
107
|
+
Metrics/CyclomaticComplexity:
|
108
|
+
Max: 9
|
109
|
+
|
110
|
+
# Limit lines to 80 characters
|
111
|
+
Layout/LineLength:
|
112
|
+
Max: 120
|
113
|
+
Exclude:
|
114
|
+
- 'RakeFile'
|
115
|
+
- 'spec/**/*.rb'
|
116
|
+
|
117
|
+
# Avoid methods longer than 15 lines of code.
|
118
|
+
Metrics/MethodLength:
|
119
|
+
Max: 20
|
120
|
+
IgnoredMethods:
|
121
|
+
- swagger_path
|
122
|
+
- operation
|
123
|
+
|
124
|
+
|
125
|
+
# A complexity metric geared towards measuring complexity for a human reader.
|
126
|
+
Metrics/PerceivedComplexity:
|
127
|
+
Max: 10
|
128
|
+
|
129
|
+
# Naming/FileName:
|
130
|
+
# Exclude:
|
131
|
+
# - 'lib/file.rb'
|
132
|
+
|
133
|
+
# Allow `downcase == ` instead of forcing `casecmp`
|
134
|
+
Performance/Casecmp:
|
135
|
+
Enabled: false
|
136
|
+
|
137
|
+
# Require children definitions to be nested or compact in classes and modules
|
138
|
+
Style/ClassAndModuleChildren:
|
139
|
+
Enabled: false
|
140
|
+
|
141
|
+
# Document classes and non-namespace modules.
|
142
|
+
# (Disabled for now, may revisit later)
|
143
|
+
Style/Documentation:
|
144
|
+
Enabled: false
|
145
|
+
|
146
|
+
# Checks the formatting of empty method definitions.
|
147
|
+
Style/EmptyMethod:
|
148
|
+
EnforcedStyle: expanded
|
149
|
+
|
150
|
+
# Add the frozen_string_literal comment to the top of files to help transition
|
151
|
+
# to frozen string literals by default.
|
152
|
+
Style/FrozenStringLiteralComment:
|
153
|
+
EnforcedStyle: always
|
154
|
+
|
155
|
+
# Check for conditionals that can be replaced with guard clauses
|
156
|
+
Style/GuardClause:
|
157
|
+
Enabled: false
|
158
|
+
|
159
|
+
Style/MixinUsage:
|
160
|
+
Exclude:
|
161
|
+
- 'RakeFile'
|
162
|
+
|
163
|
+
# Avoid multi-line method signatures.
|
164
|
+
Style/MultilineMethodSignature:
|
165
|
+
Enabled: true
|
166
|
+
|
167
|
+
# Don't use option hashes when you can use keyword arguments.
|
168
|
+
Style/OptionHash:
|
169
|
+
Enabled: true
|
170
|
+
|
171
|
+
# Use return instead of return nil.
|
172
|
+
Style/ReturnNil:
|
173
|
+
Enabled: true
|
174
|
+
|
175
|
+
# Allow code like `return x, y` as it's occasionally handy.
|
176
|
+
Style/RedundantReturn:
|
177
|
+
AllowMultipleReturnValues: true
|
178
|
+
|
179
|
+
# Prefer symbols instead of strings as hash keys.
|
180
|
+
Style/StringHashKeys:
|
5
181
|
Enabled: true
|
6
|
-
EnforcedStyle: double_quotes
|
7
182
|
|
8
183
|
Style/StringLiteralsInInterpolation:
|
9
184
|
Enabled: true
|
10
185
|
EnforcedStyle: double_quotes
|
11
186
|
|
12
|
-
|
13
|
-
|
187
|
+
# Checks if configured preferred methods are used over non-preferred.
|
188
|
+
Style/StringMethods:
|
189
|
+
Enabled: true
|
190
|
+
|
191
|
+
# Checks for use of parentheses around ternary conditions.
|
192
|
+
Style/TernaryParentheses:
|
193
|
+
EnforcedStyle: require_parentheses_when_complex
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.0.1
|
data/Gemfile
CHANGED
data/Gemfile.lock
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
mad_flatter (1.0.0.pre.beta)
|
5
|
+
activesupport (~> 7.0, >= 7.0.3.1)
|
6
|
+
immutable_struct_ex (~> 0.2.0)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
activesupport (7.0.3.1)
|
12
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
13
|
+
i18n (>= 1.6, < 2)
|
14
|
+
minitest (>= 5.1)
|
15
|
+
tzinfo (~> 2.0)
|
16
|
+
ast (2.4.2)
|
17
|
+
byebug (11.1.3)
|
18
|
+
coderay (1.1.3)
|
19
|
+
concurrent-ruby (1.1.10)
|
20
|
+
diff-lcs (1.5.0)
|
21
|
+
docile (1.4.0)
|
22
|
+
i18n (1.12.0)
|
23
|
+
concurrent-ruby (~> 1.0)
|
24
|
+
immutable_struct_ex (0.2.2)
|
25
|
+
json (2.6.2)
|
26
|
+
kwalify (0.7.2)
|
27
|
+
method_source (1.0.0)
|
28
|
+
minitest (5.16.2)
|
29
|
+
parallel (1.22.1)
|
30
|
+
parser (3.1.2.1)
|
31
|
+
ast (~> 2.4.1)
|
32
|
+
pry (0.14.1)
|
33
|
+
coderay (~> 1.1)
|
34
|
+
method_source (~> 1.0)
|
35
|
+
pry-byebug (3.10.1)
|
36
|
+
byebug (~> 11.0)
|
37
|
+
pry (>= 0.13, < 0.15)
|
38
|
+
rainbow (3.1.1)
|
39
|
+
reek (6.1.1)
|
40
|
+
kwalify (~> 0.7.0)
|
41
|
+
parser (~> 3.1.0)
|
42
|
+
rainbow (>= 2.0, < 4.0)
|
43
|
+
regexp_parser (2.5.0)
|
44
|
+
rexml (3.2.5)
|
45
|
+
rspec (3.11.0)
|
46
|
+
rspec-core (~> 3.11.0)
|
47
|
+
rspec-expectations (~> 3.11.0)
|
48
|
+
rspec-mocks (~> 3.11.0)
|
49
|
+
rspec-core (3.11.0)
|
50
|
+
rspec-support (~> 3.11.0)
|
51
|
+
rspec-expectations (3.11.0)
|
52
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
53
|
+
rspec-support (~> 3.11.0)
|
54
|
+
rspec-mocks (3.11.1)
|
55
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
56
|
+
rspec-support (~> 3.11.0)
|
57
|
+
rspec-support (3.11.0)
|
58
|
+
rubocop (1.35.0)
|
59
|
+
json (~> 2.3)
|
60
|
+
parallel (~> 1.10)
|
61
|
+
parser (>= 3.1.2.1)
|
62
|
+
rainbow (>= 2.2.2, < 4.0)
|
63
|
+
regexp_parser (>= 1.8, < 3.0)
|
64
|
+
rexml (>= 3.2.5, < 4.0)
|
65
|
+
rubocop-ast (>= 1.20.1, < 2.0)
|
66
|
+
ruby-progressbar (~> 1.7)
|
67
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
68
|
+
rubocop-ast (1.21.0)
|
69
|
+
parser (>= 3.1.1.0)
|
70
|
+
rubocop-performance (1.14.3)
|
71
|
+
rubocop (>= 1.7.0, < 2.0)
|
72
|
+
rubocop-ast (>= 0.4.0)
|
73
|
+
rubocop-rspec (2.12.1)
|
74
|
+
rubocop (~> 1.31)
|
75
|
+
ruby-progressbar (1.11.0)
|
76
|
+
simplecov (0.21.2)
|
77
|
+
docile (~> 1.1)
|
78
|
+
simplecov-html (~> 0.11)
|
79
|
+
simplecov_json_formatter (~> 0.1)
|
80
|
+
simplecov-html (0.12.3)
|
81
|
+
simplecov_json_formatter (0.1.4)
|
82
|
+
tzinfo (2.0.5)
|
83
|
+
concurrent-ruby (~> 1.0)
|
84
|
+
unicode-display_width (2.2.0)
|
85
|
+
|
86
|
+
PLATFORMS
|
87
|
+
x86_64-darwin-19
|
88
|
+
|
89
|
+
DEPENDENCIES
|
90
|
+
mad_flatter!
|
91
|
+
pry-byebug (~> 3.9)
|
92
|
+
reek (~> 6.1, >= 6.1.1)
|
93
|
+
rspec (>= 3.10)
|
94
|
+
rubocop (~> 1.31)
|
95
|
+
rubocop-performance (~> 1.14, >= 1.14.3)
|
96
|
+
rubocop-rspec (~> 2.12, >= 2.12.1)
|
97
|
+
simplecov (~> 0.21.2)
|
98
|
+
|
99
|
+
BUNDLED WITH
|
100
|
+
2.3.20
|
data/README.md
CHANGED
@@ -1,8 +1,139 @@
|
|
1
1
|
# MadFlatter
|
2
2
|
|
3
|
-
|
3
|
+
[](https://badge.fury.io/gh/gangelo%2Fmad_flatter)
|
4
|
+
|
5
|
+
[](https://badge.fury.io/rb/mad_flatter)
|
6
|
+
|
7
|
+
[](http://www.rubydoc.info/gems/mad_flatter/)
|
8
|
+
[](http://www.rubydoc.info/gems/mad_flatter/)
|
9
|
+
|
10
|
+
[](https://github.com/gangelo/mad_flatter/issues)
|
11
|
+
|
12
|
+
[](#license)
|
13
|
+
|
14
|
+
This is a work in process. No breaking changes are expected before v1.0.0.
|
15
|
+
|
16
|
+
TODO: Add specs.
|
17
|
+
TODO: Better documentation.
|
18
|
+
|
19
|
+
mad_flatter is a Ruby gem that takes a Ruby `Hash` and flattens the Hash keys to
|
20
|
+
create a new Hash with unique Hash keys; that is, embedded Hashes use their
|
21
|
+
respective keys as namespaces to create unique keys across the entire Hash.
|
22
|
+
For example:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
user0 = {
|
26
|
+
name: 'john',
|
27
|
+
wife: { name: 'mary' },
|
28
|
+
children: {
|
29
|
+
child0: 'sam',
|
30
|
+
child1: 'martha'
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
result0 = MadFlatter::Service.new.execute(hash: user0)
|
35
|
+
#=>
|
36
|
+
{
|
37
|
+
:name=>"john",
|
38
|
+
:wife_name=>"mary",
|
39
|
+
:children_child0=>"sam",
|
40
|
+
:children_child1=>"martha"
|
41
|
+
}
|
42
|
+
```
|
43
|
+
|
44
|
+
The `:namespace` option may be used to append to all Hash keys, to ensure that all Hash keys are unique across multiple Hashes with the same structure. For example:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
# Continuing from the above example...
|
48
|
+
user1 = {
|
49
|
+
name: 'james',
|
50
|
+
wife: { name: 'molly' },
|
51
|
+
children: {
|
52
|
+
child0: 'steve',
|
53
|
+
child1: 'maybell'
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
options = { namespace: :ns1 }
|
58
|
+
result1 = MadFlatter::Service.new.execute(hash: user1, options: options)
|
59
|
+
#=>
|
60
|
+
{
|
61
|
+
:ns1_name=>"james",
|
62
|
+
:ns1_wife_name=>"molly",
|
63
|
+
:ns1_children_child0=>"steve",
|
64
|
+
:ns1_children_child1=>"maybell"
|
65
|
+
}
|
66
|
+
|
67
|
+
result0.merge(result1)
|
68
|
+
#=>
|
69
|
+
{
|
70
|
+
:name=>"john",
|
71
|
+
:wife_name=>"mary",
|
72
|
+
:children_child0=>"sam",
|
73
|
+
:children_child1=>"martha",
|
74
|
+
:ns1_name=>"james",
|
75
|
+
:ns1_wife_name=>"molly",
|
76
|
+
:ns1_children_child0=>"steve",
|
77
|
+
:ns1_children_child1=>"maybell"
|
78
|
+
}
|
79
|
+
```
|
80
|
+
|
81
|
+
The metadata can optionally be returned by setting the `:metadata` option
|
82
|
+
to true. This option is `false` by default. For example:
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
best_cake = {
|
86
|
+
name: 'black forest',
|
87
|
+
options: {
|
88
|
+
cherries: false
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
options = { namespace: :best_cake, metadata: true }
|
93
|
+
result = MadFlatter::Service.new(options: options).execute(hash: best_cake)
|
94
|
+
#=>
|
95
|
+
{
|
96
|
+
:best_cake_name=>
|
97
|
+
{
|
98
|
+
:value=>"black forest",
|
99
|
+
:metadata=> {
|
100
|
+
:key=>:name,
|
101
|
+
:dig=>[]
|
102
|
+
}
|
103
|
+
},
|
104
|
+
:best_cake_options_cherries=> {
|
105
|
+
:value=>false,
|
106
|
+
:metadata=> {
|
107
|
+
:key=>:cherries,
|
108
|
+
:dig=>[:options]
|
109
|
+
}
|
110
|
+
}
|
111
|
+
}
|
112
|
+
|
113
|
+
result.each_pair do |key, value|
|
114
|
+
original_key = value[:metadata][:key]
|
115
|
+
original_value = best_cake.dig(*value[:metadata][:dig], original_key)
|
116
|
+
puts 'Original Hash key/value:'
|
117
|
+
puts "\t#{original_key} => \"#{original_value}\""
|
118
|
+
puts 'New Hash key/value:'
|
119
|
+
puts "\t#{key} => \"#{value[:value]}\""
|
120
|
+
puts
|
121
|
+
end
|
122
|
+
```
|
123
|
+
Prints:
|
124
|
+
|
125
|
+
```
|
126
|
+
Original Hash key/value:
|
127
|
+
name => "black forest"
|
128
|
+
New Hash key/value:
|
129
|
+
best_cake_name => "black forest"
|
130
|
+
|
131
|
+
Original Hash key/value:
|
132
|
+
cherries => "false"
|
133
|
+
New Hash key/value:
|
134
|
+
best_cake_options_cherries => "false"
|
135
|
+
```
|
4
136
|
|
5
|
-
TODO: Delete this and the text above, and describe your gem
|
6
137
|
|
7
138
|
## Installation
|
8
139
|
|
@@ -20,10 +151,6 @@ Or install it yourself as:
|
|
20
151
|
|
21
152
|
$ gem install mad_flatter
|
22
153
|
|
23
|
-
## Usage
|
24
|
-
|
25
|
-
TODO: Write usage instructions here
|
26
|
-
|
27
154
|
## Development
|
28
155
|
|
29
156
|
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/bin/console
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require
|
5
|
-
require
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'mad_flatter'
|
6
6
|
|
7
7
|
# You can add fixtures and/or initialization code here to make experimenting
|
8
8
|
# with your gem easier. You can also use a different console, if you like.
|
9
9
|
|
10
10
|
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
-
|
12
|
-
|
11
|
+
require 'pry'
|
12
|
+
Pry.start
|
13
13
|
|
14
|
-
require "irb"
|
15
|
-
IRB.start(__FILE__)
|
14
|
+
# require "irb"
|
15
|
+
# IRB.start(__FILE__)
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MadFlatter
|
4
|
+
# Flattens the provided Hash and assigns the results to the #hash_info
|
5
|
+
# attribute. If a namespace is provided, the namespace is prepended to
|
6
|
+
# the Hash key name.
|
7
|
+
module HashInformable
|
8
|
+
def load_hash_info(hash:, namespace: nil, dig: [], hash_info: {})
|
9
|
+
hash.each do |key, value|
|
10
|
+
if value.is_a? Hash
|
11
|
+
load_hash_info(hash: value,
|
12
|
+
namespace: namespace,
|
13
|
+
dig: dig << key,
|
14
|
+
hash_info: hash_info)
|
15
|
+
dig.pop
|
16
|
+
else
|
17
|
+
assign_hash_info(hash_info: hash_info,
|
18
|
+
key: key,
|
19
|
+
value: value,
|
20
|
+
namespace: namespace,
|
21
|
+
dig: dig)
|
22
|
+
end
|
23
|
+
|
24
|
+
next
|
25
|
+
end
|
26
|
+
|
27
|
+
hash_info
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def assign_hash_info(hash_info:, key:, value:, namespace:, dig:)
|
33
|
+
hash_key = [namespace, *dig, key].compact.join('_').to_sym
|
34
|
+
|
35
|
+
hash_info[hash_key] = {
|
36
|
+
value: value,
|
37
|
+
metadata: {
|
38
|
+
key: key,
|
39
|
+
dig: dig.dup
|
40
|
+
}
|
41
|
+
}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'hash_informable'
|
4
|
+
|
5
|
+
module MadFlatter
|
6
|
+
# Provides methods to load and return information about a given hash.
|
7
|
+
module HashLoadable
|
8
|
+
include HashInformable
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def load_hash(hash, options)
|
13
|
+
raise ArgumentError, "Argument hash is not a Hash (#{hash.class})" unless hash.is_a? Hash
|
14
|
+
|
15
|
+
return {} if hash.blank?
|
16
|
+
|
17
|
+
hash_info = load_hash_info(hash: hash, namespace: options.namespace)
|
18
|
+
return hash_info if options.metadata?
|
19
|
+
|
20
|
+
strip_metadata(hash_info)
|
21
|
+
end
|
22
|
+
|
23
|
+
def strip_metadata(hash)
|
24
|
+
hash.transform_values do |metadata|
|
25
|
+
metadata[:value]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MadFlatter
|
4
|
+
# Defines methods to retrieve model hash values dynamically.
|
5
|
+
module HashRetrievable
|
6
|
+
module_function
|
7
|
+
|
8
|
+
# Returns the value of the hash using fully quaified hash names.
|
9
|
+
def get_hash_value(hash:, hash_info:)
|
10
|
+
hash.dig(*[hash_info[:dig], hash_info[:field_name]].flatten.compact)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MadFlatter
|
4
|
+
# Defines the metadata option hash key and acceptable hash key values.
|
5
|
+
module MetadataOptionable
|
6
|
+
# The option hash key for this option.
|
7
|
+
OPTION_METADATA = :metadata
|
8
|
+
# The valid option values for this option key.
|
9
|
+
OPTION_METADATA_INCLUDE = true
|
10
|
+
OPTION_METADATA_EXCLUDE = false
|
11
|
+
# The default value for this option.
|
12
|
+
OPTION_METADATA_DEFAULT = OPTION_METADATA_EXCLUDE
|
13
|
+
# The valid option key values for this option.
|
14
|
+
OPTION_METADATA_VALUES = [OPTION_METADATA_INCLUDE, OPTION_METADATA_EXCLUDE].freeze
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MadFlatter
|
4
|
+
# Defines the namespace option hash key
|
5
|
+
module NamespaceOptionable
|
6
|
+
# The option hash key for this option.
|
7
|
+
OPTION_NAMESPACE = :namespace
|
8
|
+
# The default value for this option - no namespace.
|
9
|
+
OPTION_NAMESPACE_DEFAULT = nil
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'immutable_struct_ex'
|
4
|
+
require_relative 'options'
|
5
|
+
require_relative 'options_validatable'
|
6
|
+
|
7
|
+
module MadFlatter
|
8
|
+
# Defines methods and attributes to manage options.
|
9
|
+
module Optionable
|
10
|
+
include OptionsValidatable
|
11
|
+
|
12
|
+
def options
|
13
|
+
@options || Options.default
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def options=(value)
|
19
|
+
options_hash = value.to_h
|
20
|
+
|
21
|
+
validate_options! options: options_hash
|
22
|
+
|
23
|
+
@options = Options.new(**options_hash)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'immutable_struct_ex'
|
4
|
+
require_relative 'options_defaultable'
|
5
|
+
require_relative 'options_validatable'
|
6
|
+
|
7
|
+
module MadFlatter
|
8
|
+
# Defines methods to create options.
|
9
|
+
module Options
|
10
|
+
extend MadFlatter::OptionsDefaultable
|
11
|
+
extend MadFlatter::OptionsValidatable
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def new(**options)
|
15
|
+
immutable_struct_ex = ImmutableStructEx.new(**options) do
|
16
|
+
def namespace?
|
17
|
+
namespace || false
|
18
|
+
end
|
19
|
+
|
20
|
+
def metadata?
|
21
|
+
metadata || false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
validate_options! options: immutable_struct_ex.to_h
|
25
|
+
immutable_struct_ex
|
26
|
+
end
|
27
|
+
|
28
|
+
def with_defaults(options, defaults: DEFAULT_OPTIONS)
|
29
|
+
new(**defaults.to_h.merge(options.to_h))
|
30
|
+
end
|
31
|
+
|
32
|
+
def default
|
33
|
+
with_defaults({})
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'metadata_optionable'
|
4
|
+
require_relative 'namespace_optionable'
|
5
|
+
|
6
|
+
module MadFlatter
|
7
|
+
# Defines default options and their optionn values.
|
8
|
+
module OptionsDefaultable
|
9
|
+
include MadFlatter::MetadataOptionable
|
10
|
+
include MadFlatter::NamespaceOptionable
|
11
|
+
|
12
|
+
DEFAULT_OPTIONS = {
|
13
|
+
OPTION_NAMESPACE => OPTION_NAMESPACE_DEFAULT,
|
14
|
+
OPTION_METADATA => OPTION_METADATA_DEFAULT
|
15
|
+
}.freeze
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'metadata_optionable'
|
4
|
+
require_relative 'namespace_optionable'
|
5
|
+
|
6
|
+
module MadFlatter
|
7
|
+
# Methods to validate options.
|
8
|
+
module OptionsValidatable
|
9
|
+
include MadFlatter::MetadataOptionable
|
10
|
+
include MadFlatter::NamespaceOptionable
|
11
|
+
|
12
|
+
OPTIONS = [OPTION_METADATA, OPTION_NAMESPACE].freeze
|
13
|
+
|
14
|
+
def validate_options!(options:)
|
15
|
+
raise ArgumentError, 'options is not a Hash' unless options.is_a? Hash
|
16
|
+
|
17
|
+
validate_options_present! options: options
|
18
|
+
|
19
|
+
validate_option_keys! options: options
|
20
|
+
validate_option_metadata! metadata: options[:metadata]
|
21
|
+
validate_option_namespace! namespace: options[:namespace]
|
22
|
+
end
|
23
|
+
|
24
|
+
def validate_options_present!(options:)
|
25
|
+
raise ArgumentError, 'options is blank?' if options.blank?
|
26
|
+
end
|
27
|
+
|
28
|
+
def validate_option_keys!(options:)
|
29
|
+
invalid_options = options.except(*OPTIONS)&.keys
|
30
|
+
|
31
|
+
return if invalid_options.blank?
|
32
|
+
|
33
|
+
raise ArgumentError, 'One or more option keys were unrecognized. ' \
|
34
|
+
"#{OPTIONS} was expected but '#{invalid_options} was received."
|
35
|
+
end
|
36
|
+
|
37
|
+
def validate_option_metadata!(metadata:)
|
38
|
+
return if [TrueClass, FalseClass].include? metadata.class
|
39
|
+
|
40
|
+
raise ArgumentError, "option :#{OPTION_METADATA} value is invalid. " \
|
41
|
+
'A TrueClass/FalseClass was expected ' \
|
42
|
+
"with the acceptable values #{OPTION_METADATA_VALUES} " \
|
43
|
+
"but '#{metadata}' (#{metadata.class}) was received."
|
44
|
+
end
|
45
|
+
|
46
|
+
def validate_option_namespace!(namespace:)
|
47
|
+
# :namespace is optional.
|
48
|
+
return if namespace.blank? || namespace.is_a?(Symbol)
|
49
|
+
|
50
|
+
raise ArgumentError, "option :#{OPTION_NAMESPACE} value is invalid. " \
|
51
|
+
'A Symbol was expected ' \
|
52
|
+
"with the acceptable values #{OPTION_METADATA_VALUES} " \
|
53
|
+
"but '#{metadata}' (#{metadata.class}) was received."
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'hash_loadable'
|
4
|
+
require_relative 'optionable'
|
5
|
+
|
6
|
+
module MadFlatter
|
7
|
+
# This class provides functionality to load and flatten a Hash.
|
8
|
+
# Default options passed to the constructor may be overridden with
|
9
|
+
# options passed to the #execute method.
|
10
|
+
class Service
|
11
|
+
include HashLoadable
|
12
|
+
include Optionable
|
13
|
+
|
14
|
+
def initialize(options: nil)
|
15
|
+
# Accept whatever options are sent, but make sure
|
16
|
+
# we have defaults set up. #with_defaults
|
17
|
+
# will merge options into OptionsDefaultable::DEFAULT_OPTIONS
|
18
|
+
# so we have defaults for any options not passed.
|
19
|
+
self.options = Options.with_defaults options
|
20
|
+
end
|
21
|
+
|
22
|
+
def execute(hash:, options: nil)
|
23
|
+
# Merge options received into the default options passed through
|
24
|
+
# the constructor. Options received here, will override any options
|
25
|
+
# passed to the constructor, allowing us to retain defaut options
|
26
|
+
# while loading, and also provide option overrides as needed.
|
27
|
+
options = Options.with_defaults(options, defaults: self.options)
|
28
|
+
|
29
|
+
load_hash(hash, options)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/mad_flatter/version.rb
CHANGED
data/lib/mad_flatter.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require 'active_support'
|
4
|
+
require_relative 'mad_flatter/version'
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
# Your code goes here...
|
6
|
+
Dir[File.join('.', 'lib/mad_flatter/**/*.rb')].each do |f|
|
7
|
+
require f
|
8
8
|
end
|
metadata
CHANGED
@@ -1,15 +1,165 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mad_flatter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.pre.
|
4
|
+
version: 1.0.0.pre.beta
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- gangelo
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-08-
|
12
|
-
dependencies:
|
11
|
+
date: 2022-08-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '7.0'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 7.0.3.1
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '7.0'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 7.0.3.1
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: immutable_struct_ex
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 0.2.0
|
40
|
+
type: :runtime
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 0.2.0
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: pry-byebug
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '3.9'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '3.9'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: reek
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '6.1'
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: 6.1.1
|
71
|
+
type: :development
|
72
|
+
prerelease: false
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - "~>"
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '6.1'
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: 6.1.1
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: rspec
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '3.10'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '3.10'
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: rubocop
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - "~>"
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '1.31'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - "~>"
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '1.31'
|
109
|
+
- !ruby/object:Gem::Dependency
|
110
|
+
name: rubocop-performance
|
111
|
+
requirement: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - "~>"
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '1.14'
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: 1.14.3
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - "~>"
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '1.14'
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: 1.14.3
|
129
|
+
- !ruby/object:Gem::Dependency
|
130
|
+
name: rubocop-rspec
|
131
|
+
requirement: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - "~>"
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '2.12'
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 2.12.1
|
139
|
+
type: :development
|
140
|
+
prerelease: false
|
141
|
+
version_requirements: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '2.12'
|
146
|
+
- - ">="
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
version: 2.12.1
|
149
|
+
- !ruby/object:Gem::Dependency
|
150
|
+
name: simplecov
|
151
|
+
requirement: !ruby/object:Gem::Requirement
|
152
|
+
requirements:
|
153
|
+
- - "~>"
|
154
|
+
- !ruby/object:Gem::Version
|
155
|
+
version: 0.21.2
|
156
|
+
type: :development
|
157
|
+
prerelease: false
|
158
|
+
version_requirements: !ruby/object:Gem::Requirement
|
159
|
+
requirements:
|
160
|
+
- - "~>"
|
161
|
+
- !ruby/object:Gem::Version
|
162
|
+
version: 0.21.2
|
13
163
|
description: mad_flatter gem
|
14
164
|
email:
|
15
165
|
- web.gma@gmail.com
|
@@ -17,17 +167,30 @@ executables: []
|
|
17
167
|
extensions: []
|
18
168
|
extra_rdoc_files: []
|
19
169
|
files:
|
170
|
+
- ".reek.yml"
|
20
171
|
- ".rspec"
|
21
172
|
- ".rubocop.yml"
|
173
|
+
- ".ruby-version"
|
22
174
|
- CHANGELOG.md
|
23
175
|
- CODE_OF_CONDUCT.md
|
24
176
|
- Gemfile
|
177
|
+
- Gemfile.lock
|
25
178
|
- LICENSE.txt
|
26
179
|
- README.md
|
27
180
|
- Rakefile
|
28
181
|
- bin/console
|
29
182
|
- bin/setup
|
30
183
|
- lib/mad_flatter.rb
|
184
|
+
- lib/mad_flatter/hash_informable.rb
|
185
|
+
- lib/mad_flatter/hash_loadable.rb
|
186
|
+
- lib/mad_flatter/hash_retrievable.rb
|
187
|
+
- lib/mad_flatter/metadata_optionable.rb
|
188
|
+
- lib/mad_flatter/namespace_optionable.rb
|
189
|
+
- lib/mad_flatter/optionable.rb
|
190
|
+
- lib/mad_flatter/options.rb
|
191
|
+
- lib/mad_flatter/options_defaultable.rb
|
192
|
+
- lib/mad_flatter/options_validatable.rb
|
193
|
+
- lib/mad_flatter/service.rb
|
31
194
|
- lib/mad_flatter/version.rb
|
32
195
|
- sig/mad_flatter.rbs
|
33
196
|
homepage: https://github.com/gangelo/mad_flatter
|
@@ -37,7 +200,7 @@ metadata:
|
|
37
200
|
homepage_uri: https://github.com/gangelo/mad_flatter
|
38
201
|
source_code_uri: https://github.com/gangelo/mad_flatter
|
39
202
|
changelog_uri: https://github.com/gangelo/mad_flatter/blob/main/CHANGELOG.md
|
40
|
-
post_install_message:
|
203
|
+
post_install_message:
|
41
204
|
rdoc_options: []
|
42
205
|
require_paths:
|
43
206
|
- lib
|
@@ -52,8 +215,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
52
215
|
- !ruby/object:Gem::Version
|
53
216
|
version: 1.3.1
|
54
217
|
requirements: []
|
55
|
-
rubygems_version: 3.2.
|
56
|
-
signing_key:
|
218
|
+
rubygems_version: 3.2.15
|
219
|
+
signing_key:
|
57
220
|
specification_version: 4
|
58
221
|
summary: mad_flatter gem
|
59
222
|
test_files: []
|