music_ids 0.2.0 → 0.3.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.
- checksums.yaml +4 -4
- data/lib/music_ids/grid.rb +18 -77
- data/lib/music_ids/id.rb +105 -0
- data/lib/music_ids/isrc.rb +18 -78
- data/lib/music_ids/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ab2b1cbddfb673d888c9dbf7f493cf87b4536c99
|
4
|
+
data.tar.gz: 7a3eb549ec25b0cce0af4424e7f981d237b9eb8f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b955c505b78a891e7ca4fc2f03d9baddb7506e0c6faf97af297b17ec5b5594184ea41e587b24bf5cbabcad2a0a24a85be1ef1cd3129e9fc1a059dea3dcd10e66
|
7
|
+
data.tar.gz: eb5119e0309675bd2abf35eb71297f25e65f7ea95a87fa8387f91d745c6a48a73b377b938ac26b80fbcac5eb017a0e45ff3c61a3b4363bc3674bcd01c30b11b9
|
data/lib/music_ids/grid.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'music_ids/id'
|
2
|
+
|
1
3
|
module MusicIds
|
2
4
|
# The GRid class represents a Global Release Identifier, and
|
3
5
|
# provides simple methods to parse and re-present them.
|
@@ -8,7 +10,7 @@ module MusicIds
|
|
8
10
|
# often used as key identifiers in the delivery and reporting of digital
|
9
11
|
# music products.
|
10
12
|
#
|
11
|
-
# You can get more details from
|
13
|
+
# You can get more details from
|
12
14
|
# https://en.wikipedia.org/wiki/Global_Release_Identifier and
|
13
15
|
# http://www.ifpi.org/grid.php
|
14
16
|
#
|
@@ -33,112 +35,51 @@ module MusicIds
|
|
33
35
|
# instance that will return <tt>false</tt> from <tt>#ok?</tt> and will return
|
34
36
|
# nil from all the component methods like <tt>#issuer</tt>
|
35
37
|
class GRid
|
36
|
-
|
37
|
-
WELL_FORMED_INPUT = /\AA1-?[A-Z0-9]{5}-?[A-Z0-9]{10}-?[A-Z0-9]\Z/
|
38
|
-
|
39
|
-
class << self
|
40
|
-
# Parse a GRid string into a GRid instance
|
41
|
-
#
|
42
|
-
# @param input [String] The input GRid string to parse
|
43
|
-
# @param opts [Hash] Parsing options
|
44
|
-
# @option opts [true, false] :relaxed (false) Whether to parse in relaxed mode
|
45
|
-
# @return [GRid] the grid instance
|
46
|
-
def parse(input, opts = {})
|
47
|
-
input = input.to_s.upcase
|
48
|
-
opts[:relaxed] ? parse_relaxed(input) : parse_strict(input)
|
49
|
-
end
|
50
|
-
|
51
|
-
private
|
52
|
-
|
53
|
-
def parse_strict(input)
|
54
|
-
if WELL_FORMED_INPUT.match(input)
|
55
|
-
new(input.gsub('-', ''))
|
56
|
-
else
|
57
|
-
raise ArgumentError, "'#{input}' is not the right length to be a 12- or 15-character ISRC"
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def parse_relaxed(input)
|
62
|
-
if WELL_FORMED_INPUT.match(input)
|
63
|
-
new(input.gsub('-', ''))
|
64
|
-
else
|
65
|
-
new(input, ok: false)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
38
|
+
include Id
|
69
39
|
|
70
|
-
#
|
71
|
-
|
72
|
-
|
73
|
-
# @option opts [true, false] :ok (true) Whether the GRid is well-formed or not
|
74
|
-
def initialize(grid_string, opts = {ok: true})
|
75
|
-
@grid_string = grid_string.dup.freeze
|
76
|
-
@ok = opts[:ok] ? true : false
|
40
|
+
# See http://www.ifpi.org/downloads/GRid_Standard_v2_1.pdf §5
|
41
|
+
def self.id_blocks
|
42
|
+
['A1', '[A-Z0-9]{5}', '[A-Z0-9]{10}', '[A-Z0-9]']
|
77
43
|
end
|
78
44
|
|
79
|
-
#
|
80
|
-
#
|
81
|
-
def
|
82
|
-
@
|
45
|
+
# The prefix to use for generating a prefixed string representation of the GRID
|
46
|
+
# (see http://www.ifpi.org/downloads/GRid_Standard_v2_1.pdf §6
|
47
|
+
def self.prefix
|
48
|
+
@prefix ||= 'GRID'.freeze
|
83
49
|
end
|
84
50
|
|
85
51
|
# Return the GRid's two-letter scheme identifier
|
86
52
|
# @return [String]
|
87
53
|
def scheme
|
88
|
-
|
89
|
-
@scheme ||= @grid_string[0,2].freeze
|
54
|
+
fetch(:@scheme) { |grid_string| grid_string[0,2] }
|
90
55
|
end
|
91
56
|
|
92
57
|
# Return the GRid's 5-character issuer code
|
93
58
|
# @return [String]
|
94
59
|
def issuer
|
95
|
-
|
96
|
-
@issuer ||= @grid_string[2,5].freeze
|
60
|
+
fetch(:@issuer) { |grid_string| grid_string[2,5] }
|
97
61
|
end
|
98
62
|
|
99
63
|
# Return the GRid's 10-character release number.
|
100
64
|
# @return [String]
|
101
65
|
def release
|
102
|
-
|
103
|
-
@release ||= @grid_string[7,10].freeze
|
66
|
+
fetch(:@release) { |grid_string| grid_string[7,10] }
|
104
67
|
end
|
105
68
|
|
106
69
|
# Return the GRid's check character.
|
107
70
|
# @return [String]
|
108
71
|
def check
|
109
|
-
|
110
|
-
@check ||= @grid_string[17,1].freeze
|
111
|
-
end
|
112
|
-
|
113
|
-
# return the GRid as a normalised 18-character string
|
114
|
-
# @reuturn [String]
|
115
|
-
def to_s
|
116
|
-
@grid_string.dup
|
72
|
+
fetch(:@check) { |grid_string| grid_string[17,1] }
|
117
73
|
end
|
118
74
|
|
119
75
|
def to_grid
|
120
76
|
self
|
121
77
|
end
|
122
78
|
|
123
|
-
|
124
|
-
to_s == other.to_s
|
125
|
-
end
|
126
|
-
|
127
|
-
# Returns the GRid as a string, either the 18-character normalised string
|
128
|
-
# (:data) or the 21-character display string (:full). Note that a
|
129
|
-
# badly-formed GRid will simply return the original string whichever format
|
130
|
-
# you ask for.
|
131
|
-
# @param format [:data, :full] the output format to use
|
79
|
+
# Generate the hyphen-separated full display GRid string
|
132
80
|
# @return [String]
|
133
|
-
def
|
134
|
-
|
135
|
-
when :data
|
136
|
-
to_s
|
137
|
-
when :full
|
138
|
-
ok? ? "#{scheme}-#{issuer}-#{release}-#{check}" : to_s
|
139
|
-
else
|
140
|
-
raise ArgumentError, "format must be one of [:data, :full], but it was #{format.inspect}"
|
141
|
-
end
|
81
|
+
def as_full
|
82
|
+
"#{scheme}-#{issuer}-#{release}-#{check}"
|
142
83
|
end
|
143
84
|
end
|
144
85
|
end
|
data/lib/music_ids/id.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
module MusicIds
|
2
|
+
module Id
|
3
|
+
module ClassMethods
|
4
|
+
# Parse an identifier string into an instance
|
5
|
+
#
|
6
|
+
# @param input [String] The input identifier string to parse
|
7
|
+
# @param opts [Hash] Parsing options
|
8
|
+
# @option opts [true, false] :relaxed (false) Whether to parse in relaxed mode
|
9
|
+
# @return [ISRC] the ISRC instance
|
10
|
+
def parse(input, opts = {})
|
11
|
+
input = input.to_s.upcase
|
12
|
+
opts[:relaxed] ? parse_relaxed(input) : parse_strict(input)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def well_formed_id_matcher
|
18
|
+
@matcher ||= begin
|
19
|
+
Regexp.compile("\\A(?:#{prefix}:)?(#{id_blocks.join('-')}|#{id_blocks.join('')})\\Z")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def parse_strict(input)
|
24
|
+
parse_string(input) { |input|
|
25
|
+
raise ArgumentError, "'#{input}' is not the right length to be a #{self.class}"
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def parse_relaxed(input)
|
30
|
+
parse_string(input) { |input|
|
31
|
+
new(input, ok: false)
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def parse_string(input)
|
36
|
+
if match = well_formed_id_matcher.match(input)
|
37
|
+
new(match[1].gsub('-', ''))
|
38
|
+
else
|
39
|
+
yield(input)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.included(base)
|
45
|
+
base.extend(ClassMethods)
|
46
|
+
end
|
47
|
+
|
48
|
+
# @api private
|
49
|
+
# @param isrc_string [String] The ISRC string
|
50
|
+
# @param opts [Hash]
|
51
|
+
# @option opts [true, false] :ok (true) Whether the ISRC is well-formed or not
|
52
|
+
def initialize(id_string, opts = {ok: true})
|
53
|
+
@id_string = id_string.dup.freeze
|
54
|
+
@ok = opts[:ok] ? true : false
|
55
|
+
end
|
56
|
+
|
57
|
+
# Is this a well-formed ID?
|
58
|
+
# @return [true,false]
|
59
|
+
def ok?
|
60
|
+
@ok
|
61
|
+
end
|
62
|
+
|
63
|
+
# return the ID as a normalised string
|
64
|
+
# @return [String]
|
65
|
+
def to_s
|
66
|
+
@id_string.dup
|
67
|
+
end
|
68
|
+
|
69
|
+
def ==(other)
|
70
|
+
to_s == other.to_s
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns the ID as a string, either as the normalised canonical string
|
74
|
+
# (:data) or the format specified for 'full' display (:full). Note that a
|
75
|
+
# badly-formed ID will simply return the original string whichever format
|
76
|
+
# you ask for.
|
77
|
+
# @param format [:data, :full, :prefixed] the output format to use
|
78
|
+
# @return [String]
|
79
|
+
def as(format)
|
80
|
+
case format
|
81
|
+
when :data
|
82
|
+
to_s
|
83
|
+
when :full
|
84
|
+
ok? ? as_full : to_s
|
85
|
+
when :prefixed
|
86
|
+
"#{prefix}:#{as_full}"
|
87
|
+
else
|
88
|
+
raise ArgumentError, "format must be one of [:data, :full, :prefixed], but it was #{format.inspect}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Return the prefix for the identifier
|
93
|
+
def prefix
|
94
|
+
self.class.prefix
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def fetch(ivar)
|
100
|
+
return unless ok?
|
101
|
+
return instance_variable_get(ivar) if instance_variable_defined?(ivar)
|
102
|
+
instance_variable_set(ivar, yield(@id_string).freeze)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
data/lib/music_ids/isrc.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'music_ids/id'
|
2
|
+
|
1
3
|
module MusicIds
|
2
4
|
# The ISRC class represents an International Standard Recording Code, and
|
3
5
|
# provides simple methods to parse and re-present them.
|
@@ -43,116 +45,54 @@ module MusicIds
|
|
43
45
|
# instance that will return <tt>false</tt> from <tt>#ok?</tt> and will return
|
44
46
|
# nil from all the component methods like <tt>#registrant</tt>
|
45
47
|
class ISRC
|
46
|
-
|
47
|
-
WELL_FORMED_INPUT = /\A[A-Z]{2}-?[A-Z0-9]{3}-?[0-9]{2}-?[0-9]{5}\Z/
|
48
|
-
|
49
|
-
class << self
|
50
|
-
# Parse an ISRC string into an ISRC instance
|
51
|
-
#
|
52
|
-
# @param input [String] The input ISRC string to parse
|
53
|
-
# @param opts [Hash] Parsing options
|
54
|
-
# @option opts [true, false] :relaxed (false) Whether to parse in relaxed mode
|
55
|
-
# @return [ISRC] the ISRC instance
|
56
|
-
def parse(input, opts = {})
|
57
|
-
input = input.to_s.upcase
|
58
|
-
opts[:relaxed] ? parse_relaxed(input) : parse_strict(input)
|
59
|
-
end
|
60
|
-
|
61
|
-
private
|
62
|
-
|
63
|
-
def parse_strict(input)
|
64
|
-
if WELL_FORMED_INPUT.match(input)
|
65
|
-
new(input.gsub('-', ''))
|
66
|
-
else
|
67
|
-
raise ArgumentError, "'#{input}' is not the right length to be a 12- or 15-character ISRC"
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def parse_relaxed(input)
|
72
|
-
if WELL_FORMED_INPUT.match(input)
|
73
|
-
new(input.gsub('-', ''))
|
74
|
-
else
|
75
|
-
new(input, ok: false)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
48
|
+
include Id
|
79
49
|
|
80
|
-
#
|
81
|
-
|
82
|
-
|
83
|
-
# @option opts [true, false] :ok (true) Whether the ISRC is well-formed or not
|
84
|
-
def initialize(isrc_string, opts = {ok: true})
|
85
|
-
@isrc_string = isrc_string.dup.freeze
|
86
|
-
@ok = opts[:ok] ? true : false
|
50
|
+
# See http://www.ifpi.org/content/library/isrc_handbook.pdf §3.5
|
51
|
+
def self.id_blocks
|
52
|
+
['[A-Z]{2}', '[A-Z0-9]{3}', '[0-9]{2}', '[0-9]{5}']
|
87
53
|
end
|
88
54
|
|
89
|
-
#
|
90
|
-
#
|
91
|
-
def
|
92
|
-
@
|
55
|
+
# The prefix to use for generating a prefixed string representation of the ISRC
|
56
|
+
# (see http://www.ifpi.org/downloads/GRid_Handbook_V2_0_final.pdf §9.2 / §9.3
|
57
|
+
def self.prefix
|
58
|
+
@prefix ||= 'ISRC'.freeze
|
93
59
|
end
|
94
60
|
|
95
61
|
# Return the ISRC's two-letter country code
|
96
62
|
# @return [String]
|
97
63
|
def country
|
98
|
-
|
99
|
-
@country ||= @isrc_string[0,2].freeze
|
64
|
+
fetch(:@country) { |isrc_string| isrc_string[0,2] }
|
100
65
|
end
|
101
66
|
|
102
67
|
# Return the ISRC's three-character registrant code
|
103
68
|
# @return [String]
|
104
69
|
def registrant
|
105
|
-
|
106
|
-
@registrant ||= @isrc_string[2,3].freeze
|
70
|
+
fetch(:@registrant) { |isrc_string| isrc_string[2,3] }
|
107
71
|
end
|
108
72
|
|
109
73
|
# Return the ISRC's two-digit year.
|
110
74
|
#
|
111
75
|
# Note that year > 40 is a 19YY year, and 00 to 39 are 20YY years
|
112
|
-
# see http://www.ifpi.org/content/library/isrc_handbook.pdf
|
76
|
+
# see http://www.ifpi.org/content/library/isrc_handbook.pdf §4.8
|
113
77
|
# @return [String]
|
114
78
|
def year
|
115
|
-
|
116
|
-
@year ||= @isrc_string[5,2].freeze
|
79
|
+
fetch(:@year) { |isrc_string| isrc_string[5,2] }
|
117
80
|
end
|
118
81
|
|
119
82
|
# Return the ISRC's five-character designation code.
|
120
|
-
#
|
121
83
|
# @return [String]
|
122
84
|
def designation
|
123
|
-
|
124
|
-
@designation ||= @isrc_string[7,5].freeze
|
125
|
-
end
|
126
|
-
|
127
|
-
# return the ISRC as a normalised 12-character string
|
128
|
-
# @reuturn [String]
|
129
|
-
def to_s
|
130
|
-
@isrc_string.dup
|
85
|
+
fetch(:@designation) { |isrc_string| isrc_string[7,5] }
|
131
86
|
end
|
132
87
|
|
133
88
|
def to_isrc
|
134
89
|
self
|
135
90
|
end
|
136
91
|
|
137
|
-
|
138
|
-
to_s == other.to_s
|
139
|
-
end
|
140
|
-
|
141
|
-
# Returns the ISRC as a string, either the 12-character normalised string
|
142
|
-
# (:data) or the 15-character display string (:full). Note that a
|
143
|
-
# badly-formed ISRC will simply return the original string whichever format
|
144
|
-
# you ask for.
|
145
|
-
# @param format [:data, :full] the output format to use
|
92
|
+
# Generate the hyphen-separated full display ISRC string
|
146
93
|
# @return [String]
|
147
|
-
def
|
148
|
-
|
149
|
-
when :data
|
150
|
-
to_s
|
151
|
-
when :full
|
152
|
-
ok? ? "#{country}-#{registrant}-#{year}-#{designation}" : to_s
|
153
|
-
else
|
154
|
-
raise ArgumentError, "format must be one of [:data, :full], but it was #{format.inspect}"
|
155
|
-
end
|
94
|
+
def as_full
|
95
|
+
"#{country}-#{registrant}-#{year}-#{designation}"
|
156
96
|
end
|
157
97
|
end
|
158
98
|
end
|
data/lib/music_ids/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: music_ids
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Patterson
|
@@ -89,6 +89,7 @@ files:
|
|
89
89
|
- bin/setup
|
90
90
|
- lib/music_ids.rb
|
91
91
|
- lib/music_ids/grid.rb
|
92
|
+
- lib/music_ids/id.rb
|
92
93
|
- lib/music_ids/isrc.rb
|
93
94
|
- lib/music_ids/version.rb
|
94
95
|
- music_ids.gemspec
|