im_onix 1.0.2 → 1.1.1
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.
- checksums.yaml +4 -4
- data/.gitignore +4 -0
- data/.yardopts +1 -0
- data/Gemfile +8 -0
- data/LICENSE.md +7 -0
- data/README.md +3 -3
- data/Rakefile +10 -0
- data/bin/html_codelist_to_yml.rb +14 -15
- data/bin/onix_bench.rb +1 -0
- data/bin/onix_pp.rb +4 -8
- data/bin/onix_serialize.rb +27 -0
- data/doc-src/handlers.rb +154 -0
- data/im_onix.gemspec +32 -0
- data/lib/im_onix.rb +0 -1
- data/lib/onix/addressee.rb +10 -0
- data/lib/onix/code.rb +108 -282
- data/lib/onix/collateral_detail.rb +24 -17
- data/lib/onix/collection.rb +38 -0
- data/lib/onix/collection_sequence.rb +7 -0
- data/lib/onix/contributor.rb +40 -39
- data/lib/onix/date.rb +73 -109
- data/lib/onix/descriptive_detail.rb +90 -417
- data/lib/onix/discount_coded.rb +3 -16
- data/lib/onix/entity.rb +28 -62
- data/lib/onix/epub_usage_constraint.rb +7 -0
- data/lib/onix/epub_usage_limit.rb +6 -0
- data/lib/onix/extent.rb +39 -0
- data/lib/onix/helper.rb +25 -25
- data/lib/onix/identifier.rb +13 -54
- data/lib/onix/language.rb +8 -0
- data/lib/onix/market.rb +5 -0
- data/lib/onix/market_publishing_detail.rb +20 -0
- data/lib/onix/onix21.rb +76 -139
- data/lib/onix/onix_message.rb +87 -100
- data/lib/onix/price.rb +19 -39
- data/lib/onix/product.rb +141 -637
- data/lib/onix/product_form_feature.rb +7 -0
- data/lib/onix/product_part.rb +89 -0
- data/lib/onix/product_supplies_extractor.rb +275 -0
- data/lib/onix/product_supply.rb +17 -58
- data/lib/onix/publishing_detail.rb +16 -32
- data/lib/onix/related_material.rb +4 -3
- data/lib/onix/related_product.rb +9 -29
- data/lib/onix/related_work.rb +3 -17
- data/lib/onix/sales_outlet.rb +2 -10
- data/lib/onix/sales_restriction.rb +8 -21
- data/lib/onix/sales_rights.rb +1 -5
- data/lib/onix/sender.rb +12 -0
- data/lib/onix/serializer.rb +156 -0
- data/lib/onix/subject.rb +9 -30
- data/lib/onix/subset.rb +88 -78
- data/lib/onix/supply_detail.rb +42 -0
- data/lib/onix/supporting_resource.rb +29 -86
- data/lib/onix/tax.rb +9 -18
- data/lib/onix/territory.rb +23 -17
- data/lib/onix/title_detail.rb +22 -0
- data/lib/onix/title_element.rb +32 -0
- data/lib/onix/website.rb +3 -16
- metadata +53 -34
@@ -1,45 +1,41 @@
|
|
1
1
|
module ONIX
|
2
2
|
class TextContent < SubsetDSL
|
3
|
-
element "TextType", :subset
|
3
|
+
element "TextType", :subset, :shortcut => :type
|
4
4
|
element "ContentAudience", :subset
|
5
5
|
element "Text", :text
|
6
6
|
element "TextAuthor", :text
|
7
7
|
element "SourceTitle", :text
|
8
8
|
|
9
|
-
scope :description, lambda { human_code_match(:text_type, "Description")}
|
10
|
-
scope :short_description, lambda { human_code_match(:text_type, "ShortDescriptionannotation")}
|
11
|
-
|
12
|
-
# shortcuts
|
13
|
-
def type
|
14
|
-
@text_type
|
15
|
-
end
|
9
|
+
scope :description, lambda { human_code_match(:text_type, "Description") }
|
10
|
+
scope :short_description, lambda { human_code_match(:text_type, "ShortDescriptionannotation") }
|
16
11
|
end
|
17
12
|
|
18
13
|
class CollateralDetail < SubsetDSL
|
19
14
|
elements "TextContent", :subset
|
20
15
|
elements "SupportingResource", :subset
|
21
16
|
|
17
|
+
# @!group High level
|
18
|
+
|
19
|
+
# product description string including HTML
|
20
|
+
# @return [String]
|
22
21
|
def description
|
23
|
-
desc_contents
|
22
|
+
desc_contents = @text_contents.description + @text_contents.short_description
|
24
23
|
if desc_contents.length > 0
|
25
24
|
desc_contents.first.text
|
26
|
-
else
|
27
|
-
nil
|
28
25
|
end
|
29
26
|
end
|
30
27
|
|
31
|
-
# largest frontcover if multiple
|
32
28
|
def frontcover_resource
|
33
|
-
fc
|
29
|
+
fc = @supporting_resources.front_cover
|
34
30
|
if fc.length > 0
|
35
31
|
if fc.length > 1
|
36
|
-
best_found=fc.select{|c| c.versions.last and c.versions.last.image_width}.sort { |c1, c2| c2.versions.last.image_width <=> c1.versions.last.image_width }.first
|
32
|
+
best_found = fc.select { |c| c.versions.last and c.versions.last.image_width }.sort { |c1, c2| c2.versions.last.image_width <=> c1.versions.last.image_width }.first
|
37
33
|
if best_found
|
38
34
|
# we take larger one
|
39
35
|
best_found.versions.last
|
40
36
|
else
|
41
37
|
# we try first that is not gif
|
42
|
-
fc.select{|sr| not sr.versions.last.file_format=="Gif"}.first.versions.last
|
38
|
+
fc.select { |sr| not sr.versions.last.file_format == "Gif" }.first.versions.last
|
43
39
|
end
|
44
40
|
else
|
45
41
|
fc.first.versions.last
|
@@ -47,18 +43,22 @@ module ONIX
|
|
47
43
|
end
|
48
44
|
end
|
49
45
|
|
46
|
+
# product larger front cover URL string
|
47
|
+
# @return [String]
|
50
48
|
def frontcover_url
|
51
49
|
if self.frontcover_resource
|
52
50
|
self.frontcover_resource.links.first.strip
|
53
51
|
end
|
54
52
|
end
|
55
53
|
|
54
|
+
# product larger front cover last updated date
|
56
55
|
def frontcover_last_updated
|
57
56
|
if self.frontcover_resource
|
58
|
-
|
57
|
+
self.frontcover_resource.last_updated
|
59
58
|
end
|
60
59
|
end
|
61
60
|
|
61
|
+
# product larger front cover mimetype
|
62
62
|
def frontcover_mimetype
|
63
63
|
if self.frontcover_resource
|
64
64
|
self.frontcover_resource.file_mimetype
|
@@ -66,29 +66,36 @@ module ONIX
|
|
66
66
|
end
|
67
67
|
|
68
68
|
def epub_sample_resource
|
69
|
-
es
|
69
|
+
es = @supporting_resources.sample_content.select { |sr| sr.versions.last and sr.versions.last.file_format == "Epub" }.first
|
70
70
|
if es
|
71
71
|
es.versions.last
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
|
+
# Epub sample URL
|
76
|
+
# @return [String]
|
75
77
|
def epub_sample_url
|
76
78
|
if self.epub_sample_resource
|
77
79
|
self.epub_sample_resource.links.first.strip
|
78
80
|
end
|
79
81
|
end
|
80
82
|
|
83
|
+
# Epub sample last updated
|
84
|
+
# @return [Date]
|
81
85
|
def epub_sample_last_updated
|
82
86
|
if self.epub_sample_resource
|
83
87
|
self.epub_sample_resource.last_updated
|
84
88
|
end
|
85
89
|
end
|
86
90
|
|
91
|
+
# Epub sample mimetype
|
92
|
+
# @return [String]
|
87
93
|
def epub_sample_mimetype
|
88
94
|
if self.epub_sample_resource
|
89
95
|
self.epub_sample_resource.file_mimetype
|
90
96
|
end
|
91
97
|
end
|
92
98
|
|
99
|
+
# @!endgroup
|
93
100
|
end
|
94
101
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module ONIX
|
2
|
+
class Collection < SubsetDSL
|
3
|
+
element "CollectionType", :subset, :shortcut => :type
|
4
|
+
elements "CollectionIdentifier", :subset, :shortcut => :identifiers
|
5
|
+
elements "TitleDetail", :subset
|
6
|
+
elements "CollectionSequence", :subset, :shortcut => :sequences
|
7
|
+
|
8
|
+
scope :publisher, lambda { human_code_match(:collection_type, "PublisherCollection") }
|
9
|
+
|
10
|
+
# @!group High level
|
11
|
+
|
12
|
+
# collection title string
|
13
|
+
def title
|
14
|
+
if collection_title_element
|
15
|
+
collection_title_element.title
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# collection subtitle string
|
20
|
+
def subtitle
|
21
|
+
if collection_title_element
|
22
|
+
collection_title_element.subtitle
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def collection_title_element
|
27
|
+
distinctive_title = @title_details.distinctive_title.first
|
28
|
+
#select { |td| td.type.human=~/DistinctiveTitle/}.first
|
29
|
+
if distinctive_title
|
30
|
+
distinctive_title.title_elements.collection_level.first
|
31
|
+
#select { |te| te.level.human=~/CollectionLevel/ or te.level.human=~/Subcollection/ }.first
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# @!endgroup
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
data/lib/onix/contributor.rb
CHANGED
@@ -4,72 +4,73 @@ require 'onix/website'
|
|
4
4
|
module ONIX
|
5
5
|
class Contributor < SubsetDSL
|
6
6
|
element "SequenceNumber", :integer
|
7
|
-
element "ContributorRole", :subset
|
8
|
-
elements "NameIdentifier", :subset
|
9
|
-
|
7
|
+
element "ContributorRole", :subset, :shortcut => :role
|
8
|
+
elements "NameIdentifier", :subset, :shortcut => :identifiers
|
10
9
|
element "PersonName", :text
|
11
10
|
element "PersonNameInverted", :text
|
12
|
-
|
13
|
-
element "NamesBeforeKey", :text
|
11
|
+
element "NamesBeforeKey", :text, :shortcut => :name_before_key
|
14
12
|
element "KeyNames", :text
|
15
|
-
|
13
|
+
element "CorporateName", :text
|
14
|
+
element "CorporateNameInverted", :text
|
16
15
|
element "BiographicalNote", :text
|
17
16
|
elements "Website", :subset
|
18
|
-
element "ContributorPlace", :subset
|
19
|
-
|
20
|
-
def role
|
21
|
-
@contributor_role
|
22
|
-
end
|
23
|
-
|
24
|
-
def identifiers
|
25
|
-
@name_identifiers
|
26
|
-
end
|
27
|
-
|
28
|
-
def place
|
29
|
-
@contributor_place
|
30
|
-
end
|
31
|
-
|
32
|
-
def name_before_key
|
33
|
-
@names_before_key
|
34
|
-
end
|
17
|
+
element "ContributorPlace", :subset, :shortcut => :place
|
18
|
+
elements "ContributorDate", :subset, :shortcut => :dates
|
35
19
|
|
36
|
-
#
|
20
|
+
# @!group High level
|
37
21
|
# flatten person name (firstname lastname)
|
22
|
+
# @return [String]
|
38
23
|
def name
|
39
|
-
if @person_name
|
40
|
-
|
41
|
-
|
42
|
-
if @
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
@key_names
|
47
|
-
end
|
24
|
+
return @person_name if @person_name
|
25
|
+
|
26
|
+
if @key_names
|
27
|
+
if @names_before_key
|
28
|
+
return "#{@names_before_key} #{@key_names}"
|
29
|
+
else
|
30
|
+
return @key_names
|
48
31
|
end
|
49
32
|
end
|
33
|
+
|
34
|
+
@corporate_name
|
50
35
|
end
|
51
36
|
|
52
|
-
# :category: High level
|
53
37
|
# inverted flatten person name
|
38
|
+
# @return [String]
|
54
39
|
def inverted_name
|
55
|
-
@person_name_inverted
|
40
|
+
@person_name_inverted || @corporate_name_inverted
|
56
41
|
end
|
57
42
|
|
58
|
-
# :category: High level
|
59
43
|
# biography string with HTML
|
44
|
+
# @return [String]
|
60
45
|
def biography
|
61
46
|
@biographical_note
|
62
47
|
end
|
63
48
|
|
64
|
-
# :category: High level
|
65
49
|
# raw biography string without HTML
|
50
|
+
# @return [String]
|
66
51
|
def raw_biography
|
67
52
|
if self.biography
|
68
53
|
Helper.strip_html(self.biography).gsub(/\s+/, " ")
|
69
|
-
else
|
70
|
-
nil
|
71
54
|
end
|
72
55
|
end
|
56
|
+
|
57
|
+
# date of birth
|
58
|
+
# @return [Time]
|
59
|
+
def birth_date
|
60
|
+
if contributor_date = @contributor_dates.find { |d| d.role.human == "DateOfBirth" }
|
61
|
+
contributor_date.date.to_time
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# date of death
|
66
|
+
# @return [Time]
|
67
|
+
def death_date
|
68
|
+
if contributor_date = @contributor_dates.find { |d| d.role.human == "DateOfDeath" }
|
69
|
+
contributor_date.date.to_time
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# @!endgroup
|
73
74
|
end
|
74
75
|
|
75
76
|
class ContributorPlace < SubsetDSL
|
data/lib/onix/date.rb
CHANGED
@@ -1,18 +1,17 @@
|
|
1
1
|
module ONIX
|
2
|
-
|
3
2
|
module DateHelper
|
4
3
|
attr_accessor :date_format, :date
|
5
4
|
|
6
5
|
def parse_date(n)
|
7
|
-
@date_format=DateFormat.from_code("00")
|
8
|
-
date_txt=nil
|
9
|
-
@date=nil
|
6
|
+
@date_format = DateFormat.from_code("00")
|
7
|
+
date_txt = nil
|
8
|
+
@date = nil
|
10
9
|
n.elements.each do |t|
|
11
10
|
case t
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
when tag_match("DateFormat")
|
12
|
+
@date_format = DateFormat.parse(t)
|
13
|
+
when tag_match("Date")
|
14
|
+
date_txt = t.text
|
16
15
|
end
|
17
16
|
|
18
17
|
if t["dateformat"]
|
@@ -20,29 +19,29 @@ module ONIX
|
|
20
19
|
end
|
21
20
|
end
|
22
21
|
|
23
|
-
code_format=format_from_code(@date_format.code)
|
24
|
-
text_format=format_from_string(date_txt)
|
22
|
+
code_format = format_from_code(@date_format.code)
|
23
|
+
text_format = format_from_string(date_txt)
|
25
24
|
|
26
|
-
format=code_format
|
25
|
+
format = code_format
|
27
26
|
|
28
|
-
if code_format!=text_format
|
29
|
-
# puts "EEE date #{n.text} (#{text_format}) do not match code #{@format.code} (#{code_format})"
|
30
|
-
format=text_format
|
27
|
+
if code_format != text_format
|
28
|
+
# puts "EEE date #{n.text} (#{text_format}) do not match code #{@format.code} (#{code_format})"
|
29
|
+
format = text_format
|
31
30
|
end
|
32
31
|
|
33
32
|
begin
|
34
33
|
if format
|
35
34
|
case @date_format.code
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
35
|
+
when "00"
|
36
|
+
@date = Date.strptime(date_txt, format)
|
37
|
+
when "01"
|
38
|
+
@date = Date.strptime(date_txt, format)
|
39
|
+
when "05"
|
40
|
+
@date = Date.strptime(date_txt, format)
|
41
|
+
when "14"
|
42
|
+
@date = Time.strptime(date_txt, format)
|
43
|
+
else
|
44
|
+
@date = nil
|
46
45
|
end
|
47
46
|
end
|
48
47
|
rescue
|
@@ -52,33 +51,33 @@ module ONIX
|
|
52
51
|
|
53
52
|
def format_from_code(code)
|
54
53
|
case code
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
54
|
+
when "00"
|
55
|
+
"%Y%m%d"
|
56
|
+
when "01"
|
57
|
+
"%Y%m"
|
58
|
+
when "05"
|
59
|
+
"%Y"
|
60
|
+
when "14"
|
61
|
+
"%Y%m%dT%H%M%S%z"
|
62
|
+
else
|
63
|
+
nil
|
65
64
|
end
|
66
65
|
end
|
67
66
|
|
68
67
|
def format_from_string(str)
|
69
68
|
case str
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
69
|
+
when /^\d{4}\d{2}\d{2}T\d{2}\d{2}\d{2}/
|
70
|
+
"%Y%m%dT%H%M%S%z"
|
71
|
+
when /^\d{4}\-\d{2}\-\d{2}$/
|
72
|
+
"%Y-%m-%d"
|
73
|
+
when /^\d{4}\d{2}\d{2}$/
|
74
|
+
"%Y%m%d"
|
75
|
+
when /^\d{4}\d{2}$/
|
76
|
+
"%Y%m"
|
77
|
+
when /^\d{4}$/
|
78
|
+
"%Y"
|
79
|
+
else
|
80
|
+
nil
|
82
81
|
end
|
83
82
|
end
|
84
83
|
|
@@ -87,17 +86,8 @@ module ONIX
|
|
87
86
|
end
|
88
87
|
end
|
89
88
|
|
90
|
-
class
|
89
|
+
class BaseDate < SubsetDSL
|
91
90
|
include DateHelper
|
92
|
-
element "Date", :ignore
|
93
|
-
element "DateFormat", :ignore
|
94
|
-
element "MarketDateRole", :subset
|
95
|
-
|
96
|
-
scope :availability, lambda { human_code_match(:market_date_role, ["PublicationDate","EmbargoDate"])}
|
97
|
-
|
98
|
-
def role
|
99
|
-
@market_date_role
|
100
|
-
end
|
101
91
|
|
102
92
|
def parse(n)
|
103
93
|
super
|
@@ -105,79 +95,53 @@ module ONIX
|
|
105
95
|
end
|
106
96
|
end
|
107
97
|
|
108
|
-
class
|
109
|
-
include DateHelper
|
98
|
+
class MarketDate < BaseDate
|
110
99
|
element "Date", :ignore
|
111
100
|
element "DateFormat", :ignore
|
112
|
-
element "
|
113
|
-
|
114
|
-
scope :from_date, lambda { human_code_match(:price_date_role, "FromDate")}
|
115
|
-
scope :until_date, lambda { human_code_match(:price_date_role, "UntilDate")}
|
101
|
+
element "MarketDateRole", :subset, :shortcut => :role
|
116
102
|
|
117
|
-
|
118
|
-
@price_date_role
|
119
|
-
end
|
120
|
-
|
121
|
-
def parse(n)
|
122
|
-
super
|
123
|
-
parse_date(n)
|
124
|
-
end
|
103
|
+
scope :availability, lambda { human_code_match(:market_date_role, ["PublicationDate", "EmbargoDate"]) }
|
125
104
|
end
|
126
105
|
|
127
|
-
class
|
128
|
-
include DateHelper
|
106
|
+
class PriceDate < BaseDate
|
129
107
|
element "Date", :ignore
|
130
108
|
element "DateFormat", :ignore
|
131
|
-
element "
|
132
|
-
|
133
|
-
scope :availability, lambda { human_code_match(:supply_date_role, ["ExpectedAvailabilityDate","EmbargoDate"])}
|
109
|
+
element "PriceDateRole", :subset, :shortcut => :role
|
134
110
|
|
135
|
-
|
136
|
-
|
137
|
-
end
|
138
|
-
|
139
|
-
def parse(n)
|
140
|
-
super
|
141
|
-
parse_date(n)
|
142
|
-
end
|
111
|
+
scope :from_date, lambda { human_code_match(:price_date_role, "FromDate") }
|
112
|
+
scope :until_date, lambda { human_code_match(:price_date_role, "UntilDate") }
|
143
113
|
end
|
144
114
|
|
145
|
-
class
|
146
|
-
include DateHelper
|
115
|
+
class SupplyDate < BaseDate
|
147
116
|
element "Date", :ignore
|
148
117
|
element "DateFormat", :ignore
|
149
|
-
element "
|
118
|
+
element "SupplyDateRole", :subset, :shortcut => :role
|
150
119
|
|
151
|
-
scope :
|
152
|
-
|
153
|
-
scope :preorder_embargo, lambda { human_code_match(:publishing_date_role, "PreorderEmbargoDate")}
|
154
|
-
scope :public_announcement, lambda { human_code_match(:publishing_date_role, "PublicAnnouncementDate")}
|
120
|
+
scope :availability, lambda { human_code_match(:supply_date_role, ["ExpectedAvailabilityDate", "EmbargoDate"]) }
|
121
|
+
end
|
155
122
|
|
156
|
-
|
157
|
-
|
158
|
-
|
123
|
+
class PublishingDate < BaseDate
|
124
|
+
element "Date", :ignore
|
125
|
+
element "DateFormat", :ignore
|
126
|
+
element "PublishingDateRole", :subset, :shortcut => :role
|
159
127
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
128
|
+
scope :publication, lambda { human_code_match(:publishing_date_role, ["PublicationDate", "PublicationDateOfPrintCounterpart"]) }
|
129
|
+
scope :embargo, lambda { human_code_match(:publishing_date_role, "EmbargoDate") }
|
130
|
+
scope :preorder_embargo, lambda { human_code_match(:publishing_date_role, "PreorderEmbargoDate") }
|
131
|
+
scope :public_announcement, lambda { human_code_match(:publishing_date_role, "PublicAnnouncementDate") }
|
164
132
|
end
|
165
133
|
|
166
|
-
class ContentDate <
|
167
|
-
include DateHelper
|
134
|
+
class ContentDate < BaseDate
|
168
135
|
element "Date", :ignore
|
169
136
|
element "DateFormat", :ignore
|
170
|
-
element "ContentDateRole", :subset
|
171
|
-
|
172
|
-
scope :last_updated, lambda { human_code_match(:content_date_role, "LastUpdated")}
|
137
|
+
element "ContentDateRole", :subset, :shortcut => :role
|
173
138
|
|
174
|
-
|
175
|
-
|
176
|
-
end
|
139
|
+
scope :last_updated, lambda { human_code_match(:content_date_role, "LastUpdated") }
|
140
|
+
end
|
177
141
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
142
|
+
class ContributorDate < BaseDate
|
143
|
+
element "Date", :ignore
|
144
|
+
element "DateFormat", :ignore
|
145
|
+
element "ContributorDateRole", :subset, :shortcut => :role
|
182
146
|
end
|
183
147
|
end
|