Deep::Hash::Struct
Struct copes with the operation that is Deep. Because the data maintenance of many hierarchies became possible, I can treat it like Hash.
Installation
Add this line to your application's Gemfile:
gem 'deep-hash-struct'
And then execute:
$ bundle
Or install it yourself as:
$ gem install deep-hash-struct
Wrapper Class Usage
The basic usage is the same as Hash Class.
Basic
wrapper = Deep::Hash::Struct::Wrapper.new
wrapper.a = 1
wrapper.b.a = 2
wrapper[:c][:a] = 3
wrapper[:c].b = 4
wrapper.to_h # => {:a=>1, :b=>{:a=>2}, :c=>{:a=>3, :b=>4}}
Block
wrapper.a do
1 + 1
end
wrapper.a # => 2
wrapper.b do
{ c: 3 }
end
wrapper.b.c # => 3
#dig
wrapper.a.b = { c: 1, d: [1, 2, [3, 4, 5]] }
wrapper.dig(:a, :b, :c) # => 1
wrapper.dig(:a, :b, :d, 2, 0) # => 3
wrapper.dig(:a, :c).blank? # => true
#merge
Deep::Hash::Struct::Wrapper Class
wrapper.a = 1
wrapper.b = 2
wrapper.c.a = 3
wrapper.c.b = 4
wrapper.c.c = 5
other = wrapper.class.new
other.a = 6
other.b = 7
other.c.a = 8
wrapper.merge(other).to_h # => {:a=>6, :b=>7, :c=>{:a=>8}}
Hash Class
wrapper.a = 1
wrapper.b = 2
wrapper.c.a = 3
wrapper.c.b = 4
wrapper.c.c = 5
other = {}
other[:a] = 6
other[:b] = 7
other[:c] = {}
other[:c][:a] = 8
wrapper.merge(other).to_h #=> {:a=>6, :b=>7, :c=>{:a=>8}}
#merge! #update
bang merge method
#deep_merge
Deep::Hash::Struct::Wrapper Class
wrapper.a = 1
wrapper.b = 2
wrapper.c.a = 3
wrapper.c.b = 4
wrapper.c.c = 5
other = wrapper.class.new
other.a = 6
other.b = 7
other.c.a = 8
wrapper.deep_merge(other).to_h # => {:a=>6, :b=>7, :c=>{:a=>8, :b=>4, :c=>5}}
Hash Class
wrapper.a = 1
wrapper.b = 2
wrapper.c.a = 3
wrapper.c.b = 4
wrapper.c.c = 5
other = {}
other[:a] = 6
other[:b] = 7
other[:c] = {}
other[:c][:a] = 8
wrapper.deep_merge(other).to_h # => {:a=>6, :b=>7, :c=>{:a=>8, :b=>4, :c=>5}}
#deep_merge!
bang deep_merge method
#reverse_merge
Deep::Hash::Struct::Wrapper Class
wrapper.a = 1
wrapper.b = 2
wrapper.c.a = 3
wrapper.c.b = 4
wrapper.c.c = 5
other = wrapper.class.new
other.a = 6
other.b = 7
other.c.a = 8
other.d.a = 9
wrapper.reverse_merge(other).to_h # => {:a=>1, :b=>2, :c=>{:a=>3, :b=>4, :c=>5}, :d=>{:a=>9}}
Hash Class
wrapper.a = 1
wrapper.b = 2
wrapper.c.a = 3
wrapper.c.b = 4
wrapper.c.c = 5
other = {}
other[:a] = 6
other[:b] = 7
other[:c] = {}
other[:c][:a] = 8
other[:d] = {}
other[:d][:a] = 9
wrapper.reverse_merge(other).to_h # => {:a=>1, :b=>2, :c=>{:a=>3, :b=>4, :c=>5}, :d=>{:a=>9}}
#reverse_merge!
bang reverse_merge method
#reverse_deep_merge
Deep::Hash::Struct::Wrapper Class
wrapper.a = 1
wrapper.b = 2
wrapper.c.a = 3
wrapper.c.b = 4
wrapper.c.c = 5
other = wrapper.class.new
other.a = 6
other.b = 7
other.c.a = 8
other.c.d = 9
wrapper.reverse_deep_merge(other).to_h # => {:a=>1, :b=>2, :c=>{:a=>3, :b=>4, :c=>5, :d=>9}}
Hash Class
wrapper.a = 1
wrapper.b = 2
wrapper.c.a = 3
wrapper.c.b = 4
wrapper.c.c = 5
other = {}
other[:a] = 6
other[:b] = 7
other[:c] = {}
other[:c][:a] = 8
other[:c][:d] = 9
wrapper.reverse_deep_merge(other).to_h # => {:a=>1, :b=>2, :c=>{:a=>3, :b=>4, :c=>5, :d=>9}}
#reverse_deep_merge!
bang reverse_deep_merge method
#fetch
wrapper.a = 1
wrapper.fetch(:a, :not_found) # => 1
wrapper.fetch(:a) { |k| "#{k} not found" } # => 1
wrapper.fetch(:b) # => nil
wrapper.fetch(:b, :not_found) # => :not_found
wrapper.fetch(:b) { |k| "#{k} not found" } # => "b not found"
#default
wrapper.a.default = 0
wrapper.b.default = []
wrapper.a.a # => 0
wrapper.b.a # => []
#map_key
wrapper.a = 1
wrapper.b = 2
wrapper.c = 3
wrapper.map_key { |k| [k] } # => [[:a], [:b], [:c]]
#map_value
wrapper.a = 1
wrapper.b = 2
wrapper.c = 3
wrapper.map_value { |k| [k] } # => [[1], [2], [3]]
#fetch_values
wrapper.a = 1
wrapper.b = 2
wrapper.fetch_values(:a, :b) # => [1, 2]
wrapper.fetch_values(:a, :c) # => KeyError: key not found: :c
wrapper.fetch_values(:a, :c) { |k| k.upcase } # => [1, :C]
#values_at
wrapper.a = 1
wrapper.b = 2
wrapper.c = 3
wrapper.values_at(:a, :b, :d) # => [1, 2, nil]
#invert
wrapper.a = 1
wrapper.b = 2
wrapper.c = 3
wrapper.invert # => {1=>:a, 2=>:b, 3=>:c}
#delete
wrapper.a = 1
wrapper.b = 2
wrapper.c = 3
wrapper.delete(:a) # => 1
wrapper.keys # => [:b, :c]
#delete_if
wrapper.a = 1
wrapper.b = 2
wrapper.c = 3
wrapper.delete_if{ |k, v| k == :a || v == 2 }.to_h # => {:c=>3}
wrapper.keys # => [:c]
#reject
wrapper.a = 1
wrapper.b = 2
wrapper.c = 3
wrapper.reject { |k, v| v > 2 }.to_h # => {:a=>1, :b=>2}
#reject!
bang reject method
#clear
wrapper.a = 1
wrapper.b = 2
wrapper.c = 3
wrapper.clear
wrapper.to_h # => {}
#flatten
wrapper.a = 1
wrapper.b = 2
wrapper.c.a = 3
wrapper.c.b = 4
wrapper.c.c = 5
wrapper.flatten # => [:a, 1, :b, 2, :c, {:a=>3, :b=>4, :c=>5}]
#has_key? #include?
wrapper.a = 1
wrapper.has_key?(:a) # => true
wrapper.has_key?(:b) # => false
#has_keys?
wrapper.a = 1
wrapper.b = 2
wrapper.c.a = 3
wrapper.has_keys?(:a) # => true
wrapper.has_keys?(:c, :a) # => true
wrapper.has_keys?(:d) # => false
wrapper.has_keys?(:c, :b) # => false
wrapper.has_keys?(:d, :a) # => false
#exclude?
wrapper.a = 1
wrapper.exclude?(:a) # => false
wrapper.exclude?(:d) # => true
#sort
wrapper.c = 1
wrapper.b = 2
wrapper.a = 3
wrapper.sort # => [[:a, 3], [:b, 2], [:c, 1]]
#shift
wrapper.a = 1
wrapper.b = 2
wrapper.c = 3
wrapper.shift # => [:a, 1]
wrapper.to_h # => {:b=>2, :c=>3}
#compact
wrapper.a = 1
wrapper.b = nil
wrapper.c.a = 2
wrapper.c.b = ""
wrapper.d.a = nil
wrapper.keys # => [:a, :b, :c, :d]
wrapper.c.keys # => [:a, :b]
wrapper.compact.keys # => [:a, :c, :d]
wrapper.compact.c.keys # => [:a, :b]
wrapper.keys # => [:a, :b, :c, :d]
wrapper.c.keys # => [:a, :b]
#compact!
bang compact method
#deep_compact
wrapper.a = 1
wrapper.b = nil
wrapper.c.a = 2
wrapper.c.b = ""
wrapper.d.a = nil
wrapper.keys # => [:a, :b, :c, :d]
wrapper.c.keys # => [:a, :b]
wrapper.deep_compact.keys # => [:a, :c]
wrapper.deep_compact.c.keys # => [:a, :b]
wrapper.keys # => [:a, :b, :c, :d]
wrapper.c.keys # => [:a, :b]
#deep_compact!
bang deep_compact method
#slice
wrapper.a = 1
wrapper.b = 2
wrapper.c = 3
wrapper.slice(:a, :b).to_h # => {:a=>1, :b=>2}
wrapper.slice(:b, :c).to_h # => {:b=>2, :c=>3}
wrapper.slice(:c, :d).to_h # => {:c=>3}
wrapper.to_h # => {:a=>1, :b=>2, :c=>3}
#slice!
bang slice method
#to_hash #to_h
wrapper.a = 1
wrapper.b = 2
wrapper.c.a = 3
wrapper.to_hash # => {:a=>1, :b=>2, :c=>{:a=>3}}
#to_json
wrapper.a = 1
wrapper.b = 2
wrapper.c.a = 3
wrapper.to_json # => "{\"a\":1,\"b\":2,\"c\":{\"a\":3}}"
#max_stages
wrapper.a = 1
wrapper.b.a = 2
wrapper.b.b = 3
wrapper.c.a.b = 4
wrapper.c.a.c = 5
wrapper.c.a.d = 6
wrapper.max_stages # => 3
wrapper.b.max_stages # => 1
wrapper.c.max_stages # => 2
#min_stages
wrapper.a = 1
wrapper.b.a = 2
wrapper.b.b = 3
wrapper.c.a.b = 4
wrapper.c.a.c = 5
wrapper.c.a.d = 6
wrapper.min_stages # => 1
wrapper.b.min_stages # => 1
wrapper.c.min_stages # => 2
Dashboard Class Usage
It is used like a two-dimensional array representing a table.
Add matrix table
dashboard = Deep::Hash::Struct::Dashboard.new
dashboard.add_table(matrix: true, side_header: "sh") do |t|
t.add_header do |h|
h.a = "h1"
h[:b] = "h2"
h.add :c, "h3"
end
t.add_side do |s|
s.a = "s1"
s[:b] = "s2"
s.add :c, "s3"
end
t.add_body do |row|
row.a.a = 11
row.a[:b] = 12
row[:a][:c] = 13
row[:b].a = 14
row.b.add :b, 15
row.b.c = 16
row.c.a = 17
row.c.b = 18
row.c.c = 19
end
end
dashboard.tables # => [#<Table matrix=true>]
table = "<table>\n"
dashboard.tables[0].each do |rows|
table << " <tr>\n"
rows.each do |row|
if row.header? || row.side?
table << " <th>#{row.name}</th>\n"
else
table << " <td>#{row.value}</td>\n"
end
end
table << " </tr>\n"
end
table << "</table>\n"
puts table
# => <table>
# => <tr>
# => <th>sh</th>
# => <th>h1</th>
# => <th>h2</th>
# => <th>h3</th>
# => </tr>
# => <tr>
# => <th>s1</th>
# => <td>11</td>
# => <td>14</td>
# => <td>17</td>
# => </tr>
# => <tr>
# => <th>s2</th>
# => <td>12</td>
# => <td>15</td>
# => <td>18</td>
# => </tr>
# => <tr>
# => <th>s3</th>
# => <td>13</td>
# => <td>16</td>
# => <td>19</td>
# => </tr>
# => </table>
Add segment table
dashboard = Deep::Hash::Struct::Dashboard.new
dashboard.add_table do |t|
t.add_header do |h|
h.a = "h1"
h[:b] = "h2"
h.add :c, "h3"
end
t.add_body do |row|
row.a = 11
row.b = 12
row.c = 13
end
t.add_body do |row|
row.c = 16
row.b = 15
row.a = 14
end
t.add_body do |row|
row.c = 19
row.a = 17
row.b = 18
end
end
dashboard.tables # => [#<Table matrix=false>]
table = "<table>\n"
dashboard.tables[0].each do |rows|
table << " <tr>\n"
rows.each do |row|
if row.header?
table << " <th>#{row.name}</th>\n"
else
table << " <td>#{row.value}</td>\n"
end
end
table << " </tr>\n"
end
table << "</table>\n"
puts table
# => <table>
# => <tr>
# => <th>h1</th>
# => <th>h2</th>
# => <th>h3</th>
# => </tr>
# => <tr>
# => <td>11</td>
# => <td>12</td>
# => <td>13</td>
# => </tr>
# => <tr>
# => <td>14</td>
# => <td>15</td>
# => <td>16</td>
# => </tr>
# => <tr>
# => <td>17</td>
# => <td>18</td>
# => <td>19</td>
# => </tr>
# => </table>
Unset value to matrix table
dashboard = Deep::Hash::Struct::Dashboard.new
dashboard.add_table(matrix: true) do |t|
t.add_header do |h|
h.a = "h1"
h[:b] = "h2"
h.add :c, "h3"
end
t.add_side do |s|
s.a = "s1"
s[:b] = "s2"
s.add :c, "s3"
end
t.add_body do |row|
row.a.b = 2
row.a.c = 3
row.b.a = 4
row.b.c = 6
row.c.a = 7
row.c.b = 8
end
end
dashboard.tables # => [#<Table matrix=true>]
table = "<table>\n"
dashboard.tables[0].each do |rows|
table << " <tr>\n"
rows.each do |row|
if row.header? || row.side?
table << " <th>#{row.name}</th>\n"
else
table << " <td>#{row.value}</td>\n"
end
end
table << " </tr>\n"
end
table << "</table>\n"
puts table
# => <table>
# => <tr>
# => <th></th>
# => <th>h1</th>
# => <th>h2</th>
# => <th>h3</th>
# => </tr>
# => <tr>
# => <th>s1</th>
# => <td></td>
# => <td>4</td>
# => <td>7</td>
# => </tr>
# => <tr>
# => <th>s2</th>
# => <td>2</td>
# => <td></td>
# => <td>8</td>
# => </tr>
# => <tr>
# => <th>s3</th>
# => <td>3</td>
# => <td>6</td>
# => <td></td>
# => </tr>
# =></table>
Unset value to segment table
dashboard = Deep::Hash::Struct::Dashboard.new
dashboard.add_table do |t|
t.add_header do |h|
h.a = "h1"
h.b = "h2"
h.c = "h3"
end
t.add_body do |row|
row.b = 2
row.c = 3
end
t.add_body do |row|
row.a = 4
row.c = 6
end
t.add_body do |row|
row.a = 7
row.b = 8
end
end
dashboard.tables # => [#<Table matrix=false>]
table = "<table>\n"
dashboard.tables[0].each do |rows|
table << " <tr>\n"
rows.each do |row|
if row.header?
table << " <th>#{row.name}</th>\n"
else
table << " <td>#{row.value}</td>\n"
end
end
table << " </tr>\n"
end
table << "</table>\n"
puts table
# => <table>
# => <tr>
# => <th>h1</th>
# => <th>h2</th>
# => <th>h3</th>
# => </tr>
# => <tr>
# => <td></td>
# => <td>2</td>
# => <td>3</td>
# => </tr>
# => <tr>
# => <td>4</td>
# => <td></td>
# => <td>6</td>
# => </tr>
# => <tr>
# => <td>7</td>
# => <td>8</td>
# => <td></td>
# => </tr>
# => </table>
Default to matrix table
dashboard = Deep::Hash::Struct::Dashboard.new
dashboard.add_table(matrix: true, default: 0) do |t|
t.add_header do |h|
h.a = "h1"
h.b = "h2"
h.c = "h3"
end
t.add_side do |s|
s.a = "s1"
s.b = "s2"
s.c = "s3"
end
t.add_body do |row|
row.a.b = 2
row.a.c = 3
row.b.a = 4
row.b.c = 6
row.c.a = 7
row.c.b = 8
end
end
dashboard.tables # => [#<Table matrix=true>]
table = "<table>\n"
dashboard.tables[0].each do |rows|
table << " <tr>\n"
rows.each do |row|
if row.header? || row.side?
table << " <th>#{row.name}</th>\n"
else
table << " <td>#{row.value}</td>\n"
end
end
table << " </tr>\n"
end
table << "</table>\n"
puts table
# => <table>
# => <tr>
# => <th></th>
# => <th>h1</th>
# => <th>h2</th>
# => <th>h3</th>
# => </tr>
# => <tr>
# => <th>s1</th>
# => <td>0</td>
# => <td>4</td>
# => <td>7</td>
# => </tr>
# => <tr>
# => <th>s2</th>
# => <td>2</td>
# => <td>0</td>
# => <td>8</td>
# => </tr>
# => <tr>
# => <th>s3</th>
# => <td>3</td>
# => <td>6</td>
# => <td>0</td>
# => </tr>
# => </table>
Default to segment table
dashboard = Deep::Hash::Struct::Dashboard.new
dashboard.add_table(default: 0) do |t|
t.add_header do |h|
h.a = "h1"
h.b = "h2"
h.c = "h3"
end
t.add_body do |row|
row.b = 2
row.c = 3
end
t.add_body do |row|
row.a = 4
row.c = 6
end
t.add_body do |row|
row.a = 7
row.b = 8
end
end
dashboard.tables # => [#<Table matrix=false>]
table = "<table>\n"
dashboard.tables[0].each do |rows|
table << " <tr>\n"
rows.each do |row|
if row.header?
table << " <th>#{row.name}</th>\n"
else
table << " <td>#{row.value}</td>\n"
end
end
table << " </tr>\n"
end
table << "</table>\n"
puts table
# => <table>
# => <tr>
# => <th>h1</th>
# => <th>h2</th>
# => <th>h3</th>
# => </tr>
# => <tr>
# => <td>0</td>
# => <td>2</td>
# => <td>3</td>
# => </tr>
# => <tr>
# => <td>4</td>
# => <td>0</td>
# => <td>6</td>
# => </tr>
# => <tr>
# => <td>7</td>
# => <td>8</td>
# => <td>0</td>
# => </tr>
# => </table>
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/etiopiamokamame/deep-hash-struct. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.
License
The gem is available as open source under the terms of the MIT License.