openapi-sourcetools 0.7.0 → 0.8.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/bin/openapi-addheaders +11 -11
- data/bin/openapi-addparameters +52 -12
- data/bin/openapi-addresponses +11 -12
- data/bin/openapi-addschemas +41 -17
- data/bin/openapi-checkschemas +21 -21
- data/bin/openapi-frequencies +24 -26
- data/bin/openapi-generate +15 -12
- data/bin/openapi-merge +21 -15
- data/bin/openapi-modifypaths +17 -16
- data/bin/openapi-processpaths +16 -27
- data/lib/openapi/sourcetools/apiobjects.rb +191 -0
- data/lib/openapi/sourcetools/common.rb +82 -0
- data/lib/openapi/sourcetools/config.rb +158 -0
- data/lib/openapi/sourcetools/docs.rb +41 -0
- data/lib/{gen.rb → openapi/sourcetools/gen.rb} +38 -13
- data/lib/openapi/sourcetools/generate.rb +96 -0
- data/lib/openapi/sourcetools/helper.rb +93 -0
- data/lib/openapi/sourcetools/loaders.rb +164 -0
- data/lib/openapi/sourcetools/output.rb +83 -0
- data/lib/openapi/sourcetools/securityschemes.rb +268 -0
- data/lib/openapi/sourcetools/task.rb +137 -0
- data/lib/openapi/sourcetools/version.rb +13 -0
- data/lib/openapi/sourcetools.rb +15 -0
- metadata +42 -18
- data/lib/apiobjects.rb +0 -333
- data/lib/common.rb +0 -110
- data/lib/docs.rb +0 -33
- data/lib/generate.rb +0 -90
- data/lib/helper.rb +0 -94
- data/lib/loaders.rb +0 -96
- data/lib/output.rb +0 -58
- data/lib/task.rb +0 -101
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright © 2024-2025 Ismo Kärkkäinen
|
4
|
+
# Licensed under Universal Permissive License. See LICENSE.txt.
|
5
|
+
|
6
|
+
module OpenAPISourceTools
|
7
|
+
# To hold documents loaded via command-line.
|
8
|
+
# Provides attribute accessor methods for each document.
|
9
|
+
# Exposed via Gen.d to tasks.
|
10
|
+
class Docs
|
11
|
+
attr_reader :docs
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@docs = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def method_missing(method_name, *args)
|
18
|
+
name = method_name.to_s
|
19
|
+
if name.end_with?('=')
|
20
|
+
name = name[0...(name.size - 1)]
|
21
|
+
super unless @docs.key?(name)
|
22
|
+
@docs[name] = args.first
|
23
|
+
return args.first
|
24
|
+
end
|
25
|
+
super unless @docs.key?(name)
|
26
|
+
@docs[name]
|
27
|
+
end
|
28
|
+
|
29
|
+
def respond_to_missing?(method_name, *args)
|
30
|
+
name = method_name.to_s
|
31
|
+
name = name[0...(name.size - 1)] if name.end_with?('=')
|
32
|
+
@docs.key?(name) || super
|
33
|
+
end
|
34
|
+
|
35
|
+
def add(name, content)
|
36
|
+
return false if docs.key?(name)
|
37
|
+
@docs[name] = content
|
38
|
+
true
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -1,37 +1,44 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
# Copyright © 2024 Ismo Kärkkäinen
|
3
|
+
# Copyright © 2024-2025 Ismo Kärkkäinen
|
4
4
|
# Licensed under Universal Permissive License. See LICENSE.txt.
|
5
5
|
|
6
6
|
require_relative 'task'
|
7
7
|
require_relative 'helper'
|
8
8
|
require_relative 'docs'
|
9
9
|
require_relative 'output'
|
10
|
+
require_relative 'config'
|
11
|
+
require 'deep_merge'
|
10
12
|
|
11
13
|
|
14
|
+
# The generation module that contains things visible to tasks.
|
12
15
|
module Gen
|
13
16
|
def self.add_doc(symbol, docstr)
|
14
17
|
return if docstr.nil?
|
15
18
|
@docsrc = [] unless instance_variable_defined?('@docsrc')
|
16
|
-
@docsrc.push("- #{symbol
|
19
|
+
@docsrc.push("- #{symbol} : #{docstr}")
|
17
20
|
end
|
21
|
+
private_class_method :add_doc
|
18
22
|
|
19
23
|
def self.read_attr(symbol, default)
|
20
24
|
return if symbol.nil?
|
21
25
|
attr_reader(symbol)
|
22
26
|
module_function(symbol)
|
23
|
-
instance_variable_set("@#{symbol
|
27
|
+
instance_variable_set("@#{symbol}", default)
|
24
28
|
end
|
29
|
+
private_class_method :read_attr
|
25
30
|
|
26
31
|
def self.mod_attr2_reader(symbol, symbol2, docstr = nil, default = nil)
|
27
32
|
read_attr(symbol, default)
|
28
33
|
read_attr(symbol2, default)
|
29
34
|
add_doc(symbol, docstr)
|
30
35
|
end
|
36
|
+
private_class_method :mod_attr2_reader
|
31
37
|
|
32
38
|
def self.mod_attr_reader(symbol, docstr = nil, default = nil)
|
33
39
|
mod_attr2_reader(symbol, nil, docstr, default)
|
34
40
|
end
|
41
|
+
private_class_method :mod_attr_reader
|
35
42
|
|
36
43
|
def self.rw_attr(symbol, default)
|
37
44
|
attr_accessor(symbol)
|
@@ -40,39 +47,56 @@ module Gen
|
|
40
47
|
module_function((s + '=').to_sym)
|
41
48
|
instance_variable_set("@#{s}", default)
|
42
49
|
end
|
50
|
+
private_class_method :rw_attr
|
43
51
|
|
44
52
|
def self.mod_attr2_accessor(symbol, symbol2, docstr = nil, default = nil)
|
45
53
|
rw_attr(symbol, default)
|
46
54
|
rw_attr(symbol2, default) unless symbol2.nil?
|
47
55
|
add_doc(symbol, docstr)
|
48
56
|
end
|
57
|
+
private_class_method :mod_attr2_accessor
|
49
58
|
|
50
59
|
def self.mod_attr_accessor(symbol, docstr = nil, default = nil)
|
51
60
|
mod_attr2_accessor(symbol, nil, docstr, default)
|
52
61
|
end
|
62
|
+
private_class_method :mod_attr_accessor
|
53
63
|
|
54
64
|
mod_attr_reader :doc, 'OpenAPI document.'
|
55
65
|
mod_attr_reader :outdir, 'Output directory name.'
|
56
|
-
mod_attr_reader :d, 'Other documents object.', Docs.new
|
66
|
+
mod_attr_reader :d, 'Other documents object.', OpenAPISourceTools::Docs.new
|
67
|
+
mod_attr_reader :wd, 'Original working directory', Dir.pwd
|
68
|
+
mod_attr_reader :configuration, 'Generator internal configuration'
|
69
|
+
mod_attr_accessor :config, 'Configuration file name for next gem or Ruby file.'
|
70
|
+
mod_attr_accessor :separator, 'Key separator in config file names.', nil
|
57
71
|
mod_attr_accessor :in_name, 'OpenAPI document name, nil if stdin.'
|
58
72
|
mod_attr_accessor :in_basename, 'OpenAPI document basename, nil if stdin.'
|
59
|
-
|
60
|
-
mod_attr_accessor :
|
61
|
-
mod_attr_accessor :a, 'Intended for instance with defined attributes.'
|
73
|
+
mod_attr_reader :g, 'Hash for storing values visible to all tasks.', {}
|
74
|
+
mod_attr_accessor :x, 'Hash for storing values visible to tasks from processor.', {}
|
62
75
|
mod_attr_accessor :h, 'Instance of class with helper methods.'
|
76
|
+
mod_attr_accessor :tasks, 'Tasks array.', []
|
63
77
|
mod_attr2_accessor :task, :t, 'Current task instance.'
|
64
78
|
mod_attr_accessor :task_index, 'Current task index.'
|
65
|
-
mod_attr_accessor :loaders, 'Array of
|
66
|
-
|
79
|
+
mod_attr_accessor :loaders, 'Array of processor loader methods.', []
|
80
|
+
mod_attr_accessor :output, 'Output-formatting helper.', OpenAPISourceTools::Output.new
|
81
|
+
|
82
|
+
def self.load_config(config_prefix)
|
83
|
+
cfg = {}
|
84
|
+
cfgs = OpenAPISourceTools::ConfigLoader.find_files(name_prefix: config_prefix)
|
85
|
+
cfgs = OpenAPISourceTools::ConfigLoader.read_contents(cfgs)
|
86
|
+
cfgs.each { |c| cfg.deep_merge!(c) }
|
87
|
+
cfg
|
88
|
+
end
|
89
|
+
private_class_method :load_config
|
67
90
|
|
68
|
-
def self.setup(document_content, input_name, output_directory)
|
91
|
+
def self.setup(document_content, input_name, output_directory, config_prefix)
|
69
92
|
@doc = document_content
|
70
93
|
@outdir = output_directory
|
71
94
|
unless input_name.nil?
|
72
95
|
@in_name = File.basename(input_name)
|
73
96
|
@in_basename = File.basename(input_name, '.*')
|
74
97
|
end
|
75
|
-
|
98
|
+
@configuration = load_config(config_prefix)
|
99
|
+
add_task(task: OpenAPISourceTools::HelperTask.new)
|
76
100
|
end
|
77
101
|
|
78
102
|
def self.add_task(task:, name: nil, executable: false, x: nil)
|
@@ -85,11 +109,12 @@ module Gen
|
|
85
109
|
end
|
86
110
|
|
87
111
|
def self.add_write_content(name:, content:, executable: false)
|
88
|
-
add_task(task: WriteTask.new(name, content, executable))
|
112
|
+
add_task(task: OpenAPISourceTools::WriteTask.new(name, content, executable))
|
89
113
|
end
|
90
114
|
|
91
115
|
def self.add(source:, template: nil, template_name: nil, name: nil, executable: false, x: nil)
|
92
|
-
add_task(task: Task.new(source, template, template_name),
|
116
|
+
add_task(task: OpenAPISourceTools::Task.new(source, template, template_name),
|
117
|
+
name:, executable:, x:)
|
93
118
|
end
|
94
119
|
|
95
120
|
def self.document
|
@@ -0,0 +1,96 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Copyright © 2024-2025 Ismo Kärkkäinen
|
5
|
+
# Licensed under Universal Permissive License. See LICENSE.txt.
|
6
|
+
|
7
|
+
require_relative 'common'
|
8
|
+
require_relative 'loaders'
|
9
|
+
require_relative 'gen'
|
10
|
+
|
11
|
+
|
12
|
+
module OpenAPISourceTools
|
13
|
+
def self.executable_bits_on(mode)
|
14
|
+
mode = mode.to_s(8).chars
|
15
|
+
mode.size.times do |k|
|
16
|
+
m = mode[k].to_i(8)
|
17
|
+
# Applies to Unix-likes. Other system, check and handle.
|
18
|
+
m += 1 unless 3 < mode.size - k || m.zero? || m.odd?
|
19
|
+
mode[k] = m
|
20
|
+
end
|
21
|
+
m = 0
|
22
|
+
mode.each do |v|
|
23
|
+
m = 8 * m + v
|
24
|
+
end
|
25
|
+
m
|
26
|
+
end
|
27
|
+
|
28
|
+
# Runs all tasks that generate the results.
|
29
|
+
# Used internally by openapi-generate.
|
30
|
+
class Generator
|
31
|
+
def initialize(document_content, input_name, output_directory, config_prefix)
|
32
|
+
Gen.setup(document_content, input_name, output_directory, config_prefix)
|
33
|
+
Gen.loaders = Loaders.loaders
|
34
|
+
end
|
35
|
+
|
36
|
+
def context_binding
|
37
|
+
binding
|
38
|
+
end
|
39
|
+
|
40
|
+
def load(generator_names)
|
41
|
+
generator_names.each do |name|
|
42
|
+
idx = Gen.loaders.index { |loader| loader.call(name) }
|
43
|
+
return Common.aargh("No loader could handle #{name}", 2) if idx.nil?
|
44
|
+
end
|
45
|
+
0
|
46
|
+
rescue StandardError => e
|
47
|
+
Common.aargh(e.to_s, 2)
|
48
|
+
end
|
49
|
+
|
50
|
+
def generate(t)
|
51
|
+
t.generate(context_binding)
|
52
|
+
rescue Exception => e
|
53
|
+
Common.aargh(e.to_s, 4)
|
54
|
+
end
|
55
|
+
|
56
|
+
def output_name(t, index)
|
57
|
+
name = t.output_name
|
58
|
+
name = "#{index}.txt" if name.nil?
|
59
|
+
File.join(Gen.outdir, name)
|
60
|
+
end
|
61
|
+
|
62
|
+
def save(name, contents, executable)
|
63
|
+
d = File.dirname(name)
|
64
|
+
FileUtils.mkdir_p(d) unless File.directory?(d)
|
65
|
+
f = File.new(name, File::WRONLY | File::CREAT | File::TRUNC)
|
66
|
+
s = executable ? f.stat : nil
|
67
|
+
f.write(contents)
|
68
|
+
f.close
|
69
|
+
return unless executable
|
70
|
+
mode = OpenAPISourceTools.executable_bits_on(s.mode)
|
71
|
+
File.chmod(mode, name) unless mode == s.mode
|
72
|
+
end
|
73
|
+
|
74
|
+
def run
|
75
|
+
# This allows tasks to be added while processing.
|
76
|
+
# Not intended to be done but might prove handy.
|
77
|
+
# Also exposes current task index in case new task is added in the middle.
|
78
|
+
Gen.task_index = 0
|
79
|
+
while Gen.task_index < Gen.tasks.size
|
80
|
+
Gen.t = Gen.tasks[Gen.task_index]
|
81
|
+
Gen.task = Gen.t
|
82
|
+
out = generate(Gen.t)
|
83
|
+
Gen.task_index += 1
|
84
|
+
return out if out.is_a?(Integer)
|
85
|
+
next if Gen.t.discard || out.empty?
|
86
|
+
name = output_name(Gen.t, Gen.task_index - 1)
|
87
|
+
begin
|
88
|
+
save(name, out, Gen.t.executable)
|
89
|
+
rescue StandardError => e
|
90
|
+
return Common.aargh("Error writing output file: #{name}\n#{e}", 3)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
0
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright © 2024-2025 Ismo Kärkkäinen
|
4
|
+
# Licensed under Universal Permissive License. See LICENSE.txt.
|
5
|
+
|
6
|
+
require_relative 'task'
|
7
|
+
|
8
|
+
module OpenAPISourceTools
|
9
|
+
# Helper class supposed to contain helpful methods.
|
10
|
+
# Exposed as Gen.h if HelperTask has been run. It is automatically
|
11
|
+
# added as the first task but later tasks can remove it.
|
12
|
+
class Helper
|
13
|
+
attr_reader :doc, :parents
|
14
|
+
attr_accessor :parent_parameters
|
15
|
+
|
16
|
+
# Stores the nearest Hash for each Hash.
|
17
|
+
def store_parents(obj, parent = nil)
|
18
|
+
if obj.is_a?(Hash)
|
19
|
+
@parents[obj] = parent
|
20
|
+
obj.each_value do |v|
|
21
|
+
store_parents(v, obj)
|
22
|
+
end
|
23
|
+
elsif obj.is_a?(Array)
|
24
|
+
obj.each do |v|
|
25
|
+
store_parents(v, parent)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(doc)
|
31
|
+
@doc = doc
|
32
|
+
@parents = {}.compare_by_identity
|
33
|
+
store_parents(@doc)
|
34
|
+
end
|
35
|
+
|
36
|
+
def parent(object)
|
37
|
+
@parents[object]
|
38
|
+
end
|
39
|
+
|
40
|
+
COMPONENTS = '#/components/'
|
41
|
+
|
42
|
+
def category_and_name(ref_or_obj)
|
43
|
+
ref = ref_or_obj.is_a?(Hash) ? ref_or_obj['$ref'] : ref_or_obj
|
44
|
+
return nil unless ref.is_a?(String)
|
45
|
+
return nil unless ref.start_with?(Helper::COMPONENTS)
|
46
|
+
idx = ref.index('/', Helper::COMPONENTS.size)
|
47
|
+
return nil if idx.nil?
|
48
|
+
category = ref[Helper::COMPONENTS.size...idx]
|
49
|
+
[ category, ref[(idx + 1)...ref.size] ]
|
50
|
+
end
|
51
|
+
|
52
|
+
def dereference(ref_or_obj)
|
53
|
+
cn = category_and_name(ref_or_obj)
|
54
|
+
return nil if cn.nil?
|
55
|
+
cs = @doc.dig('components', cn.first) || {}
|
56
|
+
cs[cn.last]
|
57
|
+
end
|
58
|
+
|
59
|
+
def basename(ref_or_obj)
|
60
|
+
cn = category_and_name(ref_or_obj)
|
61
|
+
return nil if cn.nil?
|
62
|
+
cn.last
|
63
|
+
end
|
64
|
+
|
65
|
+
def parameters(operation_object, empty_unless_local = false)
|
66
|
+
return [] if empty_unless_local && !operation_object.key?('parameters')
|
67
|
+
cps = @doc.dig('components', 'parameters') || {}
|
68
|
+
uniqs = {}
|
69
|
+
path_item_object = parent(operation_object)
|
70
|
+
[path_item_object, operation_object].each do |p|
|
71
|
+
p.fetch('parameters', []).each do |param|
|
72
|
+
r = basename(param)
|
73
|
+
r = cps[r] if r.is_a?(String)
|
74
|
+
uniqs["#{r['name']}:#{r['in']}"] = param
|
75
|
+
end
|
76
|
+
end
|
77
|
+
uniqs.keys.sort!.map { |k| uniqs[k] }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Task class to add an Helper instance to Gen.h, for convenience.
|
82
|
+
class HelperTask
|
83
|
+
include OpenAPISourceTools::TaskInterface
|
84
|
+
|
85
|
+
def generate(_context_binding)
|
86
|
+
Gen.h = Helper.new(Gen.doc) if Gen.h.nil?
|
87
|
+
end
|
88
|
+
|
89
|
+
def discard
|
90
|
+
true
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright © 2024-2025 Ismo Kärkkäinen
|
4
|
+
# Licensed under Universal Permissive License. See LICENSE.txt.
|
5
|
+
|
6
|
+
require_relative 'task'
|
7
|
+
|
8
|
+
|
9
|
+
# Original loader functions. These are accessible via Gen.loaders. New loaders
|
10
|
+
# should be added there.
|
11
|
+
module OpenAPISourceTools
|
12
|
+
# Loaders used to load gems and files and set config etc.
|
13
|
+
# Exposed as Gen.loaders if you need to modify the array.
|
14
|
+
module Loaders
|
15
|
+
# Prefix etc. and loader pairs for all loaders.
|
16
|
+
|
17
|
+
REQ_PREFIX = 'req:'
|
18
|
+
|
19
|
+
def self.req_loader(name)
|
20
|
+
return false unless name.downcase.start_with?(REQ_PREFIX)
|
21
|
+
begin
|
22
|
+
t = OpenAPISourceTools::RestoreProcessorStorage.new({})
|
23
|
+
Gen.tasks.push(t)
|
24
|
+
base = name.slice(REQ_PREFIX.size...name.size)
|
25
|
+
require(base)
|
26
|
+
Gen.config = nil
|
27
|
+
t.x = Gen.x # In case setup code replaced the object.
|
28
|
+
rescue LoadError => e
|
29
|
+
raise StandardError, "Failed to require #{name}\n#{e}"
|
30
|
+
rescue Exception => e
|
31
|
+
raise StandardError, "Problem with #{name}\n#{e}\n#{e.backtrace.join("\n")}"
|
32
|
+
end
|
33
|
+
true
|
34
|
+
end
|
35
|
+
|
36
|
+
REREQ_PREFIX = 'rereq:'
|
37
|
+
|
38
|
+
def self.rereq_loader(name)
|
39
|
+
return false unless name.downcase.start_with?(REREQ_PREFIX)
|
40
|
+
begin
|
41
|
+
t = OpenAPISourceTools::RestoreProcessorStorage.new({})
|
42
|
+
Gen.tasks.push(t)
|
43
|
+
code = name.slice(REREQ_PREFIX.size...name.size)
|
44
|
+
eval(code)
|
45
|
+
Gen.config = nil
|
46
|
+
t.x = Gen.x # In case setup code replaced the object.
|
47
|
+
rescue LoadError => e
|
48
|
+
raise StandardError, "Failed to require again #{name}\n#{e}"
|
49
|
+
rescue Exception => e
|
50
|
+
raise StandardError, "Problem with #{name}\n#{e}\n#{e.backtrace.join("\n")}"
|
51
|
+
end
|
52
|
+
true
|
53
|
+
end
|
54
|
+
|
55
|
+
RUBY_EXT = '.rb'
|
56
|
+
|
57
|
+
def self.ruby_loader(name)
|
58
|
+
return false unless name.downcase.end_with?(RUBY_EXT)
|
59
|
+
origwd = Dir.pwd
|
60
|
+
d = File.dirname(name)
|
61
|
+
Dir.chdir(d) unless d == '.'
|
62
|
+
begin
|
63
|
+
t = OpenAPISourceTools::RestoreProcessorStorage.new({})
|
64
|
+
Gen.tasks.push(t)
|
65
|
+
base = File.basename(name)
|
66
|
+
Gen.config = base[0..-4] if Gen.config.nil?
|
67
|
+
require(File.join(Dir.pwd, base))
|
68
|
+
Gen.config = nil
|
69
|
+
t.x = Gen.x # In case setup code replaced the object.
|
70
|
+
rescue LoadError => e
|
71
|
+
raise StandardError, "Failed to require #{name}\n#{e}"
|
72
|
+
rescue Exception => e
|
73
|
+
raise StandardError, "Problem with #{name}\n#{e}\n#{e.backtrace.join("\n")}"
|
74
|
+
end
|
75
|
+
Dir.chdir(origwd) unless d == '.'
|
76
|
+
true
|
77
|
+
end
|
78
|
+
|
79
|
+
YAML_PREFIX = 'yaml:'
|
80
|
+
YAML_EXTS = [ '.yaml', '.yml' ].freeze
|
81
|
+
|
82
|
+
def self.yaml_loader(name)
|
83
|
+
d = name.downcase
|
84
|
+
if d.start_with?(YAML_PREFIX)
|
85
|
+
name = name.slice(YAML_PREFIX.size...name.size)
|
86
|
+
elsif (YAML_EXTS.index { |s| d.end_with?(s) }).nil?
|
87
|
+
return false
|
88
|
+
end
|
89
|
+
n, _sep, f = name.partition(':')
|
90
|
+
raise StandardError, 'No name given.' if n.empty?
|
91
|
+
raise StandardError, 'No filename given.' if f.empty?
|
92
|
+
doc = YAML.safe_load_file(f)
|
93
|
+
raise StandardError, "#{name} #{n} exists already." unless Gen.d.add(n, doc)
|
94
|
+
true
|
95
|
+
rescue Errno::ENOENT
|
96
|
+
raise StandardError, "Not found: #{f}\n#{e}"
|
97
|
+
rescue Exception => e # Whatever was raised, we want it.
|
98
|
+
raise StandardError, "Failed to read as YAML: #{f}\n#{e}"
|
99
|
+
end
|
100
|
+
|
101
|
+
BIN_PREFIX = 'bin:'
|
102
|
+
|
103
|
+
def self.bin_loader(name)
|
104
|
+
return false unless name.downcase.start_with?(BIN_PREFIX)
|
105
|
+
n, _sep, f = name.slice(BIN_PREFIX.size...name.size).partition(':')
|
106
|
+
raise StandardError, 'No name given.' if n.empty?
|
107
|
+
raise StandardError, 'No filename given.' if f.empty?
|
108
|
+
doc = File.binread(f)
|
109
|
+
raise StandardError, "#{name} #{n} exists already." unless Gen.d.add(n, doc)
|
110
|
+
true
|
111
|
+
rescue Errno::ENOENT
|
112
|
+
raise StandardError, "Not found: #{f}\n#{e}"
|
113
|
+
rescue Exception => e # Whatever was raised, we want it.
|
114
|
+
raise StandardError, "Failed to read #{f}\n#{e}"
|
115
|
+
end
|
116
|
+
|
117
|
+
CONFIG_PREFIX = 'config:'
|
118
|
+
|
119
|
+
def self.config_loader(name)
|
120
|
+
return false unless name.downcase.start_with?(CONFIG_PREFIX)
|
121
|
+
raise StandardError, "Config name remains: #{Gen.config}" unless Gen.config.nil?
|
122
|
+
n = name.slice(CONFIG_PREFIX.size...name.size)
|
123
|
+
raise StandardError, 'No name given.' if n.empty?
|
124
|
+
# Interpretation left completely to config loading.
|
125
|
+
Gen.config = n
|
126
|
+
true
|
127
|
+
end
|
128
|
+
|
129
|
+
SEPARATOR_PREFIX = 'separator:'
|
130
|
+
|
131
|
+
def self.separator_loader(name)
|
132
|
+
return false unless name.downcase.start_with?(SEPARATOR_PREFIX)
|
133
|
+
n = name.slice(SEPARATOR_PREFIX.size...name.size)
|
134
|
+
n = nil if n.empty?
|
135
|
+
Gen.separator = n
|
136
|
+
true
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.loaders
|
140
|
+
[
|
141
|
+
method(:req_loader),
|
142
|
+
method(:rereq_loader),
|
143
|
+
method(:ruby_loader),
|
144
|
+
method(:yaml_loader),
|
145
|
+
method(:bin_loader),
|
146
|
+
method(:config_loader),
|
147
|
+
method(:separator_loader)
|
148
|
+
]
|
149
|
+
end
|
150
|
+
|
151
|
+
def self.document
|
152
|
+
<<EOB
|
153
|
+
- #{Loaders::REQ_PREFIX}req_name : requires the gem.
|
154
|
+
- #{Loaders::REREQ_PREFIX}code : runs code to add gem tasks again.
|
155
|
+
- ruby_file#{Loaders::RUBY_EXT} : changes to Ruby file directory and requires the file.
|
156
|
+
- #{Loaders::YAML_PREFIX}name:filename : Loads YAML file into Gen.d.name.
|
157
|
+
- name:filename.{#{(Loaders::YAML_EXTS.map { |s| s[1...s.size] }).join('|')}} : Loads YAML file into Gen.d.name.
|
158
|
+
- #{Loaders::BIN_PREFIX}name:filename : Loads binary file into Gen.d.name.
|
159
|
+
- #{Loaders::CONFIG_PREFIX}name : Sets Gen.config for next gem/Ruby file configuration loading.
|
160
|
+
- #{Loaders::SEPARATOR_PREFIX}string : Sets Gen.separator to string.
|
161
|
+
EOB
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright © 2024-2025 Ismo Kärkkäinen
|
4
|
+
# Licensed under Universal Permissive License. See LICENSE.txt.
|
5
|
+
|
6
|
+
|
7
|
+
module OpenAPISourceTools
|
8
|
+
# Output configuration settings for easy storage.
|
9
|
+
# You can have it in configuration and pass hash to initialize.
|
10
|
+
class OutputConfiguration
|
11
|
+
attr_reader :indent_character, :indent_step
|
12
|
+
attr_reader :tab, :tab_replaces_count
|
13
|
+
|
14
|
+
def initialize(cfg = {})
|
15
|
+
@indent_character = cfg['indent_character'] || ' '
|
16
|
+
@indent_step = cfg['indent_step'] || 4
|
17
|
+
@tab = cfg['tab'] || "\t"
|
18
|
+
@tab_replaces_count = cfg['tab_replaces_count'] || 0
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Output indentation helper class.
|
23
|
+
# Exposed as Gen.output for use from templates.
|
24
|
+
class Output
|
25
|
+
attr_reader :config
|
26
|
+
attr_accessor :last_indent
|
27
|
+
|
28
|
+
def initialize(cfg = OutputConfiguration.new)
|
29
|
+
@config = cfg
|
30
|
+
@last_indent = 0
|
31
|
+
end
|
32
|
+
|
33
|
+
def config=(cfg)
|
34
|
+
cfg = OutputConfiguration.new(cfg) if cfg.is_a?(Hash)
|
35
|
+
raise ArgumentError, "Expected OutputConfiguration or Hash, got #{cfg.class}" unless cfg.is_a?(OutputConfiguration)
|
36
|
+
@config = cfg
|
37
|
+
@last_indent = 0
|
38
|
+
end
|
39
|
+
|
40
|
+
# Takes an array of code blocks/lines or integers/booleans and produces
|
41
|
+
# indented output using the separator character.
|
42
|
+
# Set class attributes to obtain desired outcome.
|
43
|
+
def join(blocks, separator = "\n")
|
44
|
+
indented = []
|
45
|
+
blocks.flatten!
|
46
|
+
indent = 0
|
47
|
+
blocks.each do |block|
|
48
|
+
if block.nil?
|
49
|
+
indent = 0
|
50
|
+
elsif block.is_a?(Integer)
|
51
|
+
indent += block
|
52
|
+
elsif block.is_a?(TrueClass)
|
53
|
+
indent += @config.indent_step
|
54
|
+
elsif block.is_a?(FalseClass)
|
55
|
+
indent -= @config.indent_step
|
56
|
+
else
|
57
|
+
block = block.to_s unless block.is_a?(String)
|
58
|
+
if block.empty?
|
59
|
+
indented.push('')
|
60
|
+
next
|
61
|
+
end
|
62
|
+
if indent.zero?
|
63
|
+
indented.push(block)
|
64
|
+
next
|
65
|
+
end
|
66
|
+
if @config.tab_replaces_count.positive?
|
67
|
+
tabs = @config.tab * (indent / @config.tab_replaces_count)
|
68
|
+
chars = @config.indent_character * (indent % @config.tab_replaces_count)
|
69
|
+
else
|
70
|
+
tabs = ''
|
71
|
+
chars = @config.indent_character * indent
|
72
|
+
end
|
73
|
+
lines = block.lines(chomp: true)
|
74
|
+
lines.each do |line|
|
75
|
+
indented.push("#{tabs}#{chars}#{line}")
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
@last_indent = indent
|
80
|
+
indented.join(separator)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|