onix 0.4.0 → 0.4.2

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 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
@@ -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)
@@ -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 = 0
54
+ Tiny = 2
53
55
 
54
56
  String = [Major, Minor, Tiny].join('.')
55
57
  end
@@ -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)
@@ -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"
@@ -0,0 +1,9 @@
1
+ module ONIX
2
+ class Measure
3
+ include ROXML
4
+
5
+ xml_accessor :measure_type_code, :twodigit, :from => "MeasureTypeCode"
6
+ xml_accessor :measurement, :decimal, :from => "Measurement"
7
+ xml_accessor :measure_unit_code, :etext, :from => "MeasureUnitCode"
8
+ end
9
+ end
@@ -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
@@ -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 to process the file and store each
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
- # 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
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
@@ -73,11 +73,8 @@ module ONIX
73
73
  private
74
74
 
75
75
  def start_document
76
- decl = REXML::XMLDecl.new
77
- doctype = REXML::DocType.new('ONIXMessage', "SYSTEM \"#{DOCTYPE}\"")
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")
@@ -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.0
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-10-28 00:00:00 +11:00
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.rdoc
84
+ - README.markdown
83
85
  - TODO
84
86
  - CHANGELOG
85
87
  has_rdoc: true
@@ -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]