nokogiri-happymapper 0.5.6 → 0.5.7

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.
Files changed (38) hide show
  1. checksums.yaml +15 -0
  2. data/CHANGELOG.md +5 -1
  3. data/README.md +74 -53
  4. data/lib/happymapper/anonymous_mapper.rb +114 -0
  5. data/lib/happymapper/attribute.rb +20 -2
  6. data/lib/happymapper/element.rb +52 -2
  7. data/lib/happymapper/item.rb +89 -182
  8. data/lib/happymapper/supported_types.rb +140 -0
  9. data/lib/happymapper/text_node.rb +6 -1
  10. data/lib/happymapper/version.rb +3 -0
  11. data/lib/happymapper.rb +42 -22
  12. data/spec/attribute_default_value_spec.rb +50 -0
  13. data/spec/fixtures/default_namespace_combi.xml +2 -1
  14. data/spec/happymapper/attribute_spec.rb +12 -0
  15. data/spec/happymapper/element_spec.rb +9 -0
  16. data/spec/{happymapper_item_spec.rb → happymapper/item_spec.rb} +5 -5
  17. data/spec/happymapper/text_node_spec.rb +9 -0
  18. data/spec/happymapper_parse_spec.rb +87 -0
  19. data/spec/happymapper_spec.rb +9 -3
  20. data/spec/ignay_spec.rb +22 -22
  21. data/spec/inheritance_spec.rb +61 -0
  22. data/spec/parse_with_object_to_update_spec.rb +111 -0
  23. data/spec/spec_helper.rb +1 -1
  24. data/spec/to_xml_spec.rb +200 -0
  25. data/spec/to_xml_with_namespaces_spec.rb +196 -0
  26. data/spec/wilcard_tag_name_spec.rb +96 -0
  27. data/spec/wrap_spec.rb +82 -0
  28. data/spec/xpath_spec.rb +60 -59
  29. metadata +34 -33
  30. data/TODO +0 -0
  31. data/spec/happymapper_attribute_spec.rb +0 -21
  32. data/spec/happymapper_element_spec.rb +0 -21
  33. data/spec/happymapper_generic_base_spec.rb +0 -92
  34. data/spec/happymapper_text_node_spec.rb +0 -21
  35. data/spec/happymapper_to_xml_namespaces_spec.rb +0 -196
  36. data/spec/happymapper_to_xml_spec.rb +0 -203
  37. data/spec/happymapper_wrap_spec.rb +0 -69
  38. data/spec/parse_instance_spec.rb +0 -129
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/spec_helper.rb'
1
+ require 'spec_helper'
2
2
 
3
3
  module Foo
4
4
  class Bar; end
@@ -33,18 +33,18 @@ describe HappyMapper::Item do
33
33
  item = HappyMapper::Item.new(:foo, String)
34
34
  item.constant.should == 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
39
  item.constant.should == 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
44
  item.constant.should == 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')
@@ -108,7 +108,7 @@ describe HappyMapper::Item do
108
108
  end
109
109
 
110
110
  it "should work with Boolean" do
111
- item = HappyMapper::Item.new(:foo, Boolean)
111
+ item = HappyMapper::Item.new(:foo, HappyMapper::Boolean)
112
112
  item.typecast('false').should == false
113
113
  end
114
114
  end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe HappyMapper::Attribute do
4
+ describe "initialization" do
5
+ before do
6
+ @attr = HappyMapper::TextNode.new(:foo, String)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,87 @@
1
+ require File.dirname(__FILE__) + '/spec_helper.rb'
2
+
3
+ describe HappyMapper do
4
+
5
+ context ".parse" do
6
+
7
+ context "on a single root node" do
8
+
9
+ subject { described_class.parse fixture_file('address.xml') }
10
+
11
+ it "should parse child elements" do
12
+ subject.street.should == "Milchstrasse"
13
+ subject.housenumber.should == "23"
14
+ subject.postcode.should == "26131"
15
+ subject.city.should == "Oldenburg"
16
+ end
17
+
18
+ it "should not create a content entry when the xml contents no text content" do
19
+ subject.should_not respond_to :content
20
+ end
21
+
22
+ context "child elements with attributes" do
23
+
24
+ it "should parse the attributes" do
25
+ subject.country.code.should == "de"
26
+ end
27
+
28
+ it "should parse the content" do
29
+ subject.country.content.should == "Germany"
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+
36
+ context "element names with special characters" do
37
+ subject { described_class.parse fixture_file('ambigous_items.xml') }
38
+
39
+ it "should create accessor methods with similar names" do
40
+ subject.my_items.item.should be_kind_of Array
41
+ end
42
+ end
43
+
44
+ context "element names with camelCased elements and Capital Letters" do
45
+
46
+ subject { described_class.parse fixture_file('subclass_namespace.xml') }
47
+
48
+ it "should parse the elements an values correctly" do
49
+ subject.title.should == "article title"
50
+ subject.photo.publish_options.author.should == "Stephanie"
51
+ subject.gallery.photo.title.should == "photo title"
52
+ end
53
+ end
54
+
55
+ context "several elements nested deep" do
56
+ subject { described_class.parse fixture_file('ambigous_items.xml') }
57
+
58
+ it "should parse the entire relationship" do
59
+ subject.my_items.item.first.item.name.should == "My first internal item"
60
+ end
61
+ end
62
+
63
+ context "xml that contains multiple entries" do
64
+
65
+ subject { described_class.parse fixture_file('multiple_primitives.xml') }
66
+
67
+ it "should parse the elements as it would a 'has_many'" do
68
+
69
+ subject.name.should == "value"
70
+ subject.image.should == [ "image1", "image2" ]
71
+
72
+ end
73
+
74
+ end
75
+
76
+ context "xml with multiple namespaces" do
77
+
78
+ subject { described_class.parse fixture_file('subclass_namespace.xml') }
79
+
80
+ it "should parse the elements an values correctly" do
81
+ subject.title.should == "article title"
82
+ end
83
+ end
84
+
85
+ end
86
+
87
+ end
@@ -1,6 +1,4 @@
1
- require File.dirname(__FILE__) + '/spec_helper.rb'
2
- require 'pp'
3
- require 'uri'
1
+ require 'spec_helper'
4
2
 
5
3
  module Analytics
6
4
  class Property
@@ -282,6 +280,8 @@ end
282
280
  class Status
283
281
  include HappyMapper
284
282
 
283
+ register_namespace 'fake', "faka:namespace"
284
+
285
285
  element :id, Integer
286
286
  element :text, String
287
287
  element :created_at, Time
@@ -553,12 +553,14 @@ class DefaultNamespaceCombi
553
553
 
554
554
  register_namespace 'bk', "urn:loc.gov:books"
555
555
  register_namespace 'isbn', "urn:ISBN:0-395-36341-6"
556
+ register_namespace 'p', "urn:loc.gov:people"
556
557
  namespace 'bk'
557
558
 
558
559
  tag 'book'
559
560
 
560
561
  element :title, String, :namespace => 'bk', :tag => "title"
561
562
  element :number, String, :namespace => 'isbn', :tag => "number"
563
+ element :author, String, :namespace => 'p', :tag => "author"
562
564
  end
563
565
 
564
566
  describe HappyMapper do
@@ -972,6 +974,10 @@ describe HappyMapper do
972
974
  @book = DefaultNamespaceCombi.parse(file_contents, :single => true)
973
975
  end
974
976
 
977
+ it "should parse author" do
978
+ @book.author.should == "Frank Gilbreth"
979
+ end
980
+
975
981
  it "should parse title" do
976
982
  @book.title.should == "Cheaper by the Dozen"
977
983
  end
data/spec/ignay_spec.rb CHANGED
@@ -1,43 +1,43 @@
1
- require File.dirname(__FILE__) + '/spec_helper.rb'
1
+ require 'spec_helper'
2
2
 
3
3
  class CatalogTree
4
4
  include HappyMapper
5
-
5
+
6
6
  tag 'CatalogTree'
7
7
  register_namespace 'xmlns', 'urn:eventis:prodis:onlineapi:1.0'
8
8
  register_namespace 'xsi', 'http://www.w3.org/2001/XMLSchema-instance'
9
9
  register_namespace 'xsd', 'http://www.w3.org/2001/XMLSchema'
10
10
 
11
11
  attribute :code, String
12
-
12
+
13
13
  has_many :nodes, 'CatalogNode', :tag => 'Node', :xpath => '.'
14
-
14
+
15
15
  end
16
16
 
17
17
 
18
18
  class CatalogNode
19
19
  include HappyMapper
20
-
20
+
21
21
  tag 'Node'
22
-
22
+
23
23
  attribute :back_office_id, String, :tag => 'vodBackOfficeId'
24
-
24
+
25
25
  has_one :name, String, :tag => 'Name'
26
26
  # other important fields
27
-
27
+
28
28
  has_many :translations, 'CatalogNode::Translations', :tag => 'Translation', :xpath => 'child::*'
29
-
29
+
30
30
  class Translations
31
31
  include HappyMapper
32
32
  tag 'Translation'
33
-
33
+
34
34
  attribute :language, String, :tag => 'Language'
35
35
  has_one :name, String, :tag => 'Name'
36
-
36
+
37
37
  end
38
-
38
+
39
39
  has_many :nodes, CatalogNode, :tag => 'Node', :xpath => 'child::*'
40
-
40
+
41
41
  end
42
42
 
43
43
  describe HappyMapper do
@@ -45,30 +45,30 @@ describe HappyMapper do
45
45
  it "should not be nil" do
46
46
  catalog_tree.should_not be_nil
47
47
  end
48
-
48
+
49
49
  it "should have the attribute code" do
50
50
  catalog_tree.code.should == "NLD"
51
51
  end
52
-
52
+
53
53
  it "should have many nodes" do
54
54
  catalog_tree.nodes.should_not be_empty
55
55
  catalog_tree.nodes.length.should == 2
56
56
  end
57
-
57
+
58
58
  context "first node" do
59
-
59
+
60
60
  it "should have a name" do
61
61
  first_node.name.should == "Parent 1"
62
62
  end
63
-
63
+
64
64
  it "should have translations" do
65
65
  first_node.translations.length.should == 2
66
-
66
+
67
67
  first_node.translations.first.language.should == "en-GB"
68
-
68
+
69
69
  first_node.translations.last.name.should == "Parent 1 de"
70
70
  end
71
-
71
+
72
72
  it "should have subnodes" do
73
73
  first_node.nodes.should be_kind_of(Enumerable)
74
74
  first_node.nodes.should_not be_empty
@@ -82,7 +82,7 @@ describe HappyMapper do
82
82
  def first_node
83
83
  @first_node = catalog_tree.nodes.first
84
84
  end
85
-
85
+
86
86
  end
87
87
 
88
88
 
@@ -0,0 +1,61 @@
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
+ describe "Child", "a subclass of the Parent" do
23
+ let(:subject) do
24
+ xml = '<child love="99" naivety="trusting"><genetics>ABBA</genetics><immunities>Chicken Pox</immunities></child>'
25
+ Child.parse(xml)
26
+ end
27
+
28
+ context "when parsing xml" do
29
+ it 'should be possible to deserialize XML into a Child class instance' do
30
+ expect(subject.love).to eq 99
31
+ expect(subject.genetics.dna).to eq "ABBA"
32
+ expect(subject.naivety).to eq "trusting"
33
+ expect(subject.immunities).to have(1).item
34
+ end
35
+ end
36
+
37
+ context "when saving to xml" do
38
+ let(:subject) do
39
+ child = Child.new
40
+ child.love = 100
41
+ child.naivety = 'Bright Eyed'
42
+ child.immunities = [ "Small Pox", "Chicken Pox", "Mumps" ]
43
+ genetics = Genetics.new
44
+ genetics.dna = "GATTACA"
45
+ child.genetics = genetics
46
+ Nokogiri::XML(child.to_xml).root
47
+ end
48
+
49
+ it "saves both the Child and Parent attributes" do
50
+ expect(subject.xpath("@naivety").text).to eq "Bright Eyed"
51
+ expect(subject.xpath("@love").text).to eq "100"
52
+ end
53
+
54
+ it "saves both the Child and Parent elements" do
55
+ expect(subject.xpath("genetics").text).to eq "GATTACA"
56
+ expect(subject.xpath("immunities")).to have(3).items
57
+ end
58
+ end
59
+
60
+ end
61
+ end
@@ -0,0 +1,111 @@
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
+ subject.attr1.should == '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 CHANGED
@@ -1,6 +1,6 @@
1
1
  require 'rspec'
2
2
 
3
- require File.join(File.dirname(__FILE__), '..', 'lib', 'happymapper')
3
+ require 'happymapper'
4
4
 
5
5
  def fixture_file(filename)
6
6
  File.read(File.dirname(__FILE__) + "/fixtures/#{filename}")
@@ -0,0 +1,200 @@
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
+ def housenumber
26
+ "[#{@housenumber}]"
27
+ end
28
+
29
+ def when_saving_location(loc)
30
+ loc + "-live"
31
+ end
32
+
33
+ #
34
+ # Write a empty element even if this is not specified
35
+ #
36
+ element :description, String, :state_when_nil => true
37
+
38
+ #
39
+ # Perform the on_save operation when saving
40
+ #
41
+ has_one :date_created, Time, :on_save => lambda {|time| DateTime.parse(time).strftime("%T %D") if time }
42
+
43
+
44
+ #
45
+ # Execute the method with the same name
46
+
47
+ #
48
+ # Write multiple elements and call on_save when saving
49
+ #
50
+ has_many :dates_updated, Time, :on_save => lambda {|times|
51
+ times.compact.map {|time| DateTime.parse(time).strftime("%T %D") } if times }
52
+
53
+ #
54
+ # Class composition
55
+ #
56
+ element :country, 'Country', :tag => 'country'
57
+
58
+ attribute :occupied, Boolean
59
+
60
+ def initialize(parameters)
61
+ parameters.each_pair do |property,value|
62
+ send("#{property}=",value) if respond_to?("#{property}=")
63
+ end
64
+ @modified = @temporary = true
65
+ end
66
+
67
+ end
68
+
69
+ #
70
+ # Country is composed above the in Address class. Here is a demonstration
71
+ # of how to_xml will handle class composition as well as utilizing the tag
72
+ # value.
73
+ #
74
+ class Country
75
+ include HappyMapper
76
+
77
+ attribute :code, String, :tag => 'countryCode'
78
+ has_one :name, String, :tag => 'countryName'
79
+ has_one :description, 'Description', :tag => 'description'
80
+
81
+ #
82
+ # This inner-class here is to demonstrate saving a text node
83
+ # and optional attributes
84
+ #
85
+ class Description
86
+ include HappyMapper
87
+ content :description, String
88
+ attribute :category, String, :tag => 'category'
89
+ attribute :rating, String, :tag => 'rating', :state_when_nil => true
90
+
91
+ def initialize(desc)
92
+ @description = desc
93
+ end
94
+ end
95
+
96
+ def initialize(parameters)
97
+ parameters.each_pair do |property,value|
98
+ send("#{property}=",value) if respond_to?("#{property}=")
99
+ end
100
+ end
101
+
102
+ end
103
+ end
104
+
105
+ let(:subject) do
106
+ address = ToXML::Address.new 'street' => 'Mockingbird Lane',
107
+ 'location' => 'Home',
108
+ 'housenumber' => '1313',
109
+ 'postcode' => '98103',
110
+ 'city' => 'Seattle',
111
+ 'country' => ToXML::Country.new(:name => 'USA', :code => 'us', :empty_code => nil,
112
+ :description => ToXML::Country::Description.new("A lovely country") ),
113
+ 'date_created' => '2011-01-01 15:00:00',
114
+ 'occupied' => false
115
+
116
+ address.dates_updated = ["2011-01-01 16:01:00","2011-01-02 11:30:01"]
117
+
118
+ Nokogiri::XML(address.to_xml).root
119
+ end
120
+
121
+ it "saves elements" do
122
+ elements = { 'street' => 'Mockingbird Lane', 'postcode' => '98103', 'city' => 'Seattle' }
123
+ elements.each_pair do |property,value|
124
+ expect(subject.xpath("#{property}").text).to eq value
125
+ end
126
+ end
127
+
128
+ it "saves attributes" do
129
+ expect(subject.xpath('@location').text).to eq "Home-live"
130
+ end
131
+
132
+ it 'saves attributes that are Boolean and have a value of false' do
133
+ expect(subject.xpath('@occupied').text).to eq "false"
134
+ end
135
+
136
+ context "when an element has a 'read_only' parameter" do
137
+ it "does not save elements" do
138
+ expect(subject.xpath('temporary')).to be_empty
139
+ end
140
+ end
141
+
142
+ context "when an attribute has a 'read_only' parameter" do
143
+ it "does not save attributes" do
144
+ expect(subject.xpath("@modified")).to be_empty
145
+ end
146
+ end
147
+
148
+ context "when an element has a 'state_when_nil' parameter" do
149
+ it "saves an empty element" do
150
+ expect(subject.xpath('description').text).to eq ""
151
+ end
152
+ end
153
+
154
+ context "when an element has a 'on_save' parameter" do
155
+ context "with a symbol which represents a function" do
156
+ it "saves the element with the result of the function" do
157
+ expect(subject.xpath("housenumber").text).to eq "[1313]"
158
+ end
159
+ end
160
+
161
+ context "with a lambda" do
162
+ it "saves the result of the lambda" do
163
+ expect(subject.xpath('date_created').text).to eq "15:00:00 01/01/11"
164
+ end
165
+ end
166
+ end
167
+
168
+ context "when a has_many has a 'on_save' parameter" do
169
+ context "with a lambda" do
170
+ it "saves the results" do
171
+ dates_updated = subject.xpath('dates_updated')
172
+ expect(dates_updated.length).to eq 2
173
+ expect(dates_updated.first.text).to eq "16:01:00 01/01/11"
174
+ expect(dates_updated.last.text).to eq "11:30:01 01/02/11"
175
+ end
176
+ end
177
+ end
178
+
179
+ context "when an attribute has a 'on_save' parameter" do
180
+ context "with a symbol which represents a function" do
181
+ it "saves the result" do
182
+ expect(subject.xpath('@location').text).to eq "Home-live"
183
+ end
184
+ end
185
+ end
186
+
187
+ context "when an element type is a HappyMapper subclass" do
188
+ it "saves attributes" do
189
+ expect(subject.xpath('country/@countryCode').text).to eq "us"
190
+ end
191
+
192
+ it "saves elements" do
193
+ expect(subject.xpath('country/countryName').text).to eq "USA"
194
+ end
195
+
196
+ it "saves elements" do
197
+ expect(subject.xpath('country/description').text).to eq "A lovely country"
198
+ end
199
+ end
200
+ end