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 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]