scorpio 0.4.5 → 0.6.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.
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Scorpio
2
4
  # see also Faraday::Env::MethodsWithBodies
3
- METHODS_WITH_BODIES = %w(post put patch options)
5
+ METHODS_WITH_BODIES = %w(post put patch options).map(&:freeze).freeze
4
6
  class RequestSchemaFailure < Error
5
7
  end
6
8
 
@@ -20,7 +22,7 @@ module Scorpio
20
22
  def define_inheritable_accessor(accessor, default_value: nil, default_getter: -> { default_value }, on_set: nil)
21
23
  # the value before the field is set (overwritten) is the result of the default_getter proc
22
24
  define_singleton_method(accessor, &default_getter)
23
- inheritable_accessor_defaults[accessor] = self.singleton_class.instance_method(accessor)
25
+ inheritable_accessor_defaults[accessor] = singleton_class.instance_method(accessor)
24
26
  # field setter method. redefines the getter, replacing the method with one that returns the
25
27
  # setter's argument (that being inherited to the scope of the define_method(accessor) block
26
28
  define_singleton_method(:"#{accessor}=") do |value|
@@ -42,25 +44,18 @@ module Scorpio
42
44
  end
43
45
  end
44
46
  end
45
- define_inheritable_accessor(:represented_schemas, default_value: [], on_set: proc do
46
- unless represented_schemas.respond_to?(:to_ary)
47
- raise(TypeError, "represented_schemas must be an array. received: #{represented_schemas.pretty_inspect.chomp}")
48
- end
49
- if represented_schemas.all? { |s| s.is_a?(JSI::Schema) }
47
+ define_inheritable_accessor(:represented_schemas, default_value: Set[].freeze, on_set: proc do
48
+ if represented_schemas.is_a?(JSI::SchemaSet)
50
49
  represented_schemas.each do |schema|
51
- openapi_document_class.models_by_schema = openapi_document_class.models_by_schema.merge(schema => self)
50
+ new_mbs = openapi_document_class.models_by_schema.merge(schema => self).freeze
51
+ openapi_document_class.models_by_schema = new_mbs
52
52
  end
53
53
  update_dynamic_methods
54
54
  else
55
- self.represented_schemas = self.represented_schemas.map do |schema|
56
- unless schema.is_a?(JSI::Schema)
57
- schema = JSI::Schema.new(schema)
58
- end
59
- schema
60
- end
55
+ self.represented_schemas = JSI::SchemaSet.ensure_schema_set(represented_schemas)
61
56
  end
62
57
  end)
63
- define_inheritable_accessor(:models_by_schema, default_value: {})
58
+ define_inheritable_accessor(:models_by_schema, default_value: {}.freeze)
64
59
  # a model overriding this MUST include the openapi document's basePath if defined, e.g.
65
60
  # class MyModel
66
61
  # self.base_url = File.join('https://example.com/', openapi_document.basePath)
@@ -69,7 +64,7 @@ module Scorpio
69
64
  openapi_document.base_url(server: server, server_variables: server_variables)
70
65
  })
71
66
 
72
- define_inheritable_accessor(:server_variables, default_value: {}, on_set: -> {
67
+ define_inheritable_accessor(:server_variables, default_value: {}.freeze, on_set: -> {
73
68
  if openapi_document && openapi_document.v2?
74
69
  raise(ArgumentError, "server variables are not supported for OpenAPI V2")
75
70
  end
@@ -113,9 +108,9 @@ module Scorpio
113
108
  define_singleton_method(:openapi_document_class) { openapi_document_class }
114
109
  define_singleton_method(:openapi_document=) do |_|
115
110
  if self == openapi_document_class
116
- raise(ArgumentError, "openapi_document may only be set once on #{self.inspect}")
111
+ raise(ArgumentError, "openapi_document may only be set once on #{inspect}")
117
112
  else
118
- raise(ArgumentError, "openapi_document may not be overridden on subclass #{self.inspect} after it was set on #{openapi_document_class.inspect}")
113
+ raise(ArgumentError, "openapi_document may not be overridden on subclass #{inspect} after it was set on #{openapi_document_class.inspect}")
119
114
  end
120
115
  end
121
116
  # TODO blame validate openapi_document
@@ -151,7 +146,7 @@ module Scorpio
151
146
  end
152
147
 
153
148
  def all_schema_properties
154
- represented_schemas.map(&:described_object_property_names).inject(Set.new, &:|)
149
+ represented_schemas.map(&:described_object_property_names).inject(Set.new, &:merge)
155
150
  end
156
151
 
157
152
  def update_instance_accessors
@@ -170,13 +165,13 @@ module Scorpio
170
165
  end
171
166
 
172
167
  def operation_for_resource_class?(operation)
173
- return false unless tag_name
168
+ return true if tag_name && operation.tags.respond_to?(:to_ary) && operation.tags.include?(tag_name)
174
169
 
175
- return true if operation.tags.respond_to?(:to_ary) && operation.tags.include?(tag_name)
176
-
177
- if (operation.request_schemas || []).any? { |s| represented_schemas.include?(s) }
178
- return true
179
- end
170
+ request_response_schemas = operation.request_schemas | operation.response_schemas
171
+ # TODO/FIX nil instance is wrong. works for $ref and allOf, not for others.
172
+ # use all inplace applicators, not conditional on instance
173
+ all_request_response_schemas = request_response_schemas.each_inplace_applicator_schema(nil)
174
+ return true if all_request_response_schemas.any? { |s| represented_schemas.include?(s) }
180
175
 
181
176
  return false
182
177
  end
@@ -184,61 +179,69 @@ module Scorpio
184
179
  def operation_for_resource_instance?(operation)
185
180
  return false unless operation_for_resource_class?(operation)
186
181
 
187
- # define an instance method if the request schema is for this model
188
- request_resource_is_self = operation.request_schemas.any? do |request_schema|
189
- represented_schemas.include?(request_schema)
190
- end
191
-
192
- # also define an instance method depending on certain attributes the request description
193
- # might have in common with the model's schema attributes
194
- request_attributes = []
195
- # if the path has attributes in common with model schema attributes, we'll define on
196
- # instance method
197
- request_attributes |= operation.path_template.variables
198
- # TODO if the method request schema has attributes in common with the model schema attributes,
199
- # should we define an instance method?
200
- #request_attributes |= request_schema && request_schema['type'] == 'object' && request_schema['properties'] ?
201
- # request_schema['properties'].keys : []
202
- # TODO if the method parameters have attributes in common with the model schema attributes,
203
- # should we define an instance method?
204
- #request_attributes |= method_desc['parameters'] ? method_desc['parameters'].keys : []
205
-
206
- schema_attributes = represented_schemas.map(&:described_object_property_names).inject(Set.new, &:|)
207
-
208
- return request_resource_is_self || (request_attributes & schema_attributes.to_a).any?
209
- end
210
-
211
- def method_names_by_operation
212
- @method_names_by_operation ||= Hash.new do |h, operation|
213
- h[operation] = begin
214
- raise(ArgumentError, operation.pretty_inspect) unless operation.is_a?(Scorpio::OpenAPI::Operation)
215
-
216
- # if Pet is the Scorpio resource class
217
- # and Pet.tag_name is "pet"
218
- # and operation's operationId is "pet.add"
219
- # then the operation's method name on Pet will be "add".
220
- # if the operationId is just "addPet"
221
- # then the operation's method name on Pet will be "addPet".
222
- tag_name_match = tag_name &&
223
- operation.tags.respond_to?(:to_ary) && # TODO maybe operation.tags.valid?
224
- operation.tags.include?(tag_name) &&
225
- operation.operationId &&
226
- operation.operationId.match(/\A#{Regexp.escape(tag_name)}\.(\w+)\z/)
227
-
228
- if tag_name_match
229
- method_name = tag_name_match[1]
230
- else
231
- method_name = operation.operationId
182
+ # define an instance method if the operation's request schemas include any of our represented_schemas
183
+ #
184
+ # TODO/FIX nil instance is wrong. works for $ref and allOf, not for others.
185
+ # use all inplace applicators, not conditional on instance
186
+ all_request_schemas = operation.request_schemas.each_inplace_applicator_schema(nil)
187
+ return true if all_request_schemas.any? { |s| represented_schemas.include?(s) }
188
+
189
+ # the below only apply if the operation has this resource's tag
190
+ return false unless tag_name && operation.tags.respond_to?(:to_ary) && operation.tags.include?(tag_name)
191
+
192
+ # define an instance method if path or query params can be filled in from
193
+ # property names described by represented_schemas
194
+ schema_attributes = represented_schemas.map(&:described_object_property_names).inject(Set.new, &:merge)
195
+ operation.inferred_parameters.each do |param|
196
+ if param['in'] == 'path' || param['in'] == 'query'
197
+ if schema_attributes.include?(param['name'])
198
+ return true
232
199
  end
233
200
  end
234
201
  end
202
+
203
+ return false
204
+ end
205
+
206
+ # @private
207
+ # @param name [String]
208
+ # @return [Scorpio::OpenAPI::Operation, nil]
209
+ def operation_for_api_method_name(name)
210
+ openapi_document.operations.detect do |op|
211
+ operation_for_resource_class?(op) && api_method_name_by_operation(op) == name
212
+ end
213
+ end
214
+
215
+ # @private
216
+ # @param name [Scorpio::OpenAPI::Operation]
217
+ # @return [String, nil]
218
+ def api_method_name_by_operation(operation)
219
+ raise(ArgumentError, operation.pretty_inspect) unless operation.is_a?(Scorpio::OpenAPI::Operation)
220
+
221
+ # if Pet is the Scorpio resource class
222
+ # and Pet.tag_name is "pet"
223
+ # and operation's operationId is "pet.add" or "pet/add" or "pet:add"
224
+ # then the operation's method name on Pet will be "add".
225
+ # if the operationId is just "addPet"
226
+ # then the operation's method name on Pet will be "addPet".
227
+ tag_name_match = tag_name &&
228
+ operation.tags.respond_to?(:to_ary) && # TODO maybe operation.tags.valid?
229
+ operation.tags.include?(tag_name) &&
230
+ operation.operationId &&
231
+ operation.operationId.match(/\A#{Regexp.escape(tag_name)}[\.\/\:](\w+)\z/)
232
+
233
+ if tag_name_match
234
+ tag_name_match[1]
235
+ else
236
+ operation.operationId
237
+ end
235
238
  end
236
239
 
237
240
  def update_class_and_instance_api_methods
238
241
  openapi_document.paths.each do |path, path_item|
239
242
  path_item.each do |http_method, operation|
240
243
  next unless operation.is_a?(Scorpio::OpenAPI::Operation)
241
- method_name = method_names_by_operation[operation]
244
+ method_name = api_method_name_by_operation(operation)
242
245
  if method_name
243
246
  # class method
244
247
  if operation_for_resource_class?(operation) && !respond_to?(method_name)
@@ -259,8 +262,8 @@ module Scorpio
259
262
  end
260
263
 
261
264
  def call_operation(operation, call_params: nil, model_attributes: nil)
262
- call_params = JSI.stringify_symbol_keys(call_params) if call_params.respond_to?(:to_hash)
263
- model_attributes = JSI.stringify_symbol_keys(model_attributes || {})
265
+ call_params = JSI::Util.stringify_symbol_keys(call_params) if call_params.respond_to?(:to_hash)
266
+ model_attributes = JSI::Util.stringify_symbol_keys(model_attributes || {})
264
267
 
265
268
  request = Scorpio::Request.new(operation)
266
269
 
@@ -276,7 +279,7 @@ module Scorpio
276
279
  # Scorpio::ResourceBase.instance_method(:server_variables)
277
280
  # => #<UnboundMethod: #<Class:Scorpio::ResourceBase>#server_variables>
278
281
  # even though they are really the same method (the #owner for both is Scorpio::ResourceBase)
279
- inheritable_accessor_defaults[accessor] != self.singleton_class.instance_method(accessor).owner.instance_method(accessor)
282
+ inheritable_accessor_defaults[accessor] != singleton_class.instance_method(accessor).owner.instance_method(accessor)
280
283
  end
281
284
 
282
285
  # pretty ugly... may find a better way to do this.
@@ -311,11 +314,30 @@ module Scorpio
311
314
  end
312
315
 
313
316
  if operation.request_schema
317
+ request_body_for_schema = -> (o) do
318
+ if o.is_a?(JSI::Base)
319
+ # TODO check indicated schemas
320
+ if o.jsi_schemas.include?(operation.request_schema)
321
+ jsi = o
322
+ else
323
+ # TODO maybe better way than reinstantiating another jsi as request_schema
324
+ jsi = operation.request_schema.new_jsi(o.jsi_node_content)
325
+ end
326
+ else
327
+ jsi = operation.request_schema.new_jsi(o)
328
+ end
329
+ jsi.jsi_select_children_leaf_first do |node|
330
+ # we want to specifically reject only nodes described (only) by a false schema.
331
+ # note that for OpenAPI schemas, false is only a valid schema as a value
332
+ # of `additionalProperties`
333
+ node.jsi_schemas.empty? || !node.jsi_schemas.all? { |s| s.schema_content == false }
334
+ end
335
+ end
314
336
  # TODO deal with model_attributes / call_params better in nested whatever
315
337
  if call_params.nil?
316
- request.body_object = request_body_for_schema(model_attributes, operation.request_schema)
338
+ request.body_object = request_body_for_schema.(model_attributes)
317
339
  elsif call_params.respond_to?(:to_hash)
318
- body = request_body_for_schema(model_attributes.merge(call_params), operation.request_schema)
340
+ body = request_body_for_schema.(model_attributes)
319
341
  request.body_object = body.merge(call_params) # TODO
320
342
  else
321
343
  request.body_object = call_params
@@ -347,133 +369,109 @@ module Scorpio
347
369
  response_object_to_instances(ur.response.body_object, initialize_options)
348
370
  end
349
371
 
350
- def request_body_for_schema(object, schema)
351
- if object.is_a?(Scorpio::ResourceBase)
352
- # TODO request_schema_fail unless schema is for given model type
353
- request_body_for_schema(object.attributes, schema)
354
- elsif object.is_a?(JSI::PathedNode)
355
- request_body_for_schema(object.node_content, schema)
356
- else
357
- if object.respond_to?(:to_hash)
358
- object.map do |key, value|
359
- if schema
360
- if schema['type'] == 'object'
361
- # TODO code dup with response_object_to_instances
362
- if schema['properties'].respond_to?(:to_hash) && schema['properties'].key?(key)
363
- subschema = schema['properties'][key]
364
- include_pair = true
365
- else
366
- if schema['patternProperties'].respond_to?(:to_hash)
367
- _, pattern_schema = schema['patternProperties'].detect do |pattern, _|
368
- key =~ Regexp.new(pattern) # TODO map pattern to ruby syntax
369
- end
370
- end
371
- if pattern_schema
372
- subschema = pattern_schema
373
- include_pair = true
374
- else
375
- if schema['additionalProperties'] == false
376
- include_pair = false
377
- elsif [nil, true].include?(schema['additionalProperties'])
378
- include_pair = true
379
- subschema = nil
380
- else
381
- include_pair = true
382
- subschema = schema['additionalProperties']
383
- end
384
- end
385
- end
386
- elsif schema['type']
387
- request_schema_fail(object, schema)
388
- else
389
- # TODO not sure
390
- include_pair = true
391
- subschema = nil
392
- end
393
- end
394
- if include_pair
395
- {key => request_body_for_schema(value, subschema)}
396
- else
397
- {}
398
- end
399
- end.inject({}, &:update)
400
- elsif object.respond_to?(:to_ary) || object.is_a?(Set)
401
- object.map do |el|
402
- if schema
403
- if schema['type'] == 'array'
404
- # TODO index based subschema or whatever else works for array
405
- subschema = schema['items']
406
- elsif schema['type']
407
- request_schema_fail(object, schema)
408
- end
409
- end
410
- request_body_for_schema(el, subschema)
411
- end
372
+ def response_object_to_instances(object, initialize_options = {})
373
+ if object.is_a?(JSI::Base)
374
+ models = object.jsi_schemas.map { |schema| models_by_schema[schema] }.compact
375
+ if models.size == 0
376
+ model = nil
377
+ elsif models.size == 1
378
+ model = models.first
379
+ else
380
+ raise(Scorpio::OpenAPI::Error, "multiple models indicated by response JSI. models: #{models.inspect}; object: #{object.pretty_inspect.chomp}")
381
+ end
382
+
383
+ if model && object.respond_to?(:to_hash)
384
+ model.new(object, initialize_options)
412
385
  else
413
- # TODO maybe raise on anything not serializable
414
- # TODO check conformance to schema, request_schema_fail if not
415
- object
386
+ Container.new_container(object, openapi_document_class, initialize_options)
416
387
  end
388
+ else
389
+ object
417
390
  end
418
391
  end
392
+ end
393
+ end
419
394
 
420
- def request_schema_fail(object, schema)
421
- # TODO blame
395
+ class ResourceBase
396
+ module Containment
397
+ def [](key)
398
+ sub = contained_object[key]
399
+ if sub.is_a?(JSI::Base)
400
+ # TODO avoid reinstantiating the container only to throw it away if it matches the memo
401
+ sub_container = @openapi_document_class.response_object_to_instances(sub, options)
402
+
403
+ if @subscript_memos.key?(key) && @subscript_memos[key].class == sub_container.class
404
+ @subscript_memos[key]
405
+ else
406
+ @subscript_memos[key] = sub_container
407
+ end
408
+ else
409
+ sub
410
+ end
422
411
  end
423
412
 
424
- def response_object_to_instances(object, initialize_options = {})
425
- if object.is_a?(JSI::Base)
426
- model = models_by_schema[object.schema]
413
+ def []=(key, value)
414
+ @subscript_memos.delete(key)
415
+ if value.is_a?(Containment)
416
+ contained_object[key] = value.contained_object
417
+ else
418
+ contained_object[key] = value
427
419
  end
420
+ end
428
421
 
429
- if object.respond_to?(:to_hash)
430
- out = JSI::Typelike.modified_copy(object) do |_object|
431
- mod = object.map do |key, value|
432
- {key => response_object_to_instances(value, initialize_options)}
433
- end.inject({}, &:update)
434
- mod = mod.node_content if mod.is_a?(JSI::PathedNode)
435
- mod
436
- end
437
- if model
438
- model.new(out, initialize_options)
439
- else
440
- out
441
- end
442
- elsif object.respond_to?(:to_ary)
443
- JSI::Typelike.modified_copy(object) do
444
- object.map do |element|
445
- response_object_to_instances(element, initialize_options)
446
- end
447
- end
448
- else
449
- object
422
+ def as_json(*opt)
423
+ JSI::Typelike.as_json(contained_object, *opt)
424
+ end
425
+
426
+ def inspect
427
+ "\#<#{self.class.inspect} #{contained_object.inspect}>"
428
+ end
429
+
430
+ def pretty_print(q)
431
+ q.instance_exec(self) do |obj|
432
+ text "\#<#{obj.class.inspect}"
433
+ group_sub {
434
+ nest(2) {
435
+ breakable ' '
436
+ pp obj.contained_object
437
+ }
438
+ }
439
+ breakable ''
440
+ text '>'
450
441
  end
451
442
  end
443
+
444
+ include JSI::Util::FingerprintHash
445
+
446
+ def jsi_fingerprint
447
+ {class: self.class, contained_object: as_json}
448
+ end
452
449
  end
450
+ end
451
+
452
+ class ResourceBase
453
+ include Containment
453
454
 
454
455
  def initialize(attributes = {}, options = {})
455
- @attributes = JSI.stringify_symbol_keys(attributes)
456
- @options = JSI.stringify_symbol_keys(options)
456
+ @attributes = JSI::Util.stringify_symbol_keys(attributes)
457
+ @options = JSI::Util.stringify_symbol_keys(options)
457
458
  @persisted = !!@options['persisted']
459
+
460
+ @openapi_document_class = self.class.openapi_document_class
461
+ @subscript_memos = {}
458
462
  end
459
463
 
460
464
  attr_reader :attributes
461
465
  attr_reader :options
462
466
 
467
+ alias_method :contained_object, :attributes
468
+
463
469
  def persisted?
464
470
  @persisted
465
471
  end
466
472
 
467
- def [](key)
468
- @attributes[key]
469
- end
470
-
471
- def []=(key, value)
472
- @attributes[key] = value
473
- end
474
-
475
473
  def call_api_method(method_name, call_params: nil)
476
- operation = self.class.method_names_by_operation.invert[method_name] || raise(ArgumentError)
474
+ operation = self.class.operation_for_api_method_name(method_name) || raise(ArgumentError)
477
475
  call_operation(operation, call_params: call_params)
478
476
  end
479
477
 
@@ -497,31 +495,73 @@ module Scorpio
497
495
 
498
496
  response
499
497
  end
498
+ end
500
499
 
501
- def as_json(*opt)
502
- JSI::Typelike.as_json(@attributes, *opt)
503
- end
500
+ class ResourceBase
501
+ class Container
502
+ @container_classes = Hash.new do |h, modules|
503
+ container_class = Class.new(Container)
504
+ modules.each do |mod|
505
+ container_class.include(mod)
506
+ end
507
+ h[modules] = container_class
508
+ end
504
509
 
505
- def inspect
506
- "\#<#{self.class.inspect} #{attributes.inspect}>"
507
- end
508
- def pretty_print(q)
509
- q.instance_exec(self) do |obj|
510
- text "\#<#{obj.class.inspect}"
511
- group_sub {
512
- nest(2) {
513
- breakable ' '
514
- pp obj.attributes
515
- }
516
- }
517
- breakable ''
518
- text '>'
510
+ class << self
511
+ def new_container(object, openapi_document_class, options = {})
512
+ container_modules = Set[]
513
+
514
+ # TODO this is JSI internals that scorpio shouldn't really be using
515
+ if object.respond_to?(:to_hash)
516
+ container_modules << Enumerable # TODO change next JSI when PathedHashNode includes Enumerable
517
+ container_modules << JSI::PathedHashNode
518
+ end
519
+ if object.respond_to?(:to_ary)
520
+ container_modules << Enumerable # TODO change next JSI when PathedArrayNode includes Enumerable
521
+ container_modules << JSI::PathedArrayNode
522
+ end
523
+
524
+ container_modules += object.jsi_schemas.map do |schema|
525
+ JSI::SchemaClasses.accessor_module_for_schema(schema,
526
+ conflicting_modules: container_modules + [Container],
527
+ )
528
+ end
529
+
530
+ container_class = @container_classes[container_modules.freeze]
531
+
532
+ container_class.new(object, openapi_document_class, options)
533
+ end
519
534
  end
520
535
  end
521
536
 
522
- def fingerprint
523
- {class: self.class, attributes: JSI::Typelike.as_json(@attributes)}
537
+ class Container
538
+ include Containment
539
+
540
+ def initialize(contained_object, openapi_document_class, options = {})
541
+ @contained_object = contained_object
542
+ @openapi_document_class = openapi_document_class
543
+ @options = options
544
+ @subscript_memos = {}
545
+ end
546
+
547
+ attr_reader :contained_object
548
+
549
+ attr_reader :options
550
+
551
+ # @private
552
+ alias_method :jsi_node_content, :contained_object
553
+ private :jsi_node_content
554
+
555
+ # @private
556
+ # @return [Array<String>]
557
+ def jsi_object_group_text
558
+ schema_names = contained_object.jsi_schemas.map { |schema| schema.jsi_schema_module.name_from_ancestor || schema.schema_uri }.compact
559
+ if schema_names.empty?
560
+ [Container.to_s]
561
+ else
562
+ ["#{Container} (#{schema_names.join(', ')})"]
563
+ end
564
+ end
524
565
  end
525
- include JSI::FingerprintHash
526
566
  end
527
567
  end
@@ -1,13 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Scorpio
2
- class Response < ::Ur::Response
3
- # @return [::JSI::Schema] the schema for this response according to its OpenAPI doc
4
+ Response = Scorpio::Ur.properties['response']
5
+
6
+ module Response
7
+ # the schema for this response according to its OpenAPI doc
8
+ # @return [::JSI::Schema]
4
9
  def response_schema
5
10
  ur.scorpio_request.operation.response_schema(status: status, media_type: media_type)
6
11
  end
7
12
 
8
- # @return [Object] the body (String) is parsed according to the response media type and
9
- # if supported (only application/json is currently supported) instantiated according to
10
- # #response_schema
13
+ # the body (String) is parsed according to the response media type, if supported (only JSON is
14
+ # currently supported), and instantiated as a JSI instance of {#response_schema} if that is defined.
11
15
  def body_object
12
16
  if json?
13
17
  if body.empty?
@@ -22,7 +26,7 @@ module Scorpio
22
26
  end
23
27
 
24
28
  if response_schema && (body_object.respond_to?(:to_hash) || body_object.respond_to?(:to_ary))
25
- body_object = JSI.class_for_schema(response_schema).new(body_object)
29
+ body_object = response_schema.new_jsi(body_object)
26
30
  end
27
31
 
28
32
  body_object
data/lib/scorpio/ur.rb CHANGED
@@ -1,5 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Scorpio
2
- class Ur < ::Ur
4
+ # Scorpio::Ur is a JSI Schema module with which scorpio extends the ::Ur (toplevel)
5
+ # schema module from the Ur gem
6
+ Ur = JSI.new_schema_module({
7
+ '$schema' => 'http://json-schema.org/draft-07/schema#',
8
+ '$id' => 'https://schemas.jsi.unth.net/ur',
9
+ 'properties' => {
10
+ 'request' => {},
11
+ 'response' => {},
12
+ }
13
+ })
14
+
15
+ -> { Scorpio::Response }.() # invoke autoload
16
+
17
+ module Ur
3
18
  attr_accessor :scorpio_request
4
19
 
5
20
  # raises a subclass of Scorpio::HTTPError if the response has an error status.
@@ -28,19 +43,5 @@ module Scorpio
28
43
  end
29
44
  nil
30
45
  end
31
-
32
- private
33
- # overrides JSI::Base#class_for_schema to use Scorpio::Response instead of ::Ur::Response.
34
- # maybe a Scorpio::Ur::Request in the future if I need to extend that ... or Scorpio::Request
35
- # if I decide to make that subclass ::Ur::Request. not sure if that's a good idea or a terrible
36
- # idea.
37
- def class_for_schema(schema)
38
- jsi_class_for_schema = super
39
- if jsi_class_for_schema == ::Ur::Response
40
- Scorpio::Response
41
- else
42
- jsi_class_for_schema
43
- end
44
- end
45
46
  end
46
47
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Scorpio
2
- VERSION = "0.4.5"
4
+ VERSION = "0.6.0".freeze
3
5
  end