teuton 2.1.11 → 2.2.2

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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +15 -12
  3. data/bin/check_teuton +0 -2
  4. data/docs/changelog/ideas.md +132 -0
  5. data/docs/changelog/v2.1.md +14 -122
  6. data/docs/changelog/v2.2.md +52 -28
  7. data/docs/changelog/version2.1.md +4 -0
  8. data/docs/commands/README.md +58 -15
  9. data/docs/commands/example_check.md +0 -4
  10. data/docs/commands/example_run.md +0 -4
  11. data/docs/dsl/README.md +1 -1
  12. data/docs/dsl/definition/result.md +1 -0
  13. data/docs/dsl/definition/run_remote.md +12 -6
  14. data/docs/dsl/definition/target.md +9 -10
  15. data/docs/install/README.md +13 -18
  16. data/docs/install/vagrant_docker.md +1 -1
  17. data/docs/learn/README.md +8 -8
  18. data/docs/learn/example-01-target.md +25 -25
  19. data/docs/learn/example-02-config.md +38 -49
  20. data/docs/learn/example-03-remote-hosts.md +22 -22
  21. data/docs/learn/{example-11-first-test.md → example-04-new-test.md} +23 -24
  22. data/docs/learn/{example-04-use.md → example-05-use.md} +6 -6
  23. data/docs/learn/{example-05-debug.md → example-06-debug.md} +8 -8
  24. data/docs/learn/{example-06-log.md → example-07-log.md} +7 -7
  25. data/docs/learn/{example-07-readme.md → example-08-readme.md} +10 -10
  26. data/docs/learn/{example-08-preserve.md → example-09-preserve.md} +6 -6
  27. data/docs/videos.md +19 -0
  28. data/lib/teuton/application.rb +22 -3
  29. data/lib/teuton/case_manager/case/dsl/goto.rb +2 -2
  30. data/lib/teuton/case_manager/case/dsl/macro.rb +1 -0
  31. data/lib/teuton/case_manager/case/dsl/send.rb +2 -1
  32. data/lib/teuton/case_manager/case/play.rb +2 -0
  33. data/lib/teuton/case_manager/case/result/ext_compare.rb +16 -0
  34. data/lib/teuton/case_manager/case/result/result.rb +1 -1
  35. data/lib/teuton/case_manager/case/runner.rb +30 -4
  36. data/lib/teuton/case_manager/case_manager.rb +1 -1
  37. data/lib/teuton/case_manager/export_manager.rb +21 -11
  38. data/lib/teuton/case_manager/utils.rb +1 -1
  39. data/lib/teuton/{project/laboratory → check}/builtin.rb +0 -0
  40. data/lib/teuton/{project/laboratory → check}/dsl.rb +40 -28
  41. data/lib/teuton/{project/laboratory → check}/laboratory.rb +3 -8
  42. data/lib/teuton/{project/laboratory → check}/show.rb +53 -59
  43. data/lib/teuton/cli.rb +85 -14
  44. data/lib/teuton/{project/readme → readme}/dsl.rb +0 -0
  45. data/lib/teuton/{project/readme → readme}/lang.rb +1 -1
  46. data/lib/teuton/{project/readme → readme}/readme.rb +22 -18
  47. data/lib/teuton/report/formatter/array_formatter.rb +13 -1
  48. data/lib/teuton/report/formatter/base_formatter.rb +18 -5
  49. data/lib/teuton/{project/skeleton.rb → skeleton.rb} +7 -19
  50. data/lib/teuton/utils/configfile_reader.rb +121 -0
  51. data/lib/teuton/{project → utils}/name_file_finder.rb +46 -26
  52. data/lib/teuton/version.rb +8 -0
  53. data/lib/teuton.rb +39 -32
  54. metadata +107 -65
  55. data/lib/teuton/cli/check.rb +0 -38
  56. data/lib/teuton/cli/main.rb +0 -6
  57. data/lib/teuton/cli/play.rb +0 -38
  58. data/lib/teuton/cli/readme.rb +0 -26
  59. data/lib/teuton/cli/version.rb +0 -12
  60. data/lib/teuton/files/gitignore +0 -2
  61. data/lib/teuton/project/configfile_reader.rb +0 -49
  62. data/lib/teuton/project/project.rb +0 -80
@@ -1,8 +1,8 @@
1
1
  require 'terminal-table'
2
2
  require 'rainbow'
3
3
 
4
- require_relative '../../application'
5
- require_relative '../configfile_reader'
4
+ require_relative '../application'
5
+ require_relative '../utils/configfile_reader'
6
6
 
7
7
  # Laboratory
8
8
  # * show_dsl
@@ -10,68 +10,23 @@ require_relative '../configfile_reader'
10
10
  # * show_config
11
11
  class Laboratory
12
12
  ##
13
- # Display DSL on screen
14
- def show_dsl
13
+ # Display DSL checking on screen
14
+ def show
15
15
  @verbose = true
16
16
  process_content
17
17
  show_stats
18
- show_config
19
- end
20
-
21
- ##
22
- # Display stats on screen
23
- def show_stats
24
- @stats[:hosts] = 0
25
- @hosts.each_pair { |_k, v| @stats[:hosts] += v }
26
-
27
- my_screen_table = Terminal::Table.new do |st|
28
- st.add_row ['DSL Stats', 'Count']
29
- st.add_separator
30
- st.add_row ['Groups', @stats[:groups]]
31
- st.add_row ['Targets', @stats[:targets]]
32
- st.add_row ['Goto', @stats[:hosts]]
33
- @hosts.each_pair { |k, v| st.add_row [" * #{k}", v] }
34
- st.add_row ['Uniques', @stats[:uniques]]
35
- st.add_row ['Logs', @stats[:uniques]]
36
- st.add_row [' ', ' ']
37
-
38
- st.add_row ['Gets', @stats[:gets]]
39
- if @gets.count > 0
40
- list = @gets.sort_by { |_k, v| v }
41
- list.reverse_each { |item| st.add_row [" * #{item[0]}", item[1].to_s] }
42
- end
43
-
44
- st.add_row ['Sets', @stats[:sets]]
45
- if @sets.count > 0
46
- @sets.each_pair { |k, v| st.add_row [" * #{k}", v.to_s] }
47
- end
48
- end
49
- verboseln my_screen_table.to_s + "\n"
18
+ revise_config_content
50
19
  end
51
20
 
52
21
  ##
53
- # Display config on screen
54
- def show_config
22
+ # Display config for teuton panel on screen
23
+ def show_panelconfig
55
24
  @verbose = false
56
25
  process_content
57
26
  @verbose = true
58
- revise_config_content
27
+ recomended_panelconfig_content
59
28
  end
60
29
 
61
- # def show_requests
62
- # @verbose = false
63
- # process_content
64
- # @verbose = true
65
- # my_screen_table = Terminal::Table.new do |st|
66
- # st.add_row ['Lines', 'REQUEST description']
67
- # st.add_separator
68
- # @requests.each_with_index do |line, index|
69
- # st.add_row ['%03d' % index, line]
70
- # end
71
- # end
72
- # verboseln my_screen_table
73
- # end
74
-
75
30
  private
76
31
 
77
32
  def verbose(text)
@@ -84,14 +39,16 @@ class Laboratory
84
39
 
85
40
  def process_content
86
41
  groups = Application.instance.groups
42
+ option = Application.instance.options
43
+
87
44
  verboseln ''
88
45
  groups.each do |t|
89
46
  @stats[:groups] += 1
90
-
91
- msg = "GROUP: #{t[:name]}"
92
- my_screen_table = Terminal::Table.new { |st| st.add_row [msg] }
93
- verboseln my_screen_table
94
-
47
+ unless option[:panel]
48
+ msg = "GROUP: #{t[:name]}"
49
+ my_screen_table = Terminal::Table.new { |st| st.add_row [msg] }
50
+ verboseln my_screen_table
51
+ end
95
52
  instance_eval(&t[:block])
96
53
  end
97
54
  end
@@ -127,10 +84,16 @@ class Laboratory
127
84
  verboseln YAML.dump(output)
128
85
  end
129
86
 
87
+ def recomended_panelconfig_content
88
+ output = { global: {}, cases: nil }
89
+ script_vars = find_script_vars
90
+ script_vars.each { |i| output[:global][i] = 'VALUE' }
91
+ verboseln YAML.dump(output)
92
+ end
93
+
130
94
  ##
131
95
  # Revive and check config content
132
96
  def revise_config_content
133
- @verbose = true
134
97
  my_screen_table = Terminal::Table.new do |st|
135
98
  st.add_row ['Revising CONFIG file']
136
99
  end
@@ -166,4 +129,35 @@ class Laboratory
166
129
  end
167
130
  end
168
131
  end
132
+
133
+ ##
134
+ # Display stats on screen
135
+ def show_stats
136
+ @stats[:hosts] = 0
137
+ @hosts.each_pair { |_k, v| @stats[:hosts] += v }
138
+
139
+ my_screen_table = Terminal::Table.new do |st|
140
+ st.add_row ['DSL Stats', 'Count']
141
+ st.add_separator
142
+ st.add_row ['Groups', @stats[:groups]]
143
+ st.add_row ['Targets', @stats[:targets]]
144
+ st.add_row ['Goto', @stats[:hosts]]
145
+ @hosts.each_pair { |k, v| st.add_row [" * #{k}", v] }
146
+ st.add_row ['Uniques', @stats[:uniques]]
147
+ st.add_row ['Logs', @stats[:uniques]]
148
+ st.add_row [' ', ' ']
149
+
150
+ st.add_row ['Gets', @stats[:gets]]
151
+ if @gets.count > 0
152
+ list = @gets.sort_by { |_k, v| v }
153
+ list.reverse_each { |item| st.add_row [" * #{item[0]}", item[1].to_s] }
154
+ end
155
+
156
+ st.add_row ['Sets', @stats[:sets]]
157
+ if @sets.count > 0
158
+ @sets.each_pair { |k, v| st.add_row [" * #{k}", v.to_s] }
159
+ end
160
+ end
161
+ verboseln my_screen_table.to_s + "\n"
162
+ end
169
163
  end
data/lib/teuton/cli.rb CHANGED
@@ -1,39 +1,110 @@
1
+
1
2
  require 'thor'
2
- require_relative 'application'
3
- require_relative 'project/project'
4
- require_relative 'project/skeleton.rb'
5
- require_relative 'cli/main'
3
+ require_relative 'version'
4
+ require_relative '../teuton'
6
5
 
7
- ##
8
- # Command Line User Interface
9
6
  class CLI < Thor
10
7
  map ['h', '-h', '--help'] => 'help'
11
8
 
12
- map ['c', '-c', '--create', 'create'] => 'new'
9
+ map ['v', '-v', '--version'] => 'version'
10
+ desc 'version', 'Show the program version'
11
+ def version
12
+ puts "#{Teuton::APPNAME} (version #{Teuton::VERSION})"
13
+ end
14
+
15
+ map ['n', '-n', '--new'] => 'new'
13
16
  desc 'new DIRECTORY', 'Create skeleton for a new project'
14
17
  long_desc <<-LONGDESC
15
18
  Create files for a new project.
19
+ LONGDESC
20
+ def new(projectpath)
21
+ Teuton.create(projectpath)
22
+ end
23
+
24
+ map ['c', '-c', '--check'] => 'check'
25
+ option :panelconfig, type: :boolean
26
+ option :cname, type: :string
27
+ option :cpath, type: :string
28
+ desc 'check [OPTIONS] DIRECTORY',
29
+ 'Check test and config file content'
30
+ long_desc <<-LONGDESC
31
+
32
+ (1) teuton check path/to/dir/foo
33
+ , Check content of start.rb and config.yaml files.
16
34
 
17
- Example:
35
+ (2) teuton check path/to/dir/foo --cname=demo
36
+ , Check content of start.rb and demo.yaml files.
37
+
38
+ (3) teuton check path/to/file/foo.rb
39
+ , Check content of foo.rb and foo.yaml files.
40
+
41
+ (4) teuton check path/to/file/foo.rb --cname=demo
42
+ , Check content of foo.rb and demo.yaml files.
18
43
 
19
- #{$PROGRAM_NAME} create dir/foo
20
44
  LONGDESC
21
- ##
22
- # Command: create new Teuton project
23
- def new(path_to_new_dir)
24
- Skeleton.create(path_to_new_dir)
45
+ def check(projectpath)
46
+ Teuton.check(projectpath, options)
47
+ end
48
+
49
+ map ['--run', 'run'] => 'play'
50
+ option :export, type: :string
51
+ option :cname, type: :string
52
+ option :cpath, type: :string
53
+ option :case, type: :string
54
+ option :color, type: :boolean
55
+ option :quiet, type: :boolean
56
+ desc '[run] [OPTIONS] DIRECTORY',
57
+ 'Run test from directory'
58
+ long_desc <<-LONGDESC
59
+ This function execute challenge from specified directory.
60
+ By default, show progress on the screen.
61
+
62
+ Let's see others options:
63
+
64
+ (1) teuton foo, run challenge from foo/start.rb with foo/config.yaml config file.
65
+
66
+ (2) teuton run foo, same as (1).
67
+
68
+ (3) teuton run --export=json foo, run challenge and export using json format.
69
+ Others output formats availables are: txt, html, yaml, json and colored_text.
70
+
71
+ (4) teuton run --cname=demo foo, run challenge from foo/start.rb with foo/demo.yaml config file.
72
+
73
+ (5) teuton foo/demo.rb, Run challenge from foo/demo.rb with foo/demo.yaml config file.
74
+
75
+ LONGDESC
76
+ def play(filepath)
77
+ Teuton.run(filepath, options)
78
+ end
79
+
80
+ map ['--readme'] => 'readme'
81
+ option :lang, type: :string
82
+ desc 'readme DIRECTORY',
83
+ 'Show README extracted from test contents'
84
+ long_desc <<-LONGDESC
85
+
86
+ (1) teuton readme foo
87
+ , Create README.md from foo/start.rb.
88
+
89
+ (2) teuton readme foo/demo.rb
90
+ , Create README.md from foo/demo.rb.
91
+ LONGDESC
92
+
93
+ def readme(projectpath)
94
+ # Create README from teuton test
95
+ Teuton.readme(projectpath, options)
25
96
  end
26
97
 
27
98
  ##
28
99
  # These inputs are equivalents:
29
100
  # * teuton dir/foo
30
101
  # * teuton run dir/foo
31
- # * teuton play dir/foo
32
102
  def method_missing(method, *_args, &_block)
33
103
  play(method.to_s)
34
104
  end
35
105
 
36
106
  def respond_to_missing?(method_name, include_private = false)
107
+ # Respond to missing methods name
37
108
  super
38
109
  end
39
110
  end
File without changes
@@ -1,5 +1,5 @@
1
1
 
2
- require_relative '../../application'
2
+ require_relative '../application'
3
3
 
4
4
  module Lang
5
5
  def self.get(key)
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../../application'
4
- require_relative '../configfile_reader'
5
- require_relative '../../case_manager/case/result/result'
3
+ require_relative '../application'
4
+ require_relative '../utils/configfile_reader'
5
+ require_relative '../case_manager/case/result/result'
6
6
  require_relative 'dsl'
7
7
  require_relative 'lang'
8
8
 
@@ -44,6 +44,19 @@ class Readme
44
44
  reset
45
45
  end
46
46
 
47
+ ##
48
+ # Show README on screen
49
+ def show
50
+ process_content
51
+ show_head
52
+ show_content
53
+ show_tail
54
+ end
55
+
56
+ private
57
+
58
+ ##
59
+ # Reset attributes
47
60
  def reset
48
61
  app = Application.instance
49
62
  @config = ConfigFileReader.read(app.config_path)
@@ -73,13 +86,6 @@ class Readme
73
86
  @action = { readme: [] }
74
87
  end
75
88
 
76
- def show
77
- process_content
78
- show_head
79
- show_content
80
- show_tail
81
- end
82
-
83
89
  ##
84
90
  # Show README head
85
91
  def show_head
@@ -97,23 +103,21 @@ class Readme
97
103
  puts Lang::get(:hosts)
98
104
  puts "\n"
99
105
  puts '| ID | Host | Configuration |'
100
- puts '| -- | ---- | ------------- |'
106
+ puts '| --- | --- | --- |'
101
107
  @required_hosts.each_pair do |k, v|
102
108
  c = []
103
109
  v.each_pair { |k2,v2| c << "#{k2}=#{v2}" }
104
- puts "|#{i}|#{k.upcase}|#{c.join(', ')}|"
110
+ puts "| #{i} | #{k.upcase} | #{c.join(', ')} |"
105
111
  i += 1
106
112
  end
107
113
  puts "\n> NOTE: SSH Service installation is required on every host."
108
114
  end
109
115
 
110
116
  unless @cases_params.empty?
111
- @cases_params.uniq!.sort!
117
+ @cases_params.sort!
112
118
  puts Lang::get(:params)
113
119
  @cases_params.uniq.each { |i| puts format('* %s', i) }
114
- puts "\n> NOTE:"
115
- puts "> * Teuton software must known this information!"
116
- puts "> * Save every ':param: value' into config file."
120
+ puts "\n> NOTE: Save every ':param: value' into config file."
117
121
  end
118
122
  end
119
123
 
@@ -156,13 +160,13 @@ class Readme
156
160
  puts Lang::get(:global)
157
161
  puts "\n"
158
162
  puts '| Global param | Value |'
159
- puts '| ------------ | ----- |'
163
+ puts '| --- | --- |'
160
164
  @global_params.each_pair { |k,v| puts "|#{k}|#{v}|" }
161
165
  puts "\n\#\# Created params"
162
166
  puts Lang::get(:created)
163
167
  puts "\n"
164
168
  puts '| Created params | Value |'
165
- puts '| -------------- | ----- |'
169
+ puts '| --- | --- |'
166
170
  @setted_params.each_pair { |k,v| puts "|#{k}|#{v}|" }
167
171
  end
168
172
  end
@@ -2,13 +2,19 @@
2
2
 
3
3
  require_relative 'base_formatter'
4
4
 
5
+ ##
5
6
  # ArrayFormatter class: format report data into an array
6
7
  class ArrayFormatter < BaseFormatter
8
+ ##
9
+ # Initialize class
10
+ # @param report (Report) Format report data into Array
7
11
  def initialize(report)
8
12
  super(report)
9
13
  @data = {}
10
14
  end
11
15
 
16
+ ##
17
+ # Execute format action
12
18
  def process
13
19
  build_data
14
20
  w @data.to_s # Write data into ouput file
@@ -28,6 +34,8 @@ class ArrayFormatter < BaseFormatter
28
34
  @data[:config] = head
29
35
  end
30
36
 
37
+ # rubocop:disable Metrics/AbcSize
38
+ # rubocop:disable Metrics/MethodLength
31
39
  def build_history_data
32
40
  @data[:logs] = []
33
41
  groups = []
@@ -46,7 +54,8 @@ class ArrayFormatter < BaseFormatter
46
54
  end
47
55
 
48
56
  target = {}
49
- target[:target_id] = format('%02d', i[:id])
57
+ # target[:target_id] = format('%02d', i[:id])
58
+ target[:target_id] = format('%<id>02d', id: i[:id])
50
59
  target[:check] = i[:check]
51
60
  target[:score] = value
52
61
  target[:weight] = i[:weight]
@@ -66,6 +75,8 @@ class ArrayFormatter < BaseFormatter
66
75
  groups << { title: title, targets: targets } unless title.nil?
67
76
  @data[:groups] = groups
68
77
  end
78
+ # rubocop:enable Metrics/AbcSize
79
+ # rubocop:enable Metrics/MethodLength
69
80
 
70
81
  def build_final_data
71
82
  tail = {}
@@ -77,6 +88,7 @@ class ArrayFormatter < BaseFormatter
77
88
  app = Application.instance
78
89
  @data[:hall_of_fame] = {}
79
90
  return if app.options[:case_number] < 3
91
+
80
92
  fame = {}
81
93
  app.hall_of_fame.each { |line| fame[line[0]] = line[1] }
82
94
  @data[:hall_of_fame] = fame
@@ -2,25 +2,38 @@
2
2
 
3
3
  # BaseFormatter class
4
4
  class BaseFormatter
5
+ ##
6
+ # Initialize class
7
+ # @param report (Report) Format report data
5
8
  def initialize(report)
6
9
  @head = report.head
7
10
  @lines = report.lines
8
11
  @tail = report.tail
9
12
  end
10
13
 
14
+ ##
15
+ # Execute format action
16
+ def process
17
+ raise 'Empty method!'
18
+ end
19
+
20
+ ##
21
+ # Creates new output file
22
+ # @param filename (String) Path to output file
11
23
  def init(filename)
12
24
  @filename = filename
13
25
  @file = File.open(@filename, 'w')
14
26
  end
15
27
 
28
+ ##
29
+ # Write data into output file
30
+ # @param text (String) Text data to write into output file
16
31
  def w(text)
17
- @file.write text.to_s # write into output file
18
- end
19
-
20
- def process
21
- raise 'Empty method!'
32
+ @file.write text.to_s
22
33
  end
23
34
 
35
+ ##
36
+ # Close open output file
24
37
  def deinit
25
38
  @file.close
26
39
  end
@@ -3,13 +3,7 @@
3
3
  require 'fileutils'
4
4
  require 'rainbow'
5
5
 
6
- # Skeleton class
7
- # * create
8
- # * create_main_dir_and_files
9
- # * create_assets_dir_and_files
10
- # * create_dir
11
- # * create_dirs
12
- # * copyfile
6
+ # Skeleton module
13
7
  module Skeleton
14
8
  ##
15
9
  # Create teuton project skeleton
@@ -17,7 +11,7 @@ module Skeleton
17
11
  def self.create(project_dir)
18
12
  project_name = File.basename(project_dir)
19
13
  puts "\n[INFO] Creating #{Rainbow(project_name).bright} project skeleton"
20
- source_basedir = File.join(File.dirname(__FILE__), '..')
14
+ source_basedir = File.dirname(__FILE__)
21
15
  create_dir project_dir
22
16
  create_main_dir_and_files(project_dir, source_basedir)
23
17
  end
@@ -26,12 +20,11 @@ module Skeleton
26
20
  # Create main dir and files
27
21
  # @param project_dir (String)
28
22
  # @param source_basedir (String)
29
- def self.create_main_dir_and_files(project_dir, source_basedir)
23
+ private_class_method def self.create_main_dir_and_files(project_dir, source_basedir)
30
24
  # Directory and files: Ruby script, Configfile, gitignore
31
25
  items = [
32
26
  { source: 'files/config.yaml', target: 'config.yaml' },
33
27
  { source: 'files/start.rb', target: 'start.rb' }
34
- # { source: 'files/gitignore', target: '.gitignore' }
35
28
  ]
36
29
  items.each do |item|
37
30
  source = File.join(source_basedir, item[:source])
@@ -43,7 +36,7 @@ module Skeleton
43
36
  ##
44
37
  # Create dir
45
38
  # @param dirpath (String)
46
- def self.create_dir(dirpath)
39
+ private_class_method def self.create_dir(dirpath)
47
40
  if Dir.exist? dirpath
48
41
  puts "* Exists dir! => #{Rainbow(dirpath).yellow}"
49
42
  else
@@ -56,18 +49,12 @@ module Skeleton
56
49
  end
57
50
  end
58
51
 
59
- ##
60
- # Create dirs
61
- # @param args (Array)
62
- def self.create_dirs(*args)
63
- args.each { |arg| create_dir arg }
64
- end
65
-
66
52
  ##
67
53
  # Copy file
68
54
  # @param source (String) Source file
69
55
  # @param dest (String) Dest file
70
- def self.copyfile(source, dest)
56
+ # rubocop:disable Metrics/MethodLength
57
+ private_class_method def self.copyfile(source, dest)
71
58
  if File.exist? dest
72
59
  puts "* Exists file! => #{Rainbow(dest).yellow}"
73
60
  else
@@ -80,4 +67,5 @@ module Skeleton
80
67
  end
81
68
  end
82
69
  end
70
+ # rubocop:enable Metrics/MethodLength
83
71
  end
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require 'json/pure'
5
+
6
+ ##
7
+ # Functions that read data from ConfigFile using YAML or JSON formats
8
+ module ConfigFileReader
9
+ ##
10
+ # Read config file
11
+ # @param filepath (String) Path to config file
12
+ # @return Hash with config data
13
+ def self.read(filepath)
14
+ unless File.exist?(filepath)
15
+ data = {}
16
+ data[:global] = {}
17
+ data[:alias] = {}
18
+ data[:cases] = [{ tt_members: 'anonymous' }]
19
+ return data
20
+ end
21
+ return read_yaml(filepath) if File.extname(filepath) == '.yaml'
22
+
23
+ return read_json(filepath) if File.extname(filepath) == '.json'
24
+
25
+ raise "[ERROR] ConfigFileReader: #{filepath}"
26
+ end
27
+
28
+ ##
29
+ # Read YAML config file
30
+ # @param filepath (String) Path to YAML config file
31
+ # @return Hash with config data
32
+ # rubocop:disable Metrics/MethodLength
33
+ # rubocop:disable Metrics/AbcSize
34
+ # rubocop:disable Security/YAMLLoad
35
+ def self.read_yaml(filepath)
36
+ begin
37
+ data = YAML.load(File.open(filepath))
38
+ rescue StandardError => e
39
+ puts "\n" + ('=' * 80)
40
+ puts "[ERROR] ConfigFileReader#read <#{filepath}>"
41
+ puts ' I suggest to revise file format!'
42
+ puts " #{e.message}\n" + ('=' * 80)
43
+ raise "[ERROR] ConfigFileReader <#{e}>"
44
+ end
45
+ data = convert_string_keys_to_symbol(data)
46
+ data[:global] = data[:global] || {}
47
+ data[:alias] = data[:alias] || {}
48
+ data[:cases] = data[:cases] || []
49
+ read_included_files!(filepath, data)
50
+ data
51
+ end
52
+ # rubocop:enable Metrics/MethodLength
53
+ # rubocop:enable Metrics/AbcSize
54
+ # rubocop:enable Security/YAMLLoad
55
+
56
+ ##
57
+ # Read JSON config file
58
+ # @param filepath (String) Path to JSON config file
59
+ # @return Hash with config data
60
+ def self.read_json(filepath)
61
+ data = JSON.parse(File.read(filepath), symbolize_names: true)
62
+ data = convert_string_keys_to_symbol(data)
63
+ data[:global] = data[:global] || {}
64
+ data[:alias] = data[:alias] || {}
65
+ data[:cases] = data[:cases] || []
66
+ read_included_files!(filepath, data)
67
+ data
68
+ end
69
+
70
+ ##
71
+ # Read all configuration files from "filepath" folder.
72
+ # @param filepath (String) Folder with config files
73
+ # @param data (Hash) Input configuration
74
+ # rubocop:disable Security/YAMLLoad
75
+ private_class_method def self.read_included_files!(filepath, data)
76
+ return if data[:global][:tt_include].nil?
77
+
78
+ include_dir = data[:global][:tt_include]
79
+ if include_dir == File.absolute_path(include_dir)
80
+ basedir = include_dir
81
+ else
82
+ basedir = File.join(File.dirname(filepath), data[:global][:tt_include])
83
+ end
84
+ files = Dir.glob(File.join(basedir, '**/*.yaml'))
85
+ files += Dir.glob(File.join(basedir, '**/*.yml'))
86
+ files.each { |file|
87
+ begin
88
+ data[:cases] << YAML.load(File.open(file))
89
+ rescue StandardError => e
90
+ puts "\n" + ('=' * 80)
91
+ puts "[ERROR] ConfigFileReader#read <#{file}>"
92
+ puts ' I suggest to revise file format!'
93
+ puts " #{e.message}\n" + ('=' * 80)
94
+ end
95
+ }
96
+ end
97
+ # rubocop:enable Security/YAMLLoad
98
+
99
+ # rubocop:disable Metrics/MethodLength
100
+ # rubocop:disable Metrics/AbcSize
101
+ private_class_method def self.convert_string_keys_to_symbol(input)
102
+ return input if input.class != Hash
103
+
104
+ output = {}
105
+ input.each_pair do |key, value|
106
+ key2 = key
107
+ key2 = key.to_sym if key.class
108
+ value2 = value
109
+ if value.class == Hash
110
+ value2 = convert_string_keys_to_symbol(value)
111
+ elsif value.class == Array
112
+ value2 = []
113
+ value.each { |i| value2 << convert_string_keys_to_symbol(i) }
114
+ end
115
+ output[key2] = value2
116
+ end
117
+ output
118
+ end
119
+ # rubocop:enable Metrics/MethodLength
120
+ # rubocop:enable Metrics/AbcSize
121
+ end