music_ids 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|