Module: OpenapiRuby::Generator::RakeTaskSupport

Defined in:
lib/openapi_ruby/generator/rake_task_support.rb

Overview

Helpers backing the openapi_ruby:generate rake task. Extracted so the framework detection / script generation logic is testable without booting Rake.

Constant Summary collapse

HOST_ENV_VARS =

Environment variables the host reads to decide it is running under test. Sinatra takes APP_ENV first and falls back to RACK_ENV; other Rack apps read one or the other, so a Rack host gets both.

{
  "rails" => ["RAILS_ENV"],
  "hanami" => ["HANAMI_ENV"],
  "rack" => ["APP_ENV", "RACK_ENV"]
}.freeze

Class Method Summary collapse

Class Method Details

.default_pattern_for(framework) ⇒ Object



62
63
64
65
66
67
68
# File 'lib/openapi_ruby/generator/rake_task_support.rb', line 62

def default_pattern_for(framework)
  case framework
  when "rspec" then "spec/**/*_spec.rb"
  when "minitest" then "test/**/*_test.rb"
  when "hybrid" then "spec/**/*_spec.rb,test/**/*_test.rb"
  end
end

.detect_hostObject

Which framework hosts the app being generated for. Detected from the filesystem as well as loaded constants: the rake process has not booted the app yet. Hanami keeps its app class in config/app.rb where Rails uses config/application.rb; a config.ru with neither is a Rack app (Sinatra, Roda, bare Rack).



32
33
34
35
36
37
38
39
40
41
# File 'lib/openapi_ruby/generator/rake_task_support.rb', line 32

def detect_host
  # Files describe the app being generated for; loaded constants only say
  # what the Rakefile happened to require, which in a mixed bundle can be
  # either framework.
  return "rails" if File.exist?("config/application.rb")
  return "hanami" if File.exist?("config/app.rb")
  return "rack" if File.exist?("config.ru")

  OpenapiRuby.host.to_s
end

.detect_test_frameworkObject



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/openapi_ruby/generator/rake_task_support.rb', line 11

def detect_test_framework
  rspec = File.exist?("spec/spec_helper.rb") || File.exist?("spec/rails_helper.rb")
  minitest = File.exist?("test/test_helper.rb")

  if rspec && minitest
    "hybrid"
  elsif rspec
    "rspec"
  elsif minitest
    "minitest"
  else
    raise ArgumentError,
      "Could not detect test framework. Set FRAMEWORK=rspec, FRAMEWORK=minitest, or FRAMEWORK=hybrid."
  end
end

.generate_script(framework, pattern) ⇒ Object



70
71
72
73
74
75
76
77
78
# File 'lib/openapi_ruby/generator/rake_task_support.rb', line 70

def generate_script(framework, pattern)
  case framework
  when "rspec" then rspec_script(pattern)
  when "minitest" then minitest_script(pattern)
  when "hybrid" then hybrid_script(pattern)
  else
    raise ArgumentError, "Unknown test framework '#{framework}'."
  end
end

.glob_loads(pattern) ⇒ Object



166
167
168
169
170
# File 'lib/openapi_ruby/generator/rake_task_support.rb', line 166

def glob_loads(pattern)
  pattern.split(",").map do |p|
    %[Dir.glob(#{p.strip.inspect}).sort.each { |f| require File.expand_path(f) }]
  end.join("\n")
end

.hybrid_script(pattern) ⇒ Object

Loads both adapters and both file globs in one process. Useful during a phased RSpec → Minitest migration where the suite holds both DSL styles. Consumers can guard require "rails/test_help" and require "rspec/rails" in their test helpers with unless OpenapiRuby.schema_generating? so the two test frameworks don't both register Rails lazy hooks in the same process — only the DSL needs to be live for schema generation.

Guarding is per-helper and optional: a suite that references what the guarded require defines at load time (shared examples, fixtures :all, include Devise::Test::... in a class body) fails to load those files at all, which is worse than the hook conflict. Such a helper stays unguarded and the pattern narrows instead.

Each glob runs with its own framework's directory at the head of $LOAD_PATH so the typical require "openapi_helper" / require "rails_helper" / require "test_helper" resolves to the right file. Without this, both spec/ and test/ getting unshifted in one block leads to whichever was unshifted last winning every lookup — and the wrong helper getting loaded for the other side's files.



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/openapi_ruby/generator/rake_task_support.rb', line 135

def hybrid_script(pattern)
  globs = pattern.split(",").map(&:strip)
  spec_globs = globs.grep(%r{\bspec/})
  test_globs = globs.grep(%r{\btest/})
  other_globs = globs - spec_globs - test_globs

  <<~RUBY
    require "rspec/core"
    require "openapi_ruby/rspec"
    require "openapi_ruby/minitest"
    #{suppress_autorun}

    load_with_path = lambda do |dir, glob|
      path = File.expand_path(dir)
      added = !$LOAD_PATH.include?(path)
      $LOAD_PATH.unshift(path) if added
      begin
        Dir.glob(glob).sort.each { |f| require File.expand_path(f) }
      ensure
        $LOAD_PATH.delete(path) if added
      end
    end

    #{spec_globs.map { |g| "load_with_path.call(\"spec\", #{g.inspect})" }.join("\n          ")}
    #{test_globs.map { |g| "load_with_path.call(\"test\", #{g.inspect})" }.join("\n          ")}
    #{other_globs.map { |g| %[Dir.glob(#{g.inspect}).sort.each { |f| require File.expand_path(f) }] }.join("\n          ")}

    OpenapiRuby::Generator::SchemaWriter.generate_all!
  RUBY
end

.minitest_script(pattern) ⇒ Object



102
103
104
105
106
107
108
109
110
# File 'lib/openapi_ruby/generator/rake_task_support.rb', line 102

def minitest_script(pattern)
  <<~RUBY
    require "openapi_ruby/minitest"
    #{suppress_autorun}
    $LOAD_PATH.unshift(File.expand_path("test")) unless $LOAD_PATH.include?(File.expand_path("test"))
    #{glob_loads(pattern)}
    OpenapiRuby::Generator::SchemaWriter.generate_all!
  RUBY
end

.rspec_script(pattern) ⇒ Object



92
93
94
95
96
97
98
99
100
# File 'lib/openapi_ruby/generator/rake_task_support.rb', line 92

def rspec_script(pattern)
  <<~RUBY
    require "rspec/core"
    #{suppress_autorun}
    $LOAD_PATH.unshift(File.expand_path("spec")) unless $LOAD_PATH.include?(File.expand_path("spec"))
    #{glob_loads(pattern)}
    OpenapiRuby::Generator::SchemaWriter.generate_all!
  RUBY
end

.subprocess_env(host = detect_host) ⇒ Object

Env for the generation subprocess: the host's own environment variables plus the flag that tells the gem it is generating rather than serving.



54
55
56
57
58
59
60
# File 'lib/openapi_ruby/generator/rake_task_support.rb', line 54

def subprocess_env(host = detect_host)
  env = HOST_ENV_VARS.fetch(host, HOST_ENV_VARS["rails"]).to_h do |var|
    [var, ENV.fetch(var, "test")]
  end

  env.merge("OPENAPI_RUBY_GENERATING" => "true")
end

.suppress_autorunObject

Prepended to every generated script, before any consumer file is required. Loading the consumer's test files must neither run them (AutorunSuppressor) nor drag in a database (TestSchemaSuppressor).



83
84
85
86
87
88
89
90
# File 'lib/openapi_ruby/generator/rake_task_support.rb', line 83

def suppress_autorun
  <<~RUBY.chomp
    require "openapi_ruby/generator/autorun_suppressor"
    require "openapi_ruby/generator/test_schema_suppressor"
    OpenapiRuby::Generator::AutorunSuppressor.install!
    OpenapiRuby::Generator::TestSchemaSuppressor.install!
  RUBY
end