nokogiri-happymapper 0.5.6 → 0.5.7

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