jsapi 1.4 → 1.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 87f305e41facfb84c96e5e2406b636aa9e10a27545a25d42a5029f4d4dd93488
4
- data.tar.gz: 6723179cd4981c590a4e8f0c45808d9b69a0436be48e1e89f06c7ef0a4b56ddb
3
+ metadata.gz: 7651bda7e379186840e78ede1870f022b69deca559b6ba5ad6d0cbc8aac5c840
4
+ data.tar.gz: 16a661d4c24e1195f23d6fa0a23d27ba53fb944819b2f9a57c2174456f5ca913
5
5
  SHA512:
6
- metadata.gz: 7ddefbaaa678ed70e29055dbe4452ee0f4f738fa2386e8e3f5dbaa42e682ca973fa0bd04e5c6b22b2570bfd290fd4fcfbb42c8f7e50650ffda50d0cfbb160cc7
7
- data.tar.gz: 3fc210ea9c236f319f1c94dcb741d7d910eb7edb90020aafcbbee2a033bd232a9bb88dd26fc306869f7c3be715487f4655a517e5968f63461a6bbad57074099a
6
+ metadata.gz: 0e1eebb7f9d53a3e2f28a562edac148093b506ade987384e093530f5d337b85a5aabeb5acae70ca80233f75215e1dbc158b4a786724a8eb77670e63ee0be348d
7
+ data.tar.gz: 51d183148b0575ba9d437c2d3208257ab6e0b1cdec9d3024aa63a6261a56542e9bee8907acc4bb5f042360f90114bf668f1e46836e97dcf1332d30b616f8d264
@@ -12,12 +12,16 @@ module Jsapi
12
12
  self.class.api_definitions
13
13
  end
14
14
 
15
+ ##
16
+ # :method: api_operation
17
+ # :args: operation_name = nil, omit: nil, status: nil, strong: false, &block
18
+ #
15
19
  # Performs an API operation by calling the given block. The request parameters are
16
20
  # passed as an instance of the operation's model class to the block. The object
17
21
  # returned by the block is implicitly rendered according to the appropriate +response+
18
- # specification when the content type is a JSON MIME type. When content type is
22
+ # specification when the content type is a \JSON MIME type. When content type is
19
23
  # <code>application/json-seq</code>, the object returned by the block is streamed in
20
- # JSON sequence text format.
24
+ # \JSON sequence text format.
21
25
  #
22
26
  # api_operation('foo') do |api_params|
23
27
  # # ...
@@ -36,41 +40,68 @@ module Jsapi
36
40
  # - +:nil+ - All of the properties whose value is +nil+ are omitted.
37
41
  #
38
42
  # Raises an +ArgumentError+ when +:omit+ is other than +:empty+, +:nil+ or +nil+.
39
- def api_operation(operation_name = nil,
40
- omit: nil,
41
- status: nil,
42
- strong: false,
43
- &block)
44
- _api_operation(
45
- operation_name,
46
- bang: false,
47
- omit: omit,
48
- status: status,
49
- strong: strong,
50
- &block
51
- )
52
- end
53
43
 
54
- # Like +api_operation+, except that a ParametersInvalid exception is raised on
55
- # invalid request parameters.
44
+ ##
45
+ # :method: api_operation!
46
+ # :args: operation_name = nil, omit: nil, status: nil, strong: false, &block
47
+ #
48
+ # Like +api_operation+, except that a ParametersInvalid exception is raised
49
+ # when request parameters are invalid.
56
50
  #
57
51
  # api_operation!('foo') do |api_params|
58
52
  # # ...
59
53
  # end
60
- #
61
- def api_operation!(operation_name = nil,
62
- omit: nil,
63
- status: nil,
64
- strong: false,
65
- &block)
66
- _api_operation(
67
- operation_name,
68
- bang: true,
69
- omit: omit,
70
- status: status,
71
- strong: strong,
72
- &block
73
- )
54
+
55
+ [true, false].each do |bang|
56
+ define_method(bang ? :api_operation! : :api_operation) \
57
+ do |operation_name = nil, omit: nil, status: nil, strong: false, &block|
58
+ definitions = api_definitions
59
+ operation_model = _find_api_operation_model(operation_name, definitions)
60
+ response_model = _find_api_response_model(operation_model, status, definitions)
61
+ head(status) && return unless block
62
+
63
+ # Perform operation
64
+ api_params = _api_params(operation_model, definitions, strong: strong)
65
+ api_response = Response.new(
66
+ begin
67
+ raise ParametersInvalid.new(api_params) if bang && api_params.invalid?
68
+
69
+ block.call(api_params)
70
+ rescue StandardError => e
71
+ # Lookup a rescue handler
72
+ rescue_handler = definitions.rescue_handler_for(e)
73
+ raise e if rescue_handler.nil?
74
+
75
+ # Change the HTTP status code and response model
76
+ status = rescue_handler.status
77
+ response_model = operation_model.response(status)&.resolve(definitions)
78
+ raise e if response_model.nil?
79
+
80
+ # Call on_rescue callbacks
81
+ definitions.on_rescue_callbacks.each do |callback|
82
+ callback.respond_to?(:call) ? callback.call(e) : send(callback, e)
83
+ end
84
+
85
+ Error.new(e, status: status)
86
+ end,
87
+ response_model, definitions, omit: omit
88
+ )
89
+ # Write response
90
+ media_type = response_model.content_type
91
+
92
+ if media_type == Media::Type::APPLICATION_JSON_SEQ
93
+ self.content_type = media_type.to_s
94
+ response.status = status
95
+
96
+ response.stream.tap do |stream|
97
+ api_response.write_json_seq_to(stream)
98
+ ensure
99
+ stream.close
100
+ end
101
+ elsif media_type.json?
102
+ render(json: api_response, status: status, content_type: media_type.to_s)
103
+ end
104
+ end
74
105
  end
75
106
 
76
107
  # Returns the request parameters as an instance of the operation's model class.
@@ -88,7 +119,7 @@ module Jsapi
88
119
  def api_params(operation_name = nil, strong: false)
89
120
  definitions = api_definitions
90
121
  _api_params(
91
- _find_api_operation(operation_name, definitions),
122
+ _find_api_operation_model(operation_name, definitions),
92
123
  definitions,
93
124
  strong: strong
94
125
  )
@@ -110,89 +141,34 @@ module Jsapi
110
141
  # Raises an +ArgumentError+ when +:omit+ is other than +:empty+, +:nil+ or +nil+.
111
142
  def api_response(result, operation_name = nil, omit: nil, status: nil)
112
143
  definitions = api_definitions
113
- operation = _find_api_operation(operation_name, definitions)
114
- response_model = _api_response(operation, status, definitions)
144
+ operation_model = _find_api_operation_model(operation_name, definitions)
145
+ response_model = _find_api_response_model(operation_model, status, definitions)
115
146
 
116
- Response.new(result, response_model, api_definitions, omit: omit)
147
+ Response.new(result, response_model, definitions, omit: omit)
117
148
  end
118
149
 
119
150
  private
120
151
 
121
- def _api_operation(operation_name, bang:, omit:, status:, strong:, &block)
122
- definitions = api_definitions
123
- operation = _find_api_operation(operation_name, definitions)
124
-
125
- # Perform operation
126
- response_model = _api_response(operation, status, definitions)
127
- head(status) && return unless block
128
-
129
- params = _api_params(operation, definitions, strong: strong)
130
-
131
- result = begin
132
- raise ParametersInvalid.new(params) if bang && params.invalid?
133
-
134
- block.call(params)
135
- rescue StandardError => e
136
- # Lookup a rescue handler
137
- rescue_handler = definitions.rescue_handler_for(e)
138
- raise e if rescue_handler.nil?
139
-
140
- # Change the HTTP status code and response model
141
- status = rescue_handler.status
142
- response_model = operation.response(status)&.resolve(definitions)
143
- raise e if response_model.nil?
144
-
145
- # Call on_rescue callbacks
146
- definitions.on_rescue_callbacks.each do |callback|
147
- if callback.respond_to?(:call)
148
- callback.call(e)
149
- else
150
- send(callback, e)
151
- end
152
- end
153
-
154
- Error.new(e, status: status)
155
- end
156
-
157
- # Write response
158
- return unless response_model.json_type? || response_model.json_seq_type?
159
-
160
- response = Response.new(result, response_model, definitions, omit: omit)
161
- self.content_type = response_model.content_type
162
-
163
- if response_model.json_seq_type?
164
- self.response.status = status
165
-
166
- self.response.stream.tap do |stream|
167
- response.write_json_seq_to(stream)
168
- ensure
169
- stream.close
170
- end
171
- else
172
- render(json: response, status: status)
173
- end
174
- end
175
-
176
- def _api_params(operation, definitions, strong:)
177
- (operation.model || Model::Base).new(
152
+ def _api_params(operation_model, definitions, strong:)
153
+ (operation_model.model || Model::Base).new(
178
154
  Parameters.new(
179
155
  params.except(:action, :controller, :format).permit!,
180
156
  request,
181
- operation,
157
+ operation_model,
182
158
  definitions,
183
159
  strong: strong
184
160
  )
185
161
  )
186
162
  end
187
163
 
188
- def _api_response(operation, status, definitions)
164
+ def _find_api_response_model(operation, status, definitions)
189
165
  response = operation.response(status)
190
166
  return response.resolve(definitions) if response
191
167
 
192
168
  raise "status code not defined: #{status}"
193
169
  end
194
170
 
195
- def _find_api_operation(operation_name, definitions)
171
+ def _find_api_operation_model(operation_name, definitions)
196
172
  operation = definitions.find_operation(operation_name)
197
173
  return operation if operation
198
174
 
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'type_and_subtype'
4
+
5
+ module Jsapi
6
+ module Media
7
+ # Represents a media range.
8
+ class Range
9
+ include Comparable
10
+ include TypeAndSubtype
11
+
12
+ class << self
13
+ # Transforms +value+ to an instance of this class.
14
+ #
15
+ # Raises an ArgumentError when +value+ could not be transformed.
16
+ def from(value)
17
+ media_range = try_from(value)
18
+ return media_range unless media_range.nil?
19
+
20
+ raise ArgumentError, "invalid media range: #{value.inspect}"
21
+ end
22
+
23
+ # Tries to transform +value+ to an instance of this class. Returns nil
24
+ # if +value+ could not be transformed.
25
+ def try_from(value)
26
+ return value if value.is_a?(Range)
27
+
28
+ type_and_subtype = pattern.match(value.to_s)&.captures
29
+ new(*type_and_subtype) if type_and_subtype&.count == 2
30
+ end
31
+
32
+ private
33
+
34
+ def pattern
35
+ @pattern ||= begin
36
+ name = '[0-9a-zA-Z-]+'
37
+ %r{(\*|#{name})/(\*|(?:#{name}(?:\.#{name})?(?:\+#{name})?))}.freeze
38
+ end
39
+ end
40
+ end
41
+
42
+ # Compares it with +other+ by +priority+.
43
+ def <=>(other)
44
+ return unless other.is_a?(self.class)
45
+
46
+ result = priority <=> other.priority
47
+ return result unless result.zero?
48
+
49
+ result = type <=> other.type
50
+ return result unless result.zero?
51
+
52
+ subtype <=> other.subtype
53
+ end
54
+
55
+ # Returns true if the given media type matches the media range.
56
+ def match?(media_type)
57
+ media_type = Type.from(media_type) unless media_type.nil?
58
+
59
+ (type == '*' || type == media_type&.type) &&
60
+ (subtype == '*' || subtype == media_type&.subtype)
61
+ end
62
+
63
+ # Returns the level of priority of the media range.
64
+ def priority
65
+ @priority ||= (type == '*' ? 2 : 0) + (subtype == '*' ? 1 : 0) + 1
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'type_and_subtype'
4
+
5
+ module Jsapi
6
+ module Media
7
+ # Represents a media type.
8
+ class Type
9
+ include Comparable
10
+ include TypeAndSubtype
11
+
12
+ # The <code>"application/json"</code> media type.
13
+ APPLICATION_JSON = Type.new('application', 'json')
14
+
15
+ # The <code>"application/json-seq"</code> media type.
16
+ APPLICATION_JSON_SEQ = Type.new('application', 'json-seq')
17
+
18
+ class << self
19
+ # Transforms +value+ to an instance of this class.
20
+ #
21
+ # Raises an ArgumentError when +value+ could not be transformed.
22
+ def from(value)
23
+ media_type = try_from(value)
24
+ return media_type unless media_type.nil?
25
+
26
+ raise ArgumentError, "invalid media type: #{value.inspect}"
27
+ end
28
+
29
+ # Tries to transform +value+ to an instance of this class. Returns nil
30
+ # if +value+ could not be transformed.
31
+ def try_from(value)
32
+ return value if value.is_a?(Type)
33
+
34
+ type_and_subtype = pattern.match(value.to_s)&.captures
35
+ new(*type_and_subtype) if type_and_subtype&.count == 2
36
+ end
37
+
38
+ private
39
+
40
+ def pattern
41
+ @pattern ||= begin
42
+ name = '[0-9a-zA-Z-]+'
43
+ %r{(#{name})/(#{name}(?:\.#{name})?(?:\+#{name})?)}.freeze
44
+ end
45
+ end
46
+ end
47
+
48
+ # Compares it with +other+ by +type+ and +subtype+.
49
+ def <=>(other)
50
+ return unless other.is_a?(self.class)
51
+
52
+ result = type <=> other.type
53
+ return result unless result.zero?
54
+
55
+ subtype <=> other.subtype
56
+ end
57
+
58
+ # Returns true if it represents a JSON media type as specified by
59
+ # https://mimesniff.spec.whatwg.org/#json-mime-type.
60
+ def json?
61
+ (type.in?(%w[application text]) && subtype == 'json') ||
62
+ subtype.end_with?('+json')
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jsapi
4
+ module Media
5
+ module TypeAndSubtype # :nodoc:
6
+ def self.included(base)
7
+ base.attr_reader :type, :subtype
8
+ end
9
+
10
+ def initialize(type, subtype)
11
+ @type = type.downcase
12
+ @subtype = subtype.downcase
13
+ end
14
+
15
+ def ==(other)
16
+ other.is_a?(self.class) &&
17
+ type == other.type &&
18
+ subtype == other.subtype
19
+ end
20
+
21
+ alias eql? ==
22
+
23
+ def hash
24
+ @hash ||= [type, subtype].hash
25
+ end
26
+
27
+ def inspect
28
+ "#<#{self.class} #{to_s.inspect}>"
29
+ end
30
+
31
+ def to_s
32
+ @to_s ||= "#{type}/#{subtype}"
33
+ end
34
+
35
+ alias as_json to_s
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'media/range'
4
+ require_relative 'media/type'
@@ -266,17 +266,18 @@ module Jsapi
266
266
  ]
267
267
  end.presence
268
268
 
269
- openapi_objects =
270
- if version.major == 2
271
- %i[base_path external_docs info host parameters responses parameters schemas
272
- schemes security_requirements security_schemes tags]
269
+ openapi_objects = (
270
+ %i[external_docs info parameters responses schemas
271
+ security_requirements security_schemes tags] +
272
+ if version == OpenAPI::V2_0
273
+ %i[base_path host schemes]
273
274
  else
274
- %i[callbacks examples external_docs headers info links parameters request_bodies
275
- responses schemas security_requirements security_schemes servers tags]
276
- end.to_h { |key| [key, object_to_openapi(objects[key], version).presence] }
275
+ %i[callbacks examples headers links request_bodies servers]
276
+ end
277
+ ).index_with { |key| object_to_openapi(objects[key], version).presence }
277
278
 
278
279
  with_openapi_extensions(
279
- if version.major == 2
280
+ if version == OpenAPI::V2_0
280
281
  openapi_server = objects[:servers].first || default_server
281
282
  uri = URI(openapi_server.url) if openapi_server
282
283
  {
@@ -290,7 +291,7 @@ module Jsapi
290
291
  operation.consumes(self)
291
292
  end.uniq.sort.presence,
292
293
  produces: operations.flat_map do |operation|
293
- operation.produces(self)
294
+ operation.produces(self).map(&:to_s)
294
295
  end.uniq.sort.presence,
295
296
  paths: openapi_paths,
296
297
  definitions: openapi_objects[:schemas],
@@ -301,12 +302,7 @@ module Jsapi
301
302
  else
302
303
  {
303
304
  # Order according to the OpenAPI specification 3.x
304
- openapi:
305
- case version.minor
306
- when 0 then '3.0.3'
307
- when 1 then '3.1.1'
308
- when 2 then '3.2.0'
309
- end,
305
+ openapi: version.to_s,
310
306
  info: openapi_objects[:info],
311
307
  servers:
312
308
  openapi_objects[:servers] ||
@@ -30,7 +30,9 @@ module Jsapi
30
30
  # or must be +false+.
31
31
  PRESENT = new(4)
32
32
 
33
- # Creates a new instance from +value+.
33
+ # Transforms +value+ to an instance of this class.
34
+ #
35
+ # Raises an +ArgumentError+ if +value+ could not be transformed.
34
36
  def self.from(value)
35
37
  return value if value.is_a?(Existence)
36
38
 
@@ -6,9 +6,9 @@ module Jsapi
6
6
  class Version
7
7
  include Comparable
8
8
 
9
- # Creates an \OpenAPI version from +version+.
9
+ # Transforms +version+ to an instance of this class.
10
10
  #
11
- # Raises an +ArgumentError+ if +version+ isn`t supported.
11
+ # Raises an +ArgumentError+ if +version+ could not be transformed.
12
12
  def self.from(version)
13
13
  return version if version.is_a?(Version)
14
14
 
@@ -48,13 +48,25 @@ module Jsapi
48
48
  minor <=> other.minor
49
49
  end
50
50
 
51
- def inspect
51
+ def inspect # :nodoc:
52
52
  "<#{self.class.name} #{self}>"
53
53
  end
54
54
 
55
55
  def to_s # :nodoc:
56
- "#{major}.#{minor}"
56
+ @to_s ||=
57
+ case [major, minor]
58
+ when [3, 0]
59
+ '3.0.3'
60
+ when [3, 1]
61
+ '3.1.1'
62
+ when [3, 2]
63
+ '3.2.0'
64
+ else
65
+ "#{major}.#{minor}"
66
+ end
57
67
  end
68
+
69
+ alias as_json to_s
58
70
  end
59
71
  end
60
72
  end
@@ -117,12 +117,12 @@ module Jsapi
117
117
  parent_path + path
118
118
  end
119
119
 
120
- # Returns the MIME type consumed by the operation.
120
+ # Returns the media type consumed by the operation.
121
121
  def consumes(definitions)
122
122
  request_body&.resolve(definitions)&.content_type
123
123
  end
124
124
 
125
- # Returns an array containing the MIME types produced by the operation.
125
+ # Returns an array containing the media types produced by the operation.
126
126
  def produces(definitions)
127
127
  responses.values.filter_map do |response|
128
128
  response.resolve(definitions).content_type
@@ -154,7 +154,7 @@ module Jsapi
154
154
  result[:consumes] = [consumes]
155
155
  end
156
156
  if (produces = produces(definitions)).present?
157
- result[:produces] = produces
157
+ result[:produces] = produces.map(&:to_s)
158
158
  end
159
159
  result[:schemes] = schemes if schemes.present?
160
160
  elsif servers.present?
@@ -11,7 +11,7 @@ module Jsapi
11
11
 
12
12
  ##
13
13
  # :attr: content_type
14
- # The content type used to describe complex parameters in \OpenAPI 3.0 and higher.
14
+ # The media type used to describe complex parameters in \OpenAPI 3.0 and higher.
15
15
  attribute :content_type, String
16
16
 
17
17
  ##
@@ -5,7 +5,7 @@ module Jsapi
5
5
  # Represents a relative path name.
6
6
  class Pathname
7
7
  class << self
8
- # Creates a Pathname from +name+.
8
+ # Transforms +name+ to an instance of this class.
9
9
  def from(name)
10
10
  return name if name.is_a?(Pathname)
11
11
 
@@ -30,8 +30,8 @@ module Jsapi
30
30
 
31
31
  alias eql? ==
32
32
 
33
- # Creates a new Pathname by appending +other+ to +self+.
34
- # Returns +self+ if +other+ is nil.
33
+ # Creates a new Pathname by appending +other+ to itself.
34
+ # Returns itself if +other+ is nil.
35
35
  def +(other)
36
36
  return self if other.nil?
37
37
 
@@ -7,15 +7,12 @@ module Jsapi
7
7
  class Base < Model::Base
8
8
  include OpenAPI::Extensions
9
9
 
10
- JSON_TYPE = %r{(^application/|^text/|\+)json$}.freeze # :nodoc:
11
- JSON_SEQ_TYPE = 'application/json-seq' # :nodoc:
12
-
13
10
  delegate_missing_to :schema
14
11
 
15
12
  ##
16
13
  # :attr: content_type
17
- # The content type, <code>"application/json"</code> by default.
18
- attribute :content_type, String, default: 'application/json'
14
+ # The media type of the response, <code>"application/json"</code> by default.
15
+ attribute :content_type, Media::Type, default: Media::Type::APPLICATION_JSON
19
16
 
20
17
  ##
21
18
  # :attr: description
@@ -66,17 +63,6 @@ module Jsapi
66
63
  @schema = Schema.new(keywords)
67
64
  end
68
65
 
69
- # Returns true if content type is a JSON MIME type as specified by
70
- # https://mimesniff.spec.whatwg.org/#json-mime-type.
71
- def json_type?
72
- content_type.match?(JSON_TYPE)
73
- end
74
-
75
- # Returns true if content type is <code>"application/json-seq"</code>.
76
- def json_seq_type?
77
- content_type == JSON_SEQ_TYPE
78
- end
79
-
80
66
  # Returns a hash representing the \OpenAPI response object.
81
67
  def to_openapi(version, definitions)
82
68
  version = OpenAPI::Version.from(version)
@@ -91,7 +77,7 @@ module Jsapi
91
77
  end.compact.presence,
92
78
  examples: (
93
79
  if (example = examples.values.first).present?
94
- { content_type => example.resolve(definitions).value }
80
+ { content_type.to_s => example.resolve(definitions).value }
95
81
  end
96
82
  )
97
83
  }
@@ -103,8 +89,9 @@ module Jsapi
103
89
  header.to_openapi(version)
104
90
  end.presence,
105
91
  content: {
106
- content_type => {
107
- **if json_seq_type? && schema.array? && version >= OpenAPI::V3_2
92
+ content_type.to_s => {
93
+ **if content_type == Media::Type::APPLICATION_JSON_SEQ &&
94
+ schema.array? && version >= OpenAPI::V3_2
108
95
  { itemSchema: schema.items.to_openapi(version) }
109
96
  else
110
97
  { schema: schema.to_openapi(version) }
@@ -4,6 +4,7 @@ module Jsapi
4
4
  module Meta
5
5
  module Schema
6
6
  class Boundary
7
+ # Transforms +value+ to an instance of this class.
7
8
  def self.from(value)
8
9
  case value
9
10
  when Boundary
data/lib/jsapi/version.rb CHANGED
@@ -5,6 +5,6 @@ module Jsapi
5
5
  # NOTE: See https://bundler.io/guides/creating_gem.html
6
6
 
7
7
  # The current GEM version.
8
- VERSION = '1.4'
8
+ VERSION = '1.4.1'
9
9
  end
10
10
  end
data/lib/jsapi.rb CHANGED
@@ -4,6 +4,7 @@ require 'jsapi/invalid_value_helper'
4
4
  require 'jsapi/invalid_value_error'
5
5
  require 'jsapi/invalid_argument_error'
6
6
  require 'jsapi/configuration'
7
+ require 'jsapi/media'
7
8
  require 'jsapi/model'
8
9
  require 'jsapi/meta'
9
10
  require 'jsapi/dsl'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsapi
3
3
  version: !ruby/object:Gem::Version
4
- version: '1.4'
4
+ version: 1.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Denis Göller
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-11-15 00:00:00.000000000 Z
11
+ date: 2025-11-23 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email: denis@dmgoeller.de
@@ -50,6 +50,10 @@ files:
50
50
  - lib/jsapi/json/object.rb
51
51
  - lib/jsapi/json/string.rb
52
52
  - lib/jsapi/json/value.rb
53
+ - lib/jsapi/media.rb
54
+ - lib/jsapi/media/range.rb
55
+ - lib/jsapi/media/type.rb
56
+ - lib/jsapi/media/type_and_subtype.rb
53
57
  - lib/jsapi/meta.rb
54
58
  - lib/jsapi/meta/callable.rb
55
59
  - lib/jsapi/meta/callable/symbol_evaluator.rb