nokogiri-happymapper 0.8.1 → 0.9.0

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