protocol-media 0.1.0 → 0.2.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/protocol/media/map.rb +79 -53
- data/lib/protocol/media/range.rb +61 -18
- data/lib/protocol/media/set.rb +247 -0
- data/lib/protocol/media/type.rb +23 -3
- data/lib/protocol/media/version.rb +1 -1
- data/releases.md +6 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 41cc93afe36dc9af35b5ca77721b8859543b270627a3cb9f7f6cc2df2334d83e
|
|
4
|
+
data.tar.gz: 306bf200c34635b7eba643945121d6150054759ffa7ffa9ce5f08bf0668aa3b0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7ab4060ead68e7b62f6486e298155e23060019bf7eb4bc9d2524efcb5cfe65e0a018945d46305730359b0c7e6bca158b44632ce5b7ca06ffd6c3c0bc2b8d327c
|
|
7
|
+
data.tar.gz: 84ccb4d5c42a6431cdc7da7d5ba6d1c776581bb561f0bde140d002c7cc5549f13304023c5d55bf4385c3e1de5a17be86c9d0aefcd2f708969aef3476772faa39
|
data/lib/protocol/media/map.rb
CHANGED
|
@@ -3,88 +3,114 @@
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
4
|
# Copyright, 2026, by Samuel Williams.
|
|
5
5
|
|
|
6
|
-
require_relative "
|
|
6
|
+
require_relative "type"
|
|
7
7
|
|
|
8
8
|
module Protocol
|
|
9
9
|
module Media
|
|
10
|
-
#
|
|
10
|
+
# An immutable map from concrete media types to objects for range-based lookup.
|
|
11
11
|
class Map
|
|
12
|
-
#
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
# Incrementally constructs an immutable media map.
|
|
13
|
+
class Builder
|
|
14
|
+
# Initialize an empty media map builder.
|
|
15
|
+
def initialize
|
|
16
|
+
@index = {}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Associate a concrete media type with an object.
|
|
20
|
+
#
|
|
21
|
+
# @parameter media_type [String | Object] The concrete media type or compatible object.
|
|
22
|
+
# @parameter object [Object] The object associated with the media type.
|
|
23
|
+
def []=(media_type, object)
|
|
24
|
+
media_type = Type.for(media_type)
|
|
25
|
+
|
|
26
|
+
# Preserve the first registration as the default for wildcard queries:
|
|
27
|
+
@index["*/*"] ||= object
|
|
28
|
+
@index["#{media_type.type}/*"] ||= object
|
|
29
|
+
@index[media_type.name] = object
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Compile the current registrations into an immutable map.
|
|
33
|
+
# @returns [Map] The immutable media map.
|
|
34
|
+
def build
|
|
35
|
+
return Map.new(@index).freeze
|
|
36
|
+
end
|
|
15
37
|
end
|
|
16
38
|
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
# @
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
39
|
+
# Construct an immutable map using a builder.
|
|
40
|
+
# @yields {|builder| ...} The mutable builder.
|
|
41
|
+
# @returns [Map] The immutable media map.
|
|
42
|
+
def self.build
|
|
43
|
+
builder = Builder.new
|
|
44
|
+
|
|
45
|
+
if block_given?
|
|
46
|
+
yield builder
|
|
47
|
+
end
|
|
23
48
|
|
|
24
|
-
|
|
49
|
+
return builder.build
|
|
25
50
|
end
|
|
26
51
|
|
|
27
|
-
#
|
|
52
|
+
# Convert keyed entries into a media map.
|
|
28
53
|
#
|
|
29
|
-
#
|
|
54
|
+
# Existing maps are returned unchanged, including their frozen state.
|
|
30
55
|
#
|
|
31
|
-
# @parameter
|
|
32
|
-
# @returns [
|
|
33
|
-
def
|
|
34
|
-
|
|
35
|
-
|
|
56
|
+
# @parameter entries [Map | Enumerable] The existing map or keyed media range entries.
|
|
57
|
+
# @returns [Map] The existing map or a newly constructed immutable map.
|
|
58
|
+
def self.for(entries)
|
|
59
|
+
return entries if entries.instance_of?(self)
|
|
60
|
+
|
|
61
|
+
return build do |builder|
|
|
62
|
+
entries.each do |media_type, object|
|
|
63
|
+
builder[media_type] = object
|
|
64
|
+
end
|
|
36
65
|
end
|
|
37
66
|
end
|
|
38
67
|
|
|
39
|
-
#
|
|
68
|
+
# Initialize a new media map with a given index.
|
|
40
69
|
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return [entry.last, range]
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
return nil
|
|
70
|
+
# The index is retained without copying or freezing it.
|
|
71
|
+
#
|
|
72
|
+
# @parameter index [Hash] The keyed media range entries.
|
|
73
|
+
def initialize(index)
|
|
74
|
+
@index = index
|
|
51
75
|
end
|
|
52
76
|
|
|
53
|
-
# Freeze the map and its
|
|
54
|
-
#
|
|
55
|
-
# @returns [self] The frozen map.
|
|
77
|
+
# Freeze the media map and its index.
|
|
78
|
+
# @returns [self]
|
|
56
79
|
def freeze
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
end
|
|
80
|
+
return self if frozen?
|
|
81
|
+
|
|
82
|
+
@index.freeze
|
|
61
83
|
|
|
62
|
-
super
|
|
84
|
+
return super
|
|
63
85
|
end
|
|
64
86
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
87
|
+
# Find the object matching a media type or range.
|
|
88
|
+
#
|
|
89
|
+
# @parameter media_range [String | Object] The media type or compatible range.
|
|
90
|
+
# @returns [Object | nil] The matching object, if one exists.
|
|
91
|
+
def [](media_range)
|
|
92
|
+
return lookup(Range.for(media_range))
|
|
69
93
|
end
|
|
70
94
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
95
|
+
# Find the first object matching an ordered sequence of media ranges.
|
|
96
|
+
#
|
|
97
|
+
# @parameter media_ranges [Enumerable] The media types or ranges in preference order.
|
|
98
|
+
# @returns [Array(Object, Range | String) | nil] The matching object and original range, if one exists.
|
|
99
|
+
def for(media_ranges)
|
|
100
|
+
media_ranges.each do |media_range|
|
|
101
|
+
if object = lookup(Range.for(media_range))
|
|
102
|
+
return [object, media_range]
|
|
103
|
+
end
|
|
77
104
|
end
|
|
78
105
|
|
|
79
106
|
return nil
|
|
80
107
|
end
|
|
81
108
|
|
|
82
|
-
|
|
83
|
-
match_component?(left.type, right.type) && match_component?(left.subtype, right.subtype)
|
|
84
|
-
end
|
|
109
|
+
private
|
|
85
110
|
|
|
86
|
-
def
|
|
87
|
-
|
|
111
|
+
def lookup(media_range)
|
|
112
|
+
media_range = Range.build(media_range.type, media_range.subtype)
|
|
113
|
+
return @index[media_range.name]
|
|
88
114
|
end
|
|
89
115
|
end
|
|
90
116
|
end
|
data/lib/protocol/media/range.rb
CHANGED
|
@@ -16,6 +16,26 @@ module Protocol
|
|
|
16
16
|
MEDIA_TYPE = /(?<type>#{TOKEN})\/(?<subtype>#{TOKEN})/
|
|
17
17
|
PARAMETER = /\s*;\s*(?<key>#{TOKEN})=((?<value>#{TOKEN})|(?<quoted_value>#{QUOTED_STRING}))/
|
|
18
18
|
|
|
19
|
+
# Build a normalized media range.
|
|
20
|
+
#
|
|
21
|
+
# @parameter type [String] The top-level type.
|
|
22
|
+
# @parameter subtype [String] The subtype.
|
|
23
|
+
# @parameter parameters [Hash] The media range parameters.
|
|
24
|
+
# @returns [Range] The normalized media range.
|
|
25
|
+
def self.build(type, subtype = "*", parameters = {})
|
|
26
|
+
type = type.downcase
|
|
27
|
+
subtype = subtype.downcase
|
|
28
|
+
|
|
29
|
+
# A wildcard type requires a wildcard subtype:
|
|
30
|
+
if type == "*"
|
|
31
|
+
if subtype != "*"
|
|
32
|
+
raise ArgumentError, "Invalid wildcards in media range: #{type}/#{subtype}"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
return new(type, subtype, parameters)
|
|
37
|
+
end
|
|
38
|
+
|
|
19
39
|
# Parse strings into media ranges while preserving compatible objects.
|
|
20
40
|
#
|
|
21
41
|
# @parameter value [String | Object] A media range string or compatible object.
|
|
@@ -48,7 +68,7 @@ module Protocol
|
|
|
48
68
|
raise ArgumentError, "Invalid media range: #{text.inspect}"
|
|
49
69
|
end
|
|
50
70
|
|
|
51
|
-
|
|
71
|
+
return build(type, subtype, parameters)
|
|
52
72
|
end
|
|
53
73
|
|
|
54
74
|
# Parse media type parameters from the scanner.
|
|
@@ -85,12 +105,8 @@ module Protocol
|
|
|
85
105
|
# @parameter subtype [String] The subtype.
|
|
86
106
|
# @parameter parameters [Hash] The media range parameters.
|
|
87
107
|
def initialize(type, subtype = "*", parameters = {})
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
@type = type.downcase
|
|
93
|
-
@subtype = subtype.downcase
|
|
108
|
+
@type = type
|
|
109
|
+
@subtype = subtype
|
|
94
110
|
@parameters = parameters
|
|
95
111
|
end
|
|
96
112
|
|
|
@@ -114,13 +130,34 @@ module Protocol
|
|
|
114
130
|
|
|
115
131
|
alias to_str to_s
|
|
116
132
|
|
|
133
|
+
# Freeze the media range and its direct values.
|
|
134
|
+
# @returns [self]
|
|
135
|
+
def freeze
|
|
136
|
+
return self if frozen?
|
|
137
|
+
|
|
138
|
+
@type.freeze
|
|
139
|
+
@subtype.freeze
|
|
140
|
+
@parameters.freeze
|
|
141
|
+
|
|
142
|
+
return super
|
|
143
|
+
end
|
|
144
|
+
|
|
117
145
|
# Whether this range matches the given media type or range.
|
|
118
146
|
#
|
|
119
|
-
# @parameter
|
|
147
|
+
# @parameter media_range [Range] The media type or range to match.
|
|
120
148
|
# @returns [Boolean] Whether the type and subtype are compatible.
|
|
121
|
-
def match?(
|
|
122
|
-
|
|
123
|
-
|
|
149
|
+
def match?(media_range)
|
|
150
|
+
# Both type components must be compatible:
|
|
151
|
+
unless component_match?(@type, media_range.type)
|
|
152
|
+
return false
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Both subtype components must be compatible:
|
|
156
|
+
unless component_match?(@subtype, media_range.subtype)
|
|
157
|
+
return false
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
return true
|
|
124
161
|
end
|
|
125
162
|
|
|
126
163
|
alias === match?
|
|
@@ -129,11 +166,11 @@ module Protocol
|
|
|
129
166
|
#
|
|
130
167
|
# @parameter other [Object] The object to compare.
|
|
131
168
|
# @returns [Boolean] Whether the values are equal.
|
|
132
|
-
def
|
|
133
|
-
other.instance_of?(self.class) && @type
|
|
169
|
+
def eql?(other)
|
|
170
|
+
other.instance_of?(self.class) && @type.eql?(other.type) && @subtype.eql?(other.subtype) && @parameters.eql?(other.parameters)
|
|
134
171
|
end
|
|
135
172
|
|
|
136
|
-
alias eql?
|
|
173
|
+
alias == eql?
|
|
137
174
|
|
|
138
175
|
# Generate a hash key consistent with {#eql?}.
|
|
139
176
|
#
|
|
@@ -144,11 +181,17 @@ module Protocol
|
|
|
144
181
|
|
|
145
182
|
private
|
|
146
183
|
|
|
147
|
-
def
|
|
148
|
-
|
|
149
|
-
|
|
184
|
+
def component_match?(component, other_component)
|
|
185
|
+
# A wildcard on either side matches any component:
|
|
186
|
+
if component == "*"
|
|
187
|
+
return true
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
if other_component == "*"
|
|
191
|
+
return true
|
|
192
|
+
end
|
|
150
193
|
|
|
151
|
-
|
|
194
|
+
return component == other_component
|
|
152
195
|
end
|
|
153
196
|
|
|
154
197
|
def self.unquote(value, normalize_whitespace)
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Released under the MIT License.
|
|
4
|
+
# Copyright, 2026, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
require_relative "range"
|
|
7
|
+
|
|
8
|
+
require "set"
|
|
9
|
+
|
|
10
|
+
module Protocol
|
|
11
|
+
module Media
|
|
12
|
+
# An immutable set of compatible media ranges.
|
|
13
|
+
class Set
|
|
14
|
+
include Enumerable
|
|
15
|
+
|
|
16
|
+
# An immutable set which matches any media range.
|
|
17
|
+
class Any
|
|
18
|
+
include Enumerable
|
|
19
|
+
|
|
20
|
+
RANGE = Range.new("*", "*").freeze
|
|
21
|
+
private_constant :RANGE
|
|
22
|
+
|
|
23
|
+
# Whether the set contains a compatible media type or range.
|
|
24
|
+
# @parameter media_range [String | Object] The media type or range to validate.
|
|
25
|
+
# @returns [Boolean]
|
|
26
|
+
def include?(media_range)
|
|
27
|
+
media_range = Range.for(media_range)
|
|
28
|
+
|
|
29
|
+
Range.build(media_range.type, media_range.subtype)
|
|
30
|
+
|
|
31
|
+
return true
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
alias === include?
|
|
35
|
+
alias match? include?
|
|
36
|
+
|
|
37
|
+
# Enumerate the universal media range.
|
|
38
|
+
# @yields {|media_range| ...} The universal media range.
|
|
39
|
+
# @returns [Enumerator | self]
|
|
40
|
+
def each
|
|
41
|
+
return to_enum unless block_given?
|
|
42
|
+
|
|
43
|
+
yield RANGE
|
|
44
|
+
|
|
45
|
+
return self
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# The number of membership ranges.
|
|
49
|
+
# @returns [Integer]
|
|
50
|
+
def size
|
|
51
|
+
return 1
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Whether there are no membership ranges.
|
|
55
|
+
# @returns [Boolean]
|
|
56
|
+
def empty?
|
|
57
|
+
return false
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
ANY = Any.new.freeze
|
|
62
|
+
|
|
63
|
+
private_constant :Any, :ANY
|
|
64
|
+
|
|
65
|
+
# Incrementally constructs an immutable media set.
|
|
66
|
+
class Builder
|
|
67
|
+
# Initialize an empty media set builder.
|
|
68
|
+
def initialize
|
|
69
|
+
@types = {}
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Add a media range to the set under construction.
|
|
73
|
+
# @parameter media_range [String | Object] The media range or compatible object.
|
|
74
|
+
# @returns [self]
|
|
75
|
+
def add(media_range)
|
|
76
|
+
if @types.instance_of?(Any)
|
|
77
|
+
return self
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
media_range = Range.for(media_range)
|
|
81
|
+
media_range = Range.build(media_range.type, media_range.subtype)
|
|
82
|
+
type = media_range.type.freeze
|
|
83
|
+
subtype = media_range.subtype.freeze
|
|
84
|
+
|
|
85
|
+
if type == "*"
|
|
86
|
+
@types = ANY
|
|
87
|
+
elsif subtype == "*"
|
|
88
|
+
@types[type] = ANY
|
|
89
|
+
elsif subtypes = @types[type]
|
|
90
|
+
unless subtypes.instance_of?(Any)
|
|
91
|
+
subtypes.add(subtype)
|
|
92
|
+
end
|
|
93
|
+
else
|
|
94
|
+
@types[type] = ::Set.new([subtype])
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
return self
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
alias << add
|
|
101
|
+
|
|
102
|
+
# Compile the current ranges into an immutable set.
|
|
103
|
+
# @returns [Set] The immutable media set.
|
|
104
|
+
def build
|
|
105
|
+
if @types.instance_of?(Any)
|
|
106
|
+
return @types
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
return Set.new(@types).freeze
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Construct an immutable set using a builder.
|
|
114
|
+
# @yields {|builder| ...} The mutable builder.
|
|
115
|
+
# @returns [Set] The immutable media set.
|
|
116
|
+
def self.build
|
|
117
|
+
builder = Builder.new
|
|
118
|
+
|
|
119
|
+
if block_given?
|
|
120
|
+
yield builder
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
return builder.build
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Convert a sequence of ranges into a media set.
|
|
127
|
+
#
|
|
128
|
+
# Existing sets are returned unchanged, including their frozen state.
|
|
129
|
+
#
|
|
130
|
+
# @parameter media_ranges [Set | Enumerable] The existing set or media ranges.
|
|
131
|
+
# @returns [Set] The existing set or a newly constructed immutable set.
|
|
132
|
+
def self.for(media_ranges)
|
|
133
|
+
# Preserve an existing media set:
|
|
134
|
+
if media_ranges.instance_of?(self)
|
|
135
|
+
return media_ranges
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Preserve the universal media set:
|
|
139
|
+
if media_ranges.instance_of?(Any)
|
|
140
|
+
return media_ranges
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
return build do |builder|
|
|
144
|
+
media_ranges.each do |media_range|
|
|
145
|
+
builder << media_range
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Initialize a new media set with the given type index.
|
|
151
|
+
#
|
|
152
|
+
# The index is retained without copying or freezing it.
|
|
153
|
+
#
|
|
154
|
+
# @parameter types [Hash] The indexed media ranges.
|
|
155
|
+
def initialize(types)
|
|
156
|
+
@types = types
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Freeze the media set and its index.
|
|
160
|
+
# @returns [self]
|
|
161
|
+
def freeze
|
|
162
|
+
return self if frozen?
|
|
163
|
+
|
|
164
|
+
@types.each_value(&:freeze)
|
|
165
|
+
@types.freeze
|
|
166
|
+
|
|
167
|
+
return super
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Whether the set contains a compatible media type or range.
|
|
171
|
+
# @parameter media_range [String | Object] The media type or range to match.
|
|
172
|
+
# @returns [Boolean]
|
|
173
|
+
def include?(media_range)
|
|
174
|
+
media_range = normalize(media_range)
|
|
175
|
+
type = media_range.type
|
|
176
|
+
subtype = media_range.subtype
|
|
177
|
+
|
|
178
|
+
# A wildcard type matches any non-empty set:
|
|
179
|
+
if type == "*"
|
|
180
|
+
return !@types.empty?
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
subtypes = @types[type]
|
|
184
|
+
|
|
185
|
+
# An unknown type cannot match:
|
|
186
|
+
unless subtypes
|
|
187
|
+
return false
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# A wildcard subtype matches any known type:
|
|
191
|
+
if subtype == "*"
|
|
192
|
+
return true
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# A type-wide wildcard matches every subtype:
|
|
196
|
+
if subtypes.instance_of?(Any)
|
|
197
|
+
return true
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
return subtypes.include?(subtype)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
alias === include?
|
|
204
|
+
alias match? include?
|
|
205
|
+
|
|
206
|
+
# Enumerate the canonical media ranges which define membership.
|
|
207
|
+
# @yields {|media_range| ...} Each canonical media range.
|
|
208
|
+
# @returns [Enumerator | self]
|
|
209
|
+
def each
|
|
210
|
+
return to_enum unless block_given?
|
|
211
|
+
|
|
212
|
+
@types.each do |type, subtypes|
|
|
213
|
+
if subtypes.instance_of?(Any)
|
|
214
|
+
yield Range.new(type, "*")
|
|
215
|
+
else
|
|
216
|
+
subtypes.each do |subtype|
|
|
217
|
+
yield Range.new(type, subtype)
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
return self
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# The number of canonical membership ranges.
|
|
226
|
+
# @returns [Integer]
|
|
227
|
+
def size
|
|
228
|
+
return @types.sum do |_type, subtypes|
|
|
229
|
+
subtypes.size
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Whether the set contains no media ranges.
|
|
234
|
+
# @returns [Boolean]
|
|
235
|
+
def empty?
|
|
236
|
+
return @types.empty?
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
private
|
|
240
|
+
|
|
241
|
+
def normalize(media_range)
|
|
242
|
+
media_range = Range.for(media_range)
|
|
243
|
+
return Range.build(media_range.type, media_range.subtype)
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
data/lib/protocol/media/type.rb
CHANGED
|
@@ -9,16 +9,36 @@ module Protocol
|
|
|
9
9
|
module Media
|
|
10
10
|
# A concrete media type and its parameters.
|
|
11
11
|
class Type < Range
|
|
12
|
+
# Parse strings and normalize compatible media type objects.
|
|
13
|
+
#
|
|
14
|
+
# @parameter value [String | Object] A media type string or compatible object.
|
|
15
|
+
# @returns [Type] The normalized concrete media type.
|
|
16
|
+
def self.for(value)
|
|
17
|
+
if value.is_a?(String)
|
|
18
|
+
return parse(value)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
return build(value.type, value.subtype, value.parameters)
|
|
22
|
+
end
|
|
12
23
|
|
|
24
|
+
# Build a normalized concrete media type.
|
|
25
|
+
#
|
|
13
26
|
# @parameter type [String] The top-level type.
|
|
14
27
|
# @parameter subtype [String] The subtype.
|
|
15
28
|
# @parameter parameters [Hash] The media type parameters.
|
|
16
|
-
|
|
17
|
-
|
|
29
|
+
# @returns [Type] The normalized concrete media type.
|
|
30
|
+
def self.build(type, subtype, parameters = {})
|
|
31
|
+
# Concrete media types cannot use a wildcard type:
|
|
32
|
+
if type == "*"
|
|
33
|
+
raise ArgumentError, "Media types cannot contain wildcards: #{type}/#{subtype}"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Concrete media types cannot use a wildcard subtype:
|
|
37
|
+
if subtype == "*"
|
|
18
38
|
raise ArgumentError, "Media types cannot contain wildcards: #{type}/#{subtype}"
|
|
19
39
|
end
|
|
20
40
|
|
|
21
|
-
super
|
|
41
|
+
return super
|
|
22
42
|
end
|
|
23
43
|
end
|
|
24
44
|
end
|
data/releases.md
ADDED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: protocol-media
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
@@ -15,10 +15,12 @@ extra_rdoc_files: []
|
|
|
15
15
|
files:
|
|
16
16
|
- lib/protocol/media/map.rb
|
|
17
17
|
- lib/protocol/media/range.rb
|
|
18
|
+
- lib/protocol/media/set.rb
|
|
18
19
|
- lib/protocol/media/type.rb
|
|
19
20
|
- lib/protocol/media/version.rb
|
|
20
21
|
- license.md
|
|
21
22
|
- readme.md
|
|
23
|
+
- releases.md
|
|
22
24
|
homepage: https://github.com/socketry/protocol-media
|
|
23
25
|
licenses:
|
|
24
26
|
- MIT
|