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,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>