mad_flatter 1.0.0.pre.alpha → 1.0.0.pre.beta
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![GitHub version](http://badge.fury.io/gh/gangelo%2Fmad_flatter.svg)](https://badge.fury.io/gh/gangelo%2Fmad_flatter)
|
4
|
+
|
5
|
+
[![Gem Version](https://badge.fury.io/rb/mad_flatter.svg)](https://badge.fury.io/rb/mad_flatter)
|
6
|
+
|
7
|
+
[![](http://ruby-gem-downloads-badge.herokuapp.com/mad_flatter?type=total)](http://www.rubydoc.info/gems/mad_flatter/)
|
8
|
+
[![Documentation](http://img.shields.io/badge/docs-rdoc.info-blue.svg)](http://www.rubydoc.info/gems/mad_flatter/)
|
9
|
+
|
10
|
+
[![Report Issues](https://img.shields.io/badge/report-issues-red.svg)](https://github.com/gangelo/mad_flatter/issues)
|
11
|
+
|
12
|
+
[![License](http://img.shields.io/badge/license-MIT-yellowgreen.svg)](#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: []
|