rbook-onix 0.5

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 (42) hide show
  1. data/COPYING +340 -0
  2. data/LICENSE +13 -0
  3. data/README +0 -0
  4. data/Rakefile +90 -0
  5. data/examples/stream_reader.rb +13 -0
  6. data/lib/rbook/onix.rb +70 -0
  7. data/lib/rbook/onix/contributor.rb +59 -0
  8. data/lib/rbook/onix/lists.rb +2 -0
  9. data/lib/rbook/onix/lists/contributor_role.rb +10 -0
  10. data/lib/rbook/onix/lists/product_form.rb +100 -0
  11. data/lib/rbook/onix/message.rb +121 -0
  12. data/lib/rbook/onix/product.rb +208 -0
  13. data/lib/rbook/onix/sales_restriction.rb +50 -0
  14. data/lib/rbook/onix/stream_reader.rb +120 -0
  15. data/lib/rbook/onix/stream_writer.rb +40 -0
  16. data/lib/rbook/onix/supply_detail.rb +75 -0
  17. data/lib/rbook/onix/xchar.rb +62 -0
  18. data/specs/contributor_class_spec.rb +33 -0
  19. data/specs/contributor_with_data_spec.rb +36 -0
  20. data/specs/data/10_products.xml +469 -0
  21. data/specs/data/2_products_utf16.xml +0 -0
  22. data/specs/data/abingdon.xml +38931 -0
  23. data/specs/data/augsburg.xml +39009 -0
  24. data/specs/data/chalice.xml +10851 -0
  25. data/specs/data/eerdsman.xml +36942 -0
  26. data/specs/data/invalid_no_product.xml +9 -0
  27. data/specs/data/invalid_xml.xml +24 -0
  28. data/specs/data/not_xml.csv +1 -0
  29. data/specs/data/single_product.xml +55 -0
  30. data/specs/data/xml_not_onix.xml +7 -0
  31. data/specs/message_class_spec.rb +110 -0
  32. data/specs/message_with_data_spec.rb +81 -0
  33. data/specs/product_class_spec.rb +42 -0
  34. data/specs/product_with_data_spec.rb +66 -0
  35. data/specs/sales_restriction_class_spec.rb +32 -0
  36. data/specs/sales_restriction_with_data_spec.rb +31 -0
  37. data/specs/stream_reader_spec.rb +31 -0
  38. data/specs/stream_writer_spec.rb +30 -0
  39. data/specs/supply_detail_class_spec.rb +35 -0
  40. data/specs/supply_detail_with_data_spec.rb +42 -0
  41. data/specs/xchar_spec.rb +39 -0
  42. metadata +116 -0
@@ -0,0 +1,9 @@
1
+ <?xml version='1.0'?>
2
+ <!DOCTYPE ONIXMessage SYSTEM "http://www.editeur.org/onix/2.1/reference/onix-international.dtd">
3
+ <ONIXMessage>
4
+ <Header>
5
+ <FromCompany>Schwartz Publishing Pty Ltd</FromCompany>
6
+ <FromPerson>James Healy</FromPerson>
7
+ <SentDate>20060522</SentDate>
8
+ </Header>
9
+ </ONIXMessage>
@@ -0,0 +1,24 @@
1
+ <?xml version='1.0'?>
2
+ <!DOCTYPE ONIXMessage SYSTEM "http://www.editeur.org/onix/2.1/reference/onix-international.dtd">
3
+ <ONIXMessage>
4
+ <Header>
5
+ <FromCompany>Chalice</FromCompany>
6
+ <FromPerson>James Healy</FromPerson>
7
+ <SentDate>20060521</SentDate>
8
+ </Header>
9
+ <Product>
10
+ <RecordReference>0827200528</RecordReference>
11
+ <NotificationType>03</NotificationType>
12
+ <ProductIdentifier>
13
+ <ProductIDType>02</ProductIDType>
14
+ <IDValue>0827200528</IDValue>
15
+ </ProductIdentifier>
16
+ <ProductForm>BA</ProductForm>
17
+ <NoSeries/>
18
+ <Title>
19
+ <TitleType>01</TitleType>
20
+ <TitleText>And Their Eyes Are Opened</TitleText>
21
+ <Subtitle>Story Sermons Embracing the World</Subtitle>
22
+ </Title>
23
+ <Contributor>
24
+ </ONIXMessage>
@@ -0,0 +1 @@
1
+ "this file is not XML","Remind you of 1990?"
@@ -0,0 +1,55 @@
1
+ <?xml version='1.0'?>
2
+ <!DOCTYPE ONIXMessage SYSTEM "http://www.editeur.org/onix/2.1/reference/onix-international.dtd">
3
+ <ONIXMessage>
4
+ <Header>
5
+ <FromCompany>Chalice</FromCompany>
6
+ <FromPerson>James Healy</FromPerson>
7
+ <SentDate>20060521</SentDate>
8
+ </Header>
9
+ <Product>
10
+ <RecordReference>0827200528</RecordReference>
11
+ <NotificationType>03</NotificationType>
12
+ <ProductIdentifier>
13
+ <ProductIDType>02</ProductIDType>
14
+ <IDValue>0827200528</IDValue>
15
+ </ProductIdentifier>
16
+ <ProductForm>BA</ProductForm>
17
+ <NoSeries/>
18
+ <Title>
19
+ <TitleType>01</TitleType>
20
+ <TitleText>And Their Eyes Are Opened</TitleText>
21
+ <Subtitle>Story Sermons Embracing the World</Subtitle>
22
+ </Title>
23
+ <Contributor>
24
+ <SequenceNumber>0</SequenceNumber>
25
+ <ContributorRole>A01</ContributorRole>
26
+ <PersonNameInverted>Healy, James</PersonNameInverted>
27
+ </Contributor>
28
+ <NoEdition/>
29
+ <BICMainSubject>YFH</BICMainSubject>
30
+ <MediaFile>
31
+ <MediaFileTypeCode>04</MediaFileTypeCode>
32
+ <MediaFileLinkTypeCode>01</MediaFileLinkTypeCode>
33
+ <MediaFileLink>http://www.example.com/0827200528.jpg</MediaFileLink>
34
+ </MediaFile>
35
+ <NumberOfPages>0</NumberOfPages>
36
+ <Publisher>
37
+ <PublishingRole>01</PublishingRole>
38
+ <PublisherName>Chalice</PublisherName>
39
+ </Publisher>
40
+ <PublicationDate>20050303</PublicationDate>
41
+ <SalesRestriction>
42
+ <SalesRestrictionType>00</SalesRestrictionType>
43
+ <SalesRestrictionDetail>Unknown</SalesRestrictionDetail>
44
+ </SalesRestriction>
45
+ <SupplyDetail>
46
+ <SupplierName>Chalice</SupplierName>
47
+ <AvailabilityCode>CS</AvailabilityCode>
48
+ <IntermediaryAvailabilityCode>99</IntermediaryAvailabilityCode>
49
+ <Price>
50
+ <PriceTypeCode>02</PriceTypeCode>
51
+ <PriceAmount>29.95</PriceAmount>
52
+ </Price>
53
+ </SupplyDetail>
54
+ </Product>
55
+ </ONIXMessage>
@@ -0,0 +1,7 @@
1
+ <?xml version='1.0'?>
2
+ <root>
3
+ <tag1/>
4
+ <tag2>Some Data</tag2>
5
+ <tag3 attr1="cliche">Hello World</tag3>
6
+ <tag4 attr2="for completeness" />
7
+ </root>
@@ -0,0 +1,110 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
2
+
3
+ require 'rbook/onix'
4
+ include RBook
5
+
6
+ context "The message class" do
7
+
8
+ #setup do
9
+ #end
10
+
11
+ # test loading a valid onix file. Make sure the correct number of products
12
+ # exist, but don't worry about the content. The product unit tests can check them
13
+ specify "should create a object tree from an xmldoc object" do
14
+ msg = nil
15
+ File.open(File.dirname(__FILE__)+"/data/single_product.xml", "r") do |f|
16
+ doc = REXML::Document.new(f)
17
+ msg = RBook::Onix::Message.load_from_xmldoc(doc)
18
+ end
19
+
20
+ #check header elements
21
+ msg.from_company.should eql("Chalice")
22
+ msg.from_person.should eql("James Healy")
23
+ msg.sent_date.should eql(Date.civil(2006, 5, 21))
24
+
25
+ #check product count
26
+ msg.products.size.should eql(1)
27
+ end
28
+
29
+ # load a UTF-16 XML file into memory, converting it to UTF-8 on the fly
30
+ specify "should create a object tree from a UTF-16 xmldoc object" do
31
+ msg = nil
32
+ File.open(File.dirname(__FILE__)+"/data/2_products_utf16.xml", "r") do |f|
33
+ doc = REXML::Document.new(f)
34
+ msg = RBook::Onix::Message.load_from_xmldoc(doc)
35
+ end
36
+
37
+ #check header elements
38
+ msg.from_company.should eql("HarperCollins Publishers")
39
+ msg.sent_date.should eql(Date.civil(2007, 4, 23))
40
+
41
+ #check product count
42
+ msg.products.size.should eql(2)
43
+ end
44
+
45
+ # test loading a valid onix file. Make sure the correct number of products
46
+ # exist, but don't worry about the content. The product unit tests can check them
47
+ specify "should create a object tree from a string" do
48
+ str = File.read(File.dirname(__FILE__)+"/data/single_product.xml")
49
+ msg = RBook::Onix::Message.load_from_string(str)
50
+
51
+ #check header elements
52
+ msg.from_company.should eql("Chalice")
53
+ msg.from_person.should eql("James Healy")
54
+ msg.sent_date.should eql(Date.civil(2006, 5, 21))
55
+
56
+ #check product count
57
+ msg.products.size.should eql(1)
58
+ end
59
+
60
+ # load a UTF-16 XML file into memory, converting it to UTF-8 on the fly
61
+ specify "should create an object tree from a UTF-16 string" do
62
+ str = File.read(File.dirname(__FILE__)+"/data/2_products_utf16.xml")
63
+ msg = RBook::Onix::Message.load_from_string(str)
64
+
65
+ #check header elements
66
+ msg.from_company.should eql("HarperCollins Publishers")
67
+ msg.sent_date.should eql(Date.civil(2007, 4, 23))
68
+
69
+ #check product count
70
+ msg.products.size.should eql(2)
71
+ end
72
+
73
+ # try loading an onix file with no products
74
+ specify "should raise an exception if attempting to load an ONIX file with no products" do
75
+ doc = nil
76
+ File.open(File.dirname(__FILE__)+"/data/invalid_no_product.xml", "r") do |f|
77
+ doc = REXML::Document.new(f)
78
+ end
79
+
80
+ lambda { msg = RBook::Onix::Message.load_from_xmldoc(doc) }.should raise_error(LoadError)
81
+ end
82
+
83
+ # test loading an xml file that's not an onix file
84
+ specify "should raise an exception if attempting to load an invalid ONIX file" do
85
+ doc = nil
86
+ File.open(File.dirname(__FILE__)+"/data/xml_not_onix.xml", "r") do |f|
87
+ doc = REXML::Document.new(f)
88
+ end
89
+
90
+ lambda { msg = RBook::Onix::Message.load_from_xmldoc(doc) }.should raise_error(LoadError)
91
+ end
92
+
93
+ # test loading an xml file that's is invalid (an open tag)
94
+ specify "should raise an exception if attempting to load an XML file with an open tag" do
95
+ doc = File.read(File.dirname(__FILE__)+"/data/invalid_xml.xml")
96
+
97
+ lambda { msg = RBook::Onix::Message.load_from_string(doc) }.should raise_error(LoadError)
98
+ end
99
+
100
+ # test loading a non XML file
101
+ specify "should raise an exception if attempting to load a non XML file" do
102
+ doc = nil
103
+ File.open(File.dirname(__FILE__)+"/data/not_xml.csv", "r") do |f|
104
+ doc = REXML::Document.new(f)
105
+ end
106
+
107
+ lambda { msg = RBook::Onix::Message.load_from_xmldoc(doc) }.should raise_error(LoadError)
108
+ end
109
+
110
+ end
@@ -0,0 +1,81 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
2
+
3
+ require 'rbook/onix'
4
+ include RBook
5
+
6
+ context "A message object with valid data" do
7
+
8
+ #setup do
9
+ #end
10
+
11
+ # ensure header information is output to xml correctly and that a
12
+ # product is created
13
+ specify "should output a valid ONIX file" do
14
+ msg = RBook::Onix::Message.new
15
+ msg.from_person = "James Healy"
16
+ msg.from_company = "Healy Inc."
17
+ msg.to_person = "RSpec"
18
+
19
+ product = RBook::Onix::Product.new
20
+ product.product_identifier = '0977616606'
21
+ msg.add_product(product)
22
+
23
+ doc = msg.to_xml
24
+
25
+ REXML::XPath.first(doc, '/ONIXMessage/Header/FromPerson').should_not be_nil
26
+ REXML::XPath.first(doc, '/ONIXMessage/Header/FromPerson').text.should eql("James Healy")
27
+ REXML::XPath.first(doc, '/ONIXMessage/Header/FromCompany').should_not be_nil
28
+ REXML::XPath.first(doc, '/ONIXMessage/Header/FromCompany').text.should eql("Healy Inc.")
29
+ REXML::XPath.first(doc, '/ONIXMessage/Header/ToPerson').should_not be_nil
30
+ REXML::XPath.first(doc, '/ONIXMessage/Header/ToPerson').text.should eql("RSpec")
31
+ REXML::XPath.first(doc, '/ONIXMessage/Product').should_not be_nil
32
+ end
33
+
34
+ # test for an exception when a onixmessage with no products is converted to an xml document
35
+ specify "should raise an exception if the user attempts to convert a message with no products to XML" do
36
+ msg = RBook::Onix::Message.new
37
+ msg.from_person = "James Healy"
38
+ msg.from_company = "Healy Inc."
39
+ msg.to_person = "RSpec"
40
+
41
+ # TODO: fix this to use a RBook custom error
42
+ lambda { msg.to_xml }.should raise_error(RuntimeError)
43
+ end
44
+
45
+ specify "should output a valid string" do
46
+ msg = RBook::Onix::Message.new
47
+ msg.from_person = "James Healy"
48
+ msg.from_company = "Healy Inc."
49
+ msg.to_person = "RSpec"
50
+
51
+ product = RBook::Onix::Product.new
52
+ product.product_identifier = '0977616606'
53
+ msg.add_product(product)
54
+
55
+ msg.to_s.should be_a_kind_of(String)
56
+ end
57
+
58
+ # ensure all output is cleansed UTF-8
59
+ specify "should output valid UTF-8, regardless of the input" do
60
+ msg = RBook::Onix::Message.new
61
+ msg.from_person = "James Healy\x00"
62
+ msg.from_company = "Healy Inc.\x11"
63
+ msg.from_email = "someone@example.com\x11"
64
+ msg.to_person = "RSpec"
65
+ msg.to_company = "Sample Company\x11"
66
+ msg.message_note = "This is a sample ONIX message\x11"
67
+
68
+ product = RBook::Onix::Product.new
69
+ product.product_identifier = '0977616606'
70
+ product.title = 'Test Title'
71
+ msg.add_product(product)
72
+
73
+ doc = msg.to_s
74
+
75
+ doc.each_byte do |c|
76
+ n = XChar::CP1252[c] || c
77
+ n = nil unless XChar::VALID.find {|range| range.include? n}
78
+ n.should_not be_nil
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,42 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
2
+
3
+ require 'rbook/onix'
4
+ include RBook
5
+
6
+ context "The product class" do
7
+
8
+ #setup do
9
+ #end
10
+
11
+ # test loading a valid onix file. Make sure the correct number of products
12
+ # exist, but don't worry about the content. The product unit tests can check them
13
+ specify "should create a object tree from an xmldoc object" do
14
+ product = nil
15
+ File.open(File.dirname(__FILE__)+"/data/single_product.xml", "r") do |f|
16
+ doc = REXML::Document.new(f)
17
+ product = RBook::Onix::Product.load_from_element(REXML::XPath.first(doc, '/ONIXMessage/Product'))
18
+ end
19
+
20
+ product.notification_type.should eql("03")
21
+ product.record_reference.should eql("0827200528")
22
+ product.form.should eql("BA")
23
+ product.pages.should eql("0")
24
+ product.bicmainsubject.should eql("YFH")
25
+ product.title.should eql("And Their Eyes Are Opened")
26
+ product.subtitle.should eql("Story Sermons Embracing the World")
27
+ product.publisher.should eql("Chalice")
28
+ product.publication_date.should eql(Date.civil(2005, 3, 3))
29
+
30
+ product.contributors.size.should eql(1)
31
+ product.supply_details.size.should eql(1)
32
+ end
33
+
34
+ specify "should raise an exception when atytempting to load from a non Product XML fragment" do
35
+ element = REXML::Element.new('NonProduct')
36
+ element.add_element('NonContributor').text = 'blah'
37
+
38
+ # TODO: change this to a custom RBook error
39
+ lambda {RBook::Onix::Product.load_from_element(element) }.should raise_error(LoadError)
40
+
41
+ end
42
+ end
@@ -0,0 +1,66 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
2
+
3
+ require 'rbook/onix'
4
+ include RBook
5
+
6
+ context "A product object with valid data" do
7
+
8
+ #setup do
9
+ #end
10
+
11
+ # ensure product information is output to xml correctly
12
+ # TODO: test more than just IDValue and TitleText
13
+ specify "should output to a valid XML fragment" do
14
+ product = RBook::Onix::Product.new
15
+ product.product_identifier = '0977616606'
16
+ product.title = 'This is a title'
17
+ product.publisher = 'Chalice'
18
+
19
+ contributor = RBook::Onix::Contributor.new
20
+ contributor.sequence_number = '0'
21
+ contributor.name_inverted = 'Healy, James'
22
+ contributor.role = 'A01'
23
+ product.add_contributor(contributor)
24
+
25
+ supply_detail = RBook::Onix::SupplyDetail.new
26
+ supply_detail.supplier_name = 'Walker'
27
+ supply_detail.availability_code = 'CS'
28
+ supply_detail.price = 29.95
29
+ product.add_supply_detail(supply_detail)
30
+
31
+ doc = product.to_element
32
+
33
+ REXML::XPath.first(doc, '/ProductIdentifier/IDValue').should_not be_nil
34
+ REXML::XPath.first(doc, '/ProductIdentifier/IDValue').text.should eql("0977616606")
35
+ REXML::XPath.first(doc, '/Title/TitleText').should_not be_nil
36
+ REXML::XPath.first(doc, '/Title/TitleText').text.should eql("This is a title")
37
+ REXML::XPath.first(doc, '/Contributor').should_not be_nil
38
+ REXML::XPath.first(doc, '/Publisher/PublisherName').should_not be_nil
39
+ REXML::XPath.first(doc, '/Publisher/PublisherName').text.should eql("Chalice")
40
+ REXML::XPath.first(doc, '/SupplyDetail').should_not be_nil
41
+ end
42
+
43
+ # ensure all output is cleansed UTF-8
44
+ specify "should output valid UTF-8, regardless of the input" do
45
+ # create an Onix::Product fragment
46
+ product = RBook::Onix::Product.new
47
+ product.product_identifier = "0977616606"
48
+ product.title = "Test Title\x00"
49
+ product.subtitle = "Test Subtitle\x11"
50
+ product.website = "http://www.google.com\x11"
51
+ product.form = "BA\x11"
52
+ product.bicmainsubject = "FGD\x11"
53
+ product.description = "This is a sample description\x11"
54
+ product.publisher = "Publisher\x11"
55
+
56
+ fragment = product.to_s
57
+
58
+ # loop over each byte in the product element and check it's
59
+ # a valid UTF-8 character
60
+ fragment.each_byte do |c|
61
+ n = XChar::CP1252[c] || c
62
+ n = nil unless XChar::VALID.find {|range| range.include? n}
63
+ n.should_not be_nil
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,32 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
2
+
3
+ require 'rbook/onix'
4
+ include RBook
5
+
6
+ context "The sales restriction class" do
7
+
8
+ #setup do
9
+ #end
10
+
11
+ # test loading a valid onix file. Make sure the correct number of products
12
+ # exist, but don't worry about the content. The product unit tests can check them
13
+ specify "should create a object tree from an xmldoc object" do
14
+ restriction = nil
15
+ File.open(File.dirname(__FILE__)+"/data/single_product.xml", "r") do |f|
16
+ doc = REXML::Document.new(f)
17
+ restriction = RBook::Onix::SalesRestriction.load_from_element(REXML::XPath.first(doc, '/ONIXMessage/Product/SalesRestriction'))
18
+ end
19
+
20
+ restriction.type.should eql("00")
21
+ restriction.detail.should eql("Unknown")
22
+ end
23
+
24
+ specify "should raise an exception when atytempting to load from a non Product XML fragment" do
25
+ element = REXML::Element.new('NonRestriction')
26
+ element.add_element('Type').text = 'blah'
27
+
28
+ # TODO: change this to a custom RBook error
29
+ lambda {RBook::Onix::SalesRestriction.load_from_element(element) }.should raise_error(LoadError)
30
+
31
+ end
32
+ end
@@ -0,0 +1,31 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
2
+
3
+ require 'rbook/onix'
4
+ include RBook
5
+
6
+ context "A sales restriction object with valid data" do
7
+
8
+ # ensure product information is output to xml correctly
9
+ # TODO: test more than just IDValue and TitleText
10
+ specify "should output to a valid XML fragment" do
11
+ restriction = RBook::Onix::SalesRestriction.new
12
+ restriction.type = '00'
13
+ restriction.detail = 'Unknown'
14
+
15
+ doc = restriction.to_element
16
+
17
+ REXML::XPath.first(doc, '/SalesRestrictionType').should_not be_nil
18
+ REXML::XPath.first(doc, '/SalesRestrictionType').text.should eql("00")
19
+ REXML::XPath.first(doc, '/SalesRestrictionDetail').should_not be_nil
20
+ REXML::XPath.first(doc, '/SalesRestrictionDetail').text.should eql("Unknown")
21
+ end
22
+
23
+ specify "should output a valid string" do
24
+ restriction = RBook::Onix::SalesRestriction.new
25
+ restriction.type = '00'
26
+ restriction.detail = 'Unknown'
27
+
28
+ restriction.to_s.should be_a_kind_of(String)
29
+
30
+ end
31
+ end