union 1.2.0 → 1.3.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
- checksums.yaml.gz.sig +0 -0
- data/{CHANGES → CHANGES.md} +13 -9
- data/Gemfile +2 -6
- data/{MANIFEST → MANIFEST.md} +3 -3
- data/README.md +41 -0
- data/Rakefile +11 -3
- data/lib/union.rb +53 -10
- data/spec/union_improvements_spec.rb +328 -0
- data/spec/union_spec.rb +35 -7
- data/union.gemspec +12 -9
- data.tar.gz.sig +0 -0
- metadata +43 -17
- metadata.gz.sig +0 -0
- data/README +0 -34
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4078ed3374822297df80188098fb07eff8a7e6f7944d3b2b98dd473c2b93da41
|
4
|
+
data.tar.gz: 685ead99ae8858d415fbc20c54f17b2814b387b7619b9a529992724ada83bf0d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5f8174f0ec82fee9e98d9a469bf9e46fd485b1702bbc0b574d14f00dcee6bcaf9dba42b9a274a46eb52a3a8f479c10be535cabf341c86f48ba565337a97b191a
|
7
|
+
data.tar.gz: 94ec356be18a6173be737683881e44ffb6696d6d66b1dab565c00e050cb089c2ce9648fbe0eedc154c57960fbe3a210dc5c1814ec43df43b2ae0f0b4dbbcace4
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/{CHANGES → CHANGES.md}
RENAMED
@@ -1,37 +1,41 @@
|
|
1
|
-
|
1
|
+
## 1.3.0 - 18-Jul-2020
|
2
|
+
* Added two methods, which_member and current_value.
|
3
|
+
* Internal refactoring that also adds some stricter argument checking.
|
4
|
+
|
5
|
+
## 1.2.0 - 24-Sep-2020
|
2
6
|
* Switched from test-unit to rspec.
|
3
7
|
* Added a Gemfile.
|
4
8
|
|
5
|
-
|
9
|
+
## 1.1.0 - 24-Jun-2020
|
6
10
|
* Switch to Apache-2.0 license, and add LICENSE file to repo.
|
7
11
|
|
8
|
-
|
12
|
+
## 1.0.6 - 4-Nov-2018
|
9
13
|
* The VERSION constant is now frozen.
|
10
14
|
* Added some metadata to the gemspec.
|
11
15
|
* Updated cert.
|
12
16
|
|
13
|
-
|
17
|
+
## 1.0.5 - 5-Dec-2015
|
14
18
|
* This gem is now signed.
|
15
19
|
* Updated gemspec, adding a cert_chain and fixing the home page.
|
16
20
|
* The gem related tasks now assume Rubygems 2.x.
|
17
21
|
|
18
|
-
|
22
|
+
## 1.0.4 - 8-Oct-2014
|
19
23
|
* Updated Rakefile for Rubygems 2.x.
|
20
24
|
* Updated README and gemspec, removed Rubyforge references.
|
21
25
|
|
22
|
-
|
26
|
+
## 1.0.3 - 22-Sep-2011
|
23
27
|
* Refactored the Rakefile. Removed the old install task, added
|
24
28
|
a default task, and reorganized the gem related tasks.
|
25
29
|
* Minor fix/update for gemspec.
|
26
30
|
|
27
|
-
|
31
|
+
## 1.0.2 - 3-Oct-2009
|
28
32
|
* Some gemspec updates and one fix.
|
29
33
|
* Added the :gem rake task.
|
30
34
|
|
31
|
-
|
35
|
+
## 1.0.1 - 31-Jul-2009
|
32
36
|
* Now compatible with Ruby 1.9.x
|
33
37
|
* Changed the license to Artistic 2.0.
|
34
38
|
* Some gemspec updates, including the addition of a license.
|
35
39
|
|
36
|
-
|
40
|
+
## 1.0.0 - 10-Jun-2008
|
37
41
|
* Initial release
|
data/Gemfile
CHANGED
data/{MANIFEST → MANIFEST.md}
RENAMED
data/README.md
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
[](https://github.com/djberg96/union/actions/workflows/ruby.yml)
|
2
|
+
|
3
|
+
## Description
|
4
|
+
The union library provides the Ruby analog of a C union.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
`gem install union`
|
8
|
+
|
9
|
+
## Adding the trusted cert
|
10
|
+
`gem cert --add <(curl -Ls https://raw.githubusercontent.com/djberg96/union/main/certs/djberg96_pub.pem)`
|
11
|
+
|
12
|
+
## Synopsis
|
13
|
+
```ruby
|
14
|
+
require 'union'
|
15
|
+
|
16
|
+
Union.new('Human', :name, :age, :height)
|
17
|
+
h = Union::Human.new
|
18
|
+
|
19
|
+
# Only one attribute of the union may be set
|
20
|
+
h.name = 'Daniel' # => #<struct Union::Human name="Daniel", age=nil>
|
21
|
+
h.age = 38 # => #<struct Union::Human name=nil, age=38>
|
22
|
+
```
|
23
|
+
|
24
|
+
## Known issues or bugs
|
25
|
+
None that I'm aware of. Please report any bugs you find on the project
|
26
|
+
page at on the github project page at https://github.com/djberg96/union.
|
27
|
+
|
28
|
+
## License
|
29
|
+
Artistic-2.0
|
30
|
+
|
31
|
+
## Copyright
|
32
|
+
(C) 2003-2021 Daniel J. Berger
|
33
|
+
All Rights Reserved.
|
34
|
+
|
35
|
+
## Warranty
|
36
|
+
This package is provided "as is" and without any express or
|
37
|
+
implied warranties, including, without limitation, the implied
|
38
|
+
warranties of merchantability and fitness for a particular purpose.
|
39
|
+
|
40
|
+
## Author
|
41
|
+
Daniel J. Berger
|
data/Rakefile
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
require 'rake'
|
2
2
|
require 'rake/clean'
|
3
3
|
require 'rspec/core/rake_task'
|
4
|
+
require 'rubocop/rake_task'
|
4
5
|
|
5
|
-
CLEAN.include("**/*.gem", "**/*.rbc", "**/*.rbx")
|
6
|
+
CLEAN.include("**/*.gem", "**/*.rbc", "**/*.rbx", "**/*.lock")
|
6
7
|
|
7
8
|
namespace :gem do
|
8
9
|
desc 'Build the union gem'
|
9
10
|
task :create => [:clean] do
|
10
11
|
require 'rubygems/package'
|
11
|
-
spec =
|
12
|
+
spec = Gem::Specification.load('union.gemspec')
|
12
13
|
spec.signing_key = File.join(Dir.home, '.ssh', 'gem-private_key.pem')
|
13
|
-
Gem::Package.build(spec
|
14
|
+
Gem::Package.build(spec)
|
14
15
|
end
|
15
16
|
|
16
17
|
task :install => [:create] do
|
@@ -19,7 +20,14 @@ namespace :gem do
|
|
19
20
|
end
|
20
21
|
end
|
21
22
|
|
23
|
+
RuboCop::RakeTask.new
|
24
|
+
|
22
25
|
desc "Run the test suite"
|
23
26
|
RSpec::Core::RakeTask.new(:spec)
|
24
27
|
|
28
|
+
# Clean up afterwards
|
29
|
+
Rake::Task[:spec].enhance do
|
30
|
+
Rake::Task[:clean].invoke
|
31
|
+
end
|
32
|
+
|
25
33
|
task :default => :spec
|
data/lib/union.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# The Union class provides an analogue of a C union. Only one member of
|
2
4
|
# a Union object can be set to a non-nil value at a time.
|
3
5
|
#
|
4
6
|
class Union < Struct
|
5
7
|
# The version of the union library
|
6
|
-
VERSION = '1.
|
8
|
+
VERSION = '1.3.0'
|
7
9
|
|
8
10
|
# Creates and returns a new Union. Unlike Struct::Class.new, this does not
|
9
11
|
# take any arguments. You must assign attributes individually.
|
@@ -20,14 +22,7 @@ class Union < Struct
|
|
20
22
|
#
|
21
23
|
def initialize
|
22
24
|
super
|
23
|
-
|
24
|
-
m = %Q{
|
25
|
-
def #{attribute}=(value)
|
26
|
-
self['#{attribute}'] = value
|
27
|
-
end
|
28
|
-
}
|
29
|
-
instance_eval(m)
|
30
|
-
}
|
25
|
+
define_union_setters
|
31
26
|
end
|
32
27
|
|
33
28
|
# Identical to Struct attribute assignment, except that setting one instance
|
@@ -41,7 +36,55 @@ class Union < Struct
|
|
41
36
|
# h[:age] = 38 # => #<struct Union::Human name=nil, age=38>
|
42
37
|
#
|
43
38
|
def []=(symbol, value)
|
39
|
+
# Validate that the symbol is a valid member
|
40
|
+
symbol_str = symbol.to_s
|
41
|
+
unless members.any? { |m| m.to_s == symbol_str }
|
42
|
+
raise ArgumentError, "no member '#{symbol}' in union"
|
43
|
+
end
|
44
|
+
|
45
|
+
# Set the value for the specified member
|
44
46
|
super(symbol, value)
|
45
|
-
|
47
|
+
|
48
|
+
# Clear all other members
|
49
|
+
members.each { |m| super(m, nil) unless m.to_s == symbol_str }
|
50
|
+
|
51
|
+
value
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns the name of the currently set member (the one with a non-nil value)
|
55
|
+
# Returns nil if no member is set
|
56
|
+
#
|
57
|
+
# Union.new('Human', :name, :age)
|
58
|
+
# h = Union::Human.new
|
59
|
+
# h.name = 'Daniel'
|
60
|
+
# h.which_member # => :name
|
61
|
+
#
|
62
|
+
def which_member
|
63
|
+
members.find { |member| !self[member].nil? }
|
64
|
+
end
|
65
|
+
|
66
|
+
# Returns the current value (the value of the non-nil member)
|
67
|
+
# Returns nil if no member is set
|
68
|
+
#
|
69
|
+
# Union.new('Human', :name, :age)
|
70
|
+
# h = Union::Human.new
|
71
|
+
# h.name = 'Daniel'
|
72
|
+
# h.current_value # => 'Daniel'
|
73
|
+
#
|
74
|
+
def current_value
|
75
|
+
member = which_member
|
76
|
+
member ? self[member] : nil
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
# Define setter methods for each union member using define_singleton_method
|
82
|
+
# This is safer and more efficient than instance_eval with string interpolation
|
83
|
+
def define_union_setters
|
84
|
+
members.each do |attribute|
|
85
|
+
define_singleton_method("#{attribute}=") do |value|
|
86
|
+
self[attribute] = value
|
87
|
+
end
|
88
|
+
end
|
46
89
|
end
|
47
90
|
end
|
@@ -0,0 +1,328 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'union'
|
4
|
+
require 'rspec'
|
5
|
+
|
6
|
+
RSpec.describe 'Union improvements' do
|
7
|
+
before(:all) do
|
8
|
+
Union.new('TestUnion', :name, :age, :height, :weight)
|
9
|
+
end
|
10
|
+
|
11
|
+
before do
|
12
|
+
@union = Union::TestUnion.new
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#which_member' do
|
16
|
+
context 'when no member is set' do
|
17
|
+
it 'returns nil' do
|
18
|
+
expect(@union.which_member).to be_nil
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'when a member is set' do
|
23
|
+
it 'returns the symbol of the currently set member' do
|
24
|
+
@union.name = 'Alice'
|
25
|
+
expect(@union.which_member).to eq(:name)
|
26
|
+
|
27
|
+
@union.age = 30
|
28
|
+
expect(@union.which_member).to eq(:age)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'works with different data types' do
|
32
|
+
@union.height = 180.5
|
33
|
+
expect(@union.which_member).to eq(:height)
|
34
|
+
|
35
|
+
@union.weight = { value: 70, unit: 'kg' }
|
36
|
+
expect(@union.which_member).to eq(:weight)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'works when setting via string or symbol indexing' do
|
40
|
+
@union[:name] = 'Bob'
|
41
|
+
expect(@union.which_member).to eq(:name)
|
42
|
+
|
43
|
+
@union['age'] = 25
|
44
|
+
expect(@union.which_member).to eq(:age)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'when multiple members are set to nil explicitly' do
|
49
|
+
it 'returns nil' do
|
50
|
+
@union.name = 'Test'
|
51
|
+
@union.name = nil
|
52
|
+
expect(@union.which_member).to be_nil
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'when false or empty values are set' do
|
57
|
+
it 'correctly identifies members with falsy but non-nil values' do
|
58
|
+
@union.name = false
|
59
|
+
expect(@union.which_member).to eq(:name)
|
60
|
+
|
61
|
+
@union.age = 0
|
62
|
+
expect(@union.which_member).to eq(:age)
|
63
|
+
|
64
|
+
@union.height = ''
|
65
|
+
expect(@union.which_member).to eq(:height)
|
66
|
+
|
67
|
+
@union.weight = []
|
68
|
+
expect(@union.which_member).to eq(:weight)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe '#current_value' do
|
74
|
+
context 'when no member is set' do
|
75
|
+
it 'returns nil' do
|
76
|
+
expect(@union.current_value).to be_nil
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'when a member is set' do
|
81
|
+
it 'returns the value of the currently set member' do
|
82
|
+
@union.name = 'Bob'
|
83
|
+
expect(@union.current_value).to eq('Bob')
|
84
|
+
|
85
|
+
@union.height = 180.5
|
86
|
+
expect(@union.current_value).to eq(180.5)
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'works with complex data types' do
|
90
|
+
complex_data = { name: 'John', details: [1, 2, 3] }
|
91
|
+
@union.weight = complex_data
|
92
|
+
expect(@union.current_value).to eq(complex_data)
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'returns falsy but non-nil values correctly' do
|
96
|
+
@union.name = false
|
97
|
+
expect(@union.current_value).to eq(false)
|
98
|
+
|
99
|
+
@union.age = 0
|
100
|
+
expect(@union.current_value).to eq(0)
|
101
|
+
|
102
|
+
@union.height = ''
|
103
|
+
expect(@union.current_value).to eq('')
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context 'consistency with which_member' do
|
108
|
+
it 'returns the value of the member returned by which_member' do
|
109
|
+
test_values = ['Alice', 42, 180.5, { test: 'data' }]
|
110
|
+
members = [:name, :age, :height, :weight]
|
111
|
+
|
112
|
+
test_values.zip(members).each do |value, member|
|
113
|
+
@union.send("#{member}=", value)
|
114
|
+
expect(@union.which_member).to eq(member)
|
115
|
+
expect(@union.current_value).to eq(value)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe '#[]= enhanced error handling' do
|
122
|
+
context 'with invalid member names' do
|
123
|
+
it 'raises ArgumentError for symbol keys' do
|
124
|
+
expect { @union[:invalid] = 'test' }.to raise_error(ArgumentError, "no member 'invalid' in union")
|
125
|
+
expect { @union[:nonexistent] = 'test' }.to raise_error(ArgumentError, "no member 'nonexistent' in union")
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'raises ArgumentError for string keys' do
|
129
|
+
expect { @union['invalid'] = 'test' }.to raise_error(ArgumentError, "no member 'invalid' in union")
|
130
|
+
expect { @union['nonexistent'] = 'test' }.to raise_error(ArgumentError, "no member 'nonexistent' in union")
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'includes the invalid member name in the error message' do
|
134
|
+
expect { @union[:bad_key] = 'value' }.to raise_error(ArgumentError, /bad_key/)
|
135
|
+
expect { @union['another_bad_key'] = 'value' }.to raise_error(ArgumentError, /another_bad_key/)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context 'with valid member names' do
|
140
|
+
it 'accepts both string and symbol keys' do
|
141
|
+
expect { @union[:name] = 'Alice' }.not_to raise_error
|
142
|
+
expect { @union['age'] = 30 }.not_to raise_error
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'treats string and symbol keys as equivalent' do
|
146
|
+
@union[:name] = 'Alice'
|
147
|
+
expect(@union['name']).to eq('Alice')
|
148
|
+
|
149
|
+
@union['age'] = 30
|
150
|
+
expect(@union[:age]).to eq(30)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
context 'return value' do
|
155
|
+
it 'returns the assigned value' do
|
156
|
+
result = (@union[:name] = 'Charlie')
|
157
|
+
expect(result).to eq('Charlie')
|
158
|
+
|
159
|
+
result = (@union['age'] = 42)
|
160
|
+
expect(result).to eq(42)
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'returns the assigned value even for falsy values' do
|
164
|
+
result = (@union[:name] = false)
|
165
|
+
expect(result).to eq(false)
|
166
|
+
|
167
|
+
result = (@union['age'] = 0)
|
168
|
+
expect(result).to eq(0)
|
169
|
+
|
170
|
+
result = (@union[:height] = nil)
|
171
|
+
expect(result).to be_nil
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
describe 'setter methods safety and functionality' do
|
177
|
+
context 'with various data types' do
|
178
|
+
it 'handles strings correctly' do
|
179
|
+
@union.name = 'String value'
|
180
|
+
expect(@union.name).to eq('String value')
|
181
|
+
expect(@union.age).to be_nil
|
182
|
+
expect(@union.height).to be_nil
|
183
|
+
expect(@union.weight).to be_nil
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'handles numbers correctly' do
|
187
|
+
@union.age = 42
|
188
|
+
expect(@union.age).to eq(42)
|
189
|
+
expect(@union.name).to be_nil
|
190
|
+
expect(@union.height).to be_nil
|
191
|
+
expect(@union.weight).to be_nil
|
192
|
+
end
|
193
|
+
|
194
|
+
it 'handles arrays correctly' do
|
195
|
+
test_array = [1, 2, 3, 'test']
|
196
|
+
@union.height = test_array
|
197
|
+
expect(@union.height).to eq(test_array)
|
198
|
+
expect(@union.name).to be_nil
|
199
|
+
expect(@union.age).to be_nil
|
200
|
+
expect(@union.weight).to be_nil
|
201
|
+
end
|
202
|
+
|
203
|
+
it 'handles hashes correctly' do
|
204
|
+
test_hash = { key: 'value', nested: { data: 123 } }
|
205
|
+
@union.weight = test_hash
|
206
|
+
expect(@union.weight).to eq(test_hash)
|
207
|
+
expect(@union.name).to be_nil
|
208
|
+
expect(@union.age).to be_nil
|
209
|
+
expect(@union.height).to be_nil
|
210
|
+
end
|
211
|
+
|
212
|
+
it 'handles objects correctly' do
|
213
|
+
test_object = Object.new
|
214
|
+
@union.name = test_object
|
215
|
+
expect(@union.name).to eq(test_object)
|
216
|
+
expect(@union.age).to be_nil
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
context 'union behavior enforcement' do
|
221
|
+
it 'ensures only one member is set at a time via setters' do
|
222
|
+
@union.name = 'Alice'
|
223
|
+
@union.age = 30
|
224
|
+
@union.height = 170.5
|
225
|
+
|
226
|
+
# Only the last set member should have a value
|
227
|
+
expect(@union.name).to be_nil
|
228
|
+
expect(@union.age).to be_nil
|
229
|
+
expect(@union.height).to eq(170.5)
|
230
|
+
end
|
231
|
+
|
232
|
+
it 'ensures only one member is set at a time via []=' do
|
233
|
+
@union[:name] = 'Alice'
|
234
|
+
@union['age'] = 30
|
235
|
+
@union[:height] = 170.5
|
236
|
+
|
237
|
+
# Only the last set member should have a value
|
238
|
+
expect(@union[:name]).to be_nil
|
239
|
+
expect(@union['age']).to be_nil
|
240
|
+
expect(@union[:height]).to eq(170.5)
|
241
|
+
end
|
242
|
+
|
243
|
+
it 'works correctly when mixing setter methods and []=' do
|
244
|
+
@union.name = 'Alice'
|
245
|
+
@union[:age] = 30
|
246
|
+
@union['height'] = 170.5
|
247
|
+
|
248
|
+
expect(@union.name).to be_nil
|
249
|
+
expect(@union.age).to be_nil
|
250
|
+
expect(@union.height).to eq(170.5)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
describe 'method definition safety' do
|
256
|
+
it 'uses define_singleton_method instead of instance_eval for security' do
|
257
|
+
# This test ensures that the setter methods are properly defined
|
258
|
+
# and work correctly without security vulnerabilities
|
259
|
+
expect(@union).to respond_to(:name=)
|
260
|
+
expect(@union).to respond_to(:age=)
|
261
|
+
expect(@union).to respond_to(:height=)
|
262
|
+
expect(@union).to respond_to(:weight=)
|
263
|
+
|
264
|
+
# Test that the methods work as expected
|
265
|
+
@union.name = 'Test'
|
266
|
+
expect(@union.name).to eq('Test')
|
267
|
+
end
|
268
|
+
|
269
|
+
it 'defines methods that properly delegate to []=' do
|
270
|
+
# Ensure that setter methods use the enhanced []= method
|
271
|
+
expect(@union).to receive(:[]=).with(:name, 'Test Value')
|
272
|
+
@union.name = 'Test Value'
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
describe 'edge cases and robustness' do
|
277
|
+
context 'with nil values' do
|
278
|
+
it 'allows explicitly setting members to nil' do
|
279
|
+
@union.name = 'Alice'
|
280
|
+
expect(@union.name).to eq('Alice')
|
281
|
+
|
282
|
+
@union.name = nil
|
283
|
+
expect(@union.name).to be_nil
|
284
|
+
expect(@union.which_member).to be_nil
|
285
|
+
expect(@union.current_value).to be_nil
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
context 'with empty collections' do
|
290
|
+
it 'treats empty arrays as valid values' do
|
291
|
+
@union.height = []
|
292
|
+
expect(@union.height).to eq([])
|
293
|
+
expect(@union.which_member).to eq(:height)
|
294
|
+
expect(@union.current_value).to eq([])
|
295
|
+
end
|
296
|
+
|
297
|
+
it 'treats empty hashes as valid values' do
|
298
|
+
@union.weight = {}
|
299
|
+
expect(@union.weight).to eq({})
|
300
|
+
expect(@union.which_member).to eq(:weight)
|
301
|
+
expect(@union.current_value).to eq({})
|
302
|
+
end
|
303
|
+
|
304
|
+
it 'treats empty strings as valid values' do
|
305
|
+
@union.name = ''
|
306
|
+
expect(@union.name).to eq('')
|
307
|
+
expect(@union.which_member).to eq(:name)
|
308
|
+
expect(@union.current_value).to eq('')
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
context 'with boolean values' do
|
313
|
+
it 'handles true values correctly' do
|
314
|
+
@union.name = true
|
315
|
+
expect(@union.name).to eq(true)
|
316
|
+
expect(@union.which_member).to eq(:name)
|
317
|
+
expect(@union.current_value).to eq(true)
|
318
|
+
end
|
319
|
+
|
320
|
+
it 'handles false values correctly' do
|
321
|
+
@union.name = false
|
322
|
+
expect(@union.name).to eq(false)
|
323
|
+
expect(@union.which_member).to eq(:name)
|
324
|
+
expect(@union.current_value).to eq(false)
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
data/spec/union_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'union'
|
2
4
|
require 'rspec'
|
3
5
|
|
@@ -10,21 +12,21 @@ RSpec.describe Union do
|
|
10
12
|
@union = Union::Human.new
|
11
13
|
end
|
12
14
|
|
13
|
-
example
|
14
|
-
expect(Union::VERSION).to eq('1.
|
15
|
+
example 'VERSION constant is set to expected value' do
|
16
|
+
expect(Union::VERSION).to eq('1.3.0')
|
15
17
|
expect(Union::VERSION).to be_frozen
|
16
18
|
end
|
17
19
|
|
18
|
-
example
|
20
|
+
example 'constructor does not accept arguments' do
|
19
21
|
expect{ Union::Human.new('Matz') }.to raise_error(ArgumentError)
|
20
22
|
end
|
21
23
|
|
22
|
-
example
|
24
|
+
example 'basic union attribute assignment functionality' do
|
23
25
|
expect{ @union.name = 'Daniel' }.not_to raise_error
|
24
26
|
expect(@union.name).to eq('Daniel')
|
25
27
|
end
|
26
28
|
|
27
|
-
example
|
29
|
+
example 'union attributes via method are set to expected value' do
|
28
30
|
expect{ @union.name = 'Daniel' }.not_to raise_error
|
29
31
|
expect{ @union.age = 38 }.not_to raise_error
|
30
32
|
expect(@union.name).to be_nil
|
@@ -32,7 +34,7 @@ RSpec.describe Union do
|
|
32
34
|
expect(@union.age).to eq(38)
|
33
35
|
end
|
34
36
|
|
35
|
-
example
|
37
|
+
example 'union attributes via string ref are set to expected value' do
|
36
38
|
expect{ @union['name'] = 'Daniel' }.not_to raise_error
|
37
39
|
expect{ @union['age'] = 38 }.not_to raise_error
|
38
40
|
expect(@union['name']).to be_nil
|
@@ -40,11 +42,37 @@ RSpec.describe Union do
|
|
40
42
|
expect(@union['age']).to eq(38)
|
41
43
|
end
|
42
44
|
|
43
|
-
example
|
45
|
+
example 'union attributes via symbol ref are set to expected value' do
|
44
46
|
expect{ @union[:name] = 'Daniel' }.not_to raise_error
|
45
47
|
expect{ @union[:age] = 38 }.not_to raise_error
|
46
48
|
expect(@union[:name]).to be_nil
|
47
49
|
expect(@union[:height]).to be_nil
|
48
50
|
expect(@union[:age]).to eq(38)
|
49
51
|
end
|
52
|
+
|
53
|
+
example 'enhanced []= method returns assigned value' do
|
54
|
+
result = (@union[:name] = 'Test Value')
|
55
|
+
expect(result).to eq('Test Value')
|
56
|
+
|
57
|
+
result = (@union['age'] = 42)
|
58
|
+
expect(result).to eq(42)
|
59
|
+
end
|
60
|
+
|
61
|
+
example 'enhanced []= method validates member names' do
|
62
|
+
expect { @union[:invalid_member] = 'test' }.to raise_error(ArgumentError, /no member 'invalid_member' in union/)
|
63
|
+
expect { @union['another_invalid'] = 'test' }.to raise_error(ArgumentError, /no member 'another_invalid' in union/)
|
64
|
+
end
|
65
|
+
|
66
|
+
example 'setter methods are properly defined and work safely' do
|
67
|
+
# Test that all expected setter methods exist
|
68
|
+
expect(@union).to respond_to(:name=)
|
69
|
+
expect(@union).to respond_to(:age=)
|
70
|
+
expect(@union).to respond_to(:height=)
|
71
|
+
|
72
|
+
# Test that they work correctly
|
73
|
+
@union.name = 'Safe Test'
|
74
|
+
expect(@union.name).to eq('Safe Test')
|
75
|
+
expect(@union.age).to be_nil
|
76
|
+
expect(@union.height).to be_nil
|
77
|
+
end
|
50
78
|
end
|
data/union.gemspec
CHANGED
@@ -2,7 +2,7 @@ require 'rubygems'
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |spec|
|
4
4
|
spec.name = 'union'
|
5
|
-
spec.version = '1.
|
5
|
+
spec.version = '1.3.0'
|
6
6
|
spec.author = 'Daniel J. Berger'
|
7
7
|
spec.license = 'Apache-2.0'
|
8
8
|
spec.email = 'djberg96@gmail.com'
|
@@ -12,18 +12,21 @@ Gem::Specification.new do |spec|
|
|
12
12
|
spec.files = Dir['**/*'].reject{ |f| f.include?('git') }
|
13
13
|
spec.cert_chain = Dir['certs/*']
|
14
14
|
|
15
|
-
spec.extra_rdoc_files = ['README', 'CHANGES', 'MANIFEST']
|
16
|
-
|
17
15
|
spec.add_development_dependency('rake')
|
18
16
|
spec.add_development_dependency('rspec', '~> 3.9')
|
17
|
+
spec.add_development_dependency('rubocop')
|
18
|
+
spec.add_development_dependency('rubocop-rspec')
|
19
19
|
|
20
20
|
spec.metadata = {
|
21
|
-
'homepage_uri'
|
22
|
-
'bug_tracker_uri'
|
23
|
-
'changelog_uri'
|
24
|
-
'documentation_uri'
|
25
|
-
'source_code_uri'
|
26
|
-
'wiki_uri'
|
21
|
+
'homepage_uri' => 'https://github.com/djberg96/union',
|
22
|
+
'bug_tracker_uri' => 'https://github.com/djberg96/union/issues',
|
23
|
+
'changelog_uri' => 'https://github.com/djberg96/union/blob/main/CHANGES.md',
|
24
|
+
'documentation_uri' => 'https://github.com/djberg96/union/wiki',
|
25
|
+
'source_code_uri' => 'https://github.com/djberg96/union',
|
26
|
+
'wiki_uri' => 'https://github.com/djberg96/union/wiki',
|
27
|
+
'rubygems_mfa_required' => 'true',
|
28
|
+
'github_repo' => 'https://github.com/djberg96/union',
|
29
|
+
'funding_uri' => 'https://github.com/sponsors/djberg96'
|
27
30
|
}
|
28
31
|
|
29
32
|
spec.description = <<-EOF
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: union
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel J. Berger
|
@@ -35,7 +35,7 @@ cert_chain:
|
|
35
35
|
ORVCZpRuCPpmC8qmqxUnARDArzucjaclkxjLWvCVHeFa9UP7K3Nl9oTjJNv+7/jM
|
36
36
|
WZs4eecIcUc4tKdHxcAJ0MO/Dkqq7hGaiHpwKY76wQ1+8xAh
|
37
37
|
-----END CERTIFICATE-----
|
38
|
-
date:
|
38
|
+
date: 2025-07-18 00:00:00.000000000 Z
|
39
39
|
dependencies:
|
40
40
|
- !ruby/object:Gem::Dependency
|
41
41
|
name: rake
|
@@ -65,6 +65,34 @@ dependencies:
|
|
65
65
|
- - "~>"
|
66
66
|
- !ruby/object:Gem::Version
|
67
67
|
version: '3.9'
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: rubocop
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
type: :development
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: rubocop-rspec
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
type: :development
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
68
96
|
description: |2
|
69
97
|
The union library provides an analog to a C/C++ union for Ruby.
|
70
98
|
In this implementation a union is a kind of struct where multiple
|
@@ -73,34 +101,32 @@ description: |2
|
|
73
101
|
email: djberg96@gmail.com
|
74
102
|
executables: []
|
75
103
|
extensions: []
|
76
|
-
extra_rdoc_files:
|
77
|
-
- README
|
78
|
-
- CHANGES
|
79
|
-
- MANIFEST
|
104
|
+
extra_rdoc_files: []
|
80
105
|
files:
|
106
|
+
- CHANGES.md
|
107
|
+
- Gemfile
|
81
108
|
- LICENSE
|
82
|
-
-
|
83
|
-
-
|
84
|
-
- spec
|
85
|
-
- spec/union_spec.rb
|
86
|
-
- union.gemspec
|
87
|
-
- README
|
109
|
+
- MANIFEST.md
|
110
|
+
- README.md
|
88
111
|
- Rakefile
|
89
|
-
- certs
|
90
112
|
- certs/djberg96_pub.pem
|
91
|
-
- lib
|
92
113
|
- lib/union.rb
|
93
|
-
-
|
114
|
+
- spec/union_improvements_spec.rb
|
115
|
+
- spec/union_spec.rb
|
116
|
+
- union.gemspec
|
94
117
|
homepage: https://github.com/djberg96/union
|
95
118
|
licenses:
|
96
119
|
- Apache-2.0
|
97
120
|
metadata:
|
98
121
|
homepage_uri: https://github.com/djberg96/union
|
99
122
|
bug_tracker_uri: https://github.com/djberg96/union/issues
|
100
|
-
changelog_uri: https://github.com/djberg96/union/blob/
|
123
|
+
changelog_uri: https://github.com/djberg96/union/blob/main/CHANGES.md
|
101
124
|
documentation_uri: https://github.com/djberg96/union/wiki
|
102
125
|
source_code_uri: https://github.com/djberg96/union
|
103
126
|
wiki_uri: https://github.com/djberg96/union/wiki
|
127
|
+
rubygems_mfa_required: 'true'
|
128
|
+
github_repo: https://github.com/djberg96/union
|
129
|
+
funding_uri: https://github.com/sponsors/djberg96
|
104
130
|
post_install_message:
|
105
131
|
rdoc_options: []
|
106
132
|
require_paths:
|
@@ -116,7 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
116
142
|
- !ruby/object:Gem::Version
|
117
143
|
version: '0'
|
118
144
|
requirements: []
|
119
|
-
rubygems_version: 3.
|
145
|
+
rubygems_version: 3.5.22
|
120
146
|
signing_key:
|
121
147
|
specification_version: 4
|
122
148
|
summary: A struct-like C union for Ruby
|
metadata.gz.sig
CHANGED
Binary file
|
data/README
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
= Description
|
2
|
-
The union library provides the Ruby analog of a C union.
|
3
|
-
|
4
|
-
= Installation
|
5
|
-
gem install union
|
6
|
-
|
7
|
-
= Synopsis
|
8
|
-
require 'union'
|
9
|
-
|
10
|
-
Union.new('Human', :name, :age, :height)
|
11
|
-
h = Union::Human.new
|
12
|
-
|
13
|
-
# Only one attribute of the union may be set
|
14
|
-
h.name = 'Daniel' # => #<struct Union::Human name="Daniel", age=nil>
|
15
|
-
h.age = 38 # => #<struct Union::Human name=nil, age=38>
|
16
|
-
|
17
|
-
= Known issues or bugs
|
18
|
-
None that I'm aware of. Please report any bugs you find on the project
|
19
|
-
page at on the github project page at https://github.com/djberg96/union.
|
20
|
-
|
21
|
-
= License
|
22
|
-
Artistic 2.0
|
23
|
-
|
24
|
-
= Copyright
|
25
|
-
(C) 2003-2018 Daniel J. Berger
|
26
|
-
All Rights Reserved.
|
27
|
-
|
28
|
-
= Warranty
|
29
|
-
This package is provided "as is" and without any express or
|
30
|
-
implied warranties, including, without limitation, the implied
|
31
|
-
warranties of merchantability and fitness for a particular purpose.
|
32
|
-
|
33
|
-
= Author
|
34
|
-
Daniel J. Berger
|