onix 0.4.0 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +8 -0
- data/README.markdown +39 -0
- data/lib/onix.rb +3 -1
- data/lib/onix/apa_product.rb +125 -0
- data/lib/onix/decimal_type.rb +49 -0
- data/lib/onix/header.rb +1 -1
- data/lib/onix/measure.rb +9 -0
- data/lib/onix/product.rb +11 -1
- data/lib/onix/reader.rb +11 -7
- data/lib/onix/writer.rb +2 -5
- data/spec/product_spec.rb +24 -2
- metadata +5 -3
- data/README.rdoc +0 -30
data/CHANGELOG
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
v0.4.2 (XXX)
|
2
|
+
- Remove final remnants of REXML code
|
3
|
+
- Minor reordering of elements to match DTD
|
4
|
+
|
5
|
+
v0.4.1 (UNRELEASED)
|
6
|
+
- Added accesses to various product measurements. Height, weight, etc.
|
7
|
+
- Reduced time for an ONIX::Reader class to initialise
|
8
|
+
|
1
9
|
v0.4.0 (28th October 2008)
|
2
10
|
- Major rework: now based on ROXML instead of xml-mapping
|
3
11
|
- Mostly API Compatible
|
data/README.markdown
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
## ONIX
|
2
|
+
|
3
|
+
The ONIX standard is a somewhat verbose XML format that is rapidly becoming the
|
4
|
+
industry standard for electronic data sharing in the book and publishing
|
5
|
+
industries.
|
6
|
+
|
7
|
+
This library provides a slim layer over the format and simplifies both reading
|
8
|
+
and writing ONIX files in your ruby applications.
|
9
|
+
|
10
|
+
This replaces the obsolete rbook-onix gem that was spectacular in its crapness.
|
11
|
+
Let us never speak of it again.
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
gem install onix
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
See files in the examples directory to get started quickly. For further reading
|
20
|
+
view the comments to the following classes:
|
21
|
+
|
22
|
+
* ONIX::Reader - For reading ONIX files
|
23
|
+
* ONIX::Writer - For writing ONIX files
|
24
|
+
|
25
|
+
## Licensing
|
26
|
+
|
27
|
+
This library is distributed under the terms of the MIT License. See the included file for
|
28
|
+
more detail.
|
29
|
+
|
30
|
+
## Contributing
|
31
|
+
|
32
|
+
All suggestions and patches welcome, preferably via a git repository I can pull from.
|
33
|
+
To be honest, I'm not really expecting any, this is a niche library.
|
34
|
+
|
35
|
+
## Further Reading
|
36
|
+
|
37
|
+
- The source: [http://github.com/yob/onix/tree/master](http://github.com/yob/onix/tree/master)
|
38
|
+
- Rubyforge project: [http://rubyforge.org/projects/rbook/](http://rubyforge.org/projects/rbook/)
|
39
|
+
- The official specs [http://www.editeur.org/onix.html](http://www.editeur.org/onix.html)
|
data/lib/onix.rb
CHANGED
@@ -9,6 +9,7 @@ require 'roxml'
|
|
9
9
|
require 'andand'
|
10
10
|
|
11
11
|
# custom xml-mapping node types
|
12
|
+
require File.join(File.dirname(__FILE__), "onix", "decimal_type")
|
12
13
|
require File.join(File.dirname(__FILE__), "onix", "etext_type")
|
13
14
|
require File.join(File.dirname(__FILE__), "onix", "integer_type")
|
14
15
|
require File.join(File.dirname(__FILE__), "onix", "two_digit_type")
|
@@ -33,6 +34,7 @@ require File.join(File.dirname(__FILE__), "onix", "sales_restriction")
|
|
33
34
|
require File.join(File.dirname(__FILE__), "onix", "stock")
|
34
35
|
require File.join(File.dirname(__FILE__), "onix", "price")
|
35
36
|
require File.join(File.dirname(__FILE__), "onix", "supply_detail")
|
37
|
+
require File.join(File.dirname(__FILE__), "onix", "measure")
|
36
38
|
require File.join(File.dirname(__FILE__), "onix", "product")
|
37
39
|
require File.join(File.dirname(__FILE__), "onix", "reader")
|
38
40
|
require File.join(File.dirname(__FILE__), "onix", "writer")
|
@@ -49,7 +51,7 @@ module ONIX
|
|
49
51
|
module Version #:nodoc:
|
50
52
|
Major = 0
|
51
53
|
Minor = 4
|
52
|
-
Tiny =
|
54
|
+
Tiny = 2
|
53
55
|
|
54
56
|
String = [Major, Minor, Tiny].join('.')
|
55
57
|
end
|
data/lib/onix/apa_product.rb
CHANGED
@@ -11,6 +11,18 @@ module ONIX
|
|
11
11
|
delegate :publishing_status, :publishing_status=
|
12
12
|
delegate :publication_date, :publication_date=
|
13
13
|
|
14
|
+
def measurement_system
|
15
|
+
@measurement_system ||= :metric
|
16
|
+
end
|
17
|
+
|
18
|
+
def measurement_system=(value)
|
19
|
+
if value == :metric || value == :imperial
|
20
|
+
@measurement_system = value
|
21
|
+
else
|
22
|
+
raise ArgumentError, "#{value} is not a recognised measurement system"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
14
26
|
# retrieve the current EAN
|
15
27
|
def ean
|
16
28
|
identifier(3).andand.id_value
|
@@ -390,6 +402,98 @@ module ONIX
|
|
390
402
|
price_set(2, num)
|
391
403
|
end
|
392
404
|
|
405
|
+
# retrieve the height of the product
|
406
|
+
#
|
407
|
+
# If APAProduct#measurement_system is metric, these will be in mm, otherwise they
|
408
|
+
# will be in inches.
|
409
|
+
#
|
410
|
+
def height
|
411
|
+
# TODO: auto unit conversion
|
412
|
+
measurement(1).andand.measurement
|
413
|
+
end
|
414
|
+
|
415
|
+
# set the height of the book
|
416
|
+
#
|
417
|
+
# If APAProduct#measurement_system is metric, this should be in mm, otherwise it
|
418
|
+
# will be in inches.
|
419
|
+
#
|
420
|
+
def height=(value)
|
421
|
+
if measurement_system == :metric
|
422
|
+
measurement_set(1,value, "mm")
|
423
|
+
elsif measurement_system == :imperial
|
424
|
+
measurement_set(1,value, "in")
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
# retrieve the width of the product
|
429
|
+
#
|
430
|
+
# If APAProduct#measurement_system is metric, these will be in mm, otherwise they
|
431
|
+
# will be in inches.
|
432
|
+
#
|
433
|
+
def width
|
434
|
+
# TODO: auto unit conversion
|
435
|
+
measurement(2).andand.measurement
|
436
|
+
end
|
437
|
+
|
438
|
+
# set the width of the product
|
439
|
+
#
|
440
|
+
# If APAProduct#measurement_system is metric, this should be in mm, otherwise it
|
441
|
+
# will be in inches.
|
442
|
+
#
|
443
|
+
def width=(value)
|
444
|
+
if measurement_system == :metric
|
445
|
+
measurement_set(2,value, "mm")
|
446
|
+
elsif measurement_system == :imperial
|
447
|
+
measurement_set(2,value, "in")
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
# retrieve the weight of the product
|
452
|
+
#
|
453
|
+
# If APAProduct#measurement_system is metric, these will be in grams, otherwise they
|
454
|
+
# will be in ounces.
|
455
|
+
#
|
456
|
+
def weight
|
457
|
+
# TODO: auto unit conversion
|
458
|
+
measurement(8).andand.measurement
|
459
|
+
end
|
460
|
+
|
461
|
+
# set the weight of the product
|
462
|
+
#
|
463
|
+
# If APAProduct#measurement_system is metric, this should be in grams, otherwise it
|
464
|
+
# will be in ounces.
|
465
|
+
#
|
466
|
+
def weight=(value)
|
467
|
+
if measurement_system == :metric
|
468
|
+
measurement_set(8,value, "gr")
|
469
|
+
elsif measurement_system == :imperial
|
470
|
+
measurement_set(8,value, "oz")
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
474
|
+
# retrieve the thickness of the product
|
475
|
+
#
|
476
|
+
# If APAProduct#measurement_system is metric, these will be in mm, otherwise they
|
477
|
+
# will be in inches.
|
478
|
+
#
|
479
|
+
def thickness
|
480
|
+
# TODO: auto unit conversion
|
481
|
+
measurement(3).andand.measurement
|
482
|
+
end
|
483
|
+
|
484
|
+
# set the thickness of the product
|
485
|
+
#
|
486
|
+
# If APAProduct#measurement_system is metric, this should be in mm, otherwise it
|
487
|
+
# will be in inches.
|
488
|
+
#
|
489
|
+
def thickness=(value)
|
490
|
+
if measurement_system == :metric
|
491
|
+
measurement_set(3,value, "mm")
|
492
|
+
elsif measurement_system == :imperial
|
493
|
+
measurement_set(3,value, "in")
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
393
497
|
private
|
394
498
|
|
395
499
|
# add a new subject to this product
|
@@ -431,6 +535,27 @@ module ONIX
|
|
431
535
|
isbn_id.id_value = value
|
432
536
|
end
|
433
537
|
|
538
|
+
# retrieve the value of a particular measurement
|
539
|
+
def measurement(type)
|
540
|
+
product.measurements.find { |m| m.measure_type_code == type }
|
541
|
+
end
|
542
|
+
|
543
|
+
# set the value of a particular measurement
|
544
|
+
def measurement_set(type, value, unit)
|
545
|
+
measure = measurement(type)
|
546
|
+
|
547
|
+
# create a new isbn record if we need to
|
548
|
+
if measure.nil?
|
549
|
+
measure = ONIX::Measure.new
|
550
|
+
measure.measure_type_code = type
|
551
|
+
product.measurements << measure
|
552
|
+
end
|
553
|
+
|
554
|
+
# store the new value
|
555
|
+
measure.measurement = value
|
556
|
+
measure.measure_unit_code = unit.to_s
|
557
|
+
end
|
558
|
+
|
434
559
|
# retrieve the value of a particular media file
|
435
560
|
def media_file(type)
|
436
561
|
product.media_files.find { |m| m.media_file_type_code == type }
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
|
3
|
+
module ONIX
|
4
|
+
|
5
|
+
class DecimalType < ROXML::XMLRef # ::nodoc::
|
6
|
+
attr_reader :cdata, :content
|
7
|
+
|
8
|
+
def initialize(accessor, args, &block)
|
9
|
+
super(accessor, args, &block)
|
10
|
+
@content = args.content?
|
11
|
+
@cdata = args.cdata?
|
12
|
+
end
|
13
|
+
|
14
|
+
# Updates the text in the given _xml_ block to
|
15
|
+
# the _value_ provided.
|
16
|
+
def update_xml(xml, value)
|
17
|
+
parent = wrap(xml)
|
18
|
+
if value.kind_of?(BigDecimal)
|
19
|
+
value = value.to_s("F")
|
20
|
+
else
|
21
|
+
value = value.to_s
|
22
|
+
end
|
23
|
+
add(parent.child_add(LibXML::XML::Node.new_element(name)), value)
|
24
|
+
xml
|
25
|
+
end
|
26
|
+
|
27
|
+
def value(xml)
|
28
|
+
if content
|
29
|
+
value = BigDecimal.new(xml.content)
|
30
|
+
else
|
31
|
+
child = xml.search(name).first
|
32
|
+
value = BigDecimal.new(child.content) if child
|
33
|
+
end
|
34
|
+
block ? block.call(value) : value
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def add(dest, value)
|
40
|
+
if cdata
|
41
|
+
dest.child_add(LibXML::XML::Node.new_cdata(value.to_utf))
|
42
|
+
else
|
43
|
+
dest.content = value.to_utf
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
ROXML::TypeRegistry.register(:decimal, ONIX::DecimalType)
|
data/lib/onix/header.rb
CHANGED
@@ -4,11 +4,11 @@ module ONIX
|
|
4
4
|
|
5
5
|
xml_name "Header"
|
6
6
|
|
7
|
-
xml_accessor :from_person, :etext, :from => "FromPerson"
|
8
7
|
xml_accessor :from_ean_number, :etext, :from => "FromEANNumber"
|
9
8
|
xml_accessor :from_san, :etext, :from => "FromSAN"
|
10
9
|
xml_accessor :sender_identifiers, [ONIX::SenderIdentifier], :from => "SenderIdentifier"
|
11
10
|
xml_accessor :from_company, :etext, :from => "FromCompany"
|
11
|
+
xml_accessor :from_person, :etext, :from => "FromPerson"
|
12
12
|
xml_accessor :from_email, :etext, :from => "FromEmail"
|
13
13
|
xml_accessor :to_ean_number, :etext, :from => "ToEANNumber"
|
14
14
|
xml_accessor :to_san, :etext, :from => "ToSAN"
|
data/lib/onix/measure.rb
ADDED
data/lib/onix/product.rb
CHANGED
@@ -11,8 +11,8 @@ module ONIX
|
|
11
11
|
xml_accessor :series, :from => "Series"
|
12
12
|
xml_accessor :edition_number, :integer, :from => "EditionNumber"
|
13
13
|
xml_accessor :titles, [ONIX::Title], :from => "Title"
|
14
|
-
xml_accessor :websites, [ONIX::Website], :from => "Website"
|
15
14
|
xml_accessor :contributors, [ONIX::Contributor], :from => "Contributor"
|
15
|
+
xml_accessor :websites, [ONIX::Website], :from => "Website"
|
16
16
|
xml_accessor :number_of_pages, :integer, :from => "NumberOfPages"
|
17
17
|
xml_accessor :bic_main_subject, :from => "BICMainSubject"
|
18
18
|
xml_accessor :subjects, [ONIX::Subject], :from => "Subject"
|
@@ -24,7 +24,17 @@ module ONIX
|
|
24
24
|
xml_accessor :publication_date, :yyyymmdd, :from => "PublicationDate"
|
25
25
|
xml_accessor :year_first_published, :integer, :from => "YearFirstPublished"
|
26
26
|
xml_accessor :sales_restrictions, [ONIX::SalesRestriction], :from => "SalesRestriction"
|
27
|
+
xml_accessor :measurements, [ONIX::Measure], :from => "Measure"
|
27
28
|
xml_accessor :supply_details, [ONIX::SupplyDetail], :from => "SupplyDetail"
|
28
29
|
|
30
|
+
# some deprecated attributes. Read only
|
31
|
+
# - See the measures array for the current way of specifying
|
32
|
+
# various measurements of the product
|
33
|
+
xml_reader :height, :decimal, :from => "Height"
|
34
|
+
xml_reader :width, :decimal, :from => "Width"
|
35
|
+
xml_reader :thickness, :decimal, :from => "Thickness"
|
36
|
+
xml_reader :weight, :decimal, :from => "Weight"
|
37
|
+
xml_reader :dimensions, :etext, :from => "Dimensions"
|
38
|
+
|
29
39
|
end
|
30
40
|
end
|
data/lib/onix/reader.rb
CHANGED
@@ -68,18 +68,22 @@ module ONIX
|
|
68
68
|
@product_klass = product_klass
|
69
69
|
|
70
70
|
# create a sized queue to store each product read from the file
|
71
|
+
# We use a separate thread to read products from the source file.
|
72
|
+
# This queue is a thread-safe way to transfer products from that
|
73
|
+
# thread back into the main one.
|
71
74
|
@queue = SizedQueue.new(100)
|
72
75
|
|
73
|
-
# launch a reader thread
|
74
|
-
# product in the queue
|
76
|
+
# launch a reader thread
|
75
77
|
Thread.abort_on_exception = true
|
76
78
|
Thread.new { read_input }
|
77
79
|
|
78
|
-
#
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
|
80
|
+
# don't return from the constructor until the reading thread
|
81
|
+
# has spun up and put at least one item into the queue. If
|
82
|
+
# it finds no Products in the file, it queues a nil, so we
|
83
|
+
# shouldn't get stuck here indefinitely
|
84
|
+
while @queue.size == 0
|
85
|
+
sleep 0.05
|
86
|
+
end
|
83
87
|
end
|
84
88
|
|
85
89
|
# Iterate over all the products in an ONIX file
|
data/lib/onix/writer.rb
CHANGED
@@ -73,11 +73,8 @@ module ONIX
|
|
73
73
|
private
|
74
74
|
|
75
75
|
def start_document
|
76
|
-
|
77
|
-
|
78
|
-
decl.encoding = "utf-8"
|
79
|
-
@output.write(decl.to_s+"\n")
|
80
|
-
@output.write(doctype.to_s+"\n")
|
76
|
+
@output.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")
|
77
|
+
@output.write("<!DOCTYPE ONIXMessage SYSTEM \"#{DOCTYPE}\">\n")
|
81
78
|
@output.write("<ONIXMessage>\n")
|
82
79
|
@output.write(@header.to_xml.to_s)
|
83
80
|
@output.write("\n")
|
data/spec/product_spec.rb
CHANGED
@@ -8,8 +8,8 @@ require 'date'
|
|
8
8
|
context "ONIX::Product" do
|
9
9
|
|
10
10
|
before(:each) do
|
11
|
-
data_path = File.join(File.dirname(__FILE__),"..","data")
|
12
|
-
file1 = File.join(data_path, "product.xml")
|
11
|
+
@data_path = File.join(File.dirname(__FILE__),"..","data")
|
12
|
+
file1 = File.join(@data_path, "product.xml")
|
13
13
|
@doc = LibXML::XML::Document.file(file1)
|
14
14
|
@product_node = @doc.root
|
15
15
|
end
|
@@ -26,6 +26,13 @@ context "ONIX::Product" do
|
|
26
26
|
product.publishing_status.should eql(4)
|
27
27
|
product.publication_date.should eql(Date.civil(1998,9,1))
|
28
28
|
product.year_first_published.should eql(1998)
|
29
|
+
|
30
|
+
# including ye olde, deprecated ones
|
31
|
+
product.height.should eql(100)
|
32
|
+
product.width.should eql(BigDecimal.new("200.5"))
|
33
|
+
product.weight.should eql(300)
|
34
|
+
product.thickness.should eql(300)
|
35
|
+
product.dimensions.should eql("100x200")
|
29
36
|
end
|
30
37
|
|
31
38
|
specify "should provide read access to product IDs" do
|
@@ -43,6 +50,11 @@ context "ONIX::Product" do
|
|
43
50
|
product.subjects.size.should eql(1)
|
44
51
|
end
|
45
52
|
|
53
|
+
specify "should provide read access to measurements" do
|
54
|
+
product = ONIX::Product.parse(@product_node.to_s)
|
55
|
+
product.measurements.size.should eql(1)
|
56
|
+
end
|
57
|
+
|
46
58
|
specify "should provide write access to first level attibutes" do
|
47
59
|
product = ONIX::Product.new
|
48
60
|
|
@@ -72,4 +84,14 @@ context "ONIX::Product" do
|
|
72
84
|
product.year_first_published = 1998
|
73
85
|
product.year_first_published.should eql(1998)
|
74
86
|
end
|
87
|
+
|
88
|
+
specify "should correctly parse files that have non-standard entties"
|
89
|
+
=begin
|
90
|
+
do
|
91
|
+
file = File.join(@data_path, "extra_entities.xml")
|
92
|
+
product = ONIX::Product.parse(File.read(file))
|
93
|
+
|
94
|
+
product.titles.first.title_text.should eql("Ipod® & Itunes® for Dummies®, 4th Edition")
|
95
|
+
end
|
96
|
+
=end
|
75
97
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: onix
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Healy
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-11-01 00:00:00 +11:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -78,8 +78,10 @@ files:
|
|
78
78
|
- lib/onix/two_digit_type.rb
|
79
79
|
- lib/onix/writer.rb
|
80
80
|
- lib/onix/etext_type.rb
|
81
|
+
- lib/onix/measure.rb
|
82
|
+
- lib/onix/decimal_type.rb
|
81
83
|
- lib/onix.rb
|
82
|
-
- README.
|
84
|
+
- README.markdown
|
83
85
|
- TODO
|
84
86
|
- CHANGELOG
|
85
87
|
has_rdoc: true
|
data/README.rdoc
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
== ONIX
|
2
|
-
|
3
|
-
The ONIX standard is a somewhat verbose XML format that is rapidly becoming the
|
4
|
-
industry standard for electronic data sharing in the book and publishing
|
5
|
-
industries.
|
6
|
-
|
7
|
-
This library provides a slim layer over the format and simplifies both reading
|
8
|
-
and writing ONIX files in your ruby applications.
|
9
|
-
|
10
|
-
== Installation
|
11
|
-
|
12
|
-
gem install onix
|
13
|
-
|
14
|
-
== Usage
|
15
|
-
|
16
|
-
See files in the examples directory to get started quickly. For further reading
|
17
|
-
view the comments to the following classes:
|
18
|
-
|
19
|
-
* ONIX::Reader - For reading ONIX files
|
20
|
-
* ONIX::Writer - For writing ONIX files
|
21
|
-
|
22
|
-
== Contributing
|
23
|
-
|
24
|
-
All suggestions and patches welcome, preferably via a git repository I can pull from.
|
25
|
-
|
26
|
-
== Further Reading
|
27
|
-
|
28
|
-
- The source: (http://github.com/yob/onix/tree/master)[http://github.com/yob/onix/tree/master]
|
29
|
-
- Rubyforge project: (http://rubyforge.org/projects/rbook/)[http://rubyforge.org/projects/rbook/]
|
30
|
-
- The official specs (http://www.editeur.org/onix.html)[http://www.editeur.org/onix.html]
|