onix 0.4.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/CHANGELOG +30 -0
- data/README.rdoc +30 -0
- data/TODO +14 -0
- data/lib/onix.rb +57 -0
- data/lib/onix/addressee_identifier.rb +9 -0
- data/lib/onix/apa_product.rb +534 -0
- data/lib/onix/contributor.rb +20 -0
- data/lib/onix/date_type.rb +54 -0
- data/lib/onix/etext_type.rb +44 -0
- data/lib/onix/header.rb +31 -0
- data/lib/onix/imprint.rb +10 -0
- data/lib/onix/integer_type.rb +43 -0
- data/lib/onix/lists/product_availability.rb +30 -0
- data/lib/onix/lists/product_form.rb +97 -0
- data/lib/onix/media_file.rb +11 -0
- data/lib/onix/other_text.rb +12 -0
- data/lib/onix/price.rb +16 -0
- data/lib/onix/product.rb +30 -0
- data/lib/onix/product_identifier.rb +8 -0
- data/lib/onix/publisher.rb +11 -0
- data/lib/onix/reader.rb +124 -0
- data/lib/onix/sales_restriction.rb +7 -0
- data/lib/onix/sender_identifier.rb +9 -0
- data/lib/onix/simple_product.rb +41 -0
- data/lib/onix/stock.rb +10 -0
- data/lib/onix/subject.rb +11 -0
- data/lib/onix/supply_detail.rb +21 -0
- data/lib/onix/title.rb +10 -0
- data/lib/onix/two_digit_type.rb +57 -0
- data/lib/onix/website.rb +9 -0
- data/lib/onix/writer.rb +87 -0
- data/spec/header_spec.rb +111 -0
- data/spec/product_spec.rb +75 -0
- data/spec/reader_spec.rb +64 -0
- data/spec/writer_spec.rb +81 -0
- metadata +117 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
module ONIX
|
2
|
+
class Contributor
|
3
|
+
include ROXML
|
4
|
+
|
5
|
+
xml_accessor :sequence_number, :from => "SequenceNumber"
|
6
|
+
xml_accessor :contributor_role, :from => "ContributorRole"
|
7
|
+
xml_accessor :language_code, :from => "LanguageCode"
|
8
|
+
xml_accessor :sequence_number_within_role, :from => "SequenceNumberWithinRole"
|
9
|
+
xml_accessor :person_name, :etext, :from => "PersonName"
|
10
|
+
xml_accessor :person_name_inverted, :etext, :from => "PersonNameInverted"
|
11
|
+
xml_accessor :titles_before_name, :etext, :from => "TitlesBeforeName"
|
12
|
+
xml_accessor :names_before_key, :etext, :from => "NamesBeforeKey"
|
13
|
+
xml_accessor :prefix_to_key, :etext, :from => "PrefixToKey"
|
14
|
+
xml_accessor :key_names, :etext, :from => "KeyNames"
|
15
|
+
xml_accessor :names_after_key, :etext, :from => "NamesArterKey"
|
16
|
+
xml_accessor :suffix_to_key, :etext, :from => "SuffixToKey"
|
17
|
+
xml_accessor :letters_after_names, :etext, :from => "LettersAfterNames"
|
18
|
+
xml_accessor :titles_after_names, :etext, :from => "TitlesAfterNames"
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module ONIX
|
4
|
+
|
5
|
+
# Internal class representing XML content date binding
|
6
|
+
#
|
7
|
+
# In context:
|
8
|
+
# <element attribute="XMLAttributeRef">
|
9
|
+
# XMLYYYYMMDDRef
|
10
|
+
# </element>
|
11
|
+
class DateType < ROXML::XMLRef # ::nodoc::
|
12
|
+
attr_reader :cdata, :content
|
13
|
+
|
14
|
+
def initialize(accessor, args, &block)
|
15
|
+
super(accessor, args, &block)
|
16
|
+
@content = args.content?
|
17
|
+
@cdata = args.cdata?
|
18
|
+
end
|
19
|
+
|
20
|
+
# Updates the text in the given _xml_ block to
|
21
|
+
# the _value_ provided.
|
22
|
+
def update_xml(xml, value)
|
23
|
+
parent = wrap(xml)
|
24
|
+
add(parent.child_add(LibXML::XML::Node.new_element(name)), value.strftime("%Y%m%d"))
|
25
|
+
xml
|
26
|
+
end
|
27
|
+
|
28
|
+
def value(xml)
|
29
|
+
begin
|
30
|
+
if content
|
31
|
+
value = Date.parse(xml.content.strip)
|
32
|
+
else
|
33
|
+
child = xml.search(name).first
|
34
|
+
value = Date.parse(child.content.strip) if child
|
35
|
+
end
|
36
|
+
rescue ArgumentError
|
37
|
+
value = nil
|
38
|
+
end
|
39
|
+
block ? block.call(value) : value
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def add(dest, value)
|
45
|
+
if cdata
|
46
|
+
dest.child_add(LibXML::XML::Node.new_cdata(value.to_utf))
|
47
|
+
else
|
48
|
+
dest.content = value.to_utf
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
ROXML::TypeRegistry.register(:yyyymmdd, ONIX::DateType)
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module ONIX
|
2
|
+
|
3
|
+
class ETextType < ROXML::XMLRef # ::nodoc::
|
4
|
+
attr_reader :cdata, :content
|
5
|
+
|
6
|
+
def initialize(accessor, args, &block)
|
7
|
+
super(accessor, args, &block)
|
8
|
+
@content = args.content?
|
9
|
+
@cdata = args.cdata?
|
10
|
+
end
|
11
|
+
|
12
|
+
# Updates the text in the given _xml_ block to
|
13
|
+
# the _value_ provided.
|
14
|
+
def update_xml(xml, value)
|
15
|
+
value = value.to_s.gsub("&","&").gsub("<","<").gsub(">",">")
|
16
|
+
parent = wrap(xml)
|
17
|
+
add(parent.child_add(LibXML::XML::Node.new_element(name)), value)
|
18
|
+
xml
|
19
|
+
end
|
20
|
+
|
21
|
+
def value(xml)
|
22
|
+
if content
|
23
|
+
value = xml.content
|
24
|
+
else
|
25
|
+
child = xml.search(name).first
|
26
|
+
value = child.content if child
|
27
|
+
end
|
28
|
+
value = value.gsub("&","&").gsub("<","<").gsub(">",">") if value
|
29
|
+
block ? block.call(value) : value
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def add(dest, value)
|
35
|
+
if cdata
|
36
|
+
dest.child_add(LibXML::XML::Node.new_cdata(value.to_utf))
|
37
|
+
else
|
38
|
+
dest.content = value.to_utf
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
ROXML::TypeRegistry.register(:etext, ONIX::ETextType)
|
data/lib/onix/header.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module ONIX
|
2
|
+
class Header
|
3
|
+
include ROXML
|
4
|
+
|
5
|
+
xml_name "Header"
|
6
|
+
|
7
|
+
xml_accessor :from_person, :etext, :from => "FromPerson"
|
8
|
+
xml_accessor :from_ean_number, :etext, :from => "FromEANNumber"
|
9
|
+
xml_accessor :from_san, :etext, :from => "FromSAN"
|
10
|
+
xml_accessor :sender_identifiers, [ONIX::SenderIdentifier], :from => "SenderIdentifier"
|
11
|
+
xml_accessor :from_company, :etext, :from => "FromCompany"
|
12
|
+
xml_accessor :from_email, :etext, :from => "FromEmail"
|
13
|
+
xml_accessor :to_ean_number, :etext, :from => "ToEANNumber"
|
14
|
+
xml_accessor :to_san, :etext, :from => "ToSAN"
|
15
|
+
xml_accessor :addressee_identifier, [ONIX::AddresseeIdentifier], :from => "AddresseeIdentifier"
|
16
|
+
xml_accessor :to_company, :etext, :from => "ToCompany"
|
17
|
+
xml_accessor :to_person, :etext, :from => "ToPerson"
|
18
|
+
xml_accessor :message_number, :etext, :from => "MessageNumber"
|
19
|
+
xml_accessor :message_repeat, :integer, :from => "MessageRepeat"
|
20
|
+
xml_accessor :sent_date, :yyyymmdd, :from => "SentDate"
|
21
|
+
xml_accessor :message_note, :etext, :from => "MessageNote"
|
22
|
+
|
23
|
+
# defaults
|
24
|
+
xml_accessor :default_language_of_text, :from => "DefaultLanguageOfText"
|
25
|
+
xml_accessor :default_price_type_code, :integer, :from => "DefaultPriceTypeCode"
|
26
|
+
xml_accessor :default_currency_code, :from => "DefaultCurrencyCode"
|
27
|
+
xml_reader :default_linear_unit, :from => "DefaultLinearUnit" # deprecated
|
28
|
+
xml_reader :default_weight_unit, :from => "DefaultWeightUnit" # deprecated
|
29
|
+
xml_accessor :default_class_of_trade, :from => "DefaultClassOfTrade"
|
30
|
+
end
|
31
|
+
end
|
data/lib/onix/imprint.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
module ONIX
|
2
|
+
class Imprint
|
3
|
+
include ROXML
|
4
|
+
|
5
|
+
xml_accessor :name_code_type, :twodigit, :from => "NameCodeType"
|
6
|
+
xml_accessor :name_code_type_name, :etext, :from => "NameCodeTypeName"
|
7
|
+
xml_accessor :name_code_value, :etext, :from => "NameCodeValue"
|
8
|
+
xml_accessor :imprint_name, :etext, :from => "ImprintName"
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
module ONIX
|
3
|
+
|
4
|
+
class IntegerType < ROXML::XMLRef # ::nodoc::
|
5
|
+
attr_reader :cdata, :content
|
6
|
+
|
7
|
+
def initialize(accessor, args, &block)
|
8
|
+
super(accessor, args, &block)
|
9
|
+
@content = args.content?
|
10
|
+
@cdata = args.cdata?
|
11
|
+
end
|
12
|
+
|
13
|
+
# Updates the text in the given _xml_ block to
|
14
|
+
# the _value_ provided.
|
15
|
+
def update_xml(xml, value)
|
16
|
+
parent = wrap(xml)
|
17
|
+
add(parent.child_add(LibXML::XML::Node.new_element(name)), value)
|
18
|
+
xml
|
19
|
+
end
|
20
|
+
|
21
|
+
def value(xml)
|
22
|
+
if content
|
23
|
+
value = xml.content.to_i
|
24
|
+
else
|
25
|
+
child = xml.search(name).first
|
26
|
+
value = child.content.to_i if child
|
27
|
+
end
|
28
|
+
block ? block.call(value) : value
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def add(dest, value)
|
34
|
+
if cdata
|
35
|
+
dest.child_add(LibXML::XML::Node.new_cdata(value.to_utf))
|
36
|
+
else
|
37
|
+
dest.content = value.to_utf
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
ROXML::TypeRegistry.register(:integer, ONIX::IntegerType)
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module ONIX
|
2
|
+
module Lists
|
3
|
+
# Code list 65
|
4
|
+
PRODUCT_AVAILABILITY = {
|
5
|
+
1 => "Cancelled",
|
6
|
+
10 => "Not Yet Available",
|
7
|
+
11 => "Awaiting Stock",
|
8
|
+
12 => "Not Yet Available, will be POD",
|
9
|
+
20 => "Available",
|
10
|
+
21 => "In Stock",
|
11
|
+
22 => "To Order",
|
12
|
+
23 => "Manufactured on Demand",
|
13
|
+
30 => "Temporarily Unavailable",
|
14
|
+
31 => "Out Of Stock",
|
15
|
+
32 => "Reprinting",
|
16
|
+
33 => "Awaiting Reissue",
|
17
|
+
40 => "Not Available",
|
18
|
+
41 => "Replaced By New Product",
|
19
|
+
42 => "Other Format Available",
|
20
|
+
43 => "No Longer Supplier By Us",
|
21
|
+
44 => "Apply Direct",
|
22
|
+
45 => "Not Sold Seperately",
|
23
|
+
46 => "Withdrawn From Sale",
|
24
|
+
47 => "Remaindered",
|
25
|
+
48 => "Out Of Print, Replaced By POD with Diff ISBN",
|
26
|
+
99 => "Uncertain"
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module ONIX
|
2
|
+
module Lists
|
3
|
+
# code list 7
|
4
|
+
PRODUCT_FORM = {
|
5
|
+
"00" => "Undefined",
|
6
|
+
"AA" => "Audio",
|
7
|
+
"AB" => "Audio - Cassette",
|
8
|
+
"AC" => "Audio - CD",
|
9
|
+
"AD" => "Audio - DAT",
|
10
|
+
"AE" => "Audio - Disk",
|
11
|
+
"AF" => "Audio - Tape",
|
12
|
+
"AZ" => "Audio - Other",
|
13
|
+
"BA" => "Book",
|
14
|
+
"BB" => "Book - Hardback",
|
15
|
+
"BC" => "Book - Paperback",
|
16
|
+
"BD" => "Book - Loose-leaf",
|
17
|
+
"BE" => "Book - Spiral Bound",
|
18
|
+
"BF" => "Book - Pamphlet",
|
19
|
+
"BG" => "Book - Leather / Fine Binding",
|
20
|
+
"BH" => "Book - Board",
|
21
|
+
"BI" => "Book - Rag",
|
22
|
+
"BJ" => "Book - Bath",
|
23
|
+
"BZ" => "Book - Other",
|
24
|
+
"CA" => "Cartographic - Sheet Map",
|
25
|
+
"CB" => "Cartographic - Sheet Map Folded",
|
26
|
+
"CC" => "Cartographic - Sheet Map Flat",
|
27
|
+
"CD" => "Cartographic - Sheet Map Rolled",
|
28
|
+
"CE" => "Cartographic - Globe",
|
29
|
+
"CZ" => "Cartographic - Other",
|
30
|
+
"DA" => "Digital",
|
31
|
+
"DB" => "Digital - CDROM",
|
32
|
+
"DC" => "Digital - CD-Interactive",
|
33
|
+
"DD" => "Digital - DVD",
|
34
|
+
"DE" => "Digital - Game Cartridge",
|
35
|
+
"DF" => "Digital - Diskette",
|
36
|
+
"DG" => "Digital - Electronic Book Text",
|
37
|
+
"DH" => "Digital - Online File",
|
38
|
+
"DZ" => "Digital - Other",
|
39
|
+
"FA" => "Film or Transparency",
|
40
|
+
"FB" => "Film",
|
41
|
+
"FC" => "Film - Slides",
|
42
|
+
"FD" => "Film - OHP Transparencies",
|
43
|
+
"FZ" => "Film - Other ",
|
44
|
+
"MA" => "Microform",
|
45
|
+
"MB" => "Microform - Microfiche",
|
46
|
+
"MC" => "Microform - Microfilm",
|
47
|
+
"MZ" => "Microform - Other",
|
48
|
+
"PA" => "Print - Misc",
|
49
|
+
"PB" => "Print - Address Book",
|
50
|
+
"PC" => "Print - Calendar",
|
51
|
+
"PD" => "Print - Cards",
|
52
|
+
"PE" => "Print - Copymasters",
|
53
|
+
"PF" => "Print - Diary",
|
54
|
+
"PG" => "Print - Frieze",
|
55
|
+
"PH" => "Print - Kit",
|
56
|
+
"PI" => "Print - Sheet Music",
|
57
|
+
"PJ" => "Print - Postcard Book or Pack",
|
58
|
+
"PK" => "Print - Poster",
|
59
|
+
"PL" => "Print - Record Book",
|
60
|
+
"PM" => "Print - Wallet",
|
61
|
+
"PN" => "Print - Pictures or Photographs",
|
62
|
+
"PO" => "Print - Wallchart",
|
63
|
+
"PZ" => "Print - Other",
|
64
|
+
"VA" => "Video",
|
65
|
+
"VB" => "Video – VHS PAL",
|
66
|
+
"VC" => "Video – VHS NTSC",
|
67
|
+
"VD" => "Video – Betamax PAL",
|
68
|
+
"VE" => "Video – Betamax NTSC",
|
69
|
+
"VF" => "Video – disk",
|
70
|
+
"VG" => "Video – VHS SECAM",
|
71
|
+
"VH" => "Video – Betamax SECAM",
|
72
|
+
"VZ" => "Video - Other Format",
|
73
|
+
"WW" => "Multiple - Mixed Media Product",
|
74
|
+
"WX" => "Misc - Quantity Pack",
|
75
|
+
"XA" => "Misc - Trade-only Material",
|
76
|
+
"XB" => "Misc - Dumpbim - Empty",
|
77
|
+
"XC" => "Misc - Dumpbin - Filled",
|
78
|
+
"XD" => "Misc - Counterpack - Empty",
|
79
|
+
"XE" => "Misc - Counterpack - Filled",
|
80
|
+
"XF" => "Misc - Poster",
|
81
|
+
"XG" => "Misc - Shelf Strip",
|
82
|
+
"XH" => "Misc - Window Piece",
|
83
|
+
"XI" => "Misc - Streamer",
|
84
|
+
"XJ" => "Misc - Spinner",
|
85
|
+
"XK" => "Misc - Large Book Display",
|
86
|
+
"XL" => "Misc - Shrink Wrapped Pack",
|
87
|
+
"XZ" => "Misc - Other Point of Sale",
|
88
|
+
"ZA" => "General Merchandise",
|
89
|
+
"ZB" => "Misc - Doll",
|
90
|
+
"ZC" => "Misc - Soft Toy",
|
91
|
+
"ZD" => "Misc - Toy",
|
92
|
+
"ZE" => "Misc - Game",
|
93
|
+
"ZF" => "Misc - T-shirt",
|
94
|
+
"ZZ" => "Misc - Other Merchandise"
|
95
|
+
}
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module ONIX
|
2
|
+
class MediaFile
|
3
|
+
include ROXML
|
4
|
+
|
5
|
+
xml_accessor :media_file_type_code, :twodigit, :from => "MediaFileTypeCode"
|
6
|
+
xml_accessor :media_file_format_code, :twodigit, :from => "MediaFileFormatCode"
|
7
|
+
xml_accessor :image_resolution, :from => "ImageResolution"
|
8
|
+
xml_accessor :media_file_link_type_code, :twodigit, :from => "MediaFileLinkTypeCode"
|
9
|
+
xml_accessor :media_file_link, :from => "MediaFileLink"
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module ONIX
|
2
|
+
class OtherText
|
3
|
+
include ROXML
|
4
|
+
|
5
|
+
xml_accessor :text_type_code, :twodigit, :from => "TextTypeCode"
|
6
|
+
xml_accessor :text_format, :etext, :from => "TextFormat"
|
7
|
+
xml_accessor :text, :etext, :from => "Text"
|
8
|
+
xml_accessor :text_link_type, :twodigit, :from => "TextLinkType"
|
9
|
+
xml_accessor :text_link, :from => "TextLink"
|
10
|
+
xml_accessor :text_author, :etext, :from => "TextAuthor"
|
11
|
+
end
|
12
|
+
end
|
data/lib/onix/price.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
module ONIX
|
2
|
+
class Price
|
3
|
+
include ROXML
|
4
|
+
|
5
|
+
xml_accessor :price_type_code, :twodigit, :from => "PriceTypeCode"
|
6
|
+
xml_accessor :price_type_qualifier, :from => "PriceTypeQualifier"
|
7
|
+
xml_accessor :price_type_description, :from => "PriceTypeDescription"
|
8
|
+
xml_accessor :price_per, :from => "PricePer"
|
9
|
+
xml_accessor :minimum_order_qty, :from => "MinimumOrderQuantity"
|
10
|
+
xml_accessor :class_of_trade, :from => "ClassOfTrade"
|
11
|
+
xml_accessor :bic_discount_group_code, :from => "BICDiscountGroupCode"
|
12
|
+
xml_accessor :price_status, :from => "PriceStatus"
|
13
|
+
xml_accessor :price_amount, :from => "PriceAmount"
|
14
|
+
xml_accessor :currency_code, :from => "CurrencyCode"
|
15
|
+
end
|
16
|
+
end
|
data/lib/onix/product.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module ONIX
|
2
|
+
class Product
|
3
|
+
include ROXML
|
4
|
+
|
5
|
+
xml_name "Product"
|
6
|
+
|
7
|
+
xml_accessor :record_reference, :from => "RecordReference"
|
8
|
+
xml_accessor :notification_type, :twodigit, :from => "NotificationType"
|
9
|
+
xml_accessor :product_identifiers, [ONIX::ProductIdentifier], :from => "ProductIdentifier"
|
10
|
+
xml_accessor :product_form, :from => "ProductForm"
|
11
|
+
xml_accessor :series, :from => "Series"
|
12
|
+
xml_accessor :edition_number, :integer, :from => "EditionNumber"
|
13
|
+
xml_accessor :titles, [ONIX::Title], :from => "Title"
|
14
|
+
xml_accessor :websites, [ONIX::Website], :from => "Website"
|
15
|
+
xml_accessor :contributors, [ONIX::Contributor], :from => "Contributor"
|
16
|
+
xml_accessor :number_of_pages, :integer, :from => "NumberOfPages"
|
17
|
+
xml_accessor :bic_main_subject, :from => "BICMainSubject"
|
18
|
+
xml_accessor :subjects, [ONIX::Subject], :from => "Subject"
|
19
|
+
xml_accessor :text, [ONIX::OtherText], :from => "OtherText"
|
20
|
+
xml_accessor :media_files, [ONIX::MediaFile], :from => "MediaFile"
|
21
|
+
xml_accessor :imprints, [ONIX::Imprint], :from => "Imprint"
|
22
|
+
xml_accessor :publishers, [ONIX::Publisher], :from => "Publisher"
|
23
|
+
xml_accessor :publishing_status, :twodigit, :from => "PublishingStatus"
|
24
|
+
xml_accessor :publication_date, :yyyymmdd, :from => "PublicationDate"
|
25
|
+
xml_accessor :year_first_published, :integer, :from => "YearFirstPublished"
|
26
|
+
xml_accessor :sales_restrictions, [ONIX::SalesRestriction], :from => "SalesRestriction"
|
27
|
+
xml_accessor :supply_details, [ONIX::SupplyDetail], :from => "SupplyDetail"
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module ONIX
|
2
|
+
class Publisher
|
3
|
+
include ROXML
|
4
|
+
|
5
|
+
xml_accessor :publishing_role, :twodigit, :from => "PublishingRole"
|
6
|
+
xml_accessor :name_code_type, :twodigit, :from => "NameCodeType"
|
7
|
+
xml_accessor :name_code_type_name, :etext, :from => "NameCodeTypeName"
|
8
|
+
xml_accessor :name_code_type_value, :etext, :from => "NameCodeTypeValue"
|
9
|
+
xml_accessor :publisher_name, :etext, :from => "PublisherName"
|
10
|
+
end
|
11
|
+
end
|
data/lib/onix/reader.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'timeout'
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
module ONIX
|
6
|
+
|
7
|
+
# This is the primary class for reading data from an ONIX file, and there's
|
8
|
+
# really not much to it
|
9
|
+
#
|
10
|
+
# Each file should contain a single header, and 1 or more products:
|
11
|
+
#
|
12
|
+
# reader = ONIX::Reader.new("somefile.xml")
|
13
|
+
#
|
14
|
+
# puts reader.header.inspect
|
15
|
+
#
|
16
|
+
# reader.each do |product|
|
17
|
+
# puts product.inspect
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# The header will be returned as an ONIX::Header object, and the product will
|
21
|
+
# be an ONIX::Product.
|
22
|
+
#
|
23
|
+
# The ONIX::Product class can be a bit of a hassle to work with, as data can be
|
24
|
+
# nested in it fairly deeply. To wrap all the products returned by the reader
|
25
|
+
# in a shim that provides simple accessor access to common attributes, pass the
|
26
|
+
# shim class as a second argument
|
27
|
+
#
|
28
|
+
# reader = ONIX::Reader.new("somefile.xml", ONIX::APAProduct)
|
29
|
+
#
|
30
|
+
# puts reader.header.inspect
|
31
|
+
#
|
32
|
+
# reader.each do |product|
|
33
|
+
# puts product.inspect
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# APAProduct stands for Australian Publishers Association and provides simple
|
37
|
+
# access to the ONIX attributes that are commonly used in the Australian market.
|
38
|
+
#
|
39
|
+
# As well as accessing the file header, there are handful of other read only
|
40
|
+
# attributes that might be useful
|
41
|
+
#
|
42
|
+
# reader = ONIX::Reader.new("somefile.xml", ONIX::APAProduct)
|
43
|
+
#
|
44
|
+
# puts reader.version
|
45
|
+
# puts reader.xml_lang
|
46
|
+
# puts reader.xml_version
|
47
|
+
# puts reader.encoding
|
48
|
+
#
|
49
|
+
# The version attribute is particuarly useful. There are multiple revisions of the
|
50
|
+
# ONIX spec, and you may need to handle the file differently based on what
|
51
|
+
# version it is.
|
52
|
+
#
|
53
|
+
class Reader
|
54
|
+
|
55
|
+
attr_reader :header, :version, :xml_lang, :xml_version, :encoding
|
56
|
+
|
57
|
+
# Create a new ONIX::Reader object
|
58
|
+
#
|
59
|
+
def initialize(input, product_klass = ::ONIX::Product)
|
60
|
+
if input.kind_of? String
|
61
|
+
@reader = LibXML::XML::Reader.file(input)
|
62
|
+
elsif input.kind_of?(IO)
|
63
|
+
@reader = LibXML::XML::Reader.io(input)
|
64
|
+
else
|
65
|
+
throw "Unable to read from path or file"
|
66
|
+
end
|
67
|
+
|
68
|
+
@product_klass = product_klass
|
69
|
+
|
70
|
+
# create a sized queue to store each product read from the file
|
71
|
+
@queue = SizedQueue.new(100)
|
72
|
+
|
73
|
+
# launch a reader thread to process the file and store each
|
74
|
+
# product in the queue
|
75
|
+
Thread.abort_on_exception = true
|
76
|
+
Thread.new { read_input }
|
77
|
+
|
78
|
+
# TODO: this is a seriously hacky way to ensure the reading thread
|
79
|
+
# has enough time to read our metadata and header objects from
|
80
|
+
# the input stream. I should be making the constructor block until
|
81
|
+
# it has actively confirmed the data has been read
|
82
|
+
sleep 1
|
83
|
+
end
|
84
|
+
|
85
|
+
# Iterate over all the products in an ONIX file
|
86
|
+
#
|
87
|
+
def each(&block)
|
88
|
+
obj = @queue.pop
|
89
|
+
while !obj.nil?
|
90
|
+
yield obj
|
91
|
+
obj = @queue.pop
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
# Walk the ONIX file, and grab the bits we're interested in.
|
98
|
+
#
|
99
|
+
# High level attributes and the header are stored as attributes of the reader
|
100
|
+
# class. Products are placed in a queue, ready to be popped off when the
|
101
|
+
# user uses the each() method.
|
102
|
+
#
|
103
|
+
def read_input
|
104
|
+
while @reader.read == 1
|
105
|
+
@xml_lang = @reader.xml_lang if @xml_lang.nil?
|
106
|
+
@xml_version = @reader.xml_version.to_f if @xml_version.nil?
|
107
|
+
@encoding = @reader.encoding if @encoding.nil?
|
108
|
+
if @reader.node_type == 10
|
109
|
+
uri = @reader.expand.to_s
|
110
|
+
m, major, minor, rev = *uri.match(/.+(\d)\.(\d)\/(\d*).*/)
|
111
|
+
@version = [major.to_i, minor.to_i, rev.to_i]
|
112
|
+
elsif @reader.name == "Header" && @reader.node_type == 1
|
113
|
+
@header = ONIX::Header.parse(@reader.expand.to_s)
|
114
|
+
@reader.next_sibling
|
115
|
+
elsif @reader.name == "Product" && @reader.node_type == 1
|
116
|
+
node = @reader.expand
|
117
|
+
@queue.push @product_klass.parse(node.to_s)
|
118
|
+
@reader.next_sibling
|
119
|
+
end
|
120
|
+
end
|
121
|
+
@queue.push nil
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|