oas_rails 1.3.6 → 1.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 014c0df55b3801ba802ffd0653f7fb40b7edea57dd6db0b39ce76d91806b7fbc
4
- data.tar.gz: 16c8ef0e16ab6743981acc211e24ce6f5618e8ab6e7b458d2f77363115557fe8
3
+ metadata.gz: 25a9bae1a6533ab3bf3b0ea8c00741c4bd07adc76e06aa5526d1cd9fa8c58d34
4
+ data.tar.gz: e75c2b1f815498fda47957d426aa6a39ce71b64cac89b1cf91d58ccea882ed3e
5
5
  SHA512:
6
- metadata.gz: 7e6f1644a2d180b3ce2dc6b81e04cec77b4c3b6876eea7c45d5b0c5169c8d79a3c4dcde33d7c212117b2cc9e7eee3b2fe925c43282650cfe0efea93f1d0586a5
7
- data.tar.gz: c1b571cfe98920827405ba76f76c4a03f685d881899a675bc153ff554d5c2986b520e8278b26ffb9353469f90bc006ae353e4efd463aed976fb6544115ba7508
6
+ metadata.gz: f8f82f7344bd96c8e4e70d65066cef4e1c647b7191318d681057df96e20e3ba7bc88346a85766f82c3e383d1548bba3e3bb9ff5c2a8ed980538ee800531765a2
7
+ data.tar.gz: ccf3a4c6566a3457b877aee8ba59bb3f8d9bfcf8bd4ee4a157e2c3033800704d6bf7b840b97155700535882d3077f99d0cd277f0fa37d986f96dff7fc38bc0fd
@@ -3,13 +3,22 @@ module OasRails
3
3
  # Include URL help if the layout is a user-customized layout.
4
4
  include Rails.application.routes.url_helpers
5
5
 
6
+ before_action :resolve_config
7
+
6
8
  def index
7
9
  respond_to do |format|
8
- format.html { render "index", layout: OasRails.config.layout }
10
+ format.html { render "index", layout: @config.layout }
9
11
  format.json do
10
- render json: OasRails.build.to_json, status: :ok
12
+ render json: OasRails.build(config: @config).to_json, status: :ok
11
13
  end
12
14
  end
13
15
  end
16
+
17
+ private
18
+
19
+ def resolve_config
20
+ config_name = params.dig("default", "configuration")&.to_sym || :default
21
+ @config = OasRails.config(config_name)
22
+ end
14
23
  end
15
24
  end
@@ -2,7 +2,7 @@ module OasRails
2
2
  module OasRailsHelper # rubocop:disable Metrics/ModuleLength
3
3
  def rapidoc_configuration_defaults
4
4
  {
5
- "spec-url" => "#{OasRails::Engine.routes.find_script_name(params.permit!.to_h.symbolize_keys)}.json",
5
+ "spec-url" => "#{request.script_name}.json",
6
6
  "show-header" => "false",
7
7
  "font-size" => "largest",
8
8
  "show-method-in-nav-bar" => "as-colored-text",
@@ -16,13 +16,13 @@ module OasRails
16
16
 
17
17
  def rapidoc_configuration_attributes
18
18
  rapidoc_configuration_defaults.merge(
19
- rapidoc_theme(OasRails.config.rapidoc_theme),
20
- OasRails.config.rapidoc_configuration
19
+ rapidoc_theme(@config.rapidoc_theme),
20
+ @config.rapidoc_configuration
21
21
  ).map { |k, v| %(#{k}=#{ERB::Util.html_escape(v)}) }.join(' ')
22
22
  end
23
23
 
24
24
  def rapidoc_logo_url
25
- OasRails.config.rapidoc_logo_url
25
+ @config.rapidoc_logo_url
26
26
  end
27
27
 
28
28
  THEMES = {
@@ -1,12 +1,13 @@
1
1
  module OasRails
2
2
  class ActiveRecordExampleFinder
3
- def initialize(context: :incoming, utils: Utils, factory_bot: FactoryBot, erb: ERB, yaml: YAML, file: File)
3
+ def initialize(context: :incoming, utils: Utils, factory_bot: FactoryBot, erb: ERB, yaml: YAML, file: File, config: nil)
4
4
  @context = context
5
5
  @utils = utils
6
6
  @factory_bot = factory_bot
7
7
  @erb = erb
8
8
  @yaml = yaml
9
9
  @file = file
10
+ @config = config
10
11
  @factory_examples = {}
11
12
  end
12
13
 
@@ -59,7 +60,8 @@ module OasRails
59
60
  end
60
61
 
61
62
  def clean_example_object(obj:)
62
- obj.reject { |key, _| OasRails.config.send("excluded_columns_#{@context}").include?(key.to_sym) }
63
+ resolved_config = @config || OasRails.config
64
+ obj.reject { |key, _| resolved_config.send("excluded_columns_#{@context}").include?(key.to_sym) }
63
65
  end
64
66
  end
65
67
  end
@@ -5,12 +5,14 @@ module OasRails
5
5
  # Builds a schema for a class when it is used as incoming API data.
6
6
  #
7
7
  # @param klass [Class] The class for which the schema is built.
8
+ # @param config [OasRails::Configuration, nil] Optional config override; falls back to OasRails.config.
8
9
  # @return [Hash] The schema as a JSON-compatible hash.
9
- def build_incoming_schema(klass:, model_to_schema_class: EasyTalk)
10
+ def build_incoming_schema(klass:, model_to_schema_class: EasyTalk, config: nil)
11
+ resolved_config = config || OasRails.config
10
12
  build_schema(
11
13
  klass: klass,
12
14
  model_to_schema_class: model_to_schema_class,
13
- excluded_columns: OasRails.config.excluded_columns_incoming,
15
+ excluded_columns: resolved_config.excluded_columns_incoming,
14
16
  exclude_primary_key: true
15
17
  )
16
18
  end
@@ -18,12 +20,14 @@ module OasRails
18
20
  # Builds a schema for a class when it is used as outgoing API data.
19
21
  #
20
22
  # @param klass [Class] The class for which the schema is built.
23
+ # @param config [OasRails::Configuration, nil] Optional config override; falls back to OasRails.config.
21
24
  # @return [Hash] The schema as a JSON-compatible hash.
22
- def build_outgoing_schema(klass:, model_to_schema_class: EasyTalk)
25
+ def build_outgoing_schema(klass:, model_to_schema_class: EasyTalk, config: nil)
26
+ resolved_config = config || OasRails.config
23
27
  build_schema(
24
28
  klass: klass,
25
29
  model_to_schema_class: model_to_schema_class,
26
- excluded_columns: OasRails.config.excluded_columns_outgoing,
30
+ excluded_columns: resolved_config.excluded_columns_outgoing,
27
31
  exclude_primary_key: false
28
32
  )
29
33
  end
@@ -7,12 +7,13 @@ module OasRails
7
7
  # source file directly so that OasRails annotations remain readable.
8
8
  KNOWN_RUNTIME_WRAPPERS = %w[sorbet-runtime].freeze
9
9
 
10
- def self.build_from_rails_route(rails_route)
11
- new(rails_route).build
10
+ def self.build_from_rails_route(rails_route, config:)
11
+ new(rails_route, config:).build
12
12
  end
13
13
 
14
- def initialize(rails_route)
14
+ def initialize(rails_route, config:)
15
15
  @rails_route = rails_route
16
+ @config = config
16
17
  end
17
18
 
18
19
  def build
@@ -46,7 +47,7 @@ module OasRails
46
47
  end
47
48
 
48
49
  def path
49
- OasRails.config.prefix_path + OasRails.config.route_extractor.clean_route(@rails_route.path.spec.to_s)
50
+ @config.prefix_path + @config.route_extractor.clean_route(@rails_route.path.spec.to_s)
50
51
  end
51
52
 
52
53
  def source_string
@@ -1,10 +1,11 @@
1
1
  module OasRails
2
2
  class Configuration < OasCore::Configuration
3
3
  attr_accessor :autodiscover_request_body, :autodiscover_responses, :ignored_actions, :rapidoc_theme, :layout, :source_oas_path, :rapidoc_configuration, :rapidoc_logo_url
4
- attr_reader :route_extractor, :include_mode, :mounted_path, :prefix_path
4
+ attr_reader :name, :route_extractor, :include_mode, :mounted_path, :prefix_path
5
5
 
6
6
  def initialize
7
7
  super
8
+ @name = :default
8
9
  @mounted_path = ""
9
10
  self.prefix_path = ENV["RAILS_RELATIVE_URL_ROOT"] || Rails.application.config.relative_url_root || ""
10
11
  @route_extractor = Extractors::RouteExtractor
@@ -2,6 +2,12 @@ module OasRails
2
2
  module Extractors
3
3
  # Extracts and processes render responses from a given source.
4
4
  module RenderResponseExtractor
5
+ # NOTE: This module is invoked as a callback by OasCore during the build pipeline.
6
+ # Because OasCore controls the call site, it is not possible to inject a named config
7
+ # here via dependency injection. As a result, calls to EsquemaBuilder and
8
+ # ActiveRecordExampleFinder from this module will use OasRails.config(:default).
9
+ # This is a known limitation of the multi-config feature.
10
+
5
11
  class << self
6
12
  # Extracts responses from the provided source string.
7
13
  #
@@ -10,25 +10,36 @@ module OasRails
10
10
  ].freeze
11
11
 
12
12
  class << self
13
- def host_routes_by_path(path)
14
- @host_routes ||= extract_host_routes
15
- @host_routes.select { |r| r.path == path }
13
+ def host_routes_by_path(path, config:)
14
+ @host_routes_cache ||= {}
15
+ @host_routes_cache[config.name] ||= extract_host_routes(config:)
16
+ @host_routes_cache[config.name].select { |r| r.path == path }
16
17
  end
17
18
 
18
- def host_routes
19
- @host_routes ||= extract_host_routes
19
+ def host_routes(config:)
20
+ @host_routes_cache ||= {}
21
+ @host_routes_cache[config.name] ||= extract_host_routes(config:)
20
22
  end
21
23
 
22
- # Clear Class Instance Variable @host_routes
24
+ # Clear the cached routes.
23
25
  #
24
- # This method clear the class instance variable @host_routes
25
- # to force a extraction of the routes again.
26
- def clear_cache
27
- @host_routes = nil
26
+ # When a +config+ is given, only that config's cache entry is cleared,
27
+ # forcing the next call to re-extract routes for that configuration.
28
+ # When called without arguments, the entire cache is cleared for all
29
+ # configurations.
30
+ def clear_cache(config: nil)
31
+ if config
32
+ @host_routes_cache&.delete(config.name)
33
+ @host_paths_cache&.delete(config.name)
34
+ else
35
+ @host_routes_cache = nil
36
+ @host_paths_cache = nil
37
+ end
28
38
  end
29
39
 
30
- def host_paths
31
- @host_paths ||= host_routes.map(&:path).uniq.sort
40
+ def host_paths(config:)
41
+ @host_paths_cache ||= {}
42
+ @host_paths_cache[config.name] ||= host_routes(config:).map(&:path).uniq.sort
32
43
  end
33
44
 
34
45
  def clean_route(route)
@@ -37,25 +48,25 @@ module OasRails
37
48
 
38
49
  private
39
50
 
40
- def extract_host_routes
41
- routes = valid_routes.map { |r| Builders::OasRouteBuilder.build_from_rails_route(r) }
51
+ def extract_host_routes(config:)
52
+ routes = valid_routes(config:).map { |r| Builders::OasRouteBuilder.build_from_rails_route(r, config:) }
42
53
 
43
- routes.select! { |route| route.tags.any? } if OasRails.config.include_mode == :with_tags
44
- routes.select! { |route| route.tags.any? { |t| t.tag_name == "oas_include" } } if OasRails.config.include_mode == :explicit
54
+ routes.select! { |route| route.tags.any? } if config.include_mode == :with_tags
55
+ routes.select! { |route| route.tags.any? { |t| t.tag_name == "oas_include" } } if config.include_mode == :explicit
45
56
  routes
46
57
  end
47
58
 
48
- def valid_routes
59
+ def valid_routes(config:)
49
60
  Rails.application.routes.routes.select do |route|
50
- valid_api_route?(route)
61
+ valid_api_route?(route, config:)
51
62
  end
52
63
  end
53
64
 
54
- def valid_api_route?(route)
65
+ def valid_api_route?(route, config:)
55
66
  return false unless valid_route_implementation?(route)
56
67
  return false if RAILS_DEFAULT_CONTROLLERS.any? { |default| route.defaults[:controller].start_with?(default) }
57
- return false unless route.path.spec.to_s.start_with?(OasRails.config.api_path)
58
- return false if ignore_custom_actions?(route)
68
+ return false unless route.path.spec.to_s.start_with?(config.api_path)
69
+ return false if ignore_custom_actions?(route, config:)
59
70
 
60
71
  true
61
72
  end
@@ -88,9 +99,9 @@ module OasRails
88
99
  # Support controller name only to ignore all controller actions.
89
100
  # Support ignoring "controller#action"
90
101
  # Ignoring "controller#action" AND "api_path/controller#action"
91
- def ignore_custom_actions?(route)
92
- api_path = "#{OasRails.config.api_path.sub(%r{\A/}, '')}/".sub(%r{/+$}, '/')
93
- ignored_actions = OasRails.config.ignored_actions.flat_map do |custom_route|
102
+ def ignore_custom_actions?(route, config:)
103
+ api_path = "#{config.api_path.sub(%r{\A/}, '')}/".sub(%r{/+$}, '/')
104
+ ignored_actions = config.ignored_actions.flat_map do |custom_route|
94
105
  if custom_route.start_with?(api_path)
95
106
  [custom_route]
96
107
  else
@@ -1,3 +1,3 @@
1
1
  module OasRails
2
- VERSION = "1.3.6"
2
+ VERSION = "1.4.0"
3
3
  end
data/lib/oas_rails.rb CHANGED
@@ -24,39 +24,48 @@ module OasRails
24
24
  end
25
25
 
26
26
  class << self
27
+ # NOTE: This lambda is called back by OasCore during build and has no way to receive
28
+ # a named config as a parameter. It falls back to OasRails.config(:default).
29
+ # This is a known limitation: excluded_columns in EsquemaBuilder will always use
30
+ # the default config when invoked from this re-entry point.
27
31
  OasCore::JsonSchemaGenerator.register_type_parser(
28
32
  ->(t) { Utils.active_record_class?(t) },
29
33
  ->(type, _required) { Builders::EsquemaBuilder.build_outgoing_schema(klass: type.constantize) }
30
34
  )
31
35
 
32
- def build
33
- clear_cache
36
+ def build(config: OasRails.config)
37
+ clear_cache(config:)
34
38
  OasCore.config = config
35
39
 
36
- host_routes = config.route_extractor.host_routes
37
- oas_source = config.source_oas_path ? read_source_oas_file : {}
40
+ host_routes = config.route_extractor.host_routes(config:)
41
+ oas_source = config.source_oas_path ? read_source_oas_file(config:) : {}
38
42
 
39
43
  OasCore.build(host_routes, oas_source: oas_source)
40
44
  end
41
45
 
42
- def configure
43
- yield config
46
+ def configure(name = :default)
47
+ cfg = Configuration.new
48
+ yield cfg
49
+ cfg.instance_variable_set(:@name, name)
50
+ @configs ||= {}
51
+ @configs[name] = cfg
44
52
  end
45
53
 
46
- def config
47
- @config ||= Configuration.new
54
+ def config(name = :default)
55
+ @configs ||= {}
56
+ @configs[name] ||= Configuration.new
48
57
  end
49
58
 
50
59
  private
51
60
 
52
- def clear_cache
61
+ def clear_cache(config:)
53
62
  return if Rails.env.production?
54
63
 
55
64
  MethodSource.clear_cache
56
- OasRails::Extractors::RouteExtractor.clear_cache
65
+ OasRails::Extractors::RouteExtractor.clear_cache(config:)
57
66
  end
58
67
 
59
- def read_source_oas_file
68
+ def read_source_oas_file(config:)
60
69
  file_path = Rails.root.join(config.source_oas_path)
61
70
  JSON.parse(File.read(file_path), symbolize_names: true)
62
71
  rescue Errno::ENOENT => e
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oas_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.6
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - a-chacon