nokogiri-happymapper 0.6.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +64 -5
  3. data/README.md +296 -192
  4. data/lib/happymapper/anonymous_mapper.rb +46 -43
  5. data/lib/happymapper/attribute.rb +7 -5
  6. data/lib/happymapper/element.rb +19 -22
  7. data/lib/happymapper/item.rb +19 -21
  8. data/lib/happymapper/supported_types.rb +20 -28
  9. data/lib/happymapper/text_node.rb +4 -3
  10. data/lib/happymapper/version.rb +3 -1
  11. data/lib/happymapper.rb +336 -362
  12. data/lib/nokogiri-happymapper.rb +4 -0
  13. metadata +124 -105
  14. data/spec/attribute_default_value_spec.rb +0 -50
  15. data/spec/attributes_spec.rb +0 -36
  16. data/spec/fixtures/address.xml +0 -9
  17. data/spec/fixtures/ambigous_items.xml +0 -22
  18. data/spec/fixtures/analytics.xml +0 -61
  19. data/spec/fixtures/analytics_profile.xml +0 -127
  20. data/spec/fixtures/atom.xml +0 -19
  21. data/spec/fixtures/commit.xml +0 -52
  22. data/spec/fixtures/current_weather.xml +0 -89
  23. data/spec/fixtures/current_weather_missing_elements.xml +0 -18
  24. data/spec/fixtures/default_namespace_combi.xml +0 -6
  25. data/spec/fixtures/dictionary.xml +0 -20
  26. data/spec/fixtures/family_tree.xml +0 -21
  27. data/spec/fixtures/inagy.xml +0 -85
  28. data/spec/fixtures/lastfm.xml +0 -355
  29. data/spec/fixtures/multiple_namespaces.xml +0 -170
  30. data/spec/fixtures/multiple_primitives.xml +0 -5
  31. data/spec/fixtures/optional_attributes.xml +0 -6
  32. data/spec/fixtures/pita.xml +0 -133
  33. data/spec/fixtures/posts.xml +0 -23
  34. data/spec/fixtures/product_default_namespace.xml +0 -18
  35. data/spec/fixtures/product_no_namespace.xml +0 -10
  36. data/spec/fixtures/product_single_namespace.xml +0 -10
  37. data/spec/fixtures/quarters.xml +0 -19
  38. data/spec/fixtures/radar.xml +0 -21
  39. data/spec/fixtures/set_config_options.xml +0 -3
  40. data/spec/fixtures/statuses.xml +0 -422
  41. data/spec/fixtures/subclass_namespace.xml +0 -50
  42. data/spec/fixtures/unformatted_address.xml +0 -1
  43. data/spec/fixtures/wrapper.xml +0 -11
  44. data/spec/happymapper/attribute_spec.rb +0 -12
  45. data/spec/happymapper/element_spec.rb +0 -9
  46. data/spec/happymapper/item_spec.rb +0 -136
  47. data/spec/happymapper/text_node_spec.rb +0 -9
  48. data/spec/happymapper_parse_spec.rb +0 -113
  49. data/spec/happymapper_spec.rb +0 -1129
  50. data/spec/has_many_empty_array_spec.rb +0 -43
  51. data/spec/ignay_spec.rb +0 -95
  52. data/spec/inheritance_spec.rb +0 -107
  53. data/spec/mixed_namespaces_spec.rb +0 -61
  54. data/spec/parse_with_object_to_update_spec.rb +0 -111
  55. data/spec/spec_helper.rb +0 -7
  56. data/spec/to_xml_spec.rb +0 -201
  57. data/spec/to_xml_with_namespaces_spec.rb +0 -232
  58. data/spec/wilcard_tag_name_spec.rb +0 -96
  59. data/spec/wrap_spec.rb +0 -82
  60. data/spec/xpath_spec.rb +0 -89
@@ -1,43 +0,0 @@
1
- require "spec_helper"
2
-
3
- module Sheep
4
- class Item
5
- include HappyMapper
6
- end
7
-
8
- class Navigator
9
- include HappyMapper
10
- tag 'navigator'
11
-
12
- # This is purposefully set to have the name 'items' with the tag 'item'.
13
- # The idea is that it should not find the empty items contained within the
14
- # xml and return an empty array. This exercises the order of how nodes
15
- # are searched for within an XML document.
16
- has_many :items, Item, tag: 'item'
17
-
18
- has_many :items_with_a_different_name, Item, tag: 'item'
19
-
20
- end
21
- end
22
-
23
- describe "emptyness" do
24
- let(:xml) do
25
- <<-EOF
26
- <navigator>
27
- <items/>
28
- </navigator>
29
- EOF
30
- end
31
-
32
- let(:navigator) do
33
- Sheep::Navigator.parse(xml)
34
- end
35
-
36
- it "returns an empty array" do
37
- expect(navigator.items_with_a_different_name).to be_empty
38
- end
39
-
40
- it "returns an empty array" do
41
- expect(navigator.items).to be_empty
42
- end
43
- end
data/spec/ignay_spec.rb DELETED
@@ -1,95 +0,0 @@
1
- require 'spec_helper'
2
-
3
- class CatalogTree
4
- include HappyMapper
5
-
6
- tag 'CatalogTree'
7
- register_namespace 'xmlns', 'urn:eventis:prodis:onlineapi:1.0'
8
- register_namespace 'xsi', 'http://www.w3.org/2001/XMLSchema-instance'
9
- register_namespace 'xsd', 'http://www.w3.org/2001/XMLSchema'
10
-
11
- attribute :code, String
12
-
13
- has_many :nodes, 'CatalogNode', :tag => 'Node', :xpath => '.'
14
-
15
- end
16
-
17
-
18
- class CatalogNode
19
- include HappyMapper
20
-
21
- tag 'Node'
22
-
23
- attribute :back_office_id, String, :tag => 'vodBackOfficeId'
24
-
25
- has_one :name, String, :tag => 'Name'
26
- # other important fields
27
-
28
- has_many :translations, 'CatalogNode::Translations', :tag => 'Translation', :xpath => 'child::*'
29
-
30
- class Translations
31
- include HappyMapper
32
- tag 'Translation'
33
-
34
- attribute :language, String, :tag => 'Language'
35
- has_one :name, String, :tag => 'Name'
36
-
37
- end
38
-
39
- has_many :nodes, CatalogNode, :tag => 'Node', :xpath => 'child::*'
40
-
41
- end
42
-
43
- describe HappyMapper do
44
-
45
- it "should not be nil" do
46
- expect(catalog_tree).not_to be_nil
47
- end
48
-
49
- it "should have the attribute code" do
50
- expect(catalog_tree.code).to eq("NLD")
51
- end
52
-
53
- it "should have many nodes" do
54
- expect(catalog_tree.nodes).not_to be_empty
55
- expect(catalog_tree.nodes.length).to eq(2)
56
- end
57
-
58
- context "first node" do
59
-
60
- it "should have a name" do
61
- expect(first_node.name).to eq("Parent 1")
62
- end
63
-
64
- it "should have translations" do
65
- expect(first_node.translations.length).to eq(2)
66
-
67
- expect(first_node.translations.first.language).to eq("en-GB")
68
-
69
- expect(first_node.translations.last.name).to eq("Parent 1 de")
70
- end
71
-
72
- it "should have subnodes" do
73
- expect(first_node.nodes).to be_kind_of(Enumerable)
74
- expect(first_node.nodes).not_to be_empty
75
- expect(first_node.nodes.length).to eq(1)
76
- end
77
-
78
- it "first node - first node name" do
79
- expect(first_node.nodes.first.name).to eq("First")
80
- end
81
-
82
- def first_node
83
- @first_node = catalog_tree.nodes.first
84
- end
85
-
86
- end
87
-
88
-
89
- def catalog_tree ; @catalog_tree ; end
90
-
91
- before(:all) do
92
- xml_reference = "#{File.dirname(__FILE__)}/fixtures/inagy.xml"
93
- @catalog_tree = CatalogTree.parse(File.read(xml_reference), :single => true)
94
- end
95
- end
@@ -1,107 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe "Using inheritance to share elements and attributes" do
4
-
5
- class Genetics
6
- include HappyMapper
7
- content :dna, String
8
- end
9
-
10
- class Parent
11
- include HappyMapper
12
- attribute :love, Integer
13
- element :genetics, Genetics
14
- end
15
-
16
- class Child < Parent
17
- include HappyMapper
18
- attribute :naivety, String
19
- has_many :immunities, String
20
- end
21
-
22
- class Overwrite < Parent
23
- include HappyMapper
24
-
25
- attribute :love, String
26
- element :genetics, Integer
27
- end
28
-
29
- describe "Overwrite" do
30
- let(:subject) do
31
- xml = '<overwrite love="love" naivety="trusting"><genetics>1001</genetics><immunities>Chicken Pox</immunities></overwrite>'
32
- Overwrite.parse(xml, single: true)
33
- end
34
-
35
- it 'overrides the parent elements and attributes' do
36
- expect(Overwrite.attributes.count).to be == Parent.attributes.count
37
- expect(Overwrite.elements.count).to be == Parent.elements.count
38
- end
39
-
40
- context "when parsing xml" do
41
- it 'parses the new overwritten attribut' do
42
- expect(subject.love).to be == "love"
43
- end
44
-
45
- it 'parses the new overwritten element' do
46
- expect(subject.genetics).to be == 1001
47
- end
48
- end
49
-
50
- context "when saving to xml" do
51
- subject do
52
- overwrite = Overwrite.new
53
- overwrite.genetics = 1
54
- overwrite.love = "love"
55
- Nokogiri::XML(overwrite.to_xml).root
56
- end
57
-
58
- it 'has only 1 genetics element' do
59
- expect(subject.xpath('//genetics').count).to be == 1
60
- end
61
-
62
- it 'has only 1 love attribute' do
63
- expect(subject.xpath('@love').text).to be == "love"
64
- end
65
- end
66
- end
67
-
68
- describe "Child", "a subclass of the Parent" do
69
- let(:subject) do
70
- xml = '<child love="99" naivety="trusting"><genetics>ABBA</genetics><immunities>Chicken Pox</immunities></child>'
71
- Child.parse(xml)
72
- end
73
-
74
- context "when parsing xml" do
75
- it 'should be possible to deserialize XML into a Child class instance' do
76
- expect(subject.love).to eq 99
77
- expect(subject.genetics.dna).to eq "ABBA"
78
- expect(subject.naivety).to eq "trusting"
79
- expect(subject.immunities.size).to eq(1)
80
- end
81
- end
82
-
83
- context "when saving to xml" do
84
- let(:subject) do
85
- child = Child.new
86
- child.love = 100
87
- child.naivety = 'Bright Eyed'
88
- child.immunities = [ "Small Pox", "Chicken Pox", "Mumps" ]
89
- genetics = Genetics.new
90
- genetics.dna = "GATTACA"
91
- child.genetics = genetics
92
- Nokogiri::XML(child.to_xml).root
93
- end
94
-
95
- it "saves both the Child and Parent attributes" do
96
- expect(subject.xpath("@naivety").text).to eq "Bright Eyed"
97
- expect(subject.xpath("@love").text).to eq "100"
98
- end
99
-
100
- it "saves both the Child and Parent elements" do
101
- expect(subject.xpath("genetics").text).to eq "GATTACA"
102
- expect(subject.xpath("immunities").size).to eq(3)
103
- end
104
- end
105
-
106
- end
107
- end
@@ -1,61 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe "A document with mixed namespaces" do
4
-
5
- #
6
- # Note that the parent element of the xml has the namespacing. The elements
7
- # contained within the xml do not share the parent element namespace so a
8
- # user of the library would likely need to clear the namespace on each of
9
- # these child elements.
10
- #
11
- let(:xml_document) do
12
- %{<prefix:address location='home' xmlns:prefix="http://www.unicornland.com/prefix"
13
- xmlns:different="http://www.trollcountry.com/different">
14
- <street>Milchstrasse</street>
15
- <street>Another Street</street>
16
- <housenumber>23</housenumber>
17
- <different:postcode>26131</different:postcode>
18
- <city>Oldenburg</city>
19
- </prefix:address>}
20
- end
21
-
22
- module MixedNamespaces
23
- class Address
24
- include HappyMapper
25
-
26
- namespace :prefix
27
- tag :address
28
-
29
- # Here each of the elements have their namespace set to nil to reset their
30
- # namespace so that it is not the same as the prefix namespace
31
-
32
- has_many :streets, String, tag: 'street', namespace: nil
33
-
34
- has_one :house_number, String, tag: 'housenumber', namespace: nil
35
- has_one :postcode, String, namespace: 'different'
36
- has_one :city, String, namespace: nil
37
- end
38
- end
39
-
40
- let(:address) do
41
- MixedNamespaces::Address.parse(xml_document, single: true)
42
- end
43
-
44
-
45
- it "has the correct streets" do
46
- expect(address.streets).to eq [ "Milchstrasse", "Another Street" ]
47
- end
48
-
49
- it "house number" do
50
- expect(address.house_number).to eq "23"
51
- end
52
-
53
- it "postcode" do
54
- expect(address.postcode).to eq "26131"
55
- end
56
-
57
- it "city" do
58
- expect(address.city).to eq "Oldenburg"
59
- end
60
-
61
- end
@@ -1,111 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe "Updating existing objects with .parse and #parse" do
4
-
5
- let(:subject) { ParseInstanceSpec::Root.parse(parse_instance_initial_xml) }
6
-
7
- let(:parse_instance_initial_xml) do
8
- %{<root attr1="initial">
9
- <item attr1="initial">
10
- <description>initial</description>
11
- <subitem attr1="initial">
12
- <name>initial</name>
13
- </subitem>
14
- <subitem attr1="initial">
15
- <name>initial</name>
16
- </subitem>
17
- </item>
18
- <item attr1="initial">
19
- <description>initial</description>
20
- <subitem attr1="initial">
21
- <name>initial</name>
22
- </subitem>
23
- <subitem attr1="initial">
24
- <name>initial</name>
25
- </subitem>
26
- </item>
27
- </root>}
28
- end
29
-
30
- let(:parse_instance_updated_xml) do
31
- %{<root attr1="updated">
32
- <item attr1="updated">
33
- <description>updated</description>
34
- <subitem attr1="updated">
35
- <name>updated</name>
36
- </subitem>
37
- <subitem attr1="updated">
38
- <name>updated</name>
39
- </subitem>
40
- </item>
41
- <item attr1="updated">
42
- <description>updated</description>
43
- <subitem attr1="updated">
44
- <name>updated</name>
45
- </subitem>
46
- <subitem attr1="updated">
47
- <name>updated</name>
48
- </subitem>
49
- </item>
50
- </root>}
51
- end
52
-
53
- module ParseInstanceSpec
54
- class SubItem
55
- include HappyMapper
56
- tag 'subitem'
57
- attribute :attr1, String
58
- element :name, String
59
- end
60
- class Item
61
- include HappyMapper
62
- tag 'item'
63
- attribute :attr1, String
64
- element :description, String
65
- has_many :sub_items, SubItem
66
- end
67
- class Root
68
- include HappyMapper
69
- tag 'root'
70
- attribute :attr1, String
71
- has_many :items, Item
72
- end
73
- end
74
-
75
- def item_is_correctly_defined(item,value='initial')
76
- expect(item.attr1).to eq value
77
- expect(item.description).to eq value
78
- expect(item.sub_items[0].attr1).to eq value
79
- expect(item.sub_items[0].name).to eq value
80
- expect(item.sub_items[1].attr1).to eq value
81
- expect(item.sub_items[1].name).to eq value
82
- end
83
-
84
- it 'initial values are correct' do
85
- expect(subject.attr1).to eq('initial')
86
- item_is_correctly_defined( subject.items[0] )
87
- item_is_correctly_defined( subject.items[1] )
88
- end
89
-
90
-
91
- describe ".parse", "specifying an existing object to update" do
92
- it 'all fields are correct' do
93
- ParseInstanceSpec::Root.parse(parse_instance_updated_xml, :update => subject)
94
- expect(subject.attr1).to eq 'updated'
95
-
96
- item_is_correctly_defined( subject.items[0], 'updated' )
97
- item_is_correctly_defined( subject.items[1], 'updated' )
98
- end
99
- end
100
-
101
- describe "#parse" do
102
- it "all fields are correct" do
103
- subject.parse(parse_instance_updated_xml)
104
- expect(subject.attr1).to eq 'updated'
105
-
106
- item_is_correctly_defined( subject.items[0], 'updated' )
107
- item_is_correctly_defined( subject.items[1], 'updated' )
108
- end
109
- end
110
-
111
- end
data/spec/spec_helper.rb DELETED
@@ -1,7 +0,0 @@
1
- require 'rspec'
2
-
3
- require 'happymapper'
4
-
5
- def fixture_file(filename)
6
- File.read(File.dirname(__FILE__) + "/fixtures/#{filename}")
7
- end
data/spec/to_xml_spec.rb DELETED
@@ -1,201 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe "Saving #to_xml" do
4
-
5
- module ToXML
6
- class Address
7
- include HappyMapper
8
-
9
- tag 'address'
10
-
11
- attribute :location, String, :on_save => :when_saving_location
12
-
13
- element :street, String
14
- element :postcode, String
15
- element :city, String
16
-
17
- element :housenumber, String
18
-
19
- attribute :modified, Boolean, :read_only => true
20
- element :temporary, Boolean, :read_only => true
21
- #
22
- # to_xml will default to the attr_accessor method and not the attribute,
23
- # allowing for that to be overwritten
24
- #
25
- undef :housenumber
26
- def housenumber
27
- "[#{@housenumber}]"
28
- end
29
-
30
- def when_saving_location(loc)
31
- loc + "-live"
32
- end
33
-
34
- #
35
- # Write a empty element even if this is not specified
36
- #
37
- element :description, String, :state_when_nil => true
38
-
39
- #
40
- # Perform the on_save operation when saving
41
- #
42
- has_one :date_created, Time, :on_save => lambda {|time| DateTime.parse(time).strftime("%T %D") if time }
43
-
44
-
45
- #
46
- # Execute the method with the same name
47
-
48
- #
49
- # Write multiple elements and call on_save when saving
50
- #
51
- has_many :dates_updated, Time, :on_save => lambda {|times|
52
- times.compact.map {|time| DateTime.parse(time).strftime("%T %D") } if times }
53
-
54
- #
55
- # Class composition
56
- #
57
- element :country, 'Country', :tag => 'country'
58
-
59
- attribute :occupied, Boolean
60
-
61
- def initialize(parameters)
62
- parameters.each_pair do |property,value|
63
- send("#{property}=",value) if respond_to?("#{property}=")
64
- end
65
- @modified = @temporary = true
66
- end
67
-
68
- end
69
-
70
- #
71
- # Country is composed above the in Address class. Here is a demonstration
72
- # of how to_xml will handle class composition as well as utilizing the tag
73
- # value.
74
- #
75
- class Country
76
- include HappyMapper
77
-
78
- attribute :code, String, :tag => 'countryCode'
79
- has_one :name, String, :tag => 'countryName'
80
- has_one :description, 'Description', :tag => 'description'
81
-
82
- #
83
- # This inner-class here is to demonstrate saving a text node
84
- # and optional attributes
85
- #
86
- class Description
87
- include HappyMapper
88
- content :description, String
89
- attribute :category, String, :tag => 'category'
90
- attribute :rating, String, :tag => 'rating', :state_when_nil => true
91
-
92
- def initialize(desc)
93
- @description = desc
94
- end
95
- end
96
-
97
- def initialize(parameters)
98
- parameters.each_pair do |property,value|
99
- send("#{property}=",value) if respond_to?("#{property}=")
100
- end
101
- end
102
-
103
- end
104
- end
105
-
106
- let(:subject) do
107
- address = ToXML::Address.new 'street' => 'Mockingbird Lane',
108
- 'location' => 'Home',
109
- 'housenumber' => '1313',
110
- 'postcode' => '98103',
111
- 'city' => 'Seattle',
112
- 'country' => ToXML::Country.new(:name => 'USA', :code => 'us', :empty_code => nil,
113
- :description => ToXML::Country::Description.new("A lovely country") ),
114
- 'date_created' => '2011-01-01 15:00:00',
115
- 'occupied' => false
116
-
117
- address.dates_updated = ["2011-01-01 16:01:00","2011-01-02 11:30:01"]
118
-
119
- Nokogiri::XML(address.to_xml).root
120
- end
121
-
122
- it "saves elements" do
123
- elements = { 'street' => 'Mockingbird Lane', 'postcode' => '98103', 'city' => 'Seattle' }
124
- elements.each_pair do |property,value|
125
- expect(subject.xpath("#{property}").text).to eq value
126
- end
127
- end
128
-
129
- it "saves attributes" do
130
- expect(subject.xpath('@location').text).to eq "Home-live"
131
- end
132
-
133
- it 'saves attributes that are Boolean and have a value of false' do
134
- expect(subject.xpath('@occupied').text).to eq "false"
135
- end
136
-
137
- context "when an element has a 'read_only' parameter" do
138
- it "does not save elements" do
139
- expect(subject.xpath('temporary')).to be_empty
140
- end
141
- end
142
-
143
- context "when an attribute has a 'read_only' parameter" do
144
- it "does not save attributes" do
145
- expect(subject.xpath("@modified")).to be_empty
146
- end
147
- end
148
-
149
- context "when an element has a 'state_when_nil' parameter" do
150
- it "saves an empty element" do
151
- expect(subject.xpath('description').text).to eq ""
152
- end
153
- end
154
-
155
- context "when an element has a 'on_save' parameter" do
156
- context "with a symbol which represents a function" do
157
- it "saves the element with the result of the function" do
158
- expect(subject.xpath("housenumber").text).to eq "[1313]"
159
- end
160
- end
161
-
162
- context "with a lambda" do
163
- it "saves the result of the lambda" do
164
- expect(subject.xpath('date_created').text).to eq "15:00:00 01/01/11"
165
- end
166
- end
167
- end
168
-
169
- context "when a has_many has a 'on_save' parameter" do
170
- context "with a lambda" do
171
- it "saves the results" do
172
- dates_updated = subject.xpath('dates_updated')
173
- expect(dates_updated.length).to eq 2
174
- expect(dates_updated.first.text).to eq "16:01:00 01/01/11"
175
- expect(dates_updated.last.text).to eq "11:30:01 01/02/11"
176
- end
177
- end
178
- end
179
-
180
- context "when an attribute has a 'on_save' parameter" do
181
- context "with a symbol which represents a function" do
182
- it "saves the result" do
183
- expect(subject.xpath('@location').text).to eq "Home-live"
184
- end
185
- end
186
- end
187
-
188
- context "when an element type is a HappyMapper subclass" do
189
- it "saves attributes" do
190
- expect(subject.xpath('country/@countryCode').text).to eq "us"
191
- end
192
-
193
- it "saves elements" do
194
- expect(subject.xpath('country/countryName').text).to eq "USA"
195
- end
196
-
197
- it "saves elements" do
198
- expect(subject.xpath('country/description').text).to eq "A lovely country"
199
- end
200
- end
201
- end