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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a619aec0c27be52603178e89364bd85c3dab620ec47971220b75acae15bcca37
4
- data.tar.gz: fa3c3681e2455953b0da6501a88bb28ff19df7364fef68d4a02a0f9127db218e
3
+ metadata.gz: 4078ed3374822297df80188098fb07eff8a7e6f7944d3b2b98dd473c2b93da41
4
+ data.tar.gz: 685ead99ae8858d415fbc20c54f17b2814b387b7619b9a529992724ada83bf0d
5
5
  SHA512:
6
- metadata.gz: bcd3b0684e8d0d278afd1262729828ee85041d42249801206ce7f9de16f6244dfcf5d55e9fbc0bb5500853653c3680f6b361f62ffc136a25389343b5f0bf8678
7
- data.tar.gz: f95468cadcb0b680fd240034d97c1c51cc9a89ec87976f72a5af3da83c2b4ab46ce59a2683f67afd499ac05f5995e7d72849c9d27637e6b917a0dd9a192ed7f6
6
+ metadata.gz: 5f8174f0ec82fee9e98d9a469bf9e46fd485b1702bbc0b574d14f00dcee6bcaf9dba42b9a274a46eb52a3a8f479c10be535cabf341c86f48ba565337a97b191a
7
+ data.tar.gz: 94ec356be18a6173be737683881e44ffb6696d6d66b1dab565c00e050cb089c2ce9648fbe0eedc154c57960fbe3a210dc5c1814ec43df43b2ae0f0b4dbbcace4
checksums.yaml.gz.sig CHANGED
Binary file
@@ -1,37 +1,41 @@
1
- == 1.2.0 - 24-Sep-2020
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
- == 1.1.0 - 24-Jun-2020
9
+ ## 1.1.0 - 24-Jun-2020
6
10
  * Switch to Apache-2.0 license, and add LICENSE file to repo.
7
11
 
8
- == 1.0.6 - 4-Nov-2018
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
- == 1.0.5 - 5-Dec-2015
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
- == 1.0.4 - 8-Oct-2014
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
- == 1.0.3 - 22-Sep-2011
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
- == 1.0.2 - 3-Oct-2009
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
- == 1.0.1 - 31-Jul-2009
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
- == 1.0.0 - 10-Jun-2008
40
+ ## 1.0.0 - 10-Jun-2008
37
41
  * Initial release
data/Gemfile CHANGED
@@ -1,6 +1,2 @@
1
- source 'https://rubygems.org' do
2
- gem 'rake'
3
- group 'test' do
4
- gem 'rspec', '~> 3.9'
5
- end
6
- end
1
+ source 'https://rubygems.org'
2
+ gemspec
@@ -1,7 +1,7 @@
1
- * CHANGES
1
+ * CHANGES.md
2
2
  * LICENSE
3
- * MANIFEST
4
- * README
3
+ * MANIFEST.md
4
+ * README.md
5
5
  * Gemfile
6
6
  * Rakefile
7
7
  * union.gemspec
data/README.md ADDED
@@ -0,0 +1,41 @@
1
+ [![Ruby](https://github.com/djberg96/union/actions/workflows/ruby.yml/badge.svg)](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 = eval(IO.read('union.gemspec'))
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, true)
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.2.0'.freeze
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
- members.each{ |attribute|
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
- members.each{ |m| super(m, nil) unless m.to_s == symbol.to_s }
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 "union_version" do
14
- expect(Union::VERSION).to eq('1.2.0')
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 "union_constructor" do
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 "union_attribute_assignment_basic" do
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 "union_attribute_assignment_by_method_name" do
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 "union_attribute_assignment_by_string_ref" do
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 "union_attribute_assignment_by_symbol_ref" do
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.2.0'
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' => 'https://github.com/djberg96/union',
22
- 'bug_tracker_uri' => 'https://github.com/djberg96/union/issues',
23
- 'changelog_uri' => 'https://github.com/djberg96/union/blob/master/CHANGES',
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'
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.2.0
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
- - CHANGES
83
- - MANIFEST
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
- - Gemfile
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/master/CHANGES
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.1.4
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