rbook-onix 0.5.2 → 0.6.0

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.
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ require 'rake/testtask'
6
6
  require "rake/gempackagetask"
7
7
  require 'spec/rake/spectask'
8
8
 
9
- PKG_VERSION = "0.5.2"
9
+ PKG_VERSION = "0.6.0"
10
10
  PKG_NAME = "rbook-onix"
11
11
  PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
12
12
  RUBYFORGE_PROJECT = 'rbook'
@@ -80,6 +80,8 @@ spec = Gem::Specification.new do |spec|
80
80
  spec.rdoc_options << '--title' << 'onix Documentation' <<
81
81
  '--main' << 'README' << '-q'
82
82
  spec.add_dependency('rbook-isbn', '>= 1.0')
83
+ spec.add_dependency('hpricot', '>= 0.6')
84
+ spec.add_dependency('builder', '>= 2.1.2')
83
85
  spec.author = "James Healy"
84
86
  spec.email = "jimmy@deefa.com"
85
87
  spec.rubyforge_project = "rbook"
@@ -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,8 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
2
+
3
+ require 'rubygems'
4
+ require 'rbook/onix'
5
+
6
+ msg = RBook::Onix::Message.load_from_string(File.read(File.dirname(__FILE__)+"/../specs/data/2_products_utf16.xml"))
7
+
8
+ puts msg.inspect
@@ -1,8 +1,9 @@
1
1
  $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
2
2
 
3
+ require 'rubygems'
3
4
  require 'rbook/onix'
4
5
 
5
- reader = RBook::Onix::StreamReader.new(File.dirname(__FILE__)+"/../specs/data/chalice.xml")
6
+ reader = RBook::Onix::StreamReader.new(File.dirname(__FILE__)+"/../specs/data/eerdsman.xml")
6
7
  counter = 0
7
8
 
8
9
  reader.each do |product|
@@ -10,6 +10,7 @@ require 'rbook/onix/xchar'
10
10
  require 'rbook/onix/stream_reader'
11
11
  require 'rbook/onix/stream_writer'
12
12
  require 'rbook/isbn'
13
+ require 'builder'
13
14
 
14
15
  module RBook
15
16
 
@@ -1,4 +1,6 @@
1
1
  require 'rexml/document'
2
+ require 'hpricot'
3
+ require 'active_support'
2
4
 
3
5
  module RBook
4
6
  module Onix
@@ -6,59 +8,55 @@ module Onix
6
8
  class Contributor
7
9
  attr_accessor :sequence_number, :role, :name, :name_inverted
8
10
 
9
- # Attempts to create a contributor object using the supplied xml element
10
- def Contributor.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, '//Contributor').nil?
14
- raise LoadError, 'supplied REXML document object does not appear contain a valid contributor fragment'
15
- end
16
-
11
+ def Contributor.load_from_string(str)
12
+ doc = Hpricot::XML(str)
17
13
  contributor = Contributor.new
18
14
 
19
- tmp = REXML::XPath.first(element, '//Contributor/SequenceNumber')
20
- contributor.sequence_number = tmp.text if tmp != nil
15
+ tmp = doc.search('//Contributor/sequencenumber')
16
+ contributor.sequence_number = tmp.inner_html unless tmp.inner_html.blank?
21
17
 
22
- tmp = REXML::XPath.first(element, '//Contributor/ContributorRole')
23
- contributor.role = tmp.text if tmp != nil
18
+ tmp = doc.search('//Contributor/contributorrole')
19
+ contributor.role = tmp.inner_html unless tmp.inner_html.blank?
24
20
 
25
- tmp = REXML::XPath.first(element, '//Contributor/PersonName')
26
- contributor.name = tmp.text if tmp != nil
21
+ tmp = doc.search('//Contributor/personname')
22
+ contributor.name = tmp.inner_html unless tmp.inner_html.blank?
27
23
 
28
- tmp = REXML::XPath.first(element, '//Contributor/PersonNameInverted')
29
- contributor.name_inverted = tmp.text if tmp != nil
24
+ tmp = doc.search('//Contributor/personnameinverted')
25
+ contributor.name_inverted = tmp.inner_html unless tmp.inner_html.blank?
30
26
 
31
27
  return contributor
32
-
33
28
  end
34
-
35
- # Return an xml element representing this contributor
36
- def to_element
37
- raise 'Contributor must have a sequence number to create an element' if @sequence_number.nil?
38
- raise 'Contributor must have a role to create an element' if @role.nil?
39
- raise '' if @name_inverted.nil? && @name.nil?
40
-
41
- contributor = REXML::Element.new('Contributor')
42
- contributor.add_element('SequenceNumber').text = self.sequence_number
43
- contributor.add_element('ContributorRole').text = self.role.to_xs
44
- contributor.add_element('PersonName').text = name.to_xs unless self.name.nil?
45
- contributor.add_element('PersonNameInverted').text = self.name_inverted.to_xs unless self.name_inverted.nil?
29
+
30
+ # Attempts to create a contributor object using the supplied xml element
31
+ # WARNING: deprecated method
32
+ def Contributor.load_from_element(element)
33
+ raise ArgumentError, 'load_from_element expects a REXML element object' unless element.class == REXML::Element
46
34
 
47
- return contributor
35
+ if REXML::XPath.first(element, '//Contributor').nil?
36
+ raise LoadError, 'supplied REXML document object does not appear contain a valid contributor fragment'
37
+ end
38
+
39
+ self.load_from_string(element.to_s)
48
40
  end
49
41
 
50
42
  # Return an XML string representing this contributor
51
43
  def to_s
52
- output = ''
44
+ raise 'Contributor must have a sequence number to create an element' if @sequence_number.nil?
45
+ raise 'Contributor must have a role to create an element' if @role.nil?
46
+ raise '' if @name_inverted.nil? && @name.nil?
47
+
48
+ builder = Builder::XmlMarkup.new(:indent => 2)
53
49
 
54
- # handle the REXML API change in 1.8.6.110. bah!
55
- if RUBY_VERSION >= "1.8.6" && RUBY_PATCHLEVEL >= 110
56
- formatter = REXML::Formatters::Pretty.new( 2 )
57
- formatter.write( to_element , output )
58
- else
59
- to_element.write(output, 0)
50
+ builder.Contributor do |c|
51
+ c.SequenceNumber self.sequence_number
52
+ c.ContributorRole self.role
53
+ c.PersonName self.name unless self.name.nil?
54
+ c.PersonNameInverted self.name_inverted unless self.name_inverted.nil?
60
55
  end
61
- return output
56
+ end
57
+
58
+ def to_element
59
+ REXML::Document.new(to_s).root
62
60
  end
63
61
  end
64
62
  end
@@ -1,4 +1,5 @@
1
1
  require 'rexml/document'
2
+ require 'rexml/formatters/transitive'
2
3
 
3
4
  module RBook
4
5
  module Onix
@@ -21,52 +22,65 @@ module RBook
21
22
  # Attempts to create a message object using thge supplied xml string
22
23
  def self.load_from_string(str)
23
24
  self.check_ruby_version
25
+
26
+ # attempt to pass the string through REXML, which will auto convert it
27
+ # to UTF-8 for us. If the file is invalid XML, then still attempt to use
28
+ # HPricot to parse it, as some data may be extractable
24
29
  begin
25
- doc = REXML::Document.new(str)
26
- self.load_from_xmldoc(doc)
27
- rescue REXML::ParseException => e
28
- raise LoadError, e.message
30
+ rexml_doc = REXML::Document.new(str)
31
+ rescue REXML::ParseException
32
+ # do nothin
29
33
  end
30
- end
31
34
 
32
- # Attempts to create a message object using thge supplied xml document
33
- def self.load_from_xmldoc(doc)
34
- self.check_ruby_version
35
- raise ArgumentError, 'load_from_xmldoc expects a REXML document object' unless doc.class == REXML::Document
36
- if REXML::XPath.first(doc, '/ONIXMessage/').nil? ||
37
- REXML::XPath.first(doc, '/ONIXMessage/Header').nil? ||
38
- REXML::XPath.first(doc, '/ONIXMessage/Product').nil?
39
- raise LoadError, 'supplied REXML document object does not appear to be a valid ONIX file'
35
+ if rexml_doc
36
+ doc = Hpricot::XML(rexml_doc.to_s)
37
+ else
38
+ doc = Hpricot::XML(str)
40
39
  end
41
40
 
42
41
  msg = Onix::Message.new
43
42
 
44
- tmpelement = REXML::XPath.first(doc, '/ONIXMessage/Header/FromCompany')
45
- msg.from_company = tmpelement.text if tmpelement != nil
43
+ tmp = doc.search('/ONIXMessage/Header/FromCompany')
44
+ msg.from_company = tmp.inner_html unless tmp.inner_html.blank?
46
45
 
47
- tmpelement = REXML::XPath.first(doc, '/ONIXMessage/Header/FromPerson')
48
- msg.from_person = tmpelement.text if tmpelement != nil
46
+ tmp = doc.search('/ONIXMessage/Header/FromPerson')
47
+ msg.from_person = tmp.inner_html unless tmp.inner_html.blank?
49
48
 
50
- tmpelement = REXML::XPath.first(doc, '/ONIXMessage/Header/FromEmail')
51
- msg.from_email = tmpelement.text if tmpelement != nil
49
+ tmp = doc.search('/ONIXMessage/Header/FromEmail')
50
+ msg.from_email = tmp.inner_html unless tmp.inner_html.blank?
52
51
 
53
- tmpelement = REXML::XPath.first(doc, '/ONIXMessage/Header/SentDate')
54
- tmpdate = Date.civil(tmpelement.text[0,4].to_i, tmpelement.text[4,2].to_i, tmpelement.text[6,2].to_i) if tmpelement != nil
55
- msg.sent_date = tmpdate if tmpdate != nil
52
+ tmp = doc.search('/ONIXMessage/Header/SentDate')
53
+ if !tmp.inner_html.blank? && tmp.inner_html.length == 8
54
+ tmpdate = Date.civil(tmp.inner_html[0,4].to_i, tmp.inner_html[4,2].to_i, tmp.inner_html[6,2].to_i)
55
+ msg.sent_date = tmpdate unless tmpdate.nil?
56
+ end
56
57
 
57
- tmpelement = REXML::XPath.first(doc, '/ONIXMessage/Header/ToCompany')
58
- msg.to_company = tmpelement.text if tmpelement != nil
58
+ tmp = doc.search('/ONIXMessage/Header/ToCompany')
59
+ msg.to_company = tmp.inner_html unless tmp.inner_html.blank?
59
60
 
60
- tmpelement = REXML::XPath.first(doc, '/ONIXMessage/Header/ToPerson')
61
- msg.to_person = tmpelement.text if tmpelement != nil
61
+ tmp = doc.search('/ONIXMessage/Header/ToPerson')
62
+ msg.to_person = tmp.inner_html unless tmp.inner_html.blank?
62
63
 
63
- products = REXML::XPath.each(doc, '/ONIXMessage/Product') { |product|
64
- msg.add_product(Onix::Product.load_from_element(product))
65
- }
64
+ doc.search('/ONIXMessage/Product').each do |product|
65
+ msg.add_product(Onix::Product.load_from_string("<Product>#{product}</Product>"))
66
+ end
66
67
 
67
68
  return msg
68
69
  end
69
70
 
71
+ # Attempts to create a message object using thge supplied xml document
72
+ def self.load_from_xmldoc(doc)
73
+ self.check_ruby_version
74
+ raise ArgumentError, 'load_from_xmldoc expects a REXML document object' unless doc.class == REXML::Document
75
+ if REXML::XPath.first(doc, '/ONIXMessage/').nil? ||
76
+ REXML::XPath.first(doc, '/ONIXMessage/Header').nil? ||
77
+ REXML::XPath.first(doc, '/ONIXMessage/Product').nil?
78
+ raise LoadError, 'supplied REXML document object does not appear to be a valid ONIX file'
79
+ end
80
+
81
+ self.load_from_string(doc.to_s)
82
+ end
83
+
70
84
  # Adds a new product to this message
71
85
  def add_product(product)
72
86
  raise ArgumentError, 'Argument must be a Onix::Product' if product.class != Onix::Product
@@ -75,51 +89,38 @@ module RBook
75
89
 
76
90
  # Returns an XML string representing this message
77
91
  def to_s
78
- doc = to_xml
79
- output = ''
80
- # stupid hack, because the ruby release mangers imported a broken REXML for this patchset
81
- if RUBY_VERSION == "1.8.6" && (RUBY_PATCHLEVEL == 110 || RUBY_PATCHLEVEL == 111)
82
- doc.write(output,-1)
83
- else
84
- doc.write(output,0)
85
- end
86
- return output
87
- end
88
-
89
- def header
90
92
  raise 'from_compnay must be set to create an onix message' if from_company == nil
91
93
  raise 'from_person must be set to create an onix message' if from_person == nil
92
-
93
- header = REXML::Element.new('Header')
94
- header.add_element('FromCompany').text = from_company.to_xs
95
- header.add_element('FromPerson').text = from_person.to_xs
96
- header.add_element('FromEmail').text = from_email.to_xs unless from_email.nil?
97
- header.add_element('MessageNote').text = message_note.to_xs unless message_note.nil?
98
- header.add_element('ToCompany').text = to_company.to_xs unless to_company.nil?
99
- header.add_element('ToPerson').text = to_person.to_xs unless to_person.nil?
100
-
101
- now = Time.now
102
- header.add_element('SentDate').text = "%.4d%.2d%.2d" % [ now.year, now.month, now.day ]
103
- return header
94
+ raise 'at least one product must be added create an onix message' if @products.size == 0
95
+
96
+ builder = Builder::XmlMarkup.new(:indent => 2)
97
+ builder.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
98
+ builder.declare! :DOCTYPE, :ONIXMessage, :SYSTEM, "http://www.editeur.org/onix/2.1/reference/onix-international.dtd"
99
+ builder.ONIXMessage do |msg|
100
+ msg << self.header
101
+ @products.each do |product|
102
+ msg << product.to_s
103
+ end
104
+ end
104
105
  end
105
-
106
- # Returns an XML document representing this message
107
- def to_xml
108
- raise 'There must be at least 1 product to burn' if @products.size < 1
109
106
 
110
- doc = REXML::Document.new
111
- doc << REXML::XMLDecl.new("1.0", "UTF-8")
112
- doc << REXML::DocType.new('ONIXMessage', "SYSTEM \"#{ONIX_DTD_URL}\"")
113
- msg = doc.add_element('ONIXMessage')
114
- msg.add_element(header)
107
+ def to_xml
108
+ REXML::Document.new(to_s)
109
+ end
115
110
 
116
- @products.each do |product|
117
- prod = msg.add_element(product.to_element)
111
+ def header
112
+ builder = Builder::XmlMarkup.new(:indent => 2)
113
+ builder.Header do |h|
114
+ h.FromCompany @from_company if @from_company
115
+ h.FromPerson @from_person if @from_person
116
+ h.FromEmail @from_email if @from_email
117
+ h.message_note @message_note if @message_note
118
+ h.SentDate @sent_date if @sent_date
119
+ h.ToPerson @to_person if @to_person
120
+ h.ToCompany @to_company if @to_company
118
121
  end
119
-
120
- return doc
121
122
  end
122
-
123
+
123
124
  end
124
125
 
125
126
  end
@@ -1,4 +1,6 @@
1
1
  require 'rexml/document'
2
+ require 'hpricot'
3
+ require 'active_support'
2
4
 
3
5
  module RBook
4
6
  module Onix
@@ -16,76 +18,88 @@ module RBook
16
18
  @sales_restrictions = []
17
19
  end
18
20
 
19
- # Attempts to create a product object using the supplied xml element
20
- def Product.load_from_element(element)
21
- raise ArgumentError, 'load_from_element expects a REXML element object' unless element.class == REXML::Element
22
-
23
- if REXML::XPath.first(element, '//Product').nil?
24
- raise LoadError, 'supplied REXML document object does not appear contain a valid product fragment'
25
- end
26
-
21
+ # Attempts to create a product object using the supplied xml fragment
22
+ def Product.load_from_string(str)
23
+ doc = Hpricot::XML(str)
24
+
27
25
  product = Product.new
28
26
 
29
- tmp = REXML::XPath.first(element, '//Product/RecordReference')
30
- product.record_reference = tmp.text if tmp != nil
27
+ tmp = doc.search("//Product/recordreference")
28
+ product.record_reference = tmp.inner_html unless tmp.inner_html.blank?
31
29
 
32
- tmp = REXML::XPath.first(element, '//Product/NotificationType')
33
- product.notification_type = tmp.text if tmp != nil
30
+ tmp = doc.search("//Product/notificationtype")
31
+ product.notification_type = tmp.inner_html unless tmp.inner_html.blank?
34
32
 
35
- tmp = REXML::XPath.first(element, '//Product/ProductIdentifier/IDValue')
36
- product.product_identifier = tmp.text if tmp != nil
33
+ tmp = doc.search("//Product/productidentifier/idvalue").first
34
+ product.product_identifier = tmp.inner_html unless tmp.inner_html.blank?
37
35
 
38
- tmp = REXML::XPath.first(element, '//Product/ProductForm')
39
- product.form = tmp.text if tmp != nil
36
+ tmp = doc.search("//Product/productform")
37
+ product.form = tmp.inner_html unless tmp.inner_html.blank?
40
38
 
41
- tmp = REXML::XPath.first(element, '//Product/NumberOfPages')
42
- product.pages = tmp.text if tmp != nil
39
+ tmp = doc.search("//Product/numberofpages")
40
+ product.pages = tmp.inner_html unless tmp.inner_html.blank?
43
41
 
44
- tmp = REXML::XPath.first(element, '//Product/BICMainSubject')
45
- product.bicmainsubject = tmp.text if tmp != nil
42
+ tmp = doc.search("//Product/bicmainsubject")
43
+ product.bicmainsubject = tmp.inner_html unless tmp.inner_html.blank?
46
44
 
47
- REXML::XPath.each(element, '//Product/MediaFile') { |mediafile|
48
- type = REXML::XPath.first(element, '//Product/MediaFile/MediaFileTypeCode')
45
+ doc.search("//Product/mediafile").each do |mediafile|
46
+ type = doc.search('//Product/mediafile/mediafiletypecode')
49
47
  # cover image
50
- if type.text.eql?("04")
51
- url = REXML::XPath.first(element, '//Product/MediaFile/MediaFileLink')
52
- product.cover_image = url.text unless url.nil?
48
+ if type.inner_html.eql?("04")
49
+ url = doc.search('//Product/mediafile/mediafilelink')
50
+ product.cover_image = url.inner_html unless url.inner_html.blank?
53
51
  end
54
- }
52
+ end
55
53
 
56
- tmp = REXML::XPath.first(element, '//Product/DistinctiveTitle')
57
- if !tmp.nil?
58
- product.title = tmp.text
59
- tmp = REXML::XPath.first(element, '//Product/Subtitle') if !tmp.nil?
60
- product.subtitle = tmp.text
54
+ tmp = doc.search("//Product/distinctivetitle")
55
+ if !tmp.inner_html.blank?
56
+ product.title = tmp.inner_html
57
+ tmp = doc.search('//Product/subtitle')
58
+ product.subtitle = tmp.inner_html unless tmp.inner_html.blank?
61
59
  else
62
- tmp = REXML::XPath.first(element, '//Product/Title/TitleText')
63
- product.title = tmp.text unless tmp.nil?
64
- tmp = REXML::XPath.first(element, '//Product/Title/Subtitle')
65
- product.subtitle = tmp.text unless tmp.nil?
60
+ tmp = doc.search('//Product/title/titletext')
61
+ product.title = tmp.inner_html unless tmp.inner_html.blank?
62
+ tmp = doc.search('//Product/title/subtitle')
63
+ product.subtitle = tmp.inner_html unless tmp.inner_html.blank?
66
64
  end
67
65
 
68
- tmp = REXML::XPath.first(element, '//Product/EditionNumber')
69
- product.edition = tmp.text if tmp != nil
66
+ tmp = doc.search('//Product/editionnumber')
67
+ product.edition = tmp.inner_html unless tmp.inner_html.blank?
70
68
 
71
- tmp = REXML::XPath.first(element, '//Product/PublicationDate')
72
- tmpdate = Date.civil(tmp.text[0,4].to_i, tmp.text[4,2].to_i, tmp.text[6,2].to_i) if tmp != nil
73
- product.publication_date = tmpdate if tmpdate != nil
69
+ tmp = doc.search('//Product/publicationdate')
74
70
 
75
- tmp = REXML::XPath.first(element, '//Product/Publisher/PublisherName')
76
- product.publisher = tmp.text if tmp != nil
71
+ if !tmp.inner_html.blank? && tmp.inner_html.length == 8
72
+ tmpdate = Date.civil(tmp.inner_html[0,4].to_i, tmp.inner_html[4,2].to_i, tmp.inner_html[6,2].to_i)
73
+ product.publication_date = tmpdate unless tmpdate.nil?
74
+ end
77
75
 
78
- REXML::XPath.each(element, '//Product/Contributor') { |contributor|
79
- product.add_contributor(Onix::Contributor.load_from_element(contributor))
80
- }
76
+ tmp = doc.search('//Product/publisher/publishername')
77
+ product.publisher = tmp.inner_html unless tmp.inner_html.blank?
81
78
 
82
- REXML::XPath.each(element, '//Product/SupplyDetail') { |detail|
83
- product.add_supply_detail(Onix::SupplyDetail.load_from_element(detail))
84
- }
79
+ doc.search('//Product/contributor').each do |contributor|
80
+ product.add_contributor(Onix::Contributor.load_from_string("<contributor>#{contributor}</contributor>"))
81
+ end
82
+
83
+ doc.search('//Product/supplydetail').each do |detail|
84
+ product.add_supply_detail(Onix::SupplyDetail.load_from_string("<SupplyDetail>#{detail}</SupplyDetail>"
85
+ ))
86
+ end
85
87
 
86
88
  return product
87
89
  end
88
90
 
91
+ # Attempts to create a product object using the supplied xml element
92
+ # WARNING: Deprecated method
93
+ def Product.load_from_element(element)
94
+ raise ArgumentError, 'load_from_element expects a REXML element object' unless element.class == REXML::Element
95
+
96
+ if REXML::XPath.first(element, '//Product').nil?
97
+ raise LoadError, 'supplied REXML document object does not appear contain a valid product fragment'
98
+ end
99
+
100
+ self.load_from_string(element.to_s)
101
+ end
102
+
89
103
  # Add a new contributor to this product
90
104
  def add_contributor(newAuthor)
91
105
  raise ArgumentError, 'Argument to add_contributor must be an ONIX::Contributor' if newAuthor.class != Contributor
@@ -104,110 +118,100 @@ module RBook
104
118
  @supply_details << detail
105
119
  end
106
120
 
107
- # Return an xml element representing this contributor
108
- def to_element
121
+ # return an XML string representing this contributor
122
+ def to_s
109
123
  raise 'product_identifier must be set' if self.product_identifier.nil?
110
124
 
111
- product = REXML::Element.new('Product')
125
+ builder = Builder::XmlMarkup.new(:indent => 2)
126
+ builder.Product do |p|
112
127
 
113
- if self.record_reference
114
- product.add_element('RecordReference').text = self.record_reference
128
+ if @record_reference
129
+ p.RecordReference self.record_reference
115
130
  else
116
- product.add_element('RecordReference').text = self.product_identifier
131
+ p.RecordReference self.product_identifier
117
132
  end
118
133
 
119
- product.add_element('NotificationType').text = '03'
134
+ p.NotificationType '03'
120
135
 
121
- product_identifier = REXML::Element.new('ProductIdentifier')
122
- if ISBN::valid_isbn13?(self.product_identifier)
123
- product_identifier.add_element('ProductIDType').text = '03'
124
- elsif ISBN::valid_isbn10?(self.product_identifier)
125
- product_identifier.add_element('ProductIDType').text = '02'
126
- else
127
- product_identifier.add_element('ProductIDType').text = '01'
136
+ p.ProductIdentifier do |id|
137
+ if ISBN::valid_isbn13?(self.product_identifier)
138
+ p.ProductIDType '03'
139
+ elsif ISBN::valid_isbn10?(self.product_identifier)
140
+ p.ProductIDType '02'
141
+ else
142
+ p.ProductIDType '01'
143
+ end
144
+ p.IDValue self.product_identifier
128
145
  end
129
- product_identifier.add_element('IDValue').text = self.product_identifier.to_xs
130
- product.add_element(product_identifier)
131
146
 
132
- if form != nil
133
- product.add_element('ProductForm').text = self.form.to_xs
134
- end
147
+ p.ProductForm self.form
135
148
 
136
- product.add_element('NoSeries')
149
+ p.NoSeries
137
150
 
138
151
  title = REXML::Element.new('Title')
139
- title.add_element('TitleType').text = '01'
140
- title.add_element('TitleText').text = self.title.to_xs unless self.title.nil?
141
- title.add_element('Subtitle').text = self.subtitle.to_xs unless self.subtitle.nil?
142
- product.add_element(title)
152
+ p.Title do |t|
153
+ t.TitleType '01'
154
+ t.TitleText self.title unless self.title.nil?
155
+ t.Subtitle self.subtitle unless self.subtitle.nil?
156
+ end
143
157
 
144
158
  if website != nil
145
- product_website = REXML::Element.new('Website')
146
- product_website.add_element('WebsiteLink').text = self.website.to_xs
147
- product.add_element(product_website)
159
+ p.Website do |w|
160
+ w.WebsiteLink self.website
161
+ end
148
162
  end
149
163
 
150
164
  unless cover_image.nil?
151
- product_mf = REXML::Element.new('MediaFile')
152
- product_mf.add_element('MediaFileTypeCode').text = "04"
153
- product_mf.add_element('MediaFileLinkTypeCode').text = "01"
154
- product_mf.add_element('MediaFileLink').text = self.cover_image.to_xs
155
- product.add_element(product_mf)
165
+ p.MediaFile do |m|
166
+ m.MediaFileTypeCode "04"
167
+ m.MediaFileLinkTypeCode "01"
168
+ m.MediaFileLink self.cover_image
169
+ end
156
170
  end
157
171
 
158
172
  contributors.each do |contributor|
159
- product.add_element(contributor.to_element)
173
+ p << contributor.to_s
160
174
  end
161
175
 
162
- product.add_element('NoEdition')
176
+ p.NoEdition
163
177
 
164
- product.add_element('NumberOfPages').text = self.pages.to_s unless self.pages.nil?
178
+ p.NumberOfPages self.pages.to_s unless self.pages.nil?
165
179
 
166
180
  if bicmainsubject != nil
167
- product.add_element('BICMainSubject').text = self.bicmainsubject.to_xs
181
+ p.BICMainSubject = self.bicmainsubject
168
182
  end
169
183
 
170
184
  if description != nil
171
- other_text = REXML::Element.new('OtherText')
172
- other_text.add_element('TextTypeCode').text = '01'
173
- other_text.add_element('Text').text = self.description.to_xs
174
- product.add_element(other_text)
185
+ p.OtherText do |t|
186
+ t.TextTypeCode '01'
187
+ t.Text self.description
188
+ end
175
189
  end
176
190
 
177
191
  if publisher != nil
178
- publisher = REXML::Element.new('Publisher')
179
- publisher.add_element('PublishingRole').text = '01'
180
- publisher.add_element('PublisherName').text = self.publisher.to_xs
181
- product.add_element(publisher)
192
+ p.Publisher do |pub|
193
+ pub.PublishingRole '01'
194
+ pub.PublisherName self.publisher
195
+ end
182
196
  end
183
197
 
184
198
  supply_details.each do |detail|
185
- product.add_element(detail.to_element)
199
+ p << detail.to_s
186
200
  end
187
201
 
188
202
  sales_restrictions.each do |restriction|
189
- product.add_element(restriction.to_element)
203
+ p << restriction.to_s
190
204
  end
191
205
 
192
206
  if publication_date != nil
193
- product.add_element('PublicationDate').text = "%.4d%.2d%.2d" % [ publication_date.year, publication_date.month, publication_date.day ]
207
+ p.PublicationDate("%.4d%.2d%.2d" % [ publication_date.year, publication_date.month, publication_date.day ])
194
208
  end
195
209
 
196
- return product
210
+ end
197
211
  end
198
212
 
199
- # return an XML string representing this contributor
200
- def to_s
201
- output = ''
202
-
203
- # handle the REXML API change in 1.8.6.110. bah!
204
- if RUBY_VERSION >= "1.8.6" && RUBY_PATCHLEVEL >= 110
205
- formatter = REXML::Formatters::Pretty.new( 2 )
206
- formatter.write( to_element , output )
207
- else
208
- to_element.write(output, 0)
209
- end
210
- return output
213
+ def to_element
214
+ REXML::Document.new(to_s).root
211
215
  end
212
216
  end
213
217