nokogiri-happymapper 0.5.9 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|