Skip to content

Commit 9c5902b

Browse files
committed
Fix frozen string literal issue
1 parent 292cb22 commit 9c5902b

File tree

5 files changed

+167
-10
lines changed

5 files changed

+167
-10
lines changed

.github/workflows/ci.yml

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,21 @@ permissions:
1212
contents: read
1313

1414
jobs:
15-
Shared:
16-
uses: fog/.github/.github/workflows/[email protected]
15+
test:
16+
17+
runs-on: ubuntu-latest
18+
19+
strategy:
20+
matrix:
21+
ruby-version: ['3.0', '3.1', '3.2', '3.3', 'head']
22+
continue-on-error: ${{ matrix.ruby-version == 'head' }}
23+
24+
steps:
25+
- uses: actions/checkout@v4
26+
- name: Set up Ruby
27+
uses: ruby/setup-ruby@v1
28+
with:
29+
ruby-version: ${{ matrix.ruby-version }}
30+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
31+
- name: Run tests
32+
run: bundle exec rake RUBYOPT="--enable-frozen-string-literal"

lib/fog/parsers/base.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def reset
1818
end
1919

2020
def characters(string)
21-
@value ||= ''
21+
@value ||= +''
2222
@value << string
2323
end
2424

lib/fog/to_hash_document.rb

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,22 @@ def initialize
55
end
66

77
def characters(string)
8-
@value ||= ""
8+
@value ||= +''
99
@value << string.strip
1010
end
1111

1212
def end_element(name)
1313
last = @stack.pop
14-
if last.empty? && @value.empty?
15-
@stack.last[name.to_sym] = ""
14+
@stack.push({}) if @stack.empty?
15+
16+
if last&.empty? && @value.empty?
17+
@stack.last[name.to_sym] = +''
1618
elsif last == { :i_nil => "true" }
1719
@stack.last[name.to_sym] = nil
1820
elsif !@value.empty?
1921
@stack.last[name.to_sym] = @value
2022
end
21-
@value = ""
23+
@value = +''
2224
end
2325

2426
def body
@@ -30,7 +32,7 @@ def response
3032
end
3133

3234
def start_element(name, attributes = [])
33-
@value = ""
35+
@value = +''
3436
parsed_attributes = {}
3537
until attributes.empty?
3638
if attributes.first.is_a?(Array)

lib/fog/xml/response.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ class Response
44
def initialize(parser)
55
@parser = parser
66
@data_stream = Nokogiri::XML::SAX::PushParser.new(parser)
7-
@response_string = ""
7+
@response_string = +''
88
end
99

1010
def call(chunk, _remaining, _total)
@@ -14,7 +14,7 @@ def call(chunk, _remaining, _total)
1414

1515
def rewind
1616
@parser.reset
17-
@response_string = ""
17+
@response_string = +''
1818
end
1919

2020
def finish

spec/fog/to_hash_document_spec.rb

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
require 'minitest_helper'
2+
require 'fog/xml'
3+
4+
# We expose accessors just for testing purposes
5+
Fog::ToHashDocument.attr_accessor(:value, :stack)
6+
7+
describe Fog::ToHashDocument do
8+
before do
9+
@document = Fog::ToHashDocument.new
10+
end
11+
12+
describe '#characters' do
13+
it 'appends characters to @value' do
14+
@document.characters("some text")
15+
_(@document.value).must_equal "some text"
16+
end
17+
18+
it 'strips whitespace from characters' do
19+
@document.characters(" some text ")
20+
_(@document.value).must_equal "some text"
21+
end
22+
end
23+
24+
describe '#end_element' do
25+
before do
26+
@document.stack << {}
27+
@document.characters("some text")
28+
end
29+
30+
it 'adds element with text content to the stack' do
31+
@document.end_element('element')
32+
33+
_(@document.stack.last).must_equal({ element: "some text" })
34+
end
35+
36+
it 'can mutate the new empty value' do
37+
@document.end_element('element')
38+
39+
_(@document.value).must_equal("")
40+
41+
# Mutate the new empty value even when frozen string literals are enabled
42+
_(@document.characters('one'))
43+
end
44+
45+
it 'adds empty string if element is empty and value is empty' do
46+
@document.value = ""
47+
48+
@document.end_element('element')
49+
50+
expected = { element: nil }
51+
_(@document.stack.last).must_equal(expected)
52+
end
53+
54+
it 'adds nil if element has :i_nil attribute' do
55+
@document.stack.last[:i_nil] = "true"
56+
@document.value = ""
57+
58+
@document.end_element('element')
59+
60+
expected = { element: nil }
61+
_(@document.stack.last).must_equal(expected)
62+
end
63+
end
64+
65+
describe '#body' do
66+
it 'returns the first element of the stack' do
67+
@document.stack << { key: "value" }
68+
69+
expected = { key: 'value' }
70+
_(@document.body).must_equal(expected)
71+
end
72+
end
73+
74+
describe '#response' do
75+
it 'returns the body' do
76+
@document.stack << { key: "value" }
77+
78+
expected = { key: 'value' }
79+
_(@document.response).must_equal(expected)
80+
end
81+
end
82+
83+
describe '#start_element' do
84+
it 'parses attributes correctly' do
85+
@document.start_element('element', [%w[key value]])
86+
87+
expected = { key: 'value' }
88+
_(@document.stack.last).must_equal(expected)
89+
end
90+
91+
it 'handles elements without attributes' do
92+
@document.start_element('element')
93+
94+
_(@document.stack.last).must_equal({})
95+
end
96+
97+
it 'adds nested elements to the stack' do
98+
@document.start_element('parent')
99+
@document.start_element('child')
100+
101+
_(@document.stack).must_equal([ {child: {} }, { child: {} }, {}])
102+
end
103+
104+
it 'adds nested elements with attributes to the stack' do
105+
@document.start_element('parent')
106+
@document.start_element('child', [%w[key value]])
107+
expected = [
108+
{ :child=>{:key=>"value"} },
109+
{ :child=>{:key=>"value"} },
110+
{ :key=>"value"}
111+
]
112+
113+
_(@document.stack).must_equal(expected)
114+
end
115+
116+
it 'handles multiple children elements correctly' do
117+
@document.start_element('parent')
118+
@document.start_element('child1')
119+
@document.end_element('child1')
120+
@document.start_element('child2', [%w[key value]])
121+
@document.end_element('child2')
122+
expected = {
123+
child1: "",
124+
child2: { key: 'value' }
125+
}
126+
127+
_(@document.stack.first).must_equal(expected)
128+
end
129+
130+
it 'handles text content within elements' do
131+
@document.start_element('parent')
132+
@document.characters('some text')
133+
@document.end_element('parent')
134+
135+
expected = { parent: 'some text' }
136+
_(@document.stack.first).must_equal(expected)
137+
end
138+
end
139+
end

0 commit comments

Comments
 (0)