rbook 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/COPYING +340 -0
  2. data/LICENSE +13 -0
  3. data/README +16 -0
  4. data/Rakefile +206 -0
  5. data/examples/titlepage.rb +14 -0
  6. data/examples/www/find_all.rb +23 -0
  7. data/examples/www/find_cover_from_amazon.rb +12 -0
  8. data/examples/www/find_url_from_rainbow.rb +12 -0
  9. data/examples/www/list.rb +13 -0
  10. data/lib/rbook/bisac.rb +175 -0
  11. data/lib/rbook/errors.rb +7 -0
  12. data/lib/rbook/isbn.rb +249 -0
  13. data/lib/rbook/onix.rb +68 -0
  14. data/lib/rbook/onix/contributor.rb +60 -0
  15. data/lib/rbook/onix/lists.rb +2 -0
  16. data/lib/rbook/onix/lists/contributor_role.rb +10 -0
  17. data/lib/rbook/onix/lists/product_form.rb +100 -0
  18. data/lib/rbook/onix/message.rb +101 -0
  19. data/lib/rbook/onix/product.rb +188 -0
  20. data/lib/rbook/onix/sales_restriction.rb +51 -0
  21. data/lib/rbook/onix/supply_detail.rb +68 -0
  22. data/lib/rbook/onix/xchar.rb +98 -0
  23. data/lib/rbook/titlepage.rb +96 -0
  24. data/lib/rbook/titlepage/TitleQueryClient.rb +62 -0
  25. data/lib/rbook/titlepage/titlepage_driver.rb +134 -0
  26. data/lib/rbook/titlepage/titlepage_utils.rb +374 -0
  27. data/lib/rbook/www.rb +172 -0
  28. data/lib/rbook/www/aau_scraper.rb +76 -0
  29. data/lib/rbook/www/amazon_uk_scraper.rb +44 -0
  30. data/lib/rbook/www/base.rb +87 -0
  31. data/lib/rbook/www/harper_au_scraper.rb +56 -0
  32. data/lib/rbook/www/harper_us_scraper.rb +55 -0
  33. data/lib/rbook/www/hha_scraper.rb +50 -0
  34. data/lib/rbook/www/macmillan_scraper.rb +62 -0
  35. data/lib/rbook/www/orbis_scraper.rb +48 -0
  36. data/lib/rbook/www/oup_scraper.rb +64 -0
  37. data/lib/rbook/www/paulist_scraper.rb +53 -0
  38. data/lib/rbook/www/pearson_au_scraper.rb +52 -0
  39. data/lib/rbook/www/penguin_scraper.rb +45 -0
  40. data/lib/rbook/www/random_au_scraper.rb +90 -0
  41. data/lib/rbook/www/random_us_scraper.rb +59 -0
  42. data/lib/rbook/www/sas_scraper.rb +54 -0
  43. data/lib/rbook/www/unireps_scraper.rb +58 -0
  44. data/lib/rbook/www/wiley_us_scraper.rb +54 -0
  45. data/test/data/abingdon.xml +38931 -0
  46. data/test/data/augsburg.xml +39009 -0
  47. data/test/data/chalice.xml +10851 -0
  48. data/test/data/eerdsman.xml +36942 -0
  49. data/test/data/invalid_no_product.xml +9 -0
  50. data/test/data/not_xml.csv +1 -0
  51. data/test/data/single_product.xml +50 -0
  52. data/test/data/xml_not_onix.xml +7 -0
  53. data/test/mocks/titlepage_driver.rb +107 -0
  54. data/test/unit/bisac_test.rb +57 -0
  55. data/test/unit/isbn_test.rb +149 -0
  56. data/test/unit/onix/contributor_test.rb +50 -0
  57. data/test/unit/onix/message_test.rb +119 -0
  58. data/test/unit/onix/product_test.rb +101 -0
  59. data/test/unit/onix/sales_restriction_test.rb +48 -0
  60. data/test/unit/onix/supply_detail_test.rb +53 -0
  61. data/test/unit/onix/xchar_test.rb +37 -0
  62. data/test/unit/titlepage_test.rb +127 -0
  63. metadata +130 -0
data/lib/rbook/onix.rb ADDED
@@ -0,0 +1,68 @@
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/isbn'
11
+
12
+ module RBook
13
+
14
+ # Ruby classes for working with ONIX files. Currently only supports a very limited
15
+ # subset of the ONIX specification, but the core attributes are there. More will be
16
+ # added over time.
17
+ #
18
+ # = Usage
19
+ #
20
+ # == Loading from an REXML document
21
+ #
22
+ # require 'rubygems'
23
+ # require 'rbook/onix'
24
+ # file = File.open("./onix_message.xml", "r")
25
+ # doc = REXML::Document.new(file)
26
+ # msg = Onix::Message.load_from_xmldoc(doc)
27
+ #
28
+ # puts msg.from_company
29
+ #
30
+ # == Creation
31
+ #
32
+ # require 'rubygems'
33
+ # require 'rbook/onix'
34
+ # msg = RBook::Onix::Message.new
35
+ # msg.from_company = 'XYZ Books'
36
+ # msg.from_name = 'Joe Blogs'
37
+ # msg.message_note = "A sample ONIX file"
38
+ #
39
+ # product = RBook::Onix::Product.new
40
+ # product.product_identifier = "020161622X"
41
+ # product.title = "Pragmatic Programmer"
42
+ # product.subtitle = "From Journeyman to Master"
43
+ # product.form = "BB"
44
+ # product.description = "A book about programming and stuff"
45
+ #
46
+ # contributor1 = RBook::Onix::Contributor.new
47
+ # contributor1.name_inverted = "Hunt, Andrew"
48
+ # contributor1.role = "A01"
49
+ # contributor1.sequence = "01"
50
+ # contributor2 = RBook::Onix::Contributor.new
51
+ # contributor2.name_inverted = "Thomas, David"
52
+ # contributor2.role = "A01"
53
+ # contributor2.sequence = "02"
54
+ # product.add_contributor(contributor1)
55
+ # product.add_contributor(contributor2)
56
+ #
57
+ # supply_detail = RBook::Onix::SupplyDetail.new
58
+ # supply_detail.supplier_name = "Rainbow Book Agencies"
59
+ # supply_detail.price = BigDecimal.new(29.95)
60
+ # supply_detail.availability = "CS"
61
+ # product.add_supply_detail(supply_detail)
62
+ #
63
+ # msg.add_product(product)
64
+ # puts msg.to_s
65
+ module Onix
66
+
67
+ end
68
+ end
@@ -0,0 +1,60 @@
1
+ require 'rexml/document'
2
+ require 'rubygems'
3
+
4
+ module RBook
5
+ module Onix
6
+
7
+ class Contributor
8
+ attr_accessor :sequence_number, :role, :name, :name_inverted
9
+
10
+ # Attempts to create a contributor object using the supplied xml element
11
+ def Contributor.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, '//Contributor').nil?
15
+ raise LoadError, 'supplied REXML document object does not appear contain a valid contributor fragment'
16
+ end
17
+
18
+ contributor = Contributor.new
19
+
20
+ tmp = REXML::XPath.first(element, '//Contributor/SequenceNumber')
21
+ contributor.sequence_number = tmp.text if tmp != nil
22
+
23
+ tmp = REXML::XPath.first(element, '//Contributor/ContributorRole')
24
+ contributor.role = tmp.text if tmp != nil
25
+
26
+ tmp = REXML::XPath.first(element, '//Contributor/PersonName')
27
+ contributor.name = tmp.text if tmp != nil
28
+
29
+ tmp = REXML::XPath.first(element, '//Contributor/PersonNameInverted')
30
+ contributor.name_inverted = tmp.text if tmp != nil
31
+
32
+ return contributor
33
+
34
+ end
35
+
36
+ # Return an xml element representing this contributor
37
+ def to_element
38
+ raise 'Contributor must have a sequence number to create an element' if @sequence_number.nil?
39
+ raise 'Contributor must have a role to create an element' if @role.nil?
40
+ raise '' if @name_inverted.nil? && @name.nil?
41
+
42
+ contributor = REXML::Element.new('Contributor')
43
+ contributor.add_element('SequenceNumber').text = self.sequence_number
44
+ contributor.add_element('ContributorRole').text = self.role.to_xs
45
+ contributor.add_element('PersonName').text = name.to_xs unless self.name.nil?
46
+ contributor.add_element('PersonNameInverted').text = self.name_inverted.to_xs unless self.name_inverted.nil?
47
+
48
+ return contributor
49
+ end
50
+
51
+ # Return an XML string representing this contributor
52
+ def to_s
53
+ tmp = to_element
54
+ output = ''
55
+ tmp.write(output, 0)
56
+ return output
57
+ end
58
+ end
59
+ end
60
+ 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,101 @@
1
+ require 'rubygems'
2
+ require 'rexml/document'
3
+
4
+ module RBook
5
+ module Onix
6
+
7
+ class Message
8
+ attr_reader :products
9
+ attr_accessor :from_company, :from_person, :from_email, :message_note, :sent_date, :to_person, :to_company
10
+
11
+ VERSION = 0.1
12
+
13
+ def initialize
14
+ @ONIX_DTD_URL = "http://www.editeur.org/onix/2.1/reference/onix-international.dtd"
15
+
16
+ @products = []
17
+ end
18
+
19
+ # Attempts to create a message object using thge supplied xml document
20
+ def Message.load_from_xmldoc(doc)
21
+ raise ArgumentError, 'load_from_xmldoc expects a REXML document object' unless doc.class == REXML::Document
22
+ if REXML::XPath.first(doc, '/ONIXMessage/').nil? ||
23
+ REXML::XPath.first(doc, '/ONIXMessage/Header').nil? ||
24
+ REXML::XPath.first(doc, '/ONIXMessage/Product').nil?
25
+ raise LoadError, 'supplied REXML document object does not appear to be a valid ONIX file'
26
+ end
27
+
28
+ msg = Onix::Message.new
29
+
30
+ tmpelement = REXML::XPath.first(doc, '/ONIXMessage/Header/FromCompany')
31
+ msg.from_company = tmpelement.text if tmpelement != nil
32
+
33
+ tmpelement = REXML::XPath.first(doc, '/ONIXMessage/Header/FromPerson')
34
+ msg.from_person = tmpelement.text if tmpelement != nil
35
+
36
+ tmpelement = REXML::XPath.first(doc, '/ONIXMessage/Header/FromEmail')
37
+ msg.from_email = tmpelement.text if tmpelement != nil
38
+
39
+ tmpelement = REXML::XPath.first(doc, '/ONIXMessage/Header/SentDate')
40
+ tmpdate = Date.civil(tmpelement.text[0,4].to_i, tmpelement.text[4,2].to_i, tmpelement.text[6,2].to_i) if tmpelement != nil
41
+ msg.sent_date = tmpdate if tmpdate != nil
42
+
43
+ tmpelement = REXML::XPath.first(doc, '/ONIXMessage/Header/ToCompany')
44
+ msg.to_company = tmpelement.text if tmpelement != nil
45
+
46
+ tmpelement = REXML::XPath.first(doc, '/ONIXMessage/Header/ToPerson')
47
+ msg.to_person = tmpelement.text if tmpelement != nil
48
+
49
+ products = REXML::XPath.each(doc, '/ONIXMessage/Product') { |product|
50
+ msg.add_product(Onix::Product.load_from_element(product))
51
+ }
52
+
53
+ return msg
54
+ end
55
+
56
+ # Adds a new product to this message
57
+ def add_product(product)
58
+ raise ArgumentError, 'Argument must be a Onix::Product' if product.class != Onix::Product
59
+ @products << product
60
+ end
61
+
62
+ # Returns an XML string representing this message
63
+ def to_s
64
+ doc = to_xml
65
+ output = ''
66
+ doc.write(output, 0)
67
+ return output
68
+ end
69
+
70
+ # Returns an XML document representing this message
71
+ def to_xml
72
+ raise 'from_compnay must be set to create an onix message' if from_company == nil
73
+ raise 'from_person must be set to create an onix message' if from_person == nil
74
+ raise 'There must be at least 1 product to burn' if @products.size < 1
75
+
76
+ doc = REXML::Document.new
77
+ doc << REXML::XMLDecl.new
78
+ doc << REXML::DocType.new('ONIXMessage', "SYSTEM \"#{@ONIX_DTD_URL}\"")
79
+ msg = doc.add_element('ONIXMessage')
80
+ header = msg.add_element('Header')
81
+ header.add_element('FromCompany').text = from_company.to_xs
82
+ header.add_element('FromPerson').text = from_person.to_xs
83
+ header.add_element('FromEmail').text = from_email.to_xs unless from_email.nil?
84
+ header.add_element('MessageNote').text = message_note.to_xs unless message_note.nil?
85
+ header.add_element('ToCompany').text = to_company.to_xs unless to_company.nil?
86
+ header.add_element('ToPerson').text = to_person.to_xs unless to_person.nil?
87
+
88
+ now = Time.now
89
+ header.add_element('SentDate').text = "%.4d%.2d%.2d" % [ now.year, now.month, now.day ]
90
+
91
+ @products.each do |product|
92
+ prod = msg.add_element(product.to_element)
93
+ end
94
+
95
+ return doc
96
+ end
97
+
98
+ end
99
+
100
+ end
101
+ end
@@ -0,0 +1,188 @@
1
+ require 'rexml/document'
2
+ require 'rubygems'
3
+
4
+ module RBook
5
+ module Onix
6
+ class Product
7
+
8
+ attr_accessor :record_reference, :notification_type, :product_identifier, :title, :subtitle, :pages, :publication_date, :price, :description, :form, :bicmainsubject, :publisher, :supplier, :website, :edition
9
+ attr_reader :contributors, :sales_restrictions, :supply_details
10
+
11
+ def initialize
12
+ @contributors = []
13
+ @supply_details = []
14
+ @sales_restrictions = []
15
+ end
16
+
17
+ # Attempts to create a product object using the supplied xml element
18
+ def Product.load_from_element(element)
19
+ raise ArgumentError, 'load_from_element expects a REXML element object' unless element.class == REXML::Element
20
+
21
+ if REXML::XPath.first(element, '//Product').nil?
22
+ raise LoadError, 'supplied REXML document object does not appear contain a valid product fragment'
23
+ end
24
+
25
+ product = Product.new
26
+
27
+ tmp = REXML::XPath.first(element, '//Product/RecordReference')
28
+ product.record_reference = tmp.text if tmp != nil
29
+
30
+ tmp = REXML::XPath.first(element, '//Product/NotificationType')
31
+ product.notification_type = tmp.text if tmp != nil
32
+
33
+ # TODO: load ProductIdentifier from the fragment
34
+
35
+ tmp = REXML::XPath.first(element, '//Product/ProductForm')
36
+ product.form = tmp.text if tmp != nil
37
+
38
+ tmp = REXML::XPath.first(element, '//Product/NumberOfPages')
39
+ product.pages = tmp.text if tmp != nil
40
+
41
+ tmp = REXML::XPath.first(element, '//Product/BICMainSubject')
42
+ product.bicmainsubject = tmp.text if tmp != nil
43
+
44
+ tmp = REXML::XPath.first(element, '//Product/DistinctiveTitle')
45
+ if !tmp.nil?
46
+ product.title = tmp.text
47
+ tmp = REXML::XPath.first(element, '//Product/Subtitle') if !tmp.nil?
48
+ product.subtitle = tmp.text
49
+ else
50
+ tmp = REXML::XPath.first(element, '//Product/Title/TitleText')
51
+ product.title = tmp.text unless tmp.nil?
52
+ tmp = REXML::XPath.first(element, '//Product/Title/Subtitle')
53
+ product.subtitle = tmp.text unless tmp.nil?
54
+ end
55
+
56
+ tmp = REXML::XPath.first(element, '//Product/EditionNumber')
57
+ product.edition = tmp.text if tmp != nil
58
+
59
+ tmp = REXML::XPath.first(element, '//Product/PublicationDate')
60
+ tmpdate = Date.civil(tmp.text[0,4].to_i, tmp.text[4,2].to_i, tmp.text[6,2].to_i) if tmp != nil
61
+ product.publication_date = tmpdate if tmpdate != nil
62
+
63
+ tmp = REXML::XPath.first(element, '//Product/Publisher/PublisherName')
64
+ product.publisher = tmp.text if tmp != nil
65
+
66
+ REXML::XPath.each(element, '//Product/Contributor') { |contributor|
67
+ product.add_contributor(Onix::Contributor.load_from_element(contributor))
68
+ }
69
+
70
+ REXML::XPath.each(element, '//Product/SupplyDetail') { |detail|
71
+ product.add_supply_detail(Onix::SupplyDetail.load_from_element(detail))
72
+ }
73
+
74
+ return product
75
+ end
76
+
77
+ # Add a new contributor to this product
78
+ def add_contributor(newAuthor)
79
+ raise ArgumentError, 'Argument to add_contributor must be an ONIX::Contributor' if newAuthor.class != Contributor
80
+ @contributors << newAuthor
81
+ end
82
+
83
+ # Add a new contributor to this product
84
+ def add_sales_restriction(restriction)
85
+ raise ArgumentError, 'Argument to add_sales_restriction must be an ONIX::SalesRestriction' if restriction.class != SalesRestriction
86
+ @sales_restrictions << restriction
87
+ end
88
+
89
+ # Add a new contributor to this product
90
+ def add_supply_detail(detail)
91
+ raise ArgumentError, 'Argument to add_supply_detail must be an ONIX::SupplyDetail' if detail.class != SupplyDetail
92
+ @supply_details << detail
93
+ end
94
+
95
+ # Return an xml element representing this contributor
96
+ def to_element
97
+ raise 'product_identifier must be set' if self.product_identifier.nil?
98
+
99
+ product = REXML::Element.new('Product')
100
+
101
+ if self.record_reference
102
+ product.add_element('RecordReference').text = self.record_reference
103
+ else
104
+ product.add_element('RecordReference').text = self.product_identifier
105
+ end
106
+
107
+ product.add_element('NotificationType').text = '03'
108
+
109
+ product_identifier = REXML::Element.new('ProductIdentifier')
110
+ if ISBN::valid_isbn13?(self.product_identifier)
111
+ product_identifier.add_element('ProductIDType').text = '03'
112
+ elsif ISBN::valid_isbn10?(self.product_identifier)
113
+ product_identifier.add_element('ProductIDType').text = '02'
114
+ else
115
+ product_identifier.add_element('ProductIDType').text = '01'
116
+ end
117
+ product_identifier.add_element('IDValue').text = self.product_identifier.to_xs
118
+ product.add_element(product_identifier)
119
+
120
+ if form != nil
121
+ product.add_element('ProductForm').text = self.form.to_xs
122
+ end
123
+
124
+ product.add_element('NoSeries')
125
+
126
+ title = REXML::Element.new('Title')
127
+ title.add_element('TitleType').text = '01'
128
+ title.add_element('TitleText').text = self.title.to_xs unless self.title.nil?
129
+ title.add_element('Subtitle').text = self.subtitle.to_xs unless self.subtitle.nil?
130
+ product.add_element(title)
131
+
132
+ if website != nil
133
+ product_website = REXML::Element.new('Website')
134
+ product_website.add_element('WebsiteLink').text = self.website.to_xs
135
+ product.add_element(product_website)
136
+ end
137
+
138
+ contributors.each do |contributor|
139
+ product.add_element(contributor.to_element)
140
+ end
141
+
142
+ product.add_element('NoEdition')
143
+
144
+ product.add_element('NumberOfPages').text = self.pages.to_s unless self.pages.nil?
145
+
146
+ if bicmainsubject != nil
147
+ product.add_element('BICMainSubject').text = self.bicmainsubject.to_xs
148
+ end
149
+
150
+ if description != nil
151
+ other_text = REXML::Element.new('OtherText')
152
+ other_text.add_element('TextTypeCode').text = '01'
153
+ other_text.add_element('Text').text = self.description.to_xs
154
+ product.add_element(other_text)
155
+ end
156
+
157
+ if publisher != nil
158
+ publisher = REXML::Element.new('Publisher')
159
+ publisher.add_element('PublishingRole').text = '01'
160
+ publisher.add_element('PublisherName').text = self.publisher.to_xs
161
+ product.add_element(publisher)
162
+ end
163
+
164
+ supply_details.each do |detail|
165
+ product.add_element(detail.to_element)
166
+ end
167
+
168
+ sales_restrictions.each do |restriction|
169
+ product.add_element(restriction.to_element)
170
+ end
171
+
172
+ if publication_date != nil
173
+ product.add_element('PublicationDate').text = "%.4d%.2d%.2d" % [ publication_date.year, publication_date.month, publication_date.day ]
174
+ end
175
+
176
+ return product
177
+ end
178
+
179
+ # return an XML string representing this contributor
180
+ def to_s
181
+ output = ''
182
+ to_element.write(output, 0)
183
+ return output
184
+ end
185
+ end
186
+
187
+ end
188
+ end