openapi-sourcetools 0.4.3 → 0.6.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/LICENSE.txt +1 -1
- data/bin/openapi-addheaders +73 -0
- data/bin/openapi-addparameters +72 -0
- data/bin/openapi-addresponses +72 -0
- data/bin/openapi-addschemas +104 -0
- data/bin/openapi-checkschemas +217 -0
- data/bin/openapi-frequencies +57 -84
- data/bin/openapi-generate +59 -0
- data/bin/openapi-merge +58 -84
- data/bin/openapi-processpaths +61 -88
- data/lib/apiobjects.rb +333 -0
- data/lib/common.rb +53 -27
- data/lib/gen.rb +87 -0
- data/lib/generate.rb +89 -0
- data/lib/helper.rb +96 -0
- data/lib/loaders.rb +51 -0
- data/lib/task.rb +101 -0
- metadata +27 -8
- data/bin/openapi-generatecode +0 -128
data/bin/openapi-frequencies
CHANGED
@@ -1,105 +1,78 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
# Copyright © 2021 Ismo Kärkkäinen
|
4
|
+
# Copyright © 2021-2024 Ismo Kärkkäinen
|
5
5
|
# Licensed under Universal Permissive License. See LICENSE.txt.
|
6
6
|
|
7
|
-
require_relative '../lib/common
|
7
|
+
require_relative '../lib/common'
|
8
8
|
require 'optparse'
|
9
9
|
require 'yaml'
|
10
|
-
require 'json'
|
11
10
|
|
11
|
+
def main
|
12
|
+
input_name = nil
|
13
|
+
output_name = nil
|
14
|
+
paths_name = nil
|
12
15
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
opts.on('-p', '--paths FILE', 'Path log file name (PATHS=FILE).') do |f|
|
31
|
-
env(:paths, f)
|
32
|
-
end
|
33
|
-
opts.on('--yaml', 'Output format is YAML (default) (FORMAT=YAML).') do
|
34
|
-
env(:format, 'YAML')
|
35
|
-
end
|
36
|
-
opts.on('--json', 'Output format is JSON. (FORMAT=JSON)') do
|
37
|
-
env(:format, 'JSON')
|
38
|
-
end
|
39
|
-
opts.on('-h', '--help', 'Print this help and exit.') do
|
40
|
-
$stdout.puts %(#{opts}
|
16
|
+
parser = OptionParser.new do |opts|
|
17
|
+
opts.summary_indent = ' '
|
18
|
+
opts.summary_width = 24
|
19
|
+
opts.banner = 'Usage: openapi-frequencies [options]'
|
20
|
+
opts.separator ''
|
21
|
+
opts.separator 'Options:'
|
22
|
+
opts.on('-i', '--input FILE', 'Read source from FILE, not stdin.') do |f|
|
23
|
+
input_name = f
|
24
|
+
end
|
25
|
+
opts.on('-o', '--output FILE', 'Output result to FILE, not stdout.') do |f|
|
26
|
+
output_name = f
|
27
|
+
end
|
28
|
+
opts.on('-p', '--paths FILE', 'Path log file name.') do |f|
|
29
|
+
paths_name = f
|
30
|
+
end
|
31
|
+
opts.on('-h', '--help', 'Print this help and exit.') do
|
32
|
+
$stdout.puts %(#{opts}
|
41
33
|
|
42
|
-
Matches given file with paths and adds
|
34
|
+
Matches given OpenAPI document file paths with paths from file and adds
|
35
|
+
into each path object matching count as "freq".
|
43
36
|
)
|
44
|
-
|
37
|
+
exit 0
|
38
|
+
end
|
45
39
|
end
|
46
|
-
|
47
|
-
parser.parse!
|
40
|
+
parser.parse!
|
48
41
|
|
49
|
-
aargh(
|
50
|
-
fn = env(:paths)
|
51
|
-
aargh("Path log file name must be given.") if fn.empty?
|
42
|
+
return aargh('Path log file name must be given.', 1) if paths_name.nil?
|
52
43
|
|
53
|
-
|
54
|
-
|
55
|
-
rescue Errno::ENOENT => e
|
56
|
-
aargh("Could not read #{filename.empty? ? 'stdin' : filename}", 2)
|
57
|
-
rescue StandardError => e
|
58
|
-
aargh(e.to_s, 3)
|
59
|
-
end
|
60
|
-
doc = read_source(env(:in))
|
44
|
+
doc = load_source(input_name)
|
45
|
+
return 2 if doc.nil?
|
61
46
|
|
62
|
-
begin
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
47
|
+
begin
|
48
|
+
# Expect one path per line with nothing else. Query is allowed and ignored.
|
49
|
+
f = File.new(paths_name, 'rt')
|
50
|
+
f.each_line do |line|
|
51
|
+
p = ServerPath.new(split_path(line))
|
52
|
+
doc['paths'].each_value do |info|
|
53
|
+
next unless (p <=> info['parts']).zero?
|
54
|
+
info['freq'] = info.fetch('freq', 0) + 1
|
55
|
+
# Lookalikes are the others that can be matched, since path has no
|
56
|
+
# variables so any change in fixed parts (not a lookalike) will be a
|
57
|
+
# mismatch with path.
|
58
|
+
info['lookalike'].each do |path|
|
59
|
+
cand = doc['paths'][path]
|
60
|
+
next unless (p <=> cand['parts']).zero?
|
61
|
+
cand['freq'] = cand.fetch('freq', 0) + 1
|
62
|
+
end
|
63
|
+
break
|
77
64
|
end
|
78
|
-
break
|
79
65
|
end
|
66
|
+
f.close
|
67
|
+
rescue Errno::ENOENT
|
68
|
+
return aargh("Could not read path log file: #{paths_name}", 4)
|
69
|
+
rescue NoMethodError
|
70
|
+
return aargh('Is input file an output of openapi-processpaths?', 5)
|
71
|
+
rescue StandardError => e
|
72
|
+
return aargh(e.to_s, 6)
|
80
73
|
end
|
81
|
-
f.close
|
82
|
-
rescue Errno::ENOENT => e
|
83
|
-
aargh("Could not read path log file: #{fn}", 4)
|
84
|
-
rescue NoMethodError
|
85
|
-
aargh('Is input file an output of openapi-processpaths?', 5)
|
86
|
-
rescue StandardError => e
|
87
|
-
aargh(e.to_s, 6)
|
88
|
-
end
|
89
74
|
|
90
|
-
|
91
|
-
if output.empty?
|
92
|
-
output = $stdout
|
93
|
-
else
|
94
|
-
begin
|
95
|
-
output = File.open(output, 'w')
|
96
|
-
rescue StandardError
|
97
|
-
aargh("Failed to open for writing: #{output}", 7)
|
98
|
-
end
|
75
|
+
dump_result(output_name, YAML.dump(doc), 3)
|
99
76
|
end
|
100
77
|
|
101
|
-
|
102
|
-
when 'JSON' then output.puts JSON.generate(doc)
|
103
|
-
when 'YAML' then output.puts YAML.dump(doc)
|
104
|
-
end
|
105
|
-
output.close
|
78
|
+
exit(main) if (defined? $unit_test).nil?
|
@@ -0,0 +1,59 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# Copyright © 2021-2024 Ismo Kärkkäinen
|
5
|
+
# Licensed under Universal Permissive License. See LICENSE.txt.
|
6
|
+
|
7
|
+
require_relative '../lib/common'
|
8
|
+
require_relative '../lib/loaders'
|
9
|
+
require_relative '../lib/gen'
|
10
|
+
require_relative '../lib/generate'
|
11
|
+
require 'optparse'
|
12
|
+
require 'yaml'
|
13
|
+
|
14
|
+
|
15
|
+
def main
|
16
|
+
input_name = nil
|
17
|
+
output_dir = nil
|
18
|
+
|
19
|
+
ENV['POSIXLY_CORRECT'] = '1'
|
20
|
+
parser = OptionParser.new do |opts|
|
21
|
+
opts.summary_indent = ' '
|
22
|
+
opts.summary_width = 26
|
23
|
+
opts.banner = 'Usage: openapi-generate [options] generator-names...'
|
24
|
+
opts.separator ''
|
25
|
+
opts.separator 'Options:'
|
26
|
+
opts.on('-i', '--input FILE', 'Read processed API from FILE, not stdin.') do |f|
|
27
|
+
input_name = f
|
28
|
+
end
|
29
|
+
opts.on('-o', '--outdir DIR', 'Output directory.') do |d|
|
30
|
+
output_dir = d
|
31
|
+
end
|
32
|
+
opts.on('-h', '--help', 'Print this help and exit.') do
|
33
|
+
$stdout.puts %(#{opts}
|
34
|
+
Loads API document in OpenAPI format and generator names. Built-in generator
|
35
|
+
loaders accept the following:
|
36
|
+
#{Loaders.document.strip}
|
37
|
+
|
38
|
+
During load each generator can add and modify tasks via Gen module:
|
39
|
+
#{Gen.document.strip}
|
40
|
+
|
41
|
+
After all generators have loaded succesfully, tasks are run.
|
42
|
+
)
|
43
|
+
exit 0
|
44
|
+
end
|
45
|
+
end
|
46
|
+
parser.parse!
|
47
|
+
|
48
|
+
return aargh('Generator names must be given.', 1) if ARGV.empty?
|
49
|
+
|
50
|
+
a = load_source(input_name)
|
51
|
+
return 2 if a.nil?
|
52
|
+
return aargh("Not a directory: #{output_dir}", 3) unless File.directory?(output_dir)
|
53
|
+
gen = Generator.new(a, input_name, output_dir)
|
54
|
+
ec = gen.load(ARGV)
|
55
|
+
return ec unless ec.zero?
|
56
|
+
gen.run
|
57
|
+
end
|
58
|
+
|
59
|
+
exit(main) if defined?($unit_test).nil?
|
data/bin/openapi-merge
CHANGED
@@ -1,63 +1,14 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
# Copyright © 2021 Ismo Kärkkäinen
|
4
|
+
# Copyright © 2021-2024 Ismo Kärkkäinen
|
5
5
|
# Licensed under Universal Permissive License. See LICENSE.txt.
|
6
6
|
|
7
|
-
require_relative '../lib/common
|
7
|
+
require_relative '../lib/common'
|
8
8
|
require 'optparse'
|
9
9
|
require 'yaml'
|
10
|
-
require 'json'
|
11
10
|
require 'set'
|
12
11
|
|
13
|
-
|
14
|
-
default_env(:out, '')
|
15
|
-
default_env(:format, 'YAML')
|
16
|
-
default_env(:prune, true);
|
17
|
-
|
18
|
-
ENV['POSIXLY_CORRECT'] = '1'
|
19
|
-
parser = OptionParser.new do |opts|
|
20
|
-
opts.summary_indent = ' '
|
21
|
-
opts.summary_width = 26
|
22
|
-
opts.banner = 'Usage: openapi-merge [options] sources...'
|
23
|
-
opts.separator ''
|
24
|
-
opts.separator 'Options (equivalent environment variable and value in parentheses):'
|
25
|
-
opts.on('-o', '--output FILE', 'Output result to FILE, not stdout (OUT=FILE).') do |f|
|
26
|
-
env(:out, f)
|
27
|
-
end
|
28
|
-
opts.on('-k', '--keep', 'Keep all unreferenced objects under components.') do
|
29
|
-
env(:prune, false)
|
30
|
-
end
|
31
|
-
opts.on('-p', '--prune', 'Prune all unreferenced objects under components (default).') do
|
32
|
-
env(:prune, true)
|
33
|
-
end
|
34
|
-
opts.on('--yaml', 'Output format is YAML (default).') do
|
35
|
-
env(:format, 'YAML')
|
36
|
-
end
|
37
|
-
opts.on('--json', 'Output format is JSON.') do
|
38
|
-
env(:format, 'JSON')
|
39
|
-
end
|
40
|
-
opts.on('-h', '--help', 'Print this help and exit.') do
|
41
|
-
$stdout.puts %(#{opts}
|
42
|
-
|
43
|
-
Source files are combined to form one API specification document. Sources are
|
44
|
-
allowed only to append to the merged document, not re-define anything in it.
|
45
|
-
)
|
46
|
-
exit 0
|
47
|
-
end
|
48
|
-
end
|
49
|
-
parser.parse!
|
50
|
-
|
51
|
-
aargh("Format neither JSON nor YAML: #{env(:format)}", 1) unless %w[JSON YAML].include? env(:format)
|
52
|
-
|
53
|
-
def read_source(filename)
|
54
|
-
YAML.safe_load(File.read(filename))
|
55
|
-
rescue Errno::ENOENT => e
|
56
|
-
aargh("Could not read #{filename}", 2)
|
57
|
-
rescue StandardError => e
|
58
|
-
aargh(e.to_s, 3)
|
59
|
-
end
|
60
|
-
|
61
12
|
def raise_se(message)
|
62
13
|
raise StandardError, message
|
63
14
|
end
|
@@ -77,12 +28,12 @@ end
|
|
77
28
|
|
78
29
|
def add_undefined(merged, incoming, filename, path, max_depths)
|
79
30
|
incoming.each_pair do |key, value|
|
80
|
-
unless merged.
|
31
|
+
unless merged.key? key
|
81
32
|
merged[key] = value
|
82
33
|
next
|
83
34
|
end
|
84
35
|
m = merged[key]
|
85
|
-
raise_se
|
36
|
+
raise_se("Path #{path_combo(path, false)} merged type #{m.class} differs from type #{value.class} in #{filename}") unless m.instance_of?(value.class)
|
86
37
|
raise_se("Re-definition of #{key} #{path_combo(path)} in #{filename}") if too_deep(path, max_depths)
|
87
38
|
if m.is_a? Hash # paths or similar
|
88
39
|
path.push key
|
@@ -97,22 +48,6 @@ def add_undefined(merged, incoming, filename, path, max_depths)
|
|
97
48
|
raise_se "Re-definition of #{key} #{path_combo(path)} in #{filename}"
|
98
49
|
end
|
99
50
|
end
|
100
|
-
rescue StandardError => e
|
101
|
-
aargh(e.to_s, 3)
|
102
|
-
end
|
103
|
-
|
104
|
-
max_depths = Hash.new(0)
|
105
|
-
max_depths['openapi'] = 1
|
106
|
-
max_depths['info'] = 1
|
107
|
-
max_depths['servers'] = 1
|
108
|
-
max_depths['paths'] = 2 # Allows get, post, options, etc. from different files.
|
109
|
-
max_depths['webhooks'] = 2
|
110
|
-
max_depths['components'] = 2
|
111
|
-
max_depths['security'] = 1
|
112
|
-
max_depths['tags'] = 1
|
113
|
-
merged = {}
|
114
|
-
ARGV.each do |filename|
|
115
|
-
add_undefined(merged, read_source(filename), filename, [], max_depths)
|
116
51
|
end
|
117
52
|
|
118
53
|
def gather_refs(doc, found)
|
@@ -129,7 +64,7 @@ def gather_refs(doc, found)
|
|
129
64
|
end
|
130
65
|
end
|
131
66
|
|
132
|
-
|
67
|
+
def prune(merged)
|
133
68
|
prev_refs = Set.new
|
134
69
|
loop do # May have references from deleted so repeat until nothing deleted.
|
135
70
|
refs = Set.new
|
@@ -153,7 +88,7 @@ if env(:prune)
|
|
153
88
|
sub = used
|
154
89
|
p.each_index do |k|
|
155
90
|
if k + 1 < p.size
|
156
|
-
sub[p[k]] = {} unless sub.
|
91
|
+
sub[p[k]] = {} unless sub.key? p[k]
|
157
92
|
sub = sub[p[k]]
|
158
93
|
else
|
159
94
|
sub[p[k]] = item
|
@@ -166,19 +101,58 @@ if env(:prune)
|
|
166
101
|
end
|
167
102
|
end
|
168
103
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
104
|
+
def main
|
105
|
+
output_file = nil
|
106
|
+
keep = false
|
107
|
+
|
108
|
+
ENV['POSIXLY_CORRECT'] = '1'
|
109
|
+
parser = OptionParser.new do |opts|
|
110
|
+
opts.summary_indent = ' '
|
111
|
+
opts.summary_width = 26
|
112
|
+
opts.banner = 'Usage: openapi-merge [options] sources...'
|
113
|
+
opts.separator ''
|
114
|
+
opts.separator 'Options:'
|
115
|
+
opts.on('-o', '--output FILE', 'Output result to FILE, not stdout.') do |f|
|
116
|
+
output_file = f
|
117
|
+
end
|
118
|
+
opts.on('-k', '--keep', 'Keep all unreferenced objects under components.') do
|
119
|
+
keep = true
|
120
|
+
end
|
121
|
+
opts.on('-p', '--prune', 'Prune all unreferenced objects under components (default).') do
|
122
|
+
keep = false
|
123
|
+
end
|
124
|
+
opts.on('-h', '--help', 'Print this help and exit.') do
|
125
|
+
$stdout.puts %(#{opts}
|
126
|
+
|
127
|
+
Source files are combined to form one API specification document. Sources are
|
128
|
+
allowed only to append to the merged document, not re-define anything in it.
|
129
|
+
)
|
130
|
+
exit 0
|
131
|
+
end
|
177
132
|
end
|
178
|
-
|
133
|
+
parser.parse!
|
134
|
+
|
135
|
+
max_depths = Hash.new(0)
|
136
|
+
max_depths['openapi'] = 1
|
137
|
+
max_depths['info'] = 1
|
138
|
+
max_depths['servers'] = 1
|
139
|
+
max_depths['paths'] = 2 # Allows get, post, etc. from different files.
|
140
|
+
max_depths['webhooks'] = 2
|
141
|
+
max_depths['components'] = 2
|
142
|
+
max_depths['security'] = 1
|
143
|
+
max_depths['tags'] = 1
|
144
|
+
merged = {}
|
145
|
+
ARGV.each do |filename|
|
146
|
+
s = load_source(filename)
|
147
|
+
return 2 if s.nil?
|
148
|
+
add_undefined(merged, s, filename, [], max_depths)
|
149
|
+
rescue StandardError => e
|
150
|
+
return aargh(e.to_s, 4)
|
151
|
+
end
|
152
|
+
|
153
|
+
prune(merged) unless keep
|
179
154
|
|
180
|
-
|
181
|
-
when 'JSON' then output.puts JSON.generate(merged)
|
182
|
-
when 'YAML' then output.puts YAML.dump(merged)
|
155
|
+
dump_result(output_file, YAML.dump(merged), 3)
|
183
156
|
end
|
184
|
-
|
157
|
+
|
158
|
+
exit(main) if (defined? $unit_test).nil?
|
data/bin/openapi-processpaths
CHANGED
@@ -1,111 +1,84 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
# Copyright © 2021 Ismo Kärkkäinen
|
4
|
+
# Copyright © 2021-2024 Ismo Kärkkäinen
|
5
5
|
# Licensed under Universal Permissive License. See LICENSE.txt.
|
6
6
|
|
7
|
-
require_relative '../lib/common
|
7
|
+
require_relative '../lib/common'
|
8
8
|
require 'optparse'
|
9
9
|
require 'yaml'
|
10
|
-
require 'json'
|
11
10
|
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
def main
|
13
|
+
input_name = nil
|
14
|
+
output_name = nil
|
15
|
+
error = true
|
17
16
|
|
18
|
-
parser = OptionParser.new do |opts|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
36
|
-
opts.on('--json', 'Output format is JSON. (FORMAT=JSON)') do
|
37
|
-
env(:format, 'JSON')
|
38
|
-
end
|
39
|
-
opts.on('-h', '--help', 'Print this help and exit.') do
|
40
|
-
$stdout.puts %(#{opts}
|
17
|
+
parser = OptionParser.new do |opts|
|
18
|
+
opts.summary_indent = ' '
|
19
|
+
opts.summary_width = 24
|
20
|
+
opts.banner = 'Usage: openapi-processpaths [options]'
|
21
|
+
opts.separator ''
|
22
|
+
opts.separator 'Options:'
|
23
|
+
opts.on('-i', '--input FILE', 'Read source from FILE, not stdin.') do |f|
|
24
|
+
input_name = f
|
25
|
+
end
|
26
|
+
opts.on('-o', '--output FILE', 'Output result to FILE, not stdout.') do |f|
|
27
|
+
output_name = f
|
28
|
+
end
|
29
|
+
opts.on('--warn', 'Only warn of paths with same fixed parts.') do
|
30
|
+
error = false
|
31
|
+
end
|
32
|
+
opts.on('-h', '--help', 'Print this help and exit.') do
|
33
|
+
$stdout.puts %(#{opts}
|
41
34
|
|
42
35
|
Processes API specification document path objects into form that is expected by
|
43
36
|
later stage tools. Checks for paths that may be ambiguous.
|
44
37
|
)
|
45
|
-
|
38
|
+
exit 0
|
39
|
+
end
|
46
40
|
end
|
47
|
-
|
48
|
-
parser.parse!
|
41
|
+
parser.parse!
|
49
42
|
|
50
|
-
|
51
|
-
|
52
|
-
def read_source(filename)
|
53
|
-
YAML.safe_load(filename.empty? ? $stdin : File.read(filename))
|
54
|
-
rescue Errno::ENOENT => e
|
55
|
-
aargh("Could not read #{filename.empty? ? 'stdin' : filename}", 2)
|
56
|
-
rescue StandardError => e
|
57
|
-
aargh(e.to_s, 3)
|
58
|
-
end
|
59
|
-
doc = read_source(env(:in))
|
43
|
+
doc = load_source(input_name)
|
44
|
+
return 2 if doc.nil?
|
60
45
|
|
61
|
-
processed = {}
|
62
|
-
doc.fetch('paths', {}).each_pair do |path, value|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
end
|
71
|
-
|
72
|
-
# Find lookalike sets.
|
73
|
-
lookalikes = false
|
74
|
-
paths = processed.keys.sort
|
75
|
-
paths.each_index do |k|
|
76
|
-
pk = paths[k]
|
77
|
-
a = processed[pk]
|
78
|
-
(0...k).each do |n|
|
79
|
-
pn = paths[n]
|
80
|
-
b = processed[pn]
|
81
|
-
next unless (a[:path].compare(b[:path])) == 0
|
82
|
-
a['lookalike'].push pn
|
83
|
-
b['lookalike'].push pk
|
84
|
-
$stderr.puts("Similar: #{pn} #{pk}")
|
85
|
-
lookalikes = true
|
46
|
+
processed = {}
|
47
|
+
doc.fetch('paths', {}).each_pair do |path, value|
|
48
|
+
parts = split_path(path, true)
|
49
|
+
processed[path] = {
|
50
|
+
'parts' => parts,
|
51
|
+
'orig' => value,
|
52
|
+
'lookalike' => [],
|
53
|
+
path: ServerPath.new(parts)
|
54
|
+
}
|
86
55
|
end
|
87
|
-
end
|
88
|
-
aargh('Similar paths found.', 7) if lookalikes && env(:error)
|
89
56
|
|
90
|
-
#
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
57
|
+
# Find lookalike sets.
|
58
|
+
lookalikes = false
|
59
|
+
paths = processed.keys.sort!
|
60
|
+
paths.size.times do |k|
|
61
|
+
pk = paths[k]
|
62
|
+
a = processed[pk]
|
63
|
+
k.times do |n|
|
64
|
+
pn = paths[n]
|
65
|
+
b = processed[pn]
|
66
|
+
next unless a[:path].compare(b[:path]).zero?
|
67
|
+
a['lookalike'].push pn
|
68
|
+
b['lookalike'].push pk
|
69
|
+
$stderr.puts("Similar: #{pn} #{pk}")
|
70
|
+
lookalikes = true
|
71
|
+
end
|
72
|
+
end
|
73
|
+
return aargh('Similar paths found.', 4) if lookalikes && error
|
95
74
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
else
|
100
|
-
begin
|
101
|
-
output = File.open(output, 'w')
|
102
|
-
rescue StandardError
|
103
|
-
aargh("Failed to open for writing: #{output}", 6)
|
75
|
+
# Remove temporary fields.
|
76
|
+
processed.each_value do |v|
|
77
|
+
v.keys.each { |k| v.delete(k) if k.is_a? Symbol }
|
104
78
|
end
|
105
|
-
|
79
|
+
doc['paths'] = processed
|
106
80
|
|
107
|
-
|
108
|
-
when 'JSON' then output.puts JSON.generate(doc)
|
109
|
-
when 'YAML' then output.puts YAML.dump(doc)
|
81
|
+
dump_result(output_name, YAML.dump(doc), 3)
|
110
82
|
end
|
111
|
-
|
83
|
+
|
84
|
+
exit(main) if (defined? $unit_test).nil?
|