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,50 @@
1
+ require 'rexml/document'
2
+
3
+ module RBook
4
+ module Onix
5
+
6
+ class SalesRestriction
7
+ attr_accessor :type, :detail
8
+
9
+ # Attempts to create a contributor object using the supplied xml element
10
+ def SalesRestriction.load_from_element(element)
11
+ raise ArgumentError, 'load_from_element expects a REXML element object' unless element.class == REXML::Element
12
+
13
+ if REXML::XPath.first(element, '//SalesRestriction').nil?
14
+ raise LoadError, 'supplied REXML document object does not appear contain a valid SalesRestriction fragment'
15
+ end
16
+
17
+ restriction = SalesRestriction.new
18
+
19
+ tmp = REXML::XPath.first(element, '//SalesRestriction/SalesRestrictionType')
20
+ restriction.type = tmp.text if tmp != nil
21
+
22
+ tmp = REXML::XPath.first(element, '//SalesRestriction/SalesRestrictionDetail')
23
+ restriction.detail = tmp.text if tmp != nil
24
+
25
+ return restriction
26
+
27
+ end
28
+
29
+ # Return an xml element representing this contributor
30
+ def to_element
31
+ raise 'SalesRestriction must have a type to create an element' if self.type.nil?
32
+ raise 'Contributor must have a detail to create an element' if self.detail.nil?
33
+
34
+ restriction = REXML::Element.new('SalesRestriction')
35
+ restriction.add_element('SalesRestrictionType').text = self.type.to_xs
36
+ restriction.add_element('SalesRestrictionDetail').text = self.detail.to_xs
37
+
38
+ return restriction
39
+ end
40
+
41
+ # Return an XML string representing this contributor
42
+ def to_s
43
+ tmp = to_element
44
+ output = ''
45
+ tmp.write(output, 0)
46
+ return output
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,120 @@
1
+ require 'rexml/document'
2
+ require 'thread'
3
+
4
+ module RBook
5
+ module Onix
6
+
7
+ # a utility class for the ONIX Stream Reader. See RBook::ONIX::StreamReader for
8
+ # basic usage instructions.
9
+ class Listener
10
+
11
+ def initialize(queue)
12
+ @queue = queue
13
+ @in_product = false
14
+ end
15
+
16
+ def doctype(name, pub_sys, long_name, uri)
17
+ end
18
+
19
+ def tag_start(name, attrs)
20
+ case name
21
+ when "Product"
22
+ # A new product tag has been read, so start
23
+ # building a new product to add to the queue
24
+ @in_product = true
25
+ @product_fragment = "<Product>"
26
+ else
27
+ @product_fragment << "<#{name}>" if @in_product
28
+ end
29
+ end
30
+
31
+ def text(text)
32
+ @product_fragment << text if @in_product
33
+ end
34
+
35
+ def tag_end(name)
36
+ case name
37
+ when "ONIXMessage"
38
+ # the ONIXMessage tag indicates the end of the file, so add
39
+ # nil to the queue to let the iterating thread know reading
40
+ # is finished
41
+ @queue.push(nil)
42
+ when "Product"
43
+ # A product tag is finished, so add it to the queue
44
+ @product_fragment << "</Product>"
45
+ begin
46
+ doc = REXML::Document.new(@product_fragment)
47
+ unless doc.root.nil?
48
+ product = RBook::Onix::Product.load_from_element(doc.root)
49
+ @queue.push(product) unless product.nil?
50
+ end
51
+ rescue
52
+ # error occurred while building the product from an XML fragment
53
+ end
54
+ @in_product = false
55
+ else
56
+ @product_fragment << "</#{name}>" if @in_product
57
+ end
58
+ end
59
+
60
+ def xmldecl(version, encoding, standalone)
61
+ # do nothing
62
+ end
63
+
64
+ def method_missing
65
+ # do nothing
66
+ end
67
+ end
68
+
69
+ # A stream reader for ONIX files. Using a stream reader is preferred for
70
+ # large XML files as the file is processed in stages, removing the need
71
+ # to store the entire thing in memory at once.
72
+ #
73
+ # This class provides forward only iteration over a single ONIX file,
74
+ # returning a RBook::ONIX::Product object for each product encountered.
75
+ #
76
+ # = Basic usage
77
+ # require 'rbook/onix'
78
+ # reader = RBook::ONIX::StreamReader.new("some_onix_file.xml")
79
+ # reader.each do |product|
80
+ # puts product.inspect
81
+ # end
82
+ class StreamReader
83
+
84
+ # creates a new stream reader to read the specified file.
85
+ # file can be specified as a String or File object
86
+ def initialize(input)
87
+ if input.class == String
88
+ @input = File.new(input)
89
+ elsif input.class == File
90
+ @input = input
91
+ else
92
+ throw "Unable to read from path or file"
93
+ end
94
+ # create a sized queue to store each product read from the file
95
+ @queue = SizedQueue.new(100)
96
+
97
+ # launch a reader thread to process the file and store each
98
+ # product in the queue
99
+ Thread.new do
100
+ producer = Listener.new(@queue)
101
+ REXML::Document.parse_stream(@input, producer)
102
+ end
103
+ end
104
+
105
+ # iterate over the file and return a product file to a block.
106
+ #
107
+ # reader = RBook::ONIX::StreamReader.new("some_onix_file.xml")
108
+ # reader.each do |product|
109
+ # puts product.inspect
110
+ # end
111
+ def each
112
+ obj = @queue.pop
113
+ while !obj.nil?
114
+ yield obj
115
+ obj = @queue.pop
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,40 @@
1
+ require 'rexml/document'
2
+
3
+ module RBook
4
+ module Onix
5
+
6
+ class StreamWriter
7
+
8
+ # Default constructor.
9
+ def initialize(output, msg)
10
+ raise ArgumentError, 'msg must be an RBook::Onix::Message object' unless msg.class == RBook::Onix::Message
11
+ @output = output
12
+ @msg = msg
13
+ end
14
+
15
+ def start_document(encoding = nil)
16
+ decl = REXML::XMLDecl.new
17
+ doctype = REXML::DocType.new('ONIXMessage', "SYSTEM \"#{RBook::Onix::Message::ONIX_DTD_URL}\"")
18
+ if encoding
19
+ decl.encoding = encoding
20
+ else
21
+ decl.encoding = "UTF-8"
22
+ end
23
+ @output.write(decl.to_s+"\n")
24
+ @output.write(doctype.to_s+"\n")
25
+ @output.write("<ONIXMessage>\n")
26
+ @output.write(@msg.header.to_s)
27
+ end
28
+
29
+ def << (product)
30
+ @output.write(product.to_s)
31
+ end
32
+
33
+ def end_document
34
+ @output.write("</ONIXMessage>\n")
35
+ end
36
+
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,75 @@
1
+ require 'rexml/document'
2
+ require 'bigdecimal'
3
+
4
+ module RBook
5
+ module Onix
6
+
7
+ class SupplyDetail
8
+ attr_accessor :supplier_name, :availability_code, :intermediary_availability_code, :price, :price_type_code
9
+
10
+ # Attempts to create a contributor object using the supplied xml element
11
+ def self.load_from_element(element)
12
+ raise ArgumentError, 'load_from_element expects a REXML element object' unless element.class == REXML::Element
13
+
14
+ if REXML::XPath.first(element, '//SupplyDetail').nil?
15
+ raise LoadError, 'supplied REXML document object does not appear contain a valid SupplyDetail fragment'
16
+ end
17
+
18
+ supply_detail = SupplyDetail.new
19
+
20
+ tmp = REXML::XPath.first(element, '//SupplyDetail/SupplierName')
21
+ supply_detail.supplier_name = tmp.text if tmp != nil
22
+
23
+ tmp = REXML::XPath.first(element, '//SupplyDetail/AvailabilityCode')
24
+ supply_detail.availability_code = tmp.text if tmp != nil
25
+
26
+ tmp = REXML::XPath.first(element, '//SupplyDetail/IntermediaryAvailabilityCode')
27
+ supply_detail.intermediary_availability_code = tmp.text if tmp != nil
28
+
29
+ tmp = REXML::XPath.first(element, '//SupplyDetail/Price/PriceAmount')
30
+ supply_detail.price = BigDecimal(tmp.text) if tmp != nil
31
+
32
+ tmp = REXML::XPath.first(element, '//SupplyDetail/Price/PriceTypeCode')
33
+ supply_detail.price_type_code = tmp.text.to_i if tmp != nil
34
+
35
+ return supply_detail
36
+
37
+ end
38
+
39
+ # Return an xml element representing this contributor
40
+ def to_element
41
+ raise 'SupplyDetail must have a supplier name to create an element' if self.supplier_name.nil?
42
+ raise 'SupplyDetail must have a availability code to create an element' if self.availability_code.nil?
43
+
44
+ supply_detail = REXML::Element.new('SupplyDetail')
45
+ supply_detail.add_element('SupplierName').text = self.supplier_name.to_xs
46
+ supply_detail.add_element('AvailabilityCode').text = self.availability_code.to_xs unless self.availability_code.nil?
47
+ supply_detail.add_element('IntermediaryAvailabilityCode').text = self.intermediary_availability_code.to_xs unless self.intermediary_availability_code.nil?
48
+ unless self.price.nil?
49
+ tmp = REXML::Element.new('Price')
50
+ if self.price.kind_of?(BigDecimal)
51
+ tmp.add_element('PriceAmount').text = self.price.to_s("F")
52
+ else
53
+ tmp.add_element('PriceAmount').text = self.price.to_s
54
+ end
55
+ supply_detail.add_element(tmp)
56
+ if self.price_type_code.nil?
57
+ tmp.add_element('PriceTypeCode').text = "02"
58
+ else
59
+ tmp.add_element('PriceTypeCode').text = self.price_type_code
60
+ end
61
+ end
62
+
63
+ return supply_detail
64
+ end
65
+
66
+ # Return an XML string representing this contributor
67
+ def to_s
68
+ tmp = to_element
69
+ output = ''
70
+ tmp.write(output, 0)
71
+ return output
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,62 @@
1
+ # sourced from http://www.intertwingly.net/blog/2005/09/28/XML-Cleansing
2
+
3
+ module XChar
4
+ # http://intertwingly.net/stories/2004/04/14/i18n.html#CleaningWindows
5
+ CP1252 = {
6
+ 128 => 8364, # euro sign
7
+ 130 => 8218, # single low-9 quotation mark
8
+ 131 => 402, # latin small letter f with hook
9
+ 132 => 8222, # double low-9 quotation mark
10
+ 133 => 8230, # horizontal ellipsis
11
+ 134 => 8224, # dagger
12
+ 135 => 8225, # double dagger
13
+ 136 => 710, # modifier letter circumflex accent
14
+ 137 => 8240, # per mille sign
15
+ 138 => 352, # latin capital letter s with caron
16
+ 139 => 8249, # single left-pointing angle quotation mark
17
+ 140 => 338, # latin capital ligature oe
18
+ 142 => 381, # latin capital letter z with caron
19
+ 145 => 8216, # left single quotation mark
20
+ 146 => 8217, # right single quotation mark
21
+ 147 => 8220, # left double quotation mark
22
+ 148 => 8221, # right double quotation mark
23
+ 149 => 8226, # bullet
24
+ 150 => 8211, # en dash
25
+ 151 => 8212, # em dash
26
+ 152 => 732, # small tilde
27
+ 153 => 8482, # trade mark sign
28
+ 154 => 353, # latin small letter s with caron
29
+ 155 => 8250, # single right-pointing angle quotation mark
30
+ 156 => 339, # latin small ligature oe
31
+ 158 => 382, # latin small letter z with caron
32
+ 159 => 376} # latin capital letter y with diaeresis
33
+
34
+ # http://www.w3.org/TR/REC-xml/#dt-chardata
35
+ PREDEFINED = {
36
+ 38 => '&amp;', # ampersand
37
+ 60 => '&lt;', # left angle bracket
38
+ 62 => '&gt;'} # right angle bracket
39
+
40
+ # http://www.w3.org/TR/REC-xml/#charsets
41
+ VALID = [[0x9, 0xA, 0xD], (0x20..0xD7FF),
42
+ (0xE000..0xFFFD), (0x10000..0x10FFFF)]
43
+ end
44
+
45
+ class Fixnum
46
+ # xml escaped version of chr
47
+ def xchr
48
+ n = XChar::CP1252[self] || self
49
+ n = 42 unless XChar::VALID.find {|range| range.include? n}
50
+ XChar::PREDEFINED[n] or (n<128 ? n.chr : "&##{n};")
51
+ end
52
+ end
53
+
54
+ class String
55
+ # xml escaped version of to_s
56
+ def to_xs
57
+ unpack('U*').map {|n| n.xchr}.join # ASCII, UTF-8
58
+ rescue
59
+ unpack('C*').map {|n| n.xchr}.join # ISO-8859-1, WIN-1252
60
+ end
61
+ end
62
+
@@ -0,0 +1,33 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
2
+
3
+ require 'rbook/onix'
4
+ include RBook
5
+
6
+ context "The contributor 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
+ contributor = nil
15
+ File.open(File.dirname(__FILE__)+"/data/single_product.xml", "r") do |f|
16
+ doc = REXML::Document.new(f)
17
+ contributor = RBook::Onix::Contributor.load_from_element(REXML::XPath.first(doc, '/ONIXMessage/Product/Contributor'))
18
+ end
19
+
20
+ contributor.sequence_number.should eql("0")
21
+ contributor.role.should eql("A01")
22
+ contributor.name_inverted.should eql("Healy, James")
23
+ end
24
+
25
+ specify "should raise an exception when atytempting to load from a non Product XML fragment" do
26
+ element = REXML::Element.new('NonContributor')
27
+ element.add_element('SequenceNumber').text = 'blah'
28
+
29
+ # TODO: change this to a custom RBook error
30
+ lambda {RBook::Onix::Contributor.load_from_element(element) }.should raise_error(LoadError)
31
+
32
+ end
33
+ end
@@ -0,0 +1,36 @@
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
+ contributor = RBook::Onix::Contributor.new
15
+ contributor.name_inverted = 'Healy, James'
16
+ contributor.sequence_number = '01'
17
+ contributor.role = 'A01'
18
+
19
+ doc = contributor.to_element
20
+
21
+ REXML::XPath.first(doc, '/PersonNameInverted').should_not be_nil
22
+ REXML::XPath.first(doc, '/PersonNameInverted').text.should eql("Healy, James")
23
+ REXML::XPath.first(doc, '/ContributorRole').should_not be_nil
24
+ REXML::XPath.first(doc, '/ContributorRole').text.should eql("A01")
25
+ end
26
+
27
+ specify "should output a valid string" do
28
+ contributor = RBook::Onix::Contributor.new
29
+ contributor.name_inverted = 'Healy, James'
30
+ contributor.sequence_number = '01'
31
+ contributor.role = 'A01'
32
+
33
+ contributor.to_s.should be_a_kind_of(String)
34
+
35
+ end
36
+ end
@@ -0,0 +1,469 @@
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
+ <Product>
56
+ <RecordReference>0827200528</RecordReference>
57
+ <NotificationType>03</NotificationType>
58
+ <ProductIdentifier>
59
+ <ProductIDType>02</ProductIDType>
60
+ <IDValue>0827200528</IDValue>
61
+ </ProductIdentifier>
62
+ <ProductForm>BA</ProductForm>
63
+ <NoSeries/>
64
+ <Title>
65
+ <TitleType>01</TitleType>
66
+ <TitleText>And Their Eyes Are Opened</TitleText>
67
+ <Subtitle>Story Sermons Embracing the World</Subtitle>
68
+ </Title>
69
+ <Contributor>
70
+ <SequenceNumber>0</SequenceNumber>
71
+ <ContributorRole>A01</ContributorRole>
72
+ <PersonNameInverted>Healy, James</PersonNameInverted>
73
+ </Contributor>
74
+ <NoEdition/>
75
+ <BICMainSubject>YFH</BICMainSubject>
76
+ <MediaFile>
77
+ <MediaFileTypeCode>04</MediaFileTypeCode>
78
+ <MediaFileLinkTypeCode>01</MediaFileLinkTypeCode>
79
+ <MediaFileLink>http://www.example.com/0827200528.jpg</MediaFileLink>
80
+ </MediaFile>
81
+ <NumberOfPages>0</NumberOfPages>
82
+ <Publisher>
83
+ <PublishingRole>01</PublishingRole>
84
+ <PublisherName>Chalice</PublisherName>
85
+ </Publisher>
86
+ <PublicationDate>20050303</PublicationDate>
87
+ <SalesRestriction>
88
+ <SalesRestrictionType>00</SalesRestrictionType>
89
+ <SalesRestrictionDetail>Unknown</SalesRestrictionDetail>
90
+ </SalesRestriction>
91
+ <SupplyDetail>
92
+ <SupplierName>Chalice</SupplierName>
93
+ <AvailabilityCode>CS</AvailabilityCode>
94
+ <IntermediaryAvailabilityCode>99</IntermediaryAvailabilityCode>
95
+ <Price>
96
+ <PriceTypeCode>02</PriceTypeCode>
97
+ <PriceAmount>29.95</PriceAmount>
98
+ </Price>
99
+ </SupplyDetail>
100
+ </Product>
101
+ <Product>
102
+ <RecordReference>0827200528</RecordReference>
103
+ <NotificationType>03</NotificationType>
104
+ <ProductIdentifier>
105
+ <ProductIDType>02</ProductIDType>
106
+ <IDValue>0827200528</IDValue>
107
+ </ProductIdentifier>
108
+ <ProductForm>BA</ProductForm>
109
+ <NoSeries/>
110
+ <Title>
111
+ <TitleType>01</TitleType>
112
+ <TitleText>And Their Eyes Are Opened</TitleText>
113
+ <Subtitle>Story Sermons Embracing the World</Subtitle>
114
+ </Title>
115
+ <Contributor>
116
+ <SequenceNumber>0</SequenceNumber>
117
+ <ContributorRole>A01</ContributorRole>
118
+ <PersonNameInverted>Healy, James</PersonNameInverted>
119
+ </Contributor>
120
+ <NoEdition/>
121
+ <BICMainSubject>YFH</BICMainSubject>
122
+ <MediaFile>
123
+ <MediaFileTypeCode>04</MediaFileTypeCode>
124
+ <MediaFileLinkTypeCode>01</MediaFileLinkTypeCode>
125
+ <MediaFileLink>http://www.example.com/0827200528.jpg</MediaFileLink>
126
+ </MediaFile>
127
+ <NumberOfPages>0</NumberOfPages>
128
+ <Publisher>
129
+ <PublishingRole>01</PublishingRole>
130
+ <PublisherName>Chalice</PublisherName>
131
+ </Publisher>
132
+ <PublicationDate>20050303</PublicationDate>
133
+ <SalesRestriction>
134
+ <SalesRestrictionType>00</SalesRestrictionType>
135
+ <SalesRestrictionDetail>Unknown</SalesRestrictionDetail>
136
+ </SalesRestriction>
137
+ <SupplyDetail>
138
+ <SupplierName>Chalice</SupplierName>
139
+ <AvailabilityCode>CS</AvailabilityCode>
140
+ <IntermediaryAvailabilityCode>99</IntermediaryAvailabilityCode>
141
+ <Price>
142
+ <PriceTypeCode>02</PriceTypeCode>
143
+ <PriceAmount>29.95</PriceAmount>
144
+ </Price>
145
+ </SupplyDetail>
146
+ </Product>
147
+ <Product>
148
+ <RecordReference>0827200528</RecordReference>
149
+ <NotificationType>03</NotificationType>
150
+ <ProductIdentifier>
151
+ <ProductIDType>02</ProductIDType>
152
+ <IDValue>0827200528</IDValue>
153
+ </ProductIdentifier>
154
+ <ProductForm>BA</ProductForm>
155
+ <NoSeries/>
156
+ <Title>
157
+ <TitleType>01</TitleType>
158
+ <TitleText>And Their Eyes Are Opened</TitleText>
159
+ <Subtitle>Story Sermons Embracing the World</Subtitle>
160
+ </Title>
161
+ <Contributor>
162
+ <SequenceNumber>0</SequenceNumber>
163
+ <ContributorRole>A01</ContributorRole>
164
+ <PersonNameInverted>Healy, James</PersonNameInverted>
165
+ </Contributor>
166
+ <NoEdition/>
167
+ <BICMainSubject>YFH</BICMainSubject>
168
+ <MediaFile>
169
+ <MediaFileTypeCode>04</MediaFileTypeCode>
170
+ <MediaFileLinkTypeCode>01</MediaFileLinkTypeCode>
171
+ <MediaFileLink>http://www.example.com/0827200528.jpg</MediaFileLink>
172
+ </MediaFile>
173
+ <NumberOfPages>0</NumberOfPages>
174
+ <Publisher>
175
+ <PublishingRole>01</PublishingRole>
176
+ <PublisherName>Chalice</PublisherName>
177
+ </Publisher>
178
+ <PublicationDate>20050303</PublicationDate>
179
+ <SalesRestriction>
180
+ <SalesRestrictionType>00</SalesRestrictionType>
181
+ <SalesRestrictionDetail>Unknown</SalesRestrictionDetail>
182
+ </SalesRestriction>
183
+ <SupplyDetail>
184
+ <SupplierName>Chalice</SupplierName>
185
+ <AvailabilityCode>CS</AvailabilityCode>
186
+ <IntermediaryAvailabilityCode>99</IntermediaryAvailabilityCode>
187
+ <Price>
188
+ <PriceTypeCode>02</PriceTypeCode>
189
+ <PriceAmount>29.95</PriceAmount>
190
+ </Price>
191
+ </SupplyDetail>
192
+ </Product>
193
+ <Product>
194
+ <RecordReference>0827200528</RecordReference>
195
+ <NotificationType>03</NotificationType>
196
+ <ProductIdentifier>
197
+ <ProductIDType>02</ProductIDType>
198
+ <IDValue>0827200528</IDValue>
199
+ </ProductIdentifier>
200
+ <ProductForm>BA</ProductForm>
201
+ <NoSeries/>
202
+ <Title>
203
+ <TitleType>01</TitleType>
204
+ <TitleText>And Their Eyes Are Opened</TitleText>
205
+ <Subtitle>Story Sermons Embracing the World</Subtitle>
206
+ </Title>
207
+ <Contributor>
208
+ <SequenceNumber>0</SequenceNumber>
209
+ <ContributorRole>A01</ContributorRole>
210
+ <PersonNameInverted>Healy, James</PersonNameInverted>
211
+ </Contributor>
212
+ <NoEdition/>
213
+ <BICMainSubject>YFH</BICMainSubject>
214
+ <MediaFile>
215
+ <MediaFileTypeCode>04</MediaFileTypeCode>
216
+ <MediaFileLinkTypeCode>01</MediaFileLinkTypeCode>
217
+ <MediaFileLink>http://www.example.com/0827200528.jpg</MediaFileLink>
218
+ </MediaFile>
219
+ <NumberOfPages>0</NumberOfPages>
220
+ <Publisher>
221
+ <PublishingRole>01</PublishingRole>
222
+ <PublisherName>Chalice</PublisherName>
223
+ </Publisher>
224
+ <PublicationDate>20050303</PublicationDate>
225
+ <SalesRestriction>
226
+ <SalesRestrictionType>00</SalesRestrictionType>
227
+ <SalesRestrictionDetail>Unknown</SalesRestrictionDetail>
228
+ </SalesRestriction>
229
+ <SupplyDetail>
230
+ <SupplierName>Chalice</SupplierName>
231
+ <AvailabilityCode>CS</AvailabilityCode>
232
+ <IntermediaryAvailabilityCode>99</IntermediaryAvailabilityCode>
233
+ <Price>
234
+ <PriceTypeCode>02</PriceTypeCode>
235
+ <PriceAmount>29.95</PriceAmount>
236
+ </Price>
237
+ </SupplyDetail>
238
+ </Product>
239
+ <Product>
240
+ <RecordReference>0827200528</RecordReference>
241
+ <NotificationType>03</NotificationType>
242
+ <ProductIdentifier>
243
+ <ProductIDType>02</ProductIDType>
244
+ <IDValue>0827200528</IDValue>
245
+ </ProductIdentifier>
246
+ <ProductForm>BA</ProductForm>
247
+ <NoSeries/>
248
+ <Title>
249
+ <TitleType>01</TitleType>
250
+ <TitleText>And Their Eyes Are Opened</TitleText>
251
+ <Subtitle>Story Sermons Embracing the World</Subtitle>
252
+ </Title>
253
+ <Contributor>
254
+ <SequenceNumber>0</SequenceNumber>
255
+ <ContributorRole>A01</ContributorRole>
256
+ <PersonNameInverted>Healy, James</PersonNameInverted>
257
+ </Contributor>
258
+ <NoEdition/>
259
+ <BICMainSubject>YFH</BICMainSubject>
260
+ <MediaFile>
261
+ <MediaFileTypeCode>04</MediaFileTypeCode>
262
+ <MediaFileLinkTypeCode>01</MediaFileLinkTypeCode>
263
+ <MediaFileLink>http://www.example.com/0827200528.jpg</MediaFileLink>
264
+ </MediaFile>
265
+ <NumberOfPages>0</NumberOfPages>
266
+ <Publisher>
267
+ <PublishingRole>01</PublishingRole>
268
+ <PublisherName>Chalice</PublisherName>
269
+ </Publisher>
270
+ <PublicationDate>20050303</PublicationDate>
271
+ <SalesRestriction>
272
+ <SalesRestrictionType>00</SalesRestrictionType>
273
+ <SalesRestrictionDetail>Unknown</SalesRestrictionDetail>
274
+ </SalesRestriction>
275
+ <SupplyDetail>
276
+ <SupplierName>Chalice</SupplierName>
277
+ <AvailabilityCode>CS</AvailabilityCode>
278
+ <IntermediaryAvailabilityCode>99</IntermediaryAvailabilityCode>
279
+ <Price>
280
+ <PriceTypeCode>02</PriceTypeCode>
281
+ <PriceAmount>29.95</PriceAmount>
282
+ </Price>
283
+ </SupplyDetail>
284
+ </Product>
285
+ <Product>
286
+ <RecordReference>0827200528</RecordReference>
287
+ <NotificationType>03</NotificationType>
288
+ <ProductIdentifier>
289
+ <ProductIDType>02</ProductIDType>
290
+ <IDValue>0827200528</IDValue>
291
+ </ProductIdentifier>
292
+ <ProductForm>BA</ProductForm>
293
+ <NoSeries/>
294
+ <Title>
295
+ <TitleType>01</TitleType>
296
+ <TitleText>And Their Eyes Are Opened</TitleText>
297
+ <Subtitle>Story Sermons Embracing the World</Subtitle>
298
+ </Title>
299
+ <Contributor>
300
+ <SequenceNumber>0</SequenceNumber>
301
+ <ContributorRole>A01</ContributorRole>
302
+ <PersonNameInverted>Healy, James</PersonNameInverted>
303
+ </Contributor>
304
+ <NoEdition/>
305
+ <BICMainSubject>YFH</BICMainSubject>
306
+ <MediaFile>
307
+ <MediaFileTypeCode>04</MediaFileTypeCode>
308
+ <MediaFileLinkTypeCode>01</MediaFileLinkTypeCode>
309
+ <MediaFileLink>http://www.example.com/0827200528.jpg</MediaFileLink>
310
+ </MediaFile>
311
+ <NumberOfPages>0</NumberOfPages>
312
+ <Publisher>
313
+ <PublishingRole>01</PublishingRole>
314
+ <PublisherName>Chalice</PublisherName>
315
+ </Publisher>
316
+ <PublicationDate>20050303</PublicationDate>
317
+ <SalesRestriction>
318
+ <SalesRestrictionType>00</SalesRestrictionType>
319
+ <SalesRestrictionDetail>Unknown</SalesRestrictionDetail>
320
+ </SalesRestriction>
321
+ <SupplyDetail>
322
+ <SupplierName>Chalice</SupplierName>
323
+ <AvailabilityCode>CS</AvailabilityCode>
324
+ <IntermediaryAvailabilityCode>99</IntermediaryAvailabilityCode>
325
+ <Price>
326
+ <PriceTypeCode>02</PriceTypeCode>
327
+ <PriceAmount>29.95</PriceAmount>
328
+ </Price>
329
+ </SupplyDetail>
330
+ </Product>
331
+ <Product>
332
+ <RecordReference>0827200528</RecordReference>
333
+ <NotificationType>03</NotificationType>
334
+ <ProductIdentifier>
335
+ <ProductIDType>02</ProductIDType>
336
+ <IDValue>0827200528</IDValue>
337
+ </ProductIdentifier>
338
+ <ProductForm>BA</ProductForm>
339
+ <NoSeries/>
340
+ <Title>
341
+ <TitleType>01</TitleType>
342
+ <TitleText>And Their Eyes Are Opened</TitleText>
343
+ <Subtitle>Story Sermons Embracing the World</Subtitle>
344
+ </Title>
345
+ <Contributor>
346
+ <SequenceNumber>0</SequenceNumber>
347
+ <ContributorRole>A01</ContributorRole>
348
+ <PersonNameInverted>Healy, James</PersonNameInverted>
349
+ </Contributor>
350
+ <NoEdition/>
351
+ <BICMainSubject>YFH</BICMainSubject>
352
+ <MediaFile>
353
+ <MediaFileTypeCode>04</MediaFileTypeCode>
354
+ <MediaFileLinkTypeCode>01</MediaFileLinkTypeCode>
355
+ <MediaFileLink>http://www.example.com/0827200528.jpg</MediaFileLink>
356
+ </MediaFile>
357
+ <NumberOfPages>0</NumberOfPages>
358
+ <Publisher>
359
+ <PublishingRole>01</PublishingRole>
360
+ <PublisherName>Chalice</PublisherName>
361
+ </Publisher>
362
+ <PublicationDate>20050303</PublicationDate>
363
+ <SalesRestriction>
364
+ <SalesRestrictionType>00</SalesRestrictionType>
365
+ <SalesRestrictionDetail>Unknown</SalesRestrictionDetail>
366
+ </SalesRestriction>
367
+ <SupplyDetail>
368
+ <SupplierName>Chalice</SupplierName>
369
+ <AvailabilityCode>CS</AvailabilityCode>
370
+ <IntermediaryAvailabilityCode>99</IntermediaryAvailabilityCode>
371
+ <Price>
372
+ <PriceTypeCode>02</PriceTypeCode>
373
+ <PriceAmount>29.95</PriceAmount>
374
+ </Price>
375
+ </SupplyDetail>
376
+ </Product>
377
+ <Product>
378
+ <RecordReference>0827200528</RecordReference>
379
+ <NotificationType>03</NotificationType>
380
+ <ProductIdentifier>
381
+ <ProductIDType>02</ProductIDType>
382
+ <IDValue>0827200528</IDValue>
383
+ </ProductIdentifier>
384
+ <ProductForm>BA</ProductForm>
385
+ <NoSeries/>
386
+ <Title>
387
+ <TitleType>01</TitleType>
388
+ <TitleText>And Their Eyes Are Opened</TitleText>
389
+ <Subtitle>Story Sermons Embracing the World</Subtitle>
390
+ </Title>
391
+ <Contributor>
392
+ <SequenceNumber>0</SequenceNumber>
393
+ <ContributorRole>A01</ContributorRole>
394
+ <PersonNameInverted>Healy, James</PersonNameInverted>
395
+ </Contributor>
396
+ <NoEdition/>
397
+ <BICMainSubject>YFH</BICMainSubject>
398
+ <MediaFile>
399
+ <MediaFileTypeCode>04</MediaFileTypeCode>
400
+ <MediaFileLinkTypeCode>01</MediaFileLinkTypeCode>
401
+ <MediaFileLink>http://www.example.com/0827200528.jpg</MediaFileLink>
402
+ </MediaFile>
403
+ <NumberOfPages>0</NumberOfPages>
404
+ <Publisher>
405
+ <PublishingRole>01</PublishingRole>
406
+ <PublisherName>Chalice</PublisherName>
407
+ </Publisher>
408
+ <PublicationDate>20050303</PublicationDate>
409
+ <SalesRestriction>
410
+ <SalesRestrictionType>00</SalesRestrictionType>
411
+ <SalesRestrictionDetail>Unknown</SalesRestrictionDetail>
412
+ </SalesRestriction>
413
+ <SupplyDetail>
414
+ <SupplierName>Chalice</SupplierName>
415
+ <AvailabilityCode>CS</AvailabilityCode>
416
+ <IntermediaryAvailabilityCode>99</IntermediaryAvailabilityCode>
417
+ <Price>
418
+ <PriceTypeCode>02</PriceTypeCode>
419
+ <PriceAmount>29.95</PriceAmount>
420
+ </Price>
421
+ </SupplyDetail>
422
+ </Product>
423
+ <Product>
424
+ <RecordReference>0827200528</RecordReference>
425
+ <NotificationType>03</NotificationType>
426
+ <ProductIdentifier>
427
+ <ProductIDType>02</ProductIDType>
428
+ <IDValue>0827200528</IDValue>
429
+ </ProductIdentifier>
430
+ <ProductForm>BA</ProductForm>
431
+ <NoSeries/>
432
+ <Title>
433
+ <TitleType>01</TitleType>
434
+ <TitleText>And Their Eyes Are Opened</TitleText>
435
+ <Subtitle>Story Sermons Embracing the World</Subtitle>
436
+ </Title>
437
+ <Contributor>
438
+ <SequenceNumber>0</SequenceNumber>
439
+ <ContributorRole>A01</ContributorRole>
440
+ <PersonNameInverted>Healy, James</PersonNameInverted>
441
+ </Contributor>
442
+ <NoEdition/>
443
+ <BICMainSubject>YFH</BICMainSubject>
444
+ <MediaFile>
445
+ <MediaFileTypeCode>04</MediaFileTypeCode>
446
+ <MediaFileLinkTypeCode>01</MediaFileLinkTypeCode>
447
+ <MediaFileLink>http://www.example.com/0827200528.jpg</MediaFileLink>
448
+ </MediaFile>
449
+ <NumberOfPages>0</NumberOfPages>
450
+ <Publisher>
451
+ <PublishingRole>01</PublishingRole>
452
+ <PublisherName>Chalice</PublisherName>
453
+ </Publisher>
454
+ <PublicationDate>20050303</PublicationDate>
455
+ <SalesRestriction>
456
+ <SalesRestrictionType>00</SalesRestrictionType>
457
+ <SalesRestrictionDetail>Unknown</SalesRestrictionDetail>
458
+ </SalesRestriction>
459
+ <SupplyDetail>
460
+ <SupplierName>Chalice</SupplierName>
461
+ <AvailabilityCode>CS</AvailabilityCode>
462
+ <IntermediaryAvailabilityCode>99</IntermediaryAvailabilityCode>
463
+ <Price>
464
+ <PriceTypeCode>02</PriceTypeCode>
465
+ <PriceAmount>29.95</PriceAmount>
466
+ </Price>
467
+ </SupplyDetail>
468
+ </Product>
469
+ </ONIXMessage>