rbook-onix 0.5.2 → 0.6.0

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