scorpio 0.4.5 → 0.6.0

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