rbook-onix 0.5

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