chef-gyoku 1.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/ci.yml +62 -0
- data/.gitignore +11 -0
- data/.rspec +1 -0
- data/CHANGELOG.md +154 -0
- data/Gemfile +6 -0
- data/MIT-LICENSE +20 -0
- data/README.md +313 -0
- data/Rakefile +12 -0
- data/gyoku.gemspec +27 -0
- data/lib/gyoku/array.rb +96 -0
- data/lib/gyoku/hash.rb +105 -0
- data/lib/gyoku/prettifier.rb +29 -0
- data/lib/gyoku/version.rb +3 -0
- data/lib/gyoku/xml_key.rb +67 -0
- data/lib/gyoku/xml_value.rb +41 -0
- data/lib/gyoku.rb +14 -0
- data/spec/gyoku/array_spec.rb +121 -0
- data/spec/gyoku/hash_spec.rb +431 -0
- data/spec/gyoku/prettifier_spec.rb +39 -0
- data/spec/gyoku/xml_key_spec.rb +76 -0
- data/spec/gyoku/xml_value_spec.rb +61 -0
- data/spec/gyoku_spec.rb +82 -0
- data/spec/spec_helper.rb +15 -0
- metadata +132 -0
@@ -0,0 +1,431 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Gyoku::Hash do
|
4
|
+
describe ".to_xml" do
|
5
|
+
describe "returns SOAP request compatible XML" do
|
6
|
+
it "for a simple Hash" do
|
7
|
+
expect(to_xml(some: "user")).to eq("<some>user</some>")
|
8
|
+
end
|
9
|
+
|
10
|
+
it "for a nested Hash" do
|
11
|
+
expect(to_xml(some: {new: "user"})).to eq("<some><new>user</new></some>")
|
12
|
+
end
|
13
|
+
|
14
|
+
context "with key_converter" do
|
15
|
+
it "expect all keys change" do
|
16
|
+
expect(to_xml({some: {new: "user"}}, {key_converter: :camelcase})).to eq("<Some><New>user</New></Some>")
|
17
|
+
end
|
18
|
+
|
19
|
+
it "and key_to_convert option should change only key" do
|
20
|
+
hash = {some: {new: "user", age: 20}}
|
21
|
+
options = {key_converter: :camelcase, key_to_convert: "some"}
|
22
|
+
result = "<Some><new>user</new><age>20</age></Some>"
|
23
|
+
expect(to_xml(hash, options)).to eq(result)
|
24
|
+
|
25
|
+
hash = {some: {new: "user", age: 20}}
|
26
|
+
options = {key_converter: :camelcase, key_to_convert: "new"}
|
27
|
+
result = "<some><New>user</New><age>20</age></some>"
|
28
|
+
expect(to_xml(hash, options)).to eq(result)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "with except option, dont convert this key" do
|
32
|
+
hash = {some: {new: "user", age: 20}}
|
33
|
+
options = {key_converter: :camelcase, except: "some"}
|
34
|
+
result = "<some><New>user</New><Age>20</Age></some>"
|
35
|
+
expect(to_xml(hash, options)).to eq(result)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it "for a Hash with multiple keys" do
|
40
|
+
expect(to_xml(all: "users", before: "whatever")).to include(
|
41
|
+
"<all>users</all>",
|
42
|
+
"<before>whatever</before>"
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "for a Hash containing an Array" do
|
47
|
+
expect(to_xml(some: ["user", "gorilla"])).to eq("<some>user</some><some>gorilla</some>")
|
48
|
+
end
|
49
|
+
|
50
|
+
it "for a Hash containing an Array of Hashes" do
|
51
|
+
expect(to_xml(some: [{new: "user"}, {old: "gorilla"}]))
|
52
|
+
.to eq("<some><new>user</new></some><some><old>gorilla</old></some>")
|
53
|
+
end
|
54
|
+
|
55
|
+
context "when :pretty_print option is set to true" do
|
56
|
+
it "returns prettified xml" do
|
57
|
+
hash = {some: {user: {name: "John", groups: ["admin", "editor"]}}}
|
58
|
+
options = {pretty_print: true}
|
59
|
+
result = "<some>\n <user>\n <name>John</name>\n <groups>admin</groups>\n <groups>editor</groups>\n </user>\n</some>"
|
60
|
+
expect(to_xml(hash, options)).to eq(result)
|
61
|
+
end
|
62
|
+
|
63
|
+
context "when :indent option is specified" do
|
64
|
+
it "returns prettified xml with specified indent" do
|
65
|
+
hash = {some: {user: {name: "John"}}}
|
66
|
+
options = {pretty_print: true, indent: 4}
|
67
|
+
result = "<some>\n <user>\n <name>John</name>\n </user>\n</some>"
|
68
|
+
expect(to_xml(hash, options)).to eq(result)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "when :compact option is specified" do
|
73
|
+
it "returns prettified xml with specified compact mode" do
|
74
|
+
hash = {some: {user: {name: "John"}}}
|
75
|
+
options = {pretty_print: true, compact: false}
|
76
|
+
result = "<some>\n <user>\n <name>\n John\n </name>\n </user>\n</some>"
|
77
|
+
expect(to_xml(hash, options)).to eq(result)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
it "converts Hash key Symbols to lowerCamelCase" do
|
84
|
+
expect(to_xml(find_or_create: "user")).to eq("<findOrCreate>user</findOrCreate>")
|
85
|
+
end
|
86
|
+
|
87
|
+
it "does not convert Hash key Strings" do
|
88
|
+
expect(to_xml("find_or_create" => "user")).to eq("<find_or_create>user</find_or_create>")
|
89
|
+
end
|
90
|
+
|
91
|
+
it "converts DateTime objects to xs:dateTime compliant Strings" do
|
92
|
+
expect(to_xml(before: DateTime.new(2012, 0o3, 22, 16, 22, 33)))
|
93
|
+
.to eq("<before>2012-03-22T16:22:33+00:00</before>")
|
94
|
+
end
|
95
|
+
|
96
|
+
it "converts Objects responding to to_datetime to xs:dateTime compliant Strings" do
|
97
|
+
singleton = Object.new
|
98
|
+
def singleton.to_datetime
|
99
|
+
DateTime.new(2012, 0o3, 22, 16, 22, 33)
|
100
|
+
end
|
101
|
+
|
102
|
+
expect(to_xml(before: singleton)).to eq("<before>2012-03-22T16:22:33+00:00</before>")
|
103
|
+
end
|
104
|
+
|
105
|
+
it "calls to_s on Strings even if they respond to to_datetime" do
|
106
|
+
singleton = "gorilla"
|
107
|
+
def singleton.to_datetime
|
108
|
+
DateTime.new(2012, 0o3, 22, 16, 22, 33)
|
109
|
+
end
|
110
|
+
|
111
|
+
expect(to_xml(name: singleton)).to eq("<name>gorilla</name>")
|
112
|
+
end
|
113
|
+
|
114
|
+
it "properly serializes nil values" do
|
115
|
+
expect(to_xml(some: nil)).to eq('<some xsi:nil="true"/>')
|
116
|
+
end
|
117
|
+
|
118
|
+
it "creates self-closing tags for Hash keys ending with a forward slash" do
|
119
|
+
expect(to_xml("self-closing/" => nil)).to eq("<self-closing/>")
|
120
|
+
end
|
121
|
+
|
122
|
+
it "calls to_s on any other Object" do
|
123
|
+
[666, true, false].each do |object|
|
124
|
+
expect(to_xml(some: object)).to eq("<some>#{object}</some>")
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
it "defaults to escape special characters" do
|
129
|
+
result = to_xml(some: {nested: "<tag />"}, tag: "<tag />")
|
130
|
+
expect(result).to include("<tag><tag /></tag>")
|
131
|
+
expect(result).to include("<some><nested><tag /></nested></some>")
|
132
|
+
end
|
133
|
+
|
134
|
+
it "does not escape special characters for keys marked with an exclamation mark" do
|
135
|
+
result = to_xml(some: {nested!: "<tag />"}, tag!: "<tag />")
|
136
|
+
expect(result).to include("<tag><tag /></tag>")
|
137
|
+
expect(result).to include("<some><nested><tag /></nested></some>")
|
138
|
+
end
|
139
|
+
|
140
|
+
it "preserves the order of Hash keys and values specified through :order!" do
|
141
|
+
hash = {find_user: {name: "Lucy", id: 666, order!: [:id, :name]}}
|
142
|
+
result = "<findUser><id>666</id><name>Lucy</name></findUser>"
|
143
|
+
expect(to_xml(hash)).to eq(result)
|
144
|
+
|
145
|
+
hash = {find_user: {mname: "in the", lname: "Sky", fname: "Lucy", order!: [:fname, :mname, :lname]}}
|
146
|
+
result = "<findUser><fname>Lucy</fname><mname>in the</mname><lname>Sky</lname></findUser>"
|
147
|
+
expect(to_xml(hash)).to eq(result)
|
148
|
+
end
|
149
|
+
|
150
|
+
it "preserves the order of Hash keys and values specified through 'order!' (as a string key)" do
|
151
|
+
hash = {find_user: {:name => "Lucy", :id => 666, "order!" => [:id, :name]}}
|
152
|
+
result = "<findUser><id>666</id><name>Lucy</name></findUser>"
|
153
|
+
expect(to_xml(hash)).to eq(result)
|
154
|
+
|
155
|
+
hash = {find_user: {:mname => "in the", :lname => "Sky", :fname => "Lucy", "order!" => [:fname, :mname, :lname]}}
|
156
|
+
result = "<findUser><fname>Lucy</fname><mname>in the</mname><lname>Sky</lname></findUser>"
|
157
|
+
expect(to_xml(hash)).to eq(result)
|
158
|
+
end
|
159
|
+
|
160
|
+
it "uses :order! symbol values for ordering but leaves the string key 'order!' if both are present" do
|
161
|
+
hash = {find_user: {:name => "Lucy", :id => 666, "order!" => "value", :order! => [:id, :name, "order!"]}}
|
162
|
+
result = "<findUser><id>666</id><name>Lucy</name><order>value</order></findUser>"
|
163
|
+
expect(to_xml(hash)).to eq(result)
|
164
|
+
end
|
165
|
+
|
166
|
+
it "raises if the :order! Array is missing Hash keys" do
|
167
|
+
hash = {name: "Lucy", id: 666, order!: [:name]}
|
168
|
+
expect { to_xml(hash) }.to raise_error(ArgumentError, "Missing elements in :order! [:id]")
|
169
|
+
end
|
170
|
+
|
171
|
+
it "raises if the :order! Array contains missing Hash keys" do
|
172
|
+
hash = {by_name: {first_name: "Lucy", last_name: "Sky", order!: [:first_name, :middle_name, :last_name]}}
|
173
|
+
expect { to_xml(hash) }.to raise_error(ArgumentError, "Spurious elements in :order! [:middle_name]")
|
174
|
+
end
|
175
|
+
|
176
|
+
it "adds attributes to Hash keys specified through :attributes!" do
|
177
|
+
hash = {find_user: {person: "Lucy", attributes!: {person: {id: 666}}}}
|
178
|
+
result = '<findUser><person id="666">Lucy</person></findUser>'
|
179
|
+
expect(to_xml(hash)).to eq(result)
|
180
|
+
|
181
|
+
hash = {find_user: {person: "Lucy", attributes!: {person: {id: 666, city: "Hamburg"}}}}
|
182
|
+
expect(to_xml(hash)).to include('id="666"', 'city="Hamburg"')
|
183
|
+
end
|
184
|
+
|
185
|
+
it "adds attributes to duplicate Hash keys specified through :attributes!" do
|
186
|
+
hash = {find_user: {person: ["Lucy", "Anna"], attributes!: {person: {id: [1, 3]}}}}
|
187
|
+
result = '<findUser><person id="1">Lucy</person><person id="3">Anna</person></findUser>'
|
188
|
+
expect(to_xml(hash)).to eq(result)
|
189
|
+
|
190
|
+
hash = {find_user: {person: ["Lucy", "Anna"], attributes!: {person: {active: "true"}}}}
|
191
|
+
result = '<findUser><person active="true">Lucy</person><person active="true">Anna</person></findUser>'
|
192
|
+
expect(to_xml(hash)).to eq(result)
|
193
|
+
end
|
194
|
+
|
195
|
+
it "skips attribute for element without attributes if there are fewer attributes than elements" do
|
196
|
+
hash = {find_user: {person: ["Lucy", "Anna", "Beth"], attributes!: {person: {id: [1, 3]}}}}
|
197
|
+
result = '<findUser><person id="1">Lucy</person><person id="3">Anna</person><person>Beth</person></findUser>'
|
198
|
+
expect(to_xml(hash)).to eq(result)
|
199
|
+
end
|
200
|
+
|
201
|
+
it "adds attributes to self-closing tags" do
|
202
|
+
hash = {
|
203
|
+
"category/" => "",
|
204
|
+
:attributes! => {"category/" => {id: 1}}
|
205
|
+
}
|
206
|
+
|
207
|
+
expect(to_xml(hash)).to eq('<category id="1"/>')
|
208
|
+
end
|
209
|
+
|
210
|
+
it "recognizes @attribute => value along :attributes!" do
|
211
|
+
hash = {
|
212
|
+
"category" => {
|
213
|
+
:content! => "users",
|
214
|
+
:@id => 1
|
215
|
+
}
|
216
|
+
}
|
217
|
+
expect(to_xml(hash)).to eq('<category id="1">users</category>')
|
218
|
+
end
|
219
|
+
|
220
|
+
it "recognizes @attribute => value along :attributes! in selfclosed tags" do
|
221
|
+
hash = {
|
222
|
+
"category/" => {
|
223
|
+
:@id => 1
|
224
|
+
}
|
225
|
+
}
|
226
|
+
expect(to_xml(hash)).to eq('<category id="1"/>')
|
227
|
+
end
|
228
|
+
|
229
|
+
it ":@attribute => value takes over :attributes!" do
|
230
|
+
hash = {
|
231
|
+
"category/" => {
|
232
|
+
:@id => 1
|
233
|
+
},
|
234
|
+
:attributes! => {
|
235
|
+
"category/" => {
|
236
|
+
"id" => 2, # will be ignored
|
237
|
+
"type" => "admins"
|
238
|
+
}
|
239
|
+
}
|
240
|
+
}
|
241
|
+
# attribute order is undefined
|
242
|
+
expect(['<category id="1" type="admins"/>', '<category type="admins" id="1"/>']).to include to_xml(hash)
|
243
|
+
|
244
|
+
# with symbols
|
245
|
+
hash = {
|
246
|
+
"category/" => {
|
247
|
+
:@id => 1
|
248
|
+
},
|
249
|
+
:attributes! => {
|
250
|
+
"category/" => {
|
251
|
+
id: 2, # will be ignored
|
252
|
+
type: "admins"
|
253
|
+
}
|
254
|
+
}
|
255
|
+
}
|
256
|
+
expect(['<category id="1" type="admins"/>', '<category type="admins" id="1"/>']).to include to_xml(hash)
|
257
|
+
end
|
258
|
+
|
259
|
+
it "recognizes :content! => value as tag content" do
|
260
|
+
hash = {
|
261
|
+
"category" => {
|
262
|
+
content!: "users"
|
263
|
+
}
|
264
|
+
}
|
265
|
+
expect(to_xml(hash)).to eq("<category>users</category>")
|
266
|
+
end
|
267
|
+
|
268
|
+
it "recognizes :content! => value as tag content with value Fixnum" do
|
269
|
+
hash = {
|
270
|
+
"category" => {
|
271
|
+
content!: 666
|
272
|
+
}
|
273
|
+
}
|
274
|
+
expect(to_xml(hash)).to eq("<category>666</category>")
|
275
|
+
end
|
276
|
+
|
277
|
+
it "recognizes :content! => value as tag content with value true" do
|
278
|
+
hash = {
|
279
|
+
"category" => {
|
280
|
+
content!: true
|
281
|
+
}
|
282
|
+
}
|
283
|
+
expect(to_xml(hash)).to eq("<category>true</category>")
|
284
|
+
end
|
285
|
+
|
286
|
+
it "recognizes :content! => value as tag content with value false" do
|
287
|
+
hash = {
|
288
|
+
"category" => {
|
289
|
+
content!: false
|
290
|
+
}
|
291
|
+
}
|
292
|
+
expect(to_xml(hash)).to eq("<category>false</category>")
|
293
|
+
end
|
294
|
+
|
295
|
+
it "recognizes :content! => value as tag content with value DateTime" do
|
296
|
+
hash = {
|
297
|
+
"before" => {
|
298
|
+
content!: DateTime.new(2012, 0o3, 22, 16, 22, 33)
|
299
|
+
}
|
300
|
+
}
|
301
|
+
expect(to_xml(hash)).to eq("<before>2012-03-22T16:22:33+00:00</before>")
|
302
|
+
end
|
303
|
+
|
304
|
+
it "ignores :content! if self-closing mark present" do
|
305
|
+
hash = {
|
306
|
+
"category/" => {
|
307
|
+
content!: "users"
|
308
|
+
}
|
309
|
+
}
|
310
|
+
expect(to_xml(hash)).to eq("<category/>")
|
311
|
+
end
|
312
|
+
|
313
|
+
it "recognizes array of attributes" do
|
314
|
+
hash = {
|
315
|
+
"category" => [{:@name => "one"}, {:@name => "two"}]
|
316
|
+
}
|
317
|
+
expect(to_xml(hash)).to eq('<category name="one"></category><category name="two"></category>')
|
318
|
+
|
319
|
+
# issue #31.
|
320
|
+
hash = {
|
321
|
+
:order! => ["foo", "bar"],
|
322
|
+
"foo" => {:@foo => "foo"},
|
323
|
+
"bar" => {:@bar => "bar", "baz" => {}}
|
324
|
+
}
|
325
|
+
expect(to_xml(hash)).to eq('<foo foo="foo"></foo><bar bar="bar"><baz></baz></bar>')
|
326
|
+
end
|
327
|
+
|
328
|
+
it "recognizes array of attributes with content in each" do
|
329
|
+
hash = {
|
330
|
+
"foo" => [{:@name => "bar", :content! => "gyoku"}, {:@name => "baz", :@some => "attr", :content! => "rocks!"}]
|
331
|
+
}
|
332
|
+
|
333
|
+
expect([
|
334
|
+
'<foo name="bar">gyoku</foo><foo name="baz" some="attr">rocks!</foo>',
|
335
|
+
'<foo name="bar">gyoku</foo><foo some="attr" name="baz">rocks!</foo>'
|
336
|
+
]).to include to_xml(hash)
|
337
|
+
end
|
338
|
+
|
339
|
+
it "recognizes array of attributes but ignores content in each if selfclosing" do
|
340
|
+
hash = {
|
341
|
+
"foo/" => [{:@name => "bar", :content! => "gyoku"}, {:@name => "baz", :@some => "attr", :content! => "rocks!"}]
|
342
|
+
}
|
343
|
+
|
344
|
+
expect([
|
345
|
+
'<foo name="bar"/><foo name="baz" some="attr"/>',
|
346
|
+
'<foo name="bar"/><foo some="attr" name="baz"/>'
|
347
|
+
]).to include to_xml(hash)
|
348
|
+
end
|
349
|
+
|
350
|
+
it "recognizes array of attributes with selfclosing tag" do
|
351
|
+
hash = {
|
352
|
+
"category/" => [{:@name => "one"}, {:@name => "two"}]
|
353
|
+
}
|
354
|
+
expect(to_xml(hash)).to eq('<category name="one"/><category name="two"/>')
|
355
|
+
end
|
356
|
+
|
357
|
+
context "with :element_form_default set to :qualified and a :namespace" do
|
358
|
+
it "adds the given :namespace to every element" do
|
359
|
+
hash = {:first => {"first_name" => "Lucy"}, ":second" => {":first_name": "Anna"}, "v2:third" => {"v2:firstName" => "Danie"}}
|
360
|
+
result = to_xml hash, element_form_default: :qualified, namespace: :v1
|
361
|
+
|
362
|
+
expect(result).to include(
|
363
|
+
"<v1:first><v1:first_name>Lucy</v1:first_name></v1:first>",
|
364
|
+
"<second><firstName>Anna</firstName></second>",
|
365
|
+
"<v2:third><v2:firstName>Danie</v2:firstName></v2:third>"
|
366
|
+
)
|
367
|
+
end
|
368
|
+
|
369
|
+
it "adds given :namespace to every element in an array" do
|
370
|
+
hash = {array: [first: "Lucy", second: "Anna"]}
|
371
|
+
result = to_xml hash, element_form_default: :qualified, namespace: :v1
|
372
|
+
|
373
|
+
expect(result).to include("<v1:array>", "<v1:first>Lucy</v1:first>", "<v1:second>Anna</v1:second>")
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
it "does not remove special keys from the original Hash" do
|
378
|
+
hash = {
|
379
|
+
persons: {
|
380
|
+
first: "Lucy",
|
381
|
+
second: "Anna",
|
382
|
+
order!: [:second, :first],
|
383
|
+
attributes!: {first: {first: true}}
|
384
|
+
},
|
385
|
+
countries: [:de, :us],
|
386
|
+
order!: [:countries, :persons],
|
387
|
+
attributes!: {countries: {array: true}}
|
388
|
+
}
|
389
|
+
|
390
|
+
to_xml(hash)
|
391
|
+
|
392
|
+
expect(hash).to eq({
|
393
|
+
persons: {
|
394
|
+
first: "Lucy",
|
395
|
+
second: "Anna",
|
396
|
+
order!: [:second, :first],
|
397
|
+
attributes!: {first: {first: true}}
|
398
|
+
},
|
399
|
+
countries: [:de, :us],
|
400
|
+
order!: [:countries, :persons],
|
401
|
+
attributes!: {countries: {array: true}}
|
402
|
+
})
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
it "doesn't modify original hash parameter by deleting its attribute keys" do
|
407
|
+
hash = {person: {name: "Johnny", surname: "Bravo", "@xsi:type": "People"}}
|
408
|
+
to_xml(hash)
|
409
|
+
expect(hash).to eq({person: {name: "Johnny", surname: "Bravo", "@xsi:type": "People"}})
|
410
|
+
end
|
411
|
+
|
412
|
+
describe ".explicit_attribute?" do
|
413
|
+
subject { described_class.explicit_attribute?(key) }
|
414
|
+
|
415
|
+
context "when key starts with an @" do
|
416
|
+
let(:key) { "@" }
|
417
|
+
|
418
|
+
it { is_expected.to eq(true) }
|
419
|
+
end
|
420
|
+
|
421
|
+
context "when key does not start with an @" do
|
422
|
+
let(:key) { "NOT@" }
|
423
|
+
|
424
|
+
it { is_expected.to eq(false) }
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
def to_xml(hash, options = {})
|
429
|
+
Gyoku::Hash.to_xml hash, options
|
430
|
+
end
|
431
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Gyoku::Prettifier do
|
4
|
+
describe "#prettify" do
|
5
|
+
context "when xml is valid" do
|
6
|
+
let!(:xml) { Gyoku::Hash.build_xml(test: {pretty: "xml"}) }
|
7
|
+
|
8
|
+
it "returns prettified xml" do
|
9
|
+
expect(subject.prettify(xml)).to eql("<test>\n <pretty>xml</pretty>\n</test>")
|
10
|
+
end
|
11
|
+
|
12
|
+
context "when indent option is specified" do
|
13
|
+
it "returns prettified xml with indent" do
|
14
|
+
options = {indent: 3}
|
15
|
+
subject = Gyoku::Prettifier.new(options)
|
16
|
+
expect(subject.prettify(xml)).to eql("<test>\n <pretty>xml</pretty>\n</test>")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context "when compact option is specified" do
|
21
|
+
it "returns prettified xml with indent" do
|
22
|
+
options = {compact: false}
|
23
|
+
subject = Gyoku::Prettifier.new(options)
|
24
|
+
expect(subject.prettify(xml)).to eql("<test>\n <pretty>\n xml\n </pretty>\n</test>")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "when xml is not valid" do
|
30
|
+
let!(:xml) do
|
31
|
+
Gyoku::Array.build_xml(["one", "two"], "test")
|
32
|
+
end
|
33
|
+
|
34
|
+
it "raises an error" do
|
35
|
+
expect { subject.prettify(xml) }.to raise_error REXML::ParseException
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Gyoku::XMLKey do
|
4
|
+
describe ".create" do
|
5
|
+
it "removes exclamation marks from the end of a String" do
|
6
|
+
expect(create("value!")).to eq("value")
|
7
|
+
end
|
8
|
+
|
9
|
+
it "removes forward slashes from the end of a String" do
|
10
|
+
expect(create("self-closing/")).to eq("self-closing")
|
11
|
+
end
|
12
|
+
|
13
|
+
it "does not convert snake_case Strings" do
|
14
|
+
expect(create("lower_camel_case")).to eq("lower_camel_case")
|
15
|
+
end
|
16
|
+
|
17
|
+
it "converts snake_case Symbols to lowerCamelCase Strings" do
|
18
|
+
expect(create(:lower_camel_case)).to eq("lowerCamelCase")
|
19
|
+
expect(create(:lower_camel_case!)).to eq("lowerCamelCase")
|
20
|
+
end
|
21
|
+
|
22
|
+
context "when the converter option is set to camelcase" do
|
23
|
+
it "should replace / with ::, and turn snake case into camel case" do
|
24
|
+
input = :"hello_world_bob/how_are_you|there:foo^bar"
|
25
|
+
expected_output = "HelloWorldBob::HowAreYou|there:foo^bar"
|
26
|
+
expect(create(input, {key_converter: :camelcase})).to eq(expected_output)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "with key_converter" do
|
31
|
+
it "accepts lambda converters" do
|
32
|
+
expect(create(:some_text, {key_converter: lambda { |k| k.reverse }})).to eq("txet_emos")
|
33
|
+
end
|
34
|
+
|
35
|
+
it "convert symbol to the specified type" do
|
36
|
+
expect(create(:some_text, {key_converter: :camelcase})).to eq("SomeText")
|
37
|
+
expect(create(:some_text, {key_converter: :upcase})).to eq("SOME_TEXT")
|
38
|
+
expect(create(:some_text, {key_converter: :none})).to eq("some_text")
|
39
|
+
end
|
40
|
+
|
41
|
+
it "when key_to_convert is defined, convert only this key" do
|
42
|
+
options = {key_converter: :camelcase, key_to_convert: "somekey"}
|
43
|
+
expect(create(:some_key, options)).to eq("someKey")
|
44
|
+
|
45
|
+
options = {key_converter: :camelcase, key_to_convert: "some_key"}
|
46
|
+
expect(create(:some_key, options)).to eq("SomeKey")
|
47
|
+
end
|
48
|
+
|
49
|
+
it "when except is defined, dont convert this key" do
|
50
|
+
options = {key_converter: :camelcase, except: "some_key"}
|
51
|
+
expect(create(:some_key, options)).to eq("someKey")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "with :element_form_default set to :qualified and a :namespace" do
|
56
|
+
it "adds the given namespace" do
|
57
|
+
key = create :qualify, element_form_default: :qualified, namespace: :v1
|
58
|
+
expect(key).to eq("v1:qualify")
|
59
|
+
end
|
60
|
+
|
61
|
+
it "does not add the given namespace if the key starts with a colon" do
|
62
|
+
key = create ":qualify", element_form_default: :qualified, namespace: :v1
|
63
|
+
expect(key).to eq("qualify")
|
64
|
+
end
|
65
|
+
|
66
|
+
it "adds a given :namespace after converting the key" do
|
67
|
+
key = create :username, element_form_default: :qualified, namespace: :v1, key_converter: :camelcase
|
68
|
+
expect(key).to eq("v1:Username")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def create(key, options = {})
|
74
|
+
Gyoku::XMLKey.create(key, options)
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Gyoku::XMLValue do
|
4
|
+
describe ".create" do
|
5
|
+
context "for DateTime objects" do
|
6
|
+
it "returns an xs:dateTime compliant String" do
|
7
|
+
expect(create(DateTime.new(2012, 3, 22, 16, 22, 33))).to eq("2012-03-22T16:22:33+00:00")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
context "for Date objects" do
|
12
|
+
it "returns an xs:date compliant String" do
|
13
|
+
expect(create(Date.new(2012, 3, 22))).to eq("2012-03-22")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "for Time objects" do
|
18
|
+
it "returns an xs:time compliant String" do
|
19
|
+
expect(create(Time.local(2012, 3, 22, 16, 22, 33))).to eq("16:22:33")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it "returns the String value and escapes special characters" do
|
24
|
+
expect(create("string")).to eq("string")
|
25
|
+
expect(create("<tag>")).to eq("<tag>")
|
26
|
+
expect(create("at&t")).to eq("at&t")
|
27
|
+
expect(create('"quotes"')).to eq(""quotes"")
|
28
|
+
end
|
29
|
+
|
30
|
+
it "returns the String value without escaping special characters" do
|
31
|
+
expect(create("<tag>", false)).to eq("<tag>")
|
32
|
+
end
|
33
|
+
|
34
|
+
it "returns an xs:dateTime compliant String for Objects responding to #to_datetime" do
|
35
|
+
singleton = Object.new
|
36
|
+
def singleton.to_datetime
|
37
|
+
DateTime.new 2012, 3, 22, 16, 22, 33
|
38
|
+
end
|
39
|
+
|
40
|
+
expect(create(singleton)).to eq("2012-03-22T16:22:33+00:00")
|
41
|
+
end
|
42
|
+
|
43
|
+
it "calls Proc objects and converts their return value" do
|
44
|
+
object = lambda { DateTime.new 2012, 3, 22, 16, 22, 33 }
|
45
|
+
expect(create(object)).to eq("2012-03-22T16:22:33+00:00")
|
46
|
+
end
|
47
|
+
|
48
|
+
it "hash objects get converted to xml" do
|
49
|
+
object = {document!: {"@version" => "2.0", :content! => {key!: "value", other_key: {"@attribute" => "value", :content! => {key: "value"}}}}}
|
50
|
+
expect(create(object)).to eq("<document version=\"2.0\"><key>value</key><otherKey attribute=\"value\"><key>value</key></otherKey></document>")
|
51
|
+
end
|
52
|
+
|
53
|
+
it "calls #to_s unless the Object responds to #to_datetime" do
|
54
|
+
expect(create("value")).to eq("value")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def create(object, escape_xml = true)
|
59
|
+
Gyoku::XMLValue.create object, escape_xml
|
60
|
+
end
|
61
|
+
end
|
data/spec/gyoku_spec.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Gyoku do
|
4
|
+
describe ".xml_tag" do
|
5
|
+
it "translates Symbols to lowerCamelCase by default" do
|
6
|
+
tag = Gyoku.xml_tag(:user_name)
|
7
|
+
expect(tag).to eq("userName")
|
8
|
+
end
|
9
|
+
|
10
|
+
it "does not translate Strings" do
|
11
|
+
tag = Gyoku.xml_tag("user_name")
|
12
|
+
expect(tag).to eq("user_name")
|
13
|
+
end
|
14
|
+
|
15
|
+
it "translates Symbols by a given key_converter" do
|
16
|
+
tag = Gyoku.xml_tag(:user_name, key_converter: :upcase)
|
17
|
+
expect(tag).to eq("USER_NAME")
|
18
|
+
end
|
19
|
+
|
20
|
+
it "does not translates Strings with a given key_converter" do
|
21
|
+
tag = Gyoku.xml_tag("user_name", key_converter: :upcase)
|
22
|
+
expect(tag).to eq("user_name")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe ".xml" do
|
27
|
+
it "translates a given Hash to XML" do
|
28
|
+
hash = {id: 1}
|
29
|
+
xml = Gyoku.xml(hash, element_form_default: :qualified)
|
30
|
+
|
31
|
+
expect(xml).to eq("<id>1</id>")
|
32
|
+
end
|
33
|
+
|
34
|
+
it "accepts a key_converter for the Hash keys" do
|
35
|
+
hash = {user_name: "finn", pass_word: "secret"}
|
36
|
+
xml = Gyoku.xml(hash, {key_converter: :upcase})
|
37
|
+
|
38
|
+
expect(xml).to include("<USER_NAME>finn</USER_NAME>")
|
39
|
+
expect(xml).to include("<PASS_WORD>secret</PASS_WORD>")
|
40
|
+
end
|
41
|
+
|
42
|
+
it "don't converts Strings keys" do
|
43
|
+
hash = {:user_name => "finn", "pass_word" => "secret"}
|
44
|
+
xml = Gyoku.xml(hash, {key_converter: :upcase})
|
45
|
+
|
46
|
+
expect(xml).to include("<USER_NAME>finn</USER_NAME>")
|
47
|
+
expect(xml).to include("<pass_word>secret</pass_word>")
|
48
|
+
end
|
49
|
+
|
50
|
+
it "when defined key_to_convert only convert this key" do
|
51
|
+
hash = {user_name: "finn", pass_word: "secret"}
|
52
|
+
options = {key_converter: :upcase, key_to_convert: "user_name"}
|
53
|
+
xml = Gyoku.xml(hash, options)
|
54
|
+
|
55
|
+
expect(xml).to include("<USER_NAME>finn</USER_NAME>")
|
56
|
+
expect(xml).to include("<passWord>secret</passWord>")
|
57
|
+
end
|
58
|
+
|
59
|
+
it "accepts key_converter for nested hash" do
|
60
|
+
hash = {user: {user_name: "finn", pass_word: "secret"}}
|
61
|
+
xml = Gyoku.xml(hash, {key_converter: :upcase})
|
62
|
+
|
63
|
+
expect(xml).to include("<USER><USER_NAME>finn</USER_NAME>")
|
64
|
+
expect(xml).to include("<PASS_WORD>secret</PASS_WORD></USER>")
|
65
|
+
end
|
66
|
+
|
67
|
+
it "does not modify the original Hash" do
|
68
|
+
hash = {
|
69
|
+
person: {
|
70
|
+
first_name: "Lucy",
|
71
|
+
last_name: "Sky",
|
72
|
+
order!: [:first_name, :last_name]
|
73
|
+
},
|
74
|
+
attributes!: {person: {id: "666"}}
|
75
|
+
}
|
76
|
+
original_hash = hash.dup
|
77
|
+
|
78
|
+
Gyoku.xml(hash)
|
79
|
+
expect(original_hash).to eq(hash)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|