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,70 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + "/../")
2
+
3
+ require 'rbook/onix/contributor'
4
+ require 'rbook/onix/lists'
5
+ require 'rbook/onix/message'
6
+ require 'rbook/onix/product'
7
+ require 'rbook/onix/sales_restriction'
8
+ require 'rbook/onix/supply_detail'
9
+ require 'rbook/onix/xchar'
10
+ require 'rbook/onix/stream_reader'
11
+ require 'rbook/onix/stream_writer'
12
+ require 'rbook/isbn'
13
+
14
+ module RBook
15
+
16
+ # Ruby classes for working with ONIX files. Currently only supports a very limited
17
+ # subset of the ONIX specification, but the core attributes are there. More will be
18
+ # added over time.
19
+ #
20
+ # = Usage
21
+ #
22
+ # == Loading from an REXML document
23
+ #
24
+ # require 'rubygems'
25
+ # require 'rbook/onix'
26
+ # file = File.open("./onix_message.xml", "r")
27
+ # doc = REXML::Document.new(file)
28
+ # msg = Onix::Message.load_from_xmldoc(doc)
29
+ #
30
+ # puts msg.from_company
31
+ #
32
+ # == Creation
33
+ #
34
+ # require 'rubygems'
35
+ # require 'rbook/onix'
36
+ # msg = RBook::Onix::Message.new
37
+ # msg.from_company = 'XYZ Books'
38
+ # msg.from_name = 'Joe Blogs'
39
+ # msg.message_note = "A sample ONIX file"
40
+ #
41
+ # product = RBook::Onix::Product.new
42
+ # product.product_identifier = "020161622X"
43
+ # product.title = "Pragmatic Programmer"
44
+ # product.subtitle = "From Journeyman to Master"
45
+ # product.form = "BB"
46
+ # product.description = "A book about programming and stuff"
47
+ #
48
+ # contributor1 = RBook::Onix::Contributor.new
49
+ # contributor1.name_inverted = "Hunt, Andrew"
50
+ # contributor1.role = "A01"
51
+ # contributor1.sequence = "01"
52
+ # contributor2 = RBook::Onix::Contributor.new
53
+ # contributor2.name_inverted = "Thomas, David"
54
+ # contributor2.role = "A01"
55
+ # contributor2.sequence = "02"
56
+ # product.add_contributor(contributor1)
57
+ # product.add_contributor(contributor2)
58
+ #
59
+ # supply_detail = RBook::Onix::SupplyDetail.new
60
+ # supply_detail.supplier_name = "Rainbow Book Agencies"
61
+ # supply_detail.price = BigDecimal.new(29.95)
62
+ # supply_detail.availability = "CS"
63
+ # product.add_supply_detail(supply_detail)
64
+ #
65
+ # msg.add_product(product)
66
+ # puts msg.to_s
67
+ module Onix
68
+ class InvalidRubyVersionError < RuntimeError;end;
69
+ end
70
+ end
@@ -0,0 +1,59 @@
1
+ require 'rexml/document'
2
+
3
+ module RBook
4
+ module Onix
5
+
6
+ class Contributor
7
+ attr_accessor :sequence_number, :role, :name, :name_inverted
8
+
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
+
17
+ contributor = Contributor.new
18
+
19
+ tmp = REXML::XPath.first(element, '//Contributor/SequenceNumber')
20
+ contributor.sequence_number = tmp.text if tmp != nil
21
+
22
+ tmp = REXML::XPath.first(element, '//Contributor/ContributorRole')
23
+ contributor.role = tmp.text if tmp != nil
24
+
25
+ tmp = REXML::XPath.first(element, '//Contributor/PersonName')
26
+ contributor.name = tmp.text if tmp != nil
27
+
28
+ tmp = REXML::XPath.first(element, '//Contributor/PersonNameInverted')
29
+ contributor.name_inverted = tmp.text if tmp != nil
30
+
31
+ return contributor
32
+
33
+ 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?
46
+
47
+ return contributor
48
+ end
49
+
50
+ # Return an XML string representing this contributor
51
+ def to_s
52
+ tmp = to_element
53
+ output = ''
54
+ tmp.write(output, 0)
55
+ return output
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,2 @@
1
+ require File.dirname(__FILE__) + '/lists/contributor_role'
2
+ require File.dirname(__FILE__) + '/lists/product_form'
@@ -0,0 +1,10 @@
1
+ module RBook
2
+ module Onix
3
+ module Lists
4
+ CONTRIBUTOR_ROLES = [
5
+ ['Author','A01'],
6
+ ['Illustrator','A02']
7
+ ].freeze
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,100 @@
1
+ module RBook
2
+ module Onix
3
+ module Lists
4
+
5
+ FORM_CODES = [
6
+ ['Undefined', '00'],
7
+ ['Audio', 'AA'],
8
+ ['Audio - Cassette', 'AB'],
9
+ ['Audio - CD', 'AC'],
10
+ ['Audio - DAT', 'AD'],
11
+ ['Audio - Disk', 'AE'],
12
+ ['Audio - Tape', 'AF'],
13
+ ['Audio - Other', 'AZ'],
14
+ ['Book', 'BA'],
15
+ ['Book - Hardback', 'BB'],
16
+ ['Book - Paperback', 'BC'],
17
+ ['Book - Loose-leaf', 'BD'],
18
+ ['Book - Spiral Bound', 'BE'],
19
+ ['Book - Pamphlet', 'BF'],
20
+ ['Book - Leather / Fine Binding', 'BG'],
21
+ ['Book - Board', 'BH'],
22
+ ['Book - Rag', 'BI'],
23
+ ['Book - Bath', 'BJ'],
24
+ ['Book - Other', 'BZ'],
25
+ ['Cartographic - Sheet Map', 'CA'],
26
+ ['Cartographic - Sheet Map, Folded', 'CB'],
27
+ ['Cartographic - Sheet Map, Flat', 'CC'],
28
+ ['Cartographic - Sheet Map, Rolled', 'CD'],
29
+ ['Cartographic - Globe', 'CE'],
30
+ ['Cartographic - Other', 'CZ'],
31
+ ['Digital', 'DA'],
32
+ ['Digital - CDROM', 'DB'],
33
+ ['Digital - CD-Interactive', 'DC'],
34
+ ['Digital - DVD', 'DD'],
35
+ ['Digital - Game Cartridge', 'DE'],
36
+ ['Digital - Diskette', 'DF'],
37
+ ['Digital - Electronic Book Text', 'DG'],
38
+ ['Digital - Online File', 'DH'],
39
+ ['Digital - Other', 'DZ'],
40
+ ['Film or Transparency', 'FA'],
41
+ ['Film', 'FB'],
42
+ ['Film - Slides', 'FC'],
43
+ ['Film - OHP Transparencies', 'FD'],
44
+ ['Film - Other ', 'FZ'],
45
+ ['Microform', 'MA'],
46
+ ['Microform - Microfiche', 'MB'],
47
+ ['Microform - Microfilm', 'MC'],
48
+ ['Microform - Other', 'MZ'],
49
+ ['Print - Misc', 'PA'],
50
+ ['Print - Address Book', 'PB'],
51
+ ['Print - Calendar', 'PC'],
52
+ ['Print - Cards', 'PD'],
53
+ ['Print - Copymasters', 'PE'],
54
+ ['Print - Diary', 'PF'],
55
+ ['Print - Frieze', 'PG'],
56
+ ['Print - Kit', 'PH'],
57
+ ['Print - Sheet Music', 'PI'],
58
+ ['Print - Postcard Book or Pack', 'PJ'],
59
+ ['Print - Poster', 'PK'],
60
+ ['Print - Record Book', 'PL'],
61
+ ['Print - Wallet', 'PM'],
62
+ ['Print - Pictures or Photographs', 'PN'],
63
+ ['Print - Wallchart', 'PO'],
64
+ ['Print - Other', 'PZ'],
65
+ ['Video', 'VA'],
66
+ ['Video - VHS, PAL', 'VB'],
67
+ ['Video - VHA, NTSC', 'VC'],
68
+ ['Video - Betamax, PAL', 'VD'],
69
+ ['Video - Betamax, NTSC', 'VE'],
70
+ ['Video - disk', 'VF'],
71
+ ['Video - VHS, SECAM', 'VG'],
72
+ ['Video - Betamax, SECAM', 'VH'],
73
+ ['Video - Other Format','VZ'],
74
+ ['Multiple - Mixed Media Product', 'WW'],
75
+ ['Misc - Quantity Pack', 'WX'],
76
+ ['Misc - Trade-only Material', 'XA'],
77
+ ['Misc - Dumpbim - Empty', 'XB'],
78
+ ['Misc - Dumpbin - Filled', 'XC'],
79
+ ['Misc - Counterpack - Empty', 'XD'],
80
+ ['Misc - Counterpack - Filled', 'XE'],
81
+ ['Misc - Poster', 'XF'],
82
+ ['Misc - Shelf Strip', 'XG'],
83
+ ['Misc - Window Piece', 'XH'],
84
+ ['Misc - Streamer', 'XI'],
85
+ ['Misc - Spinner', 'XJ'],
86
+ ['Misc - Large Book Display', 'XK'],
87
+ ['Misc - Shrink Wrapped Pack', 'XL'],
88
+ ['Misc - Other Point of Sale', 'XZ'],
89
+ ['General Merchandise', 'ZA'],
90
+ ['Misc - Doll', 'ZB'],
91
+ ['Misc - Soft Toy', 'ZC'],
92
+ ['Misc - Toy', 'ZD'],
93
+ ['Misc - Game', 'ZE'],
94
+ ['Misc - T-shirt', 'ZF'],
95
+ ['Misc - Other Merchandise', 'ZZ'],
96
+ ].freeze
97
+
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,121 @@
1
+ require 'rexml/document'
2
+
3
+ module RBook
4
+ module Onix
5
+
6
+ class Message
7
+ attr_reader :products
8
+ attr_accessor :from_company, :from_person, :from_email, :message_note, :sent_date, :to_person, :to_company
9
+
10
+ VERSION = 0.1
11
+ ONIX_DTD_URL = "http://www.editeur.org/onix/2.1/reference/onix-international.dtd"
12
+
13
+ def initialize
14
+ @products = []
15
+ end
16
+
17
+ def self.check_ruby_version
18
+ raise InvalidRubyVersionError, 'Ruby 1.8.6 or higher is required to use this class' unless RUBY_VERSION >= "1.8.6"
19
+ end
20
+
21
+ # Attempts to create a message object using thge supplied xml string
22
+ def self.load_from_string(str)
23
+ self.check_ruby_version
24
+ begin
25
+ doc = REXML::Document.new(str)
26
+ self.load_from_xmldoc(doc)
27
+ rescue REXML::ParseException => e
28
+ raise LoadError, e.message
29
+ end
30
+ end
31
+
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'
40
+ end
41
+
42
+ msg = Onix::Message.new
43
+
44
+ tmpelement = REXML::XPath.first(doc, '/ONIXMessage/Header/FromCompany')
45
+ msg.from_company = tmpelement.text if tmpelement != nil
46
+
47
+ tmpelement = REXML::XPath.first(doc, '/ONIXMessage/Header/FromPerson')
48
+ msg.from_person = tmpelement.text if tmpelement != nil
49
+
50
+ tmpelement = REXML::XPath.first(doc, '/ONIXMessage/Header/FromEmail')
51
+ msg.from_email = tmpelement.text if tmpelement != nil
52
+
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
56
+
57
+ tmpelement = REXML::XPath.first(doc, '/ONIXMessage/Header/ToCompany')
58
+ msg.to_company = tmpelement.text if tmpelement != nil
59
+
60
+ tmpelement = REXML::XPath.first(doc, '/ONIXMessage/Header/ToPerson')
61
+ msg.to_person = tmpelement.text if tmpelement != nil
62
+
63
+ products = REXML::XPath.each(doc, '/ONIXMessage/Product') { |product|
64
+ msg.add_product(Onix::Product.load_from_element(product))
65
+ }
66
+
67
+ return msg
68
+ end
69
+
70
+ # Adds a new product to this message
71
+ def add_product(product)
72
+ raise ArgumentError, 'Argument must be a Onix::Product' if product.class != Onix::Product
73
+ @products << product
74
+ end
75
+
76
+ # Returns an XML string representing this message
77
+ def to_s
78
+ doc = to_xml
79
+ output = ''
80
+ doc.write(output, 0)
81
+ return output
82
+ end
83
+
84
+ def header
85
+ raise 'from_compnay must be set to create an onix message' if from_company == nil
86
+ raise 'from_person must be set to create an onix message' if from_person == nil
87
+
88
+ header = REXML::Element.new('Header')
89
+ header.add_element('FromCompany').text = from_company.to_xs
90
+ header.add_element('FromPerson').text = from_person.to_xs
91
+ header.add_element('FromEmail').text = from_email.to_xs unless from_email.nil?
92
+ header.add_element('MessageNote').text = message_note.to_xs unless message_note.nil?
93
+ header.add_element('ToCompany').text = to_company.to_xs unless to_company.nil?
94
+ header.add_element('ToPerson').text = to_person.to_xs unless to_person.nil?
95
+
96
+ now = Time.now
97
+ header.add_element('SentDate').text = "%.4d%.2d%.2d" % [ now.year, now.month, now.day ]
98
+ return header
99
+ end
100
+
101
+ # Returns an XML document representing this message
102
+ def to_xml
103
+ raise 'There must be at least 1 product to burn' if @products.size < 1
104
+
105
+ doc = REXML::Document.new
106
+ doc << REXML::XMLDecl.new
107
+ doc << REXML::DocType.new('ONIXMessage', "SYSTEM \"#{ONIX_DTD_URL}\"")
108
+ msg = doc.add_element('ONIXMessage')
109
+ msg.add_element(header)
110
+
111
+ @products.each do |product|
112
+ prod = msg.add_element(product.to_element)
113
+ end
114
+
115
+ return doc
116
+ end
117
+
118
+ end
119
+
120
+ end
121
+ end
@@ -0,0 +1,208 @@
1
+ require 'rexml/document'
2
+
3
+ module RBook
4
+ module Onix
5
+ class Product
6
+
7
+ attr_accessor :record_reference, :notification_type, :product_identifier
8
+ attr_accessor :title, :subtitle, :pages, :publication_date, :price
9
+ attr_accessor :description, :form, :bicmainsubject, :publisher, :supplier
10
+ attr_accessor :website, :edition, :cover_image
11
+ attr_reader :contributors, :sales_restrictions, :supply_details
12
+
13
+ def initialize
14
+ @contributors = []
15
+ @supply_details = []
16
+ @sales_restrictions = []
17
+ end
18
+
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
+
27
+ product = Product.new
28
+
29
+ tmp = REXML::XPath.first(element, '//Product/RecordReference')
30
+ product.record_reference = tmp.text if tmp != nil
31
+
32
+ tmp = REXML::XPath.first(element, '//Product/NotificationType')
33
+ product.notification_type = tmp.text if tmp != nil
34
+
35
+ tmp = REXML::XPath.first(element, '//Product/ProductIdentifier/IDValue')
36
+ product.product_identifier = tmp.text if tmp != nil
37
+
38
+ tmp = REXML::XPath.first(element, '//Product/ProductForm')
39
+ product.form = tmp.text if tmp != nil
40
+
41
+ tmp = REXML::XPath.first(element, '//Product/NumberOfPages')
42
+ product.pages = tmp.text if tmp != nil
43
+
44
+ tmp = REXML::XPath.first(element, '//Product/BICMainSubject')
45
+ product.bicmainsubject = tmp.text if tmp != nil
46
+
47
+ REXML::XPath.each(element, '//Product/MediaFile') { |mediafile|
48
+ type = REXML::XPath.first(element, '//Product/MediaFile/MediaFileTypeCode')
49
+ # 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?
53
+ end
54
+ }
55
+
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
61
+ 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?
66
+ end
67
+
68
+ tmp = REXML::XPath.first(element, '//Product/EditionNumber')
69
+ product.edition = tmp.text if tmp != nil
70
+
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
74
+
75
+ tmp = REXML::XPath.first(element, '//Product/Publisher/PublisherName')
76
+ product.publisher = tmp.text if tmp != nil
77
+
78
+ REXML::XPath.each(element, '//Product/Contributor') { |contributor|
79
+ product.add_contributor(Onix::Contributor.load_from_element(contributor))
80
+ }
81
+
82
+ REXML::XPath.each(element, '//Product/SupplyDetail') { |detail|
83
+ product.add_supply_detail(Onix::SupplyDetail.load_from_element(detail))
84
+ }
85
+
86
+ return product
87
+ end
88
+
89
+ # Add a new contributor to this product
90
+ def add_contributor(newAuthor)
91
+ raise ArgumentError, 'Argument to add_contributor must be an ONIX::Contributor' if newAuthor.class != Contributor
92
+ @contributors << newAuthor
93
+ end
94
+
95
+ # Add a new contributor to this product
96
+ def add_sales_restriction(restriction)
97
+ raise ArgumentError, 'Argument to add_sales_restriction must be an ONIX::SalesRestriction' if restriction.class != SalesRestriction
98
+ @sales_restrictions << restriction
99
+ end
100
+
101
+ # Add a new contributor to this product
102
+ def add_supply_detail(detail)
103
+ raise ArgumentError, 'Argument to add_supply_detail must be an ONIX::SupplyDetail' if detail.class != SupplyDetail
104
+ @supply_details << detail
105
+ end
106
+
107
+ # Return an xml element representing this contributor
108
+ def to_element
109
+ raise 'product_identifier must be set' if self.product_identifier.nil?
110
+
111
+ product = REXML::Element.new('Product')
112
+
113
+ if self.record_reference
114
+ product.add_element('RecordReference').text = self.record_reference
115
+ else
116
+ product.add_element('RecordReference').text = self.product_identifier
117
+ end
118
+
119
+ product.add_element('NotificationType').text = '03'
120
+
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'
128
+ end
129
+ product_identifier.add_element('IDValue').text = self.product_identifier.to_xs
130
+ product.add_element(product_identifier)
131
+
132
+ if form != nil
133
+ product.add_element('ProductForm').text = self.form.to_xs
134
+ end
135
+
136
+ product.add_element('NoSeries')
137
+
138
+ 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)
143
+
144
+ 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)
148
+ end
149
+
150
+ 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)
156
+ end
157
+
158
+ contributors.each do |contributor|
159
+ product.add_element(contributor.to_element)
160
+ end
161
+
162
+ product.add_element('NoEdition')
163
+
164
+ product.add_element('NumberOfPages').text = self.pages.to_s unless self.pages.nil?
165
+
166
+ if bicmainsubject != nil
167
+ product.add_element('BICMainSubject').text = self.bicmainsubject.to_xs
168
+ end
169
+
170
+ 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)
175
+ end
176
+
177
+ 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)
182
+ end
183
+
184
+ supply_details.each do |detail|
185
+ product.add_element(detail.to_element)
186
+ end
187
+
188
+ sales_restrictions.each do |restriction|
189
+ product.add_element(restriction.to_element)
190
+ end
191
+
192
+ if publication_date != nil
193
+ product.add_element('PublicationDate').text = "%.4d%.2d%.2d" % [ publication_date.year, publication_date.month, publication_date.day ]
194
+ end
195
+
196
+ return product
197
+ end
198
+
199
+ # return an XML string representing this contributor
200
+ def to_s
201
+ output = ''
202
+ to_element.write(output, 0)
203
+ return output
204
+ end
205
+ end
206
+
207
+ end
208
+ end