openapi_blocks 0.3.1 → 0.5.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/CHANGELOG.md +45 -4
- data/README.md +188 -85
- data/README.pt-BR.md +222 -130
- data/app/controllers/openapi_blocks/spec_controller.rb +2 -2
- data/lib/generators/openapi_blocks/install/install_generator.rb +19 -0
- data/lib/generators/openapi_blocks/install/templates/initializer.rb.tt +36 -0
- data/lib/generators/openapi_blocks/openapi/openapi_generator.rb +15 -0
- data/lib/generators/openapi_blocks/openapi/templates/openapi.rb.tt +48 -0
- data/lib/generators/openapi_blocks/serializer/serializer_generator.rb +15 -0
- data/lib/generators/openapi_blocks/serializer/templates/serializer.rb.tt +15 -0
- data/lib/openapi_blocks/auto_serialize.rb +54 -0
- data/lib/openapi_blocks/base.rb +6 -35
- data/lib/openapi_blocks/builder.rb +34 -2
- data/lib/openapi_blocks/concerns/documentable.rb +26 -0
- data/lib/openapi_blocks/concerns/schemable.rb +45 -0
- data/lib/openapi_blocks/configuration.rb +8 -1
- data/lib/openapi_blocks/controller.rb +7 -27
- data/lib/openapi_blocks/railtie.rb +14 -2
- data/lib/openapi_blocks/registry.rb +76 -0
- data/lib/openapi_blocks/serialization.rb +197 -0
- data/lib/openapi_blocks/serializer.rb +12 -182
- data/lib/openapi_blocks/spec/components.rb +6 -4
- data/lib/openapi_blocks/version.rb +1 -1
- data/lib/openapi_blocks.rb +7 -3
- metadata +17 -5
- data/lib/openapi_blocks/resource.rb +0 -20
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class <%= class_name %>Openapi < OpenapiBlocks::Controller
|
|
4
|
+
# resource <%= class_name %>Serializer
|
|
5
|
+
# controller <%= class_name %>Controller
|
|
6
|
+
|
|
7
|
+
# tags "<%= class_name.pluralize %>"
|
|
8
|
+
|
|
9
|
+
# operation :index do
|
|
10
|
+
# summary "List all <%= class_name.pluralize.downcase %>"
|
|
11
|
+
# description "Returns a paginated list of <%= class_name.pluralize.downcase %>"
|
|
12
|
+
#
|
|
13
|
+
# parameter :page, in: :query, type: :integer, description: "Page number"
|
|
14
|
+
# parameter :per_page, in: :query, type: :integer, description: "Items per page"
|
|
15
|
+
#
|
|
16
|
+
# response 200, description: "List of <%= class_name.pluralize.downcase %>", schema: { type: :array, items: :<%= class_name %> }
|
|
17
|
+
# response 401, description: "Unauthorized"
|
|
18
|
+
# end
|
|
19
|
+
|
|
20
|
+
# operation :show do
|
|
21
|
+
# summary "Get a <%= class_name.downcase %>"
|
|
22
|
+
#
|
|
23
|
+
# response 200, description: "<%= class_name %> found", schema: :<%= class_name %>
|
|
24
|
+
# response 404, description: "<%= class_name %> not found"
|
|
25
|
+
# end
|
|
26
|
+
|
|
27
|
+
# operation :create do
|
|
28
|
+
# summary "Create a <%= class_name.downcase %>"
|
|
29
|
+
#
|
|
30
|
+
# response 201, description: "<%= class_name %> created", schema: :<%= class_name %>
|
|
31
|
+
# response 422, description: "Invalid data"
|
|
32
|
+
# end
|
|
33
|
+
|
|
34
|
+
# operation :update do
|
|
35
|
+
# summary "Update a <%= class_name.downcase %>"
|
|
36
|
+
#
|
|
37
|
+
# response 200, description: "<%= class_name %> updated", schema: :<%= class_name %>
|
|
38
|
+
# response 404, description: "<%= class_name %> not found"
|
|
39
|
+
# response 422, description: "Invalid data"
|
|
40
|
+
# end
|
|
41
|
+
|
|
42
|
+
# operation :destroy do
|
|
43
|
+
# summary "Delete a <%= class_name.downcase %>"
|
|
44
|
+
#
|
|
45
|
+
# response 200, description: "<%= class_name %> deleted"
|
|
46
|
+
# response 404, description: "<%= class_name %> not found"
|
|
47
|
+
# end
|
|
48
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenapiBlocks
|
|
4
|
+
module Generators
|
|
5
|
+
class SerializerGenerator < Rails::Generators::NamedBase # rubocop:disable Style/Documentation
|
|
6
|
+
source_root File.expand_path("templates", __dir__)
|
|
7
|
+
|
|
8
|
+
desc "Creates an OpenapiBlocks Serializer class in app/serializers/"
|
|
9
|
+
|
|
10
|
+
def create_serializer_file
|
|
11
|
+
template "serializer.rb.tt", "app/serializers/#{file_name}_serializer.rb"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class <%= class_name %>Serializer < OpenapiBlocks::Serializer
|
|
4
|
+
# model <%= class_name %> is inferred automatically from the class name
|
|
5
|
+
|
|
6
|
+
# ignore :password_digest, :reset_password_token
|
|
7
|
+
|
|
8
|
+
# association :posts, type: :array, read_only: true
|
|
9
|
+
# association :company
|
|
10
|
+
|
|
11
|
+
# attribute :full_name, type: :string, read_only: true
|
|
12
|
+
# def full_name
|
|
13
|
+
# "#{object.first_name} #{object.last_name}"
|
|
14
|
+
# end
|
|
15
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenapiBlocks
|
|
4
|
+
module AutoSerialize # rubocop:disable Style/Documentation
|
|
5
|
+
def render(options = nil, extra = nil, &) # rubocop:disable Metrics/MethodLength
|
|
6
|
+
if auto_serialize_candidate?(options)
|
|
7
|
+
object = options[:json]
|
|
8
|
+
serializer = Registry.resolve(object)
|
|
9
|
+
|
|
10
|
+
if serializer
|
|
11
|
+
log_serializer(object, serializer)
|
|
12
|
+
options = options.merge(json: serializer.serialize(object))
|
|
13
|
+
else
|
|
14
|
+
warn_no_serializer(object)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
super
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def auto_serialize_candidate?(options)
|
|
24
|
+
OpenapiBlocks.configuration.auto_serialize &&
|
|
25
|
+
options.is_a?(Hash) &&
|
|
26
|
+
options.key?(:json)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def log_serializer(object, serializer)
|
|
30
|
+
model = extract_model(object)
|
|
31
|
+
Rails.logger.debug(
|
|
32
|
+
"[OpenapiBlocks] #{model.name} serialized by #{serializer.name}"
|
|
33
|
+
)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def warn_no_serializer(object)
|
|
37
|
+
model = extract_model(object)
|
|
38
|
+
return unless model
|
|
39
|
+
|
|
40
|
+
Rails.logger.warn(
|
|
41
|
+
"[OpenapiBlocks] No serializer found for #{model.name}. " \
|
|
42
|
+
"Falling back to default Rails rendering. " \
|
|
43
|
+
"Create #{model.name}Serializer or use `serializes #{model.name}` explicitly."
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def extract_model(object)
|
|
48
|
+
case object
|
|
49
|
+
when Array then object.first&.class
|
|
50
|
+
else object.respond_to?(:klass) ? object.klass : object.class
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
data/lib/openapi_blocks/base.rb
CHANGED
|
@@ -1,48 +1,19 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module OpenapiBlocks
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
# <b>DEPRECATED:</b> please use <tt>OpenapiBlocks::Controllers</tt> and <tt>OpenapiBlocks::Resources</tt> instead.
|
|
5
|
+
class Base
|
|
6
|
+
include Concerns::Schemable
|
|
7
|
+
include Concerns::Documentable
|
|
8
|
+
include Serialization
|
|
6
9
|
|
|
7
10
|
class << self
|
|
8
|
-
attr_reader :_model, :_ignored, :_associations, :_virtual_attributes, :_operations, :_tags
|
|
9
|
-
|
|
10
|
-
def model(klass = nil)
|
|
11
|
-
klass ? @_model = klass : @_model ||= infer_model # rubocop:disable Naming/MemoizedInstanceVariableName
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def ignore(*attributes)
|
|
15
|
-
@_ignored ||= []
|
|
16
|
-
@_ignored.concat(attributes.map(&:to_s))
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def association(name, type: nil, read_only: false)
|
|
20
|
-
@_associations ||= []
|
|
21
|
-
@_associations << { name: name, type: type, read_only: read_only }
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def attribute(name, **)
|
|
25
|
-
@_virtual_attributes ||= []
|
|
26
|
-
@_virtual_attributes << ({ name: name, ** })
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def operation(action, &block)
|
|
30
|
-
@_operations ||= {}
|
|
31
|
-
builder = OperationBuilder.new
|
|
32
|
-
builder.instance_eval(&block) if block
|
|
33
|
-
@_operations[action] = builder
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def tags(*values)
|
|
37
|
-
values.any? ? @_tags = values : @_tags
|
|
38
|
-
end
|
|
39
|
-
|
|
40
11
|
private
|
|
41
12
|
|
|
42
13
|
def infer_model
|
|
43
14
|
model_name = name
|
|
44
15
|
.gsub(/Openapi$/, "")
|
|
45
|
-
.gsub(/
|
|
16
|
+
.gsub(/Serializer$/, "")
|
|
46
17
|
.split("::")
|
|
47
18
|
.last
|
|
48
19
|
|
|
@@ -4,21 +4,53 @@ require_relative "spec/document"
|
|
|
4
4
|
|
|
5
5
|
module OpenapiBlocks
|
|
6
6
|
class Builder # rubocop:disable Style/Documentation
|
|
7
|
+
REQUIRED_CONFIG_ERROR = <<~MSG
|
|
8
|
+
OpenapiBlocks is not configured. Add an initializer:
|
|
9
|
+
|
|
10
|
+
# config/initializers/openapi_blocks.rb
|
|
11
|
+
OpenapiBlocks.configure do |config|
|
|
12
|
+
config.openapi_version = "3.1.0" # required: "3.0.3" or "3.1.0"
|
|
13
|
+
|
|
14
|
+
config.info do
|
|
15
|
+
title "My API" # required
|
|
16
|
+
version "1.0.0" # required
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
MSG
|
|
20
|
+
|
|
7
21
|
def self.build
|
|
8
22
|
new.build
|
|
9
23
|
end
|
|
10
24
|
|
|
11
25
|
def build
|
|
26
|
+
validate_configuration!
|
|
12
27
|
Spec::Document.new(openapi_classes).build
|
|
13
28
|
end
|
|
14
29
|
|
|
15
30
|
private
|
|
16
31
|
|
|
32
|
+
def validate_configuration! # rubocop:disable Metrics/CyclomaticComplexity
|
|
33
|
+
config = OpenapiBlocks.configuration
|
|
34
|
+
errors = []
|
|
35
|
+
|
|
36
|
+
unless config.configured?
|
|
37
|
+
errors << "config.openapi_version or config.info must be defined — call OpenapiBlocks.configure"
|
|
38
|
+
end
|
|
39
|
+
errors << "config.info.title is required" if config.info&.title.blank?
|
|
40
|
+
errors << "config.info.version is required" if config.info&.version.blank?
|
|
41
|
+
|
|
42
|
+
return if errors.empty?
|
|
43
|
+
|
|
44
|
+
raise Error, "#{REQUIRED_CONFIG_ERROR}\nMissing:\n#{errors.map { |e| " - #{e}" }.join("\n")}"
|
|
45
|
+
end
|
|
46
|
+
|
|
17
47
|
def openapi_classes
|
|
18
48
|
ObjectSpace.each_object(Class).select do |klass|
|
|
19
49
|
name = Module.instance_method(:name).bind_call(klass)
|
|
20
|
-
name&.end_with?("Openapi")
|
|
21
|
-
|
|
50
|
+
next unless name&.end_with?("Openapi")
|
|
51
|
+
|
|
52
|
+
klass < OpenapiBlocks::Base ||
|
|
53
|
+
klass < OpenapiBlocks::Controller
|
|
22
54
|
end
|
|
23
55
|
end
|
|
24
56
|
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenapiBlocks
|
|
4
|
+
module Concerns
|
|
5
|
+
module Documentable # rubocop:disable Style/Documentation
|
|
6
|
+
def self.included(base)
|
|
7
|
+
base.extend(ClassMethods)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module ClassMethods # rubocop:disable Style/Documentation
|
|
11
|
+
attr_reader :_operations, :_tags
|
|
12
|
+
|
|
13
|
+
def operation(action, &block)
|
|
14
|
+
@_operations ||= {}
|
|
15
|
+
builder = OperationBuilder.new
|
|
16
|
+
builder.instance_eval(&block) if block
|
|
17
|
+
@_operations[action] = builder
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def tags(*values)
|
|
21
|
+
values.any? ? @_tags = values : @_tags
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenapiBlocks
|
|
4
|
+
module Concerns
|
|
5
|
+
module Schemable # rubocop:disable Style/Documentation
|
|
6
|
+
def self.included(base)
|
|
7
|
+
base.extend(ClassMethods)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module ClassMethods # rubocop:disable Style/Documentation
|
|
11
|
+
attr_reader :_model, :_ignored, :_associations, :_virtual_attributes, :_serializes
|
|
12
|
+
|
|
13
|
+
def model(klass = nil)
|
|
14
|
+
klass ? @_model = klass : @_model ||= infer_model # rubocop:disable Naming/MemoizedInstanceVariableName
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def ignore(*attributes)
|
|
18
|
+
@_ignored ||= []
|
|
19
|
+
@_ignored.concat(attributes.map(&:to_s))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def association(name, type: nil, read_only: false)
|
|
23
|
+
@_associations ||= []
|
|
24
|
+
@_associations << { name: name, type: type, read_only: read_only }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def attribute(name, **)
|
|
28
|
+
@_virtual_attributes ||= []
|
|
29
|
+
@_virtual_attributes << { name: name, ** }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def serializes(*models)
|
|
33
|
+
@_serializes ||= []
|
|
34
|
+
@_serializes.concat(models)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def infer_model
|
|
40
|
+
raise NotImplementedError, "#{name} must implement infer_model"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -9,14 +9,20 @@ module OpenapiBlocks
|
|
|
9
9
|
SUPPORTED_VERSIONS = %w[3.1.0 3.0.3].freeze
|
|
10
10
|
|
|
11
11
|
attr_reader :openapi_version
|
|
12
|
-
attr_accessor :watch
|
|
12
|
+
attr_accessor :watch, :auto_serialize
|
|
13
13
|
|
|
14
14
|
def initialize
|
|
15
15
|
@openapi_version = "3.1.0"
|
|
16
16
|
@watch = :development
|
|
17
|
+
@auto_serialize = false
|
|
17
18
|
@info = InfoBuilder.new
|
|
18
19
|
@servers = []
|
|
19
20
|
@security = nil
|
|
21
|
+
@configured = false
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def configured?
|
|
25
|
+
@configured
|
|
20
26
|
end
|
|
21
27
|
|
|
22
28
|
def openapi_version=(version)
|
|
@@ -29,6 +35,7 @@ module OpenapiBlocks
|
|
|
29
35
|
end
|
|
30
36
|
|
|
31
37
|
def info(&block)
|
|
38
|
+
@configured = true if block
|
|
32
39
|
@info.instance_eval(&block) if block
|
|
33
40
|
@info
|
|
34
41
|
end
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
module OpenapiBlocks
|
|
4
4
|
class Controller # rubocop:disable Style/Documentation
|
|
5
|
+
include Concerns::Documentable
|
|
6
|
+
|
|
5
7
|
class << self
|
|
6
|
-
attr_reader :_resource, :
|
|
8
|
+
attr_reader :_resource, :_controller_class
|
|
7
9
|
|
|
8
10
|
def resource(klass)
|
|
9
11
|
@_resource = klass
|
|
@@ -13,32 +15,10 @@ module OpenapiBlocks
|
|
|
13
15
|
@_controller_class = klass
|
|
14
16
|
end
|
|
15
17
|
|
|
16
|
-
def model
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def _associations
|
|
21
|
-
@_resource&._associations
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def _virtual_attributes
|
|
25
|
-
@_resource&._virtual_attributes
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def _ignored
|
|
29
|
-
@_resource&._ignored
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def operation(action, &block)
|
|
33
|
-
@_operations ||= {}
|
|
34
|
-
builder = OperationBuilder.new
|
|
35
|
-
builder.instance_eval(&block) if block
|
|
36
|
-
@_operations[action] = builder
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def tags(*values)
|
|
40
|
-
values.any? ? @_tags = values : @_tags
|
|
41
|
-
end
|
|
18
|
+
def model = @_resource&.model
|
|
19
|
+
def _associations = @_resource&._associations
|
|
20
|
+
def _virtual_attributes = @_resource&._virtual_attributes
|
|
21
|
+
def _ignored = @_resource&._ignored
|
|
42
22
|
end
|
|
43
23
|
end
|
|
44
24
|
end
|
|
@@ -4,16 +4,28 @@ require "rails"
|
|
|
4
4
|
|
|
5
5
|
module OpenapiBlocks
|
|
6
6
|
class Railtie < Rails::Railtie # rubocop:disable Style/Documentation
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
generators do
|
|
8
|
+
require "generators/openapi_blocks/install/install_generator"
|
|
9
|
+
require "generators/openapi_blocks/openapi/openapi_generator"
|
|
10
|
+
require "generators/openapi_blocks/serializer/serializer_generator"
|
|
9
11
|
end
|
|
10
12
|
|
|
11
13
|
initializer "openapi_blocks.autoload", before: :set_autoload_paths do |app|
|
|
12
14
|
app.config.eager_load_paths << app.root.join("app/openapi")
|
|
15
|
+
app.config.eager_load_paths << app.root.join("app/serializers")
|
|
13
16
|
end
|
|
14
17
|
|
|
15
18
|
config.to_prepare do
|
|
16
19
|
Dir[Rails.root.join("app/openapi/**/*.rb")].each { |f| require f }
|
|
20
|
+
Dir[Rails.root.join("app/serializers/**/*.rb")].each { |f| require f }
|
|
21
|
+
|
|
22
|
+
if OpenapiBlocks.configuration.auto_serialize
|
|
23
|
+
[ActionController::Base, ActionController::API].each do |klass|
|
|
24
|
+
klass.include(OpenapiBlocks::AutoSerialize) unless klass.ancestors.include?(OpenapiBlocks::AutoSerialize)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
OpenapiBlocks::Registry.build!
|
|
28
|
+
end
|
|
17
29
|
end
|
|
18
30
|
end
|
|
19
31
|
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OpenapiBlocks
|
|
4
|
+
module Registry # rubocop:disable Style/Documentation
|
|
5
|
+
@map = {}
|
|
6
|
+
@mutex = Mutex.new
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
def register(model, serializer)
|
|
10
|
+
@mutex.synchronize { @map[model] = serializer }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def resolve(object)
|
|
14
|
+
model = extract_model(object)
|
|
15
|
+
@mutex.synchronize { @map[model] }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def build!
|
|
19
|
+
@mutex.synchronize { @map = {} }
|
|
20
|
+
|
|
21
|
+
serializer_classes.each do |klass|
|
|
22
|
+
register_by_convention(klass)
|
|
23
|
+
register_by_explicit(klass)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def reset!
|
|
28
|
+
@mutex.synchronize { @map = {} }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def serializer_classes
|
|
34
|
+
ObjectSpace.each_object(Class).select { |klass| safe_serializer?(klass) }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def register_by_convention(klass)
|
|
38
|
+
model_name = klass_name(klass)
|
|
39
|
+
.gsub(/Serializer$/, "")
|
|
40
|
+
&.split("::")
|
|
41
|
+
&.last
|
|
42
|
+
return if model_name.blank?
|
|
43
|
+
|
|
44
|
+
model = Object.const_get(model_name)
|
|
45
|
+
return unless model < ActiveRecord::Base
|
|
46
|
+
|
|
47
|
+
register(model, klass)
|
|
48
|
+
rescue NameError
|
|
49
|
+
nil
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def register_by_explicit(klass)
|
|
53
|
+
return unless klass.respond_to?(:_serializes) && klass._serializes&.any?
|
|
54
|
+
|
|
55
|
+
klass._serializes.each { |model| register(model, klass) }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def safe_serializer?(klass)
|
|
59
|
+
klass_name(klass)&.end_with?("Serializer") && klass < OpenapiBlocks::Serializer
|
|
60
|
+
rescue StandardError
|
|
61
|
+
false
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def klass_name(klass)
|
|
65
|
+
Module.instance_method(:name).bind_call(klass)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def extract_model(object)
|
|
69
|
+
case object
|
|
70
|
+
when Array then object.first&.class
|
|
71
|
+
else object.respond_to?(:klass) ? object.klass : object.class
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|