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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0dcfa17ed18001f410a21d829f1b76e908d4434e
4
- data.tar.gz: cc4b1fb8380914370cab6d9de1b1748f70ee4299
3
+ metadata.gz: ab2b1cbddfb673d888c9dbf7f493cf87b4536c99
4
+ data.tar.gz: 7a3eb549ec25b0cce0af4424e7f981d237b9eb8f
5
5
  SHA512:
6
- metadata.gz: e1a729e07cea652ccacf911a0712b6b2a5c1696661ada9c65ebff2150edda6ab4a4bd461cfc9065e32f03bac7681f5ab968c72e206211c3095d471e1314e3e9d
7
- data.tar.gz: eafd21cf4c04b24c2fd9980343cc6d15bfbd9962836a1ebaa2560c07d6d393bbf8e8850cb87f14bf2c972ada9013e9d5fe981037033e7ee147aa43dec6de2403
6
+ metadata.gz: b955c505b78a891e7ca4fc2f03d9baddb7506e0c6faf97af297b17ec5b5594184ea41e587b24bf5cbabcad2a0a24a85be1ef1cd3129e9fc1a059dea3dcd10e66
7
+ data.tar.gz: eb5119e0309675bd2abf35eb71297f25e65f7ea95a87fa8387f91d745c6a48a73b377b938ac26b80fbcac5eb017a0e45ff3c61a3b4363bc3674bcd01c30b11b9
@@ -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
- # See http://www.ifpi.org/downloads/GRid_Standard_v2_1.pdf §5
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
- # @api private
71
- # @param grid_string [String] The GRid string
72
- # @param opts [Hash]
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
- # Is this a well-formed GRid?
80
- # @return [true,false]
81
- def ok?
82
- @ok
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
- return unless ok?
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
- return unless ok?
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
- return unless ok?
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
- return unless ok?
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
- def ==(other)
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 as(format)
134
- case format
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
@@ -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
@@ -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
- # See http://www.ifpi.org/content/library/isrc_handbook.pdf §3.5
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
- # @api private
81
- # @param isrc_string [String] The ISRC string
82
- # @param opts [Hash]
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
- # Is this a well-formed ISRC?
90
- # @return [true,false]
91
- def ok?
92
- @ok
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
- return unless ok?
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
- return unless ok?
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 section 4.8
76
+ # see http://www.ifpi.org/content/library/isrc_handbook.pdf §4.8
113
77
  # @return [String]
114
78
  def year
115
- return unless ok?
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
- return unless ok?
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
- def ==(other)
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 as(format)
148
- case format
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
@@ -1,3 +1,3 @@
1
1
  module MusicIds
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
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.2.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