recursive-open-struct 0.6.5 → 1.0.0
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/.travis.yml +7 -3
- data/CHANGELOG.md +16 -0
- data/README.md +17 -6
- data/lib/recursive_open_struct.rb +51 -19
- data/lib/recursive_open_struct/deep_dup.rb +2 -1
- data/lib/recursive_open_struct/version.rb +1 -1
- data/spec/recursive_open_struct/indifferent_access_spec.rb +42 -26
- data/spec/recursive_open_struct/ostruct_2_0_0_spec.rb +2 -2
- metadata +21 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2d1f3fe927a4c1021ecddcde1416e8f6135043c0
|
4
|
+
data.tar.gz: e9ef12ce07604f331d0ccf2d807575c1a41f078b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f430a2b3b7a980715c81d850b93e41259b5a3abaffb8101bd04a96cc55ad599862a1405314886753d9122fd8f045e0b4517622a8d9125478148da891f2e312e4
|
7
|
+
data.tar.gz: 00ef1d3a89aa84d70499a3cc888e8a69f5f2cc921e8e07512b0290af4786c9d80b3a6a0029f6d79f7b65a2f10efa6438d2ae88d9792288c188be8c2da5b55052
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
1.0.0 / 2015-12-11
|
2
|
+
==================
|
3
|
+
|
4
|
+
* API-Breaking Change: Frederico Aloi: Change `to_h` to always return symbol
|
5
|
+
keys. This is more consistent with OpenStruct.
|
6
|
+
* API-Breaking Change: No longer officially supporting Ruby 1.9.3.
|
7
|
+
* NEW/FIX: Kris Dekeyser: Ensure that ROS continues to work with the new
|
8
|
+
version of OpenStruct included in dev versions of Ruby 2.2.x and Ruby 2.3. It
|
9
|
+
now implements lazy attribute creation, which broke ROS.
|
10
|
+
* NEW: Added `preserve_original_keys` option to revert to the 0.x behavior. Set
|
11
|
+
it to true if you want methods like `to_h` to return strings and perhaps
|
12
|
+
other non-symbols.
|
13
|
+
* NEW: Ensuring support for Ruby 2.0.0+ including the upcoming 2.3 release and
|
14
|
+
JRuby 9000.
|
15
|
+
* FIX: Peter Yeremenko: Fix a mistake in one of the examples in the README
|
16
|
+
|
1
17
|
0.6.5 / 2015-06-30
|
2
18
|
==================
|
3
19
|
|
data/README.md
CHANGED
@@ -5,23 +5,34 @@ RecursiveOpenStructs.
|
|
5
5
|
|
6
6
|
It allows for hashes within hashes to be called in a chain of methods:
|
7
7
|
|
8
|
-
ros = RecursiveOpenStruct.new( { :
|
8
|
+
ros = RecursiveOpenStruct.new( { fooa: { foob: 'fooc' } } )
|
9
9
|
|
10
10
|
ros.fooa.foob # => 'fooc'
|
11
11
|
|
12
12
|
Also, if needed, nested hashes can still be accessed as hashes:
|
13
13
|
|
14
|
-
ros.fooa_as_a_hash # { :
|
14
|
+
ros.fooa_as_a_hash # { foob: 'fooc' }
|
15
15
|
|
16
16
|
RecursiveOpenStruct can also optionally recurse across arrays, although you
|
17
17
|
have to explicitly enable it:
|
18
18
|
|
19
|
-
h = { :somearr => [ { :
|
19
|
+
h = { :somearr => [ { name: 'a'}, { name: 'b' } ] }
|
20
|
+
ros = RecursiveOpenStruct.new(h, recurse_over_arrays: true )
|
20
21
|
|
21
|
-
ros
|
22
|
+
ros.somearr[0].name # => 'a'
|
23
|
+
ros.somearr[1].name # => 'b'
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
+
Also, by default it will turn all hash keys into symbols internally:
|
26
|
+
|
27
|
+
h = { 'fear' => 'is', 'the' => 'mindkiller' } }
|
28
|
+
ros = RecursiveOpenStruct.new(h)
|
29
|
+
ros.to_h # => { fear: 'is', the: 'mindkiller' }
|
30
|
+
|
31
|
+
You can preserve the original keys by enabling `:preserve_original_keys`:
|
32
|
+
|
33
|
+
h = { 'fear' => 'is', 'the' => 'mindkiller' } }
|
34
|
+
ros = RecursiveOpenStruct.new(h, preserve_original_keys: true)
|
35
|
+
ros.to_h # => { 'fear' => 'is', 'the' => 'mindkiller' }
|
25
36
|
|
26
37
|
## Installation
|
27
38
|
|
@@ -12,10 +12,13 @@ class RecursiveOpenStruct < OpenStruct
|
|
12
12
|
def initialize(hash=nil, args={})
|
13
13
|
hash ||= {}
|
14
14
|
@recurse_over_arrays = args.fetch(:recurse_over_arrays, false)
|
15
|
-
@
|
15
|
+
@preserve_original_keys = args.fetch(:preserve_original_keys, false)
|
16
|
+
@deep_dup = DeepDup.new(
|
17
|
+
recurse_over_arrays: @recurse_over_arrays,
|
18
|
+
preserve_original_keys: @preserve_original_keys
|
19
|
+
)
|
16
20
|
|
17
21
|
@table = args.fetch(:mutate_input_hash, false) ? hash : @deep_dup.call(hash)
|
18
|
-
@table && @table.each_key { |k| new_ostruct_member(k) }
|
19
22
|
|
20
23
|
@sub_elements = {}
|
21
24
|
end
|
@@ -39,17 +42,46 @@ class RecursiveOpenStruct < OpenStruct
|
|
39
42
|
send name
|
40
43
|
end
|
41
44
|
|
45
|
+
# Makes sure ROS responds as expected on #respond_to? and #method requests
|
46
|
+
def respond_to_missing?(mid, include_private = false)
|
47
|
+
mname = _get_key_from_table_(mid.to_s.chomp('=').chomp('_as_a_hash'))
|
48
|
+
@table.key?(mname) || super
|
49
|
+
end
|
50
|
+
|
51
|
+
# Adapted implementation of method_missing to accomodate the differences between ROS and OS.
|
52
|
+
def method_missing(mid, *args)
|
53
|
+
len = args.length
|
54
|
+
if mid =~ /^(.*)=$/
|
55
|
+
if len != 1
|
56
|
+
raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1)
|
57
|
+
end
|
58
|
+
modifiable[new_ostruct_member($1.to_sym)] = args[0]
|
59
|
+
elsif len == 0
|
60
|
+
key = mid
|
61
|
+
key = $1 if key =~ /^(.*)_as_a_hash$/
|
62
|
+
if @table.key?(_get_key_from_table_(key))
|
63
|
+
new_ostruct_member(key)
|
64
|
+
send(mid)
|
65
|
+
end
|
66
|
+
else
|
67
|
+
err = NoMethodError.new "undefined method `#{mid}' for #{self}", mid, args
|
68
|
+
err.set_backtrace caller(1)
|
69
|
+
raise err
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
42
73
|
def new_ostruct_member(name)
|
43
74
|
key_name = _get_key_from_table_ name
|
44
|
-
unless self.
|
75
|
+
unless self.methods.include?(name.to_sym)
|
45
76
|
class << self; self; end.class_eval do
|
46
77
|
define_method(name) do
|
47
78
|
v = @table[key_name]
|
48
79
|
if v.is_a?(Hash)
|
49
80
|
@sub_elements[key_name] ||= self.class.new(
|
50
81
|
v,
|
51
|
-
:
|
52
|
-
:
|
82
|
+
recurse_over_arrays: @recurse_over_arrays,
|
83
|
+
preserve_original_keys: @preserve_original_keys,
|
84
|
+
mutate_input_hash: true
|
53
85
|
)
|
54
86
|
elsif v.is_a?(Array) and @recurse_over_arrays
|
55
87
|
@sub_elements[key_name] ||= recurse_over_array(v)
|
@@ -68,22 +100,9 @@ class RecursiveOpenStruct < OpenStruct
|
|
68
100
|
key_name
|
69
101
|
end
|
70
102
|
|
71
|
-
# TODO: Make me private if/when we do an API-breaking change release
|
72
|
-
def recurse_over_array(array)
|
73
|
-
array.map do |a|
|
74
|
-
if a.is_a? Hash
|
75
|
-
self.class.new(a, :recurse_over_arrays => true, :mutate_input_hash => true)
|
76
|
-
elsif a.is_a? Array
|
77
|
-
recurse_over_array a
|
78
|
-
else
|
79
|
-
a
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
103
|
def delete_field(name)
|
85
104
|
sym = _get_key_from_table_(name)
|
86
|
-
singleton_class.__send__(:remove_method, sym, "#{sym}=")
|
105
|
+
singleton_class.__send__(:remove_method, sym, "#{sym}=") rescue NoMethodError # ignore if methods not yet generated.
|
87
106
|
@sub_elements.delete sym
|
88
107
|
@table.delete sym
|
89
108
|
end
|
@@ -95,4 +114,17 @@ class RecursiveOpenStruct < OpenStruct
|
|
95
114
|
return name.to_sym if @table.has_key?(name.to_sym)
|
96
115
|
name
|
97
116
|
end
|
117
|
+
|
118
|
+
def recurse_over_array(array)
|
119
|
+
array.map do |a|
|
120
|
+
if a.is_a? Hash
|
121
|
+
self.class.new(a, :recurse_over_arrays => true, :mutate_input_hash => true)
|
122
|
+
elsif a.is_a? Array
|
123
|
+
recurse_over_array a
|
124
|
+
else
|
125
|
+
a
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
98
130
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
class RecursiveOpenStruct::DeepDup
|
2
2
|
def initialize(opts={})
|
3
3
|
@recurse_over_arrays = opts.fetch(:recurse_over_arrays, false)
|
4
|
+
@preserve_original_keys = opts.fetch(:preserve_original_keys, false)
|
4
5
|
end
|
5
6
|
|
6
7
|
def call(obj)
|
@@ -12,7 +13,7 @@ class RecursiveOpenStruct::DeepDup
|
|
12
13
|
def deep_dup(obj, visited=[])
|
13
14
|
if obj.is_a?(Hash)
|
14
15
|
obj.each_with_object({}) do |(key, value), h|
|
15
|
-
h[key] = value_or_deep_dup(value, visited)
|
16
|
+
h[@preserve_original_keys ? key : key.to_sym] = value_or_deep_dup(value, visited)
|
16
17
|
end
|
17
18
|
elsif obj.is_a?(Array) && @recurse_over_arrays
|
18
19
|
obj.each_with_object([]) do |value, arr|
|
@@ -9,9 +9,10 @@ describe RecursiveOpenStruct do
|
|
9
9
|
|
10
10
|
describe 'indifferent access' do
|
11
11
|
let(:hash) { {:foo => value, 'bar' => symbol} }
|
12
|
-
|
13
|
-
|
12
|
+
let(:hash_ros_opts) { {} }
|
13
|
+
subject(:hash_ros) { RecursiveOpenStruct.new(hash, hash_ros_opts) }
|
14
14
|
|
15
|
+
context 'setting value with method' do
|
15
16
|
before(:each) do
|
16
17
|
subject.foo = value
|
17
18
|
end
|
@@ -23,7 +24,6 @@ describe RecursiveOpenStruct do
|
|
23
24
|
end
|
24
25
|
|
25
26
|
context 'setting value with symbol' do
|
26
|
-
|
27
27
|
before(:each) do
|
28
28
|
subject[:foo] = value
|
29
29
|
end
|
@@ -35,7 +35,6 @@ describe RecursiveOpenStruct do
|
|
35
35
|
end
|
36
36
|
|
37
37
|
context 'setting value with string' do
|
38
|
-
|
39
38
|
before(:each) do
|
40
39
|
subject['foo'] = value
|
41
40
|
end
|
@@ -47,9 +46,7 @@ describe RecursiveOpenStruct do
|
|
47
46
|
end
|
48
47
|
|
49
48
|
context 'overwriting values' do
|
50
|
-
|
51
49
|
context 'set with method' do
|
52
|
-
|
53
50
|
before(:each) do
|
54
51
|
subject.foo = value
|
55
52
|
end
|
@@ -63,11 +60,9 @@ describe RecursiveOpenStruct do
|
|
63
60
|
subject['foo'] = new_value
|
64
61
|
expect(subject.foo).to be new_value
|
65
62
|
end
|
66
|
-
|
67
63
|
end
|
68
64
|
|
69
65
|
context 'set with symbol' do
|
70
|
-
|
71
66
|
before(:each) do
|
72
67
|
subject[:foo] = value
|
73
68
|
end
|
@@ -81,11 +76,9 @@ describe RecursiveOpenStruct do
|
|
81
76
|
subject['foo'] = new_value
|
82
77
|
expect(subject[:foo]).to be new_value
|
83
78
|
end
|
84
|
-
|
85
79
|
end
|
86
80
|
|
87
81
|
context 'set with string' do
|
88
|
-
|
89
82
|
before(:each) do
|
90
83
|
subject['foo'] = value
|
91
84
|
end
|
@@ -99,11 +92,9 @@ describe RecursiveOpenStruct do
|
|
99
92
|
subject[:foo] = new_value
|
100
93
|
expect(subject['foo']).to be new_value
|
101
94
|
end
|
102
|
-
|
103
95
|
end
|
104
96
|
|
105
97
|
context 'set with hash' do
|
106
|
-
|
107
98
|
it('overrides with method') do
|
108
99
|
hash_ros.foo = new_value
|
109
100
|
expect(hash_ros[:foo]).to be new_value
|
@@ -121,27 +112,52 @@ describe RecursiveOpenStruct do
|
|
121
112
|
hash_ros['foo'] = new_value
|
122
113
|
expect(hash_ros[:foo]).to be new_value
|
123
114
|
end
|
115
|
+
end
|
124
116
|
|
117
|
+
context 'when preserve_original_keys is not enabled' do
|
118
|
+
context 'transforms original keys to symbols' do
|
119
|
+
subject(:recursive) { RecursiveOpenStruct.new(recursive_hash, recurse_over_arrays: true) }
|
120
|
+
let(:recursive_hash) { {:foo => [ {'bar' => [ { 'foo' => :bar} ] } ] } }
|
121
|
+
let(:symbolized_recursive_hash) { {:foo => [ {:bar => [ { :foo => :bar} ] } ] } }
|
122
|
+
let(:symbolized_modified_hash) { {:foo => [ {:bar => [ { :foo => :foo} ] } ] } }
|
123
|
+
let(:symbolized_hash) { Hash[hash.map{|(k,v)| [k.to_sym,v]}] }
|
124
|
+
|
125
|
+
specify 'after initialization' do
|
126
|
+
expect(hash_ros.to_h).to eq symbolized_hash
|
127
|
+
end
|
128
|
+
|
129
|
+
specify 'in recursive hashes' do
|
130
|
+
expect(recursive.to_h).to eq symbolized_recursive_hash
|
131
|
+
end
|
132
|
+
|
133
|
+
specify 'after resetting value' do
|
134
|
+
recursive.foo.first[:bar].first[:foo] = :foo
|
135
|
+
expect(recursive.to_h).to eq symbolized_modified_hash
|
136
|
+
end
|
137
|
+
end
|
125
138
|
end
|
126
139
|
|
127
|
-
context '
|
128
|
-
|
129
|
-
|
130
|
-
|
140
|
+
context 'when preserve_original_keys is enabled' do
|
141
|
+
context 'preserves the original keys' do
|
142
|
+
subject(:recursive) { RecursiveOpenStruct.new(recursive_hash, recurse_over_arrays: true, preserve_original_keys: true) }
|
143
|
+
let(:recursive_hash) { {:foo => [ {'bar' => [ { 'foo' => :bar} ] } ] } }
|
144
|
+
let(:modified_hash) { {:foo => [ {'bar' => [ { 'foo' => :foo} ] } ] } }
|
131
145
|
|
132
|
-
|
133
|
-
expect(hash_ros.to_h).to eq hash
|
134
|
-
end
|
146
|
+
let(:hash_ros_opts) { { preserve_original_keys: true }}
|
135
147
|
|
136
|
-
|
137
|
-
|
138
|
-
|
148
|
+
specify 'after initialization' do
|
149
|
+
expect(hash_ros.to_h).to eq hash
|
150
|
+
end
|
139
151
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
end
|
152
|
+
specify 'in recursive hashes' do
|
153
|
+
expect(recursive.to_h).to eq recursive_hash
|
154
|
+
end
|
144
155
|
|
156
|
+
specify 'after resetting value' do
|
157
|
+
recursive.foo.first[:bar].first[:foo] = :foo
|
158
|
+
expect(recursive.to_h).to eq modified_hash
|
159
|
+
end
|
160
|
+
end
|
145
161
|
end
|
146
162
|
|
147
163
|
end
|
@@ -95,8 +95,8 @@ describe RecursiveOpenStruct do
|
|
95
95
|
end
|
96
96
|
|
97
97
|
context "each_pair" do
|
98
|
-
it "iterates over hash keys" do
|
99
|
-
expect(ros.each_pair).to match
|
98
|
+
it "iterates over hash keys, with keys as symbol" do
|
99
|
+
expect(ros.each_pair).to match ({:foo => 'foo', :bar => :bar}.each_pair)
|
100
100
|
end
|
101
101
|
end
|
102
102
|
|
metadata
CHANGED
@@ -1,97 +1,97 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: recursive-open-struct
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- William (B.J.) Snow Orvis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-12-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: pry
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rdoc
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rspec
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- - ~>
|
73
|
+
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: '3.2'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- - ~>
|
80
|
+
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '3.2'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: simplecov
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- -
|
87
|
+
- - ">="
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: '0'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- -
|
94
|
+
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
97
|
description: |
|
@@ -113,10 +113,10 @@ extra_rdoc_files:
|
|
113
113
|
- LICENSE.txt
|
114
114
|
- README.md
|
115
115
|
files:
|
116
|
-
- .document
|
117
|
-
- .gitignore
|
118
|
-
- .rspec
|
119
|
-
- .travis.yml
|
116
|
+
- ".document"
|
117
|
+
- ".gitignore"
|
118
|
+
- ".rspec"
|
119
|
+
- ".travis.yml"
|
120
120
|
- CHANGELOG.md
|
121
121
|
- Gemfile
|
122
122
|
- LICENSE.txt
|
@@ -146,17 +146,17 @@ require_paths:
|
|
146
146
|
- lib
|
147
147
|
required_ruby_version: !ruby/object:Gem::Requirement
|
148
148
|
requirements:
|
149
|
-
- -
|
149
|
+
- - ">="
|
150
150
|
- !ruby/object:Gem::Version
|
151
151
|
version: '0'
|
152
152
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
153
153
|
requirements:
|
154
|
-
- -
|
154
|
+
- - ">="
|
155
155
|
- !ruby/object:Gem::Version
|
156
156
|
version: '0'
|
157
157
|
requirements: []
|
158
158
|
rubyforge_project:
|
159
|
-
rubygems_version: 2.
|
159
|
+
rubygems_version: 2.4.5.1
|
160
160
|
signing_key:
|
161
161
|
specification_version: 4
|
162
162
|
summary: OpenStruct subclass that returns nested hash attributes as RecursiveOpenStructs
|