nokogiri-happymapper 0.5.9 → 0.6.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/CHANGELOG.md +10 -1
- data/License +20 -0
- data/lib/happymapper.rb +63 -34
- data/lib/happymapper/anonymous_mapper.rb +3 -3
- data/lib/happymapper/element.rb +3 -3
- data/lib/happymapper/supported_types.rb +5 -5
- data/lib/happymapper/version.rb +1 -1
- data/spec/fixtures/unformatted_address.xml +1 -0
- data/spec/happymapper/attribute_spec.rb +1 -1
- data/spec/happymapper/item_spec.rb +39 -18
- data/spec/happymapper_parse_spec.rb +41 -15
- data/spec/happymapper_spec.rb +229 -216
- data/spec/has_many_empty_array_spec.rb +2 -2
- data/spec/ignay_spec.rb +12 -12
- data/spec/inheritance_spec.rb +2 -2
- data/spec/parse_with_object_to_update_spec.rb +1 -1
- data/spec/to_xml_spec.rb +2 -1
- data/spec/to_xml_with_namespaces_spec.rb +38 -2
- data/spec/wilcard_tag_name_spec.rb +7 -7
- data/spec/wrap_spec.rb +2 -2
- data/spec/xpath_spec.rb +3 -3
- metadata +21 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f94968fd78baee9196d17340ee0fa5731c6f6fd1
|
4
|
+
data.tar.gz: 7ca5b00acc1126a265473d583ee331beb00a50e0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e1d779907f83b14a9b392d4dbcca7515dbda14bc8e9932b48a78cbaf7924df5e65adef12454c294947a7e86167dca1866900133985da9ed6cc14bf57fb392e37
|
7
|
+
data.tar.gz: 5205073663f778a8b699d4d54aa63a1d171f12ee300cbc991bd7e3fb4ee2cc252fc2d4540b270ecadb75e0567bf8abceda913dae822f6c9f103275fe70822785
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
## 0.6.0 / Unreleased
|
2
|
+
|
3
|
+
* Prevent parsing of empty string for Date, DateTime (wushugene)
|
4
|
+
* Rescue nil dates (sarsena)
|
5
|
+
* Preserve XML value (benoist)
|
6
|
+
* Restore after_parse callback support (codekitchen)
|
7
|
+
* Parse specific types before general types (Ivo Wever)
|
8
|
+
* Higher priority for namespace on element declarations (Ivo Wever)
|
9
|
+
|
1
10
|
## 0.5.9 / 2014-02-18
|
2
11
|
|
3
12
|
* Correctly output boolean element value 'false' (confusion)
|
@@ -32,4 +41,4 @@
|
|
32
41
|
## 0.5.3/ 2012-09-23
|
33
42
|
|
34
43
|
* String is the default type for parsed fields. (crv)
|
35
|
-
* Update the attributes of an existing HappyMapper instance with new XML (benoist)
|
44
|
+
* Update the attributes of an existing HappyMapper instance with new XML (benoist)
|
data/License
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 John Nunemaker
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/lib/happymapper.rb
CHANGED
@@ -109,8 +109,8 @@ module HappyMapper
|
|
109
109
|
#
|
110
110
|
def element(name, type, options={})
|
111
111
|
element = Element.new(name, type, options)
|
112
|
+
attr_accessor element.method_name.intern unless @elements[name]
|
112
113
|
@elements[name] = element
|
113
|
-
attr_accessor element.method_name.intern
|
114
114
|
end
|
115
115
|
|
116
116
|
#
|
@@ -184,6 +184,22 @@ module HappyMapper
|
|
184
184
|
element name, type, {:single => false}.merge(options)
|
185
185
|
end
|
186
186
|
|
187
|
+
#
|
188
|
+
# The list of registered after_parse callbacks.
|
189
|
+
#
|
190
|
+
def after_parse_callbacks
|
191
|
+
@after_parse_callbacks ||= []
|
192
|
+
end
|
193
|
+
|
194
|
+
#
|
195
|
+
# Register a new after_parse callback, given as a block.
|
196
|
+
#
|
197
|
+
# @yield [object] Yields the newly-parsed object to the block after parsing.
|
198
|
+
# Sub-objects will be already populated.
|
199
|
+
def after_parse(&block)
|
200
|
+
after_parse_callbacks.push(block)
|
201
|
+
end
|
202
|
+
|
187
203
|
#
|
188
204
|
# Specify a namespace if a node and all its children are all namespaced
|
189
205
|
# elements. This is simpler than passing the :namespace option to each
|
@@ -194,7 +210,7 @@ module HappyMapper
|
|
194
210
|
#
|
195
211
|
def namespace(namespace = nil)
|
196
212
|
@namespace = namespace if namespace
|
197
|
-
@namespace
|
213
|
+
@namespace if defined? @namespace
|
198
214
|
end
|
199
215
|
|
200
216
|
#
|
@@ -249,9 +265,7 @@ module HappyMapper
|
|
249
265
|
#
|
250
266
|
# @return [Proc] the proc to pass to Nokogiri to setup parse options. nil if empty.
|
251
267
|
#
|
252
|
-
|
253
|
-
@nokogiri_config_callback
|
254
|
-
end
|
268
|
+
attr_reader :nokogiri_config_callback
|
255
269
|
|
256
270
|
# Register a config callback according to the block Nokogori expects when calling Nokogiri::XML::Document.parse().
|
257
271
|
# See http://nokogiri.org/Nokogiri/XML/Document.html#method-c-parse
|
@@ -273,7 +287,7 @@ module HappyMapper
|
|
273
287
|
def parse(xml, options = {})
|
274
288
|
|
275
289
|
# create a local copy of the objects namespace value for this parse execution
|
276
|
-
namespace = @namespace
|
290
|
+
namespace = (@namespace if defined? @namespace)
|
277
291
|
|
278
292
|
# If the XML specified is an Node then we have what we need.
|
279
293
|
if xml.is_a?(Nokogiri::XML::Node) && !xml.is_a?(Nokogiri::XML::Document)
|
@@ -410,7 +424,7 @@ module HappyMapper
|
|
410
424
|
obj.send("#{elem.method_name}=",elem.from_xml_node(n, namespace, namespaces))
|
411
425
|
end
|
412
426
|
|
413
|
-
if @content
|
427
|
+
if (defined? @content) && @content
|
414
428
|
obj.send("#{@content.method_name}=",@content.from_xml_node(n, namespace, namespaces))
|
415
429
|
end
|
416
430
|
|
@@ -420,7 +434,7 @@ module HappyMapper
|
|
420
434
|
|
421
435
|
if obj.respond_to?('xml_value=')
|
422
436
|
n.namespaces.each {|name,path| n[name] = path }
|
423
|
-
obj.xml_value = n.to_xml
|
437
|
+
obj.xml_value = n.to_xml(save_with: Nokogiri::XML::Node::SaveOptions::AS_XML)
|
424
438
|
end
|
425
439
|
|
426
440
|
# If the HappyMapper class has the method #xml_content=,
|
@@ -429,9 +443,13 @@ module HappyMapper
|
|
429
443
|
|
430
444
|
if obj.respond_to?('xml_content=')
|
431
445
|
n = n.children if n.respond_to?(:children)
|
432
|
-
obj.xml_content = n.to_xml
|
446
|
+
obj.xml_content = n.to_xml(save_with: Nokogiri::XML::Node::SaveOptions::AS_XML)
|
433
447
|
end
|
434
448
|
|
449
|
+
# Call any registered after_parse callbacks for the object's class
|
450
|
+
|
451
|
+
obj.class.after_parse_callbacks.each { |callback| callback.call(obj) }
|
452
|
+
|
435
453
|
# collect the object that we have created
|
436
454
|
|
437
455
|
obj
|
@@ -480,10 +498,13 @@ module HappyMapper
|
|
480
498
|
#
|
481
499
|
# @param [Nokogiri::XML::Builder] builder an instance of the XML builder which
|
482
500
|
# is being used when called recursively.
|
483
|
-
# @param [String] default_namespace
|
484
|
-
# default for the xml being produced; this is
|
485
|
-
#
|
486
|
-
# @param [String]
|
501
|
+
# @param [String] default_namespace The name of the namespace which is the
|
502
|
+
# default for the xml being produced; this is the namespace of the
|
503
|
+
# parent
|
504
|
+
# @param [String] namespace_override The namespace specified with the element
|
505
|
+
# declaration in the parent. Overrides the namespace declaration in the
|
506
|
+
# element class itself when calling #to_xml recursively.
|
507
|
+
# @param [String] tag_from_parent The xml tag to use on the element when being
|
487
508
|
# called recursively. This lets the parent doc define its own structure.
|
488
509
|
# Otherwise the element uses the tag it has defined for itself. Should only
|
489
510
|
# apply when calling a child HappyMapper element.
|
@@ -492,7 +513,8 @@ module HappyMapper
|
|
492
513
|
# HappyMapper object; when called recursively this is going to return
|
493
514
|
# and Nokogiri::XML::Builder object.
|
494
515
|
#
|
495
|
-
def to_xml(builder = nil,default_namespace = nil,
|
516
|
+
def to_xml(builder = nil, default_namespace = nil, namespace_override = nil,
|
517
|
+
tag_from_parent = nil)
|
496
518
|
|
497
519
|
#
|
498
520
|
# If to_xml has been called without a passed in builder instance that
|
@@ -539,7 +561,7 @@ module HappyMapper
|
|
539
561
|
# state that they should be expressed in the output.
|
540
562
|
#
|
541
563
|
if not value.nil? || attribute.options[:state_when_nil]
|
542
|
-
attribute_namespace = attribute.options[:namespace]
|
564
|
+
attribute_namespace = attribute.options[:namespace]
|
543
565
|
[ "#{attribute_namespace ? "#{attribute_namespace}:" : ""}#{attribute.tag}", value ]
|
544
566
|
else
|
545
567
|
[]
|
@@ -576,39 +598,44 @@ module HappyMapper
|
|
576
598
|
end
|
577
599
|
|
578
600
|
#
|
579
|
-
# If the object we are
|
601
|
+
# If the object we are serializing has a namespace declaration we will want
|
580
602
|
# to use that namespace or we will use the default namespace.
|
581
603
|
# When neither are specifed we are simply using whatever is default to the
|
582
604
|
# builder
|
583
605
|
#
|
606
|
+
namespace_for_parent = namespace_override
|
584
607
|
if self.class.respond_to?(:namespace) && self.class.namespace
|
585
|
-
|
586
|
-
elsif default_namespace
|
587
|
-
xml.parent.namespace = builder.doc.root.namespace_definitions.find { |x| x.prefix == default_namespace }
|
608
|
+
namespace_for_parent ||= self.class.namespace
|
588
609
|
end
|
610
|
+
namespace_for_parent ||= default_namespace
|
611
|
+
|
612
|
+
xml.parent.namespace =
|
613
|
+
builder.doc.root.namespace_definitions.find { |x| x.prefix == namespace_for_parent }
|
589
614
|
|
590
615
|
|
591
616
|
#
|
592
617
|
# When a content has been defined we add the resulting value
|
593
618
|
# the output xml
|
594
619
|
#
|
595
|
-
if
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
if on_save_action.
|
603
|
-
|
604
|
-
|
605
|
-
|
620
|
+
if self.class.instance_variable_defined?('@content')
|
621
|
+
if content = self.class.instance_variable_get('@content')
|
622
|
+
|
623
|
+
unless content.options[:read_only]
|
624
|
+
text_accessor = content.tag || content.name
|
625
|
+
value = send(text_accessor)
|
626
|
+
|
627
|
+
if on_save_action = content.options[:on_save]
|
628
|
+
if on_save_action.is_a?(Proc)
|
629
|
+
value = on_save_action.call(value)
|
630
|
+
elsif respond_to?(on_save_action)
|
631
|
+
value = send(on_save_action,value)
|
632
|
+
end
|
606
633
|
end
|
634
|
+
|
635
|
+
builder.text(value)
|
607
636
|
end
|
608
637
|
|
609
|
-
builder.text(value)
|
610
638
|
end
|
611
|
-
|
612
639
|
end
|
613
640
|
|
614
641
|
#
|
@@ -674,7 +701,9 @@ module HappyMapper
|
|
674
701
|
# process should have their contents retrieved and attached
|
675
702
|
# to the builder structure
|
676
703
|
#
|
677
|
-
item.to_xml(xml,
|
704
|
+
item.to_xml(xml, self.class.namespace || default_namespace,
|
705
|
+
element.options[:namespace],
|
706
|
+
element.options[:tag] || nil)
|
678
707
|
|
679
708
|
elsif !item.nil?
|
680
709
|
|
@@ -733,7 +762,7 @@ module HappyMapper
|
|
733
762
|
Class.new do
|
734
763
|
include HappyMapper
|
735
764
|
tag name
|
736
|
-
instance_eval
|
765
|
+
instance_eval(&blk)
|
737
766
|
end
|
738
767
|
end
|
739
768
|
end
|
@@ -64,8 +64,8 @@ module HappyMapper
|
|
64
64
|
define_attribute_on_class(happymapper_class,attribute)
|
65
65
|
end
|
66
66
|
|
67
|
-
element.children.each do |
|
68
|
-
define_element_on_class(happymapper_class,
|
67
|
+
element.children.each do |child|
|
68
|
+
define_element_on_class(happymapper_class,child)
|
69
69
|
end
|
70
70
|
|
71
71
|
happymapper_class
|
@@ -111,4 +111,4 @@ module HappyMapper
|
|
111
111
|
|
112
112
|
end
|
113
113
|
|
114
|
-
end
|
114
|
+
end
|
data/lib/happymapper/element.rb
CHANGED
@@ -23,9 +23,9 @@ module HappyMapper
|
|
23
23
|
end
|
24
24
|
else
|
25
25
|
target_path = options[:xpath] ? options[:xpath] : xpath(namespace)
|
26
|
-
node.xpath(target_path, xpath_options).collect do |
|
27
|
-
value = yield(
|
28
|
-
handle_attributes_option(
|
26
|
+
node.xpath(target_path, xpath_options).collect do |item|
|
27
|
+
value = yield(item)
|
28
|
+
handle_attributes_option(item, value, xpath_options)
|
29
29
|
value
|
30
30
|
end
|
31
31
|
end
|
@@ -109,12 +109,12 @@ module HappyMapper
|
|
109
109
|
Time.parse(value.to_s) rescue Time.at(value.to_i)
|
110
110
|
end
|
111
111
|
|
112
|
-
register_type
|
113
|
-
|
112
|
+
register_type DateTime do |value|
|
113
|
+
DateTime.parse(value.to_s) if value && !value.empty?
|
114
114
|
end
|
115
115
|
|
116
|
-
register_type
|
117
|
-
|
116
|
+
register_type Date do |value|
|
117
|
+
Date.parse(value.to_s) if value && !value.empty?
|
118
118
|
end
|
119
119
|
|
120
120
|
register_type Boolean do |value|
|
@@ -137,4 +137,4 @@ module HappyMapper
|
|
137
137
|
|
138
138
|
end
|
139
139
|
|
140
|
-
end
|
140
|
+
end
|
data/lib/happymapper/version.rb
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
<address><street>Milchstrasse</street><housenumber>23</housenumber></address>
|
@@ -12,61 +12,61 @@ describe HappyMapper::Item do
|
|
12
12
|
end
|
13
13
|
|
14
14
|
it "should accept a name" do
|
15
|
-
@item.name.
|
15
|
+
expect(@item.name).to eq('foo')
|
16
16
|
end
|
17
17
|
|
18
18
|
it 'should accept a type' do
|
19
|
-
@item.type.
|
19
|
+
expect(@item.type).to eq(String)
|
20
20
|
end
|
21
21
|
|
22
22
|
it 'should accept :tag as an option' do
|
23
|
-
@item.tag.
|
23
|
+
expect(@item.tag).to eq('foobar')
|
24
24
|
end
|
25
25
|
|
26
26
|
it "should have a method_name" do
|
27
|
-
@item.method_name.
|
27
|
+
expect(@item.method_name).to eq('foo')
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
31
|
describe "#constant" do
|
32
32
|
it "should just use type if constant" do
|
33
33
|
item = HappyMapper::Item.new(:foo, String)
|
34
|
-
item.constant.
|
34
|
+
expect(item.constant).to eq(String)
|
35
35
|
end
|
36
36
|
|
37
37
|
it "should convert string type to constant" do
|
38
38
|
item = HappyMapper::Item.new(:foo, 'String')
|
39
|
-
item.constant.
|
39
|
+
expect(item.constant).to eq(String)
|
40
40
|
end
|
41
41
|
|
42
42
|
it "should convert string with :: to constant" do
|
43
43
|
item = HappyMapper::Item.new(:foo, 'Foo::Bar')
|
44
|
-
item.constant.
|
44
|
+
expect(item.constant).to eq(Foo::Bar)
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
48
|
describe "#method_name" do
|
49
49
|
it "should convert dashes to underscores" do
|
50
50
|
item = HappyMapper::Item.new(:'foo-bar', String, :tag => 'foobar')
|
51
|
-
item.method_name.
|
51
|
+
expect(item.method_name).to eq('foo_bar')
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
55
|
describe "#xpath" do
|
56
56
|
it "should default to tag" do
|
57
57
|
item = HappyMapper::Item.new(:foo, String, :tag => 'foobar')
|
58
|
-
item.xpath.
|
58
|
+
expect(item.xpath).to eq('foobar')
|
59
59
|
end
|
60
60
|
|
61
61
|
it "should prepend with .// if options[:deep] true" do
|
62
62
|
item = HappyMapper::Item.new(:foo, String, :tag => 'foobar', :deep => true)
|
63
|
-
item.xpath.
|
63
|
+
expect(item.xpath).to eq('.//foobar')
|
64
64
|
end
|
65
65
|
|
66
66
|
it "should prepend namespace if namespace exists" do
|
67
67
|
item = HappyMapper::Item.new(:foo, String, :tag => 'foobar')
|
68
68
|
item.namespace = 'v2'
|
69
|
-
item.xpath.
|
69
|
+
expect(item.xpath).to eq('v2:foobar')
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
@@ -74,42 +74,63 @@ describe HappyMapper::Item do
|
|
74
74
|
it "should work with Strings" do
|
75
75
|
item = HappyMapper::Item.new(:foo, String)
|
76
76
|
[21, '21'].each do |a|
|
77
|
-
item.typecast(a).
|
77
|
+
expect(item.typecast(a)).to eq('21')
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
81
81
|
it "should work with Integers" do
|
82
82
|
item = HappyMapper::Item.new(:foo, Integer)
|
83
83
|
[21, 21.0, '21'].each do |a|
|
84
|
-
item.typecast(a).
|
84
|
+
expect(item.typecast(a)).to eq(21)
|
85
85
|
end
|
86
86
|
end
|
87
87
|
|
88
88
|
it "should work with Floats" do
|
89
89
|
item = HappyMapper::Item.new(:foo, Float)
|
90
90
|
[21, 21.0, '21'].each do |a|
|
91
|
-
item.typecast(a).
|
91
|
+
expect(item.typecast(a)).to eq(21.0)
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
95
95
|
it "should work with Times" do
|
96
96
|
item = HappyMapper::Item.new(:foo, Time)
|
97
|
-
item.typecast('2000-01-01 01:01:01.123456').
|
97
|
+
expect(item.typecast('2000-01-01 01:01:01.123456')).to eq(Time.local(2000, 1, 1, 1, 1, 1, 123456))
|
98
98
|
end
|
99
99
|
|
100
100
|
it "should work with Dates" do
|
101
101
|
item = HappyMapper::Item.new(:foo, Date)
|
102
|
-
item.typecast('2000-01-01').
|
102
|
+
expect(item.typecast('2000-01-01')).to eq(Date.new(2000, 1, 1))
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should handle nil Dates" do
|
106
|
+
item = HappyMapper::Item.new(:foo, Date)
|
107
|
+
expect(item.typecast(nil)).to eq(nil)
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should handle empty string Dates" do
|
111
|
+
item = HappyMapper::Item.new(:foo, Date)
|
112
|
+
expect(item.typecast("")).to eq(nil)
|
103
113
|
end
|
104
114
|
|
105
115
|
it "should work with DateTimes" do
|
106
116
|
item = HappyMapper::Item.new(:foo, DateTime)
|
107
|
-
item.typecast('2000-01-01 00:00:00').
|
117
|
+
expect(item.typecast('2000-01-01 00:00:00')).to eq(DateTime.new(2000, 1, 1, 0, 0, 0))
|
108
118
|
end
|
109
119
|
|
120
|
+
it "should handle nil DateTimes" do
|
121
|
+
item = HappyMapper::Item.new(:foo, DateTime)
|
122
|
+
expect(item.typecast(nil)).to eq(nil)
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should handle empty string DateTimes" do
|
126
|
+
item = HappyMapper::Item.new(:foo, DateTime)
|
127
|
+
expect(item.typecast("")).to eq(nil)
|
128
|
+
end
|
129
|
+
|
130
|
+
|
110
131
|
it "should work with Boolean" do
|
111
132
|
item = HappyMapper::Item.new(:foo, HappyMapper::Boolean)
|
112
|
-
item.typecast('false').
|
133
|
+
expect(item.typecast('false')).to eq(false)
|
113
134
|
end
|
114
135
|
end
|
115
136
|
end
|