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.
@@ -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("&","&amp;").gsub("<","&lt;").gsub(">","&gt;")
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("&amp;","&").gsub("&lt;","<").gsub("&gt;",">") 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)
@@ -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
@@ -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
@@ -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,8 @@
1
+ module ONIX
2
+ class ProductIdentifier
3
+ include ROXML
4
+
5
+ xml_accessor :product_id_type, :twodigit, :from => "ProductIDType"
6
+ xml_accessor :id_value, :from => "IDValue"
7
+ end
8
+ 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
@@ -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