climatic 0.2.26

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 (64) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +52 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/climatic.gemspec +28 -0
  13. data/example/simple_app/.gitignore +12 -0
  14. data/example/simple_app/.rspec +2 -0
  15. data/example/simple_app/.ruby-version +1 -0
  16. data/example/simple_app/.travis.yml +5 -0
  17. data/example/simple_app/CODE_OF_CONDUCT.md +74 -0
  18. data/example/simple_app/Gemfile +6 -0
  19. data/example/simple_app/LICENSE.txt +21 -0
  20. data/example/simple_app/README.md +43 -0
  21. data/example/simple_app/Rakefile +6 -0
  22. data/example/simple_app/bin/console +14 -0
  23. data/example/simple_app/bin/setup +8 -0
  24. data/example/simple_app/etc/command_line.yml +52 -0
  25. data/example/simple_app/exe/user +57 -0
  26. data/example/simple_app/lib/tst_climatic/version.rb +3 -0
  27. data/example/simple_app/lib/tst_climatic.rb +5 -0
  28. data/example/simple_app/spec/spec_helper.rb +14 -0
  29. data/example/simple_app/spec/tst_climatic_spec.rb +11 -0
  30. data/example/simple_app/tst_climatic.gemspec +39 -0
  31. data/lib/climatic/config_layers/command_line_layer.rb +46 -0
  32. data/lib/climatic/config_layers/command_line_manager_binder.rb +84 -0
  33. data/lib/climatic/config_layers/env_layer.rb +41 -0
  34. data/lib/climatic/config_layers/executable_gem_layer.rb +45 -0
  35. data/lib/climatic/config_layers/gem_layer.rb +50 -0
  36. data/lib/climatic/config_layers/generic_layer.rb +62 -0
  37. data/lib/climatic/config_layers/global_layer.rb +28 -0
  38. data/lib/climatic/config_layers/program_description_helper.rb +94 -0
  39. data/lib/climatic/config_layers/project_layer.rb +61 -0
  40. data/lib/climatic/config_layers/provided_config_file_layer.rb +26 -0
  41. data/lib/climatic/config_layers/source_helper.rb +48 -0
  42. data/lib/climatic/config_layers/system_layer.rb +37 -0
  43. data/lib/climatic/config_layers/user_layer.rb +40 -0
  44. data/lib/climatic/initializer.rb +87 -0
  45. data/lib/climatic/layers_manager.rb +81 -0
  46. data/lib/climatic/logger/accumulator.rb +49 -0
  47. data/lib/climatic/logger/manager.rb +50 -0
  48. data/lib/climatic/logger/wrapper.rb +12 -0
  49. data/lib/climatic/processes/base.rb +51 -0
  50. data/lib/climatic/processes/command.rb +15 -0
  51. data/lib/climatic/processes/synchronous.rb +51 -0
  52. data/lib/climatic/processes/time_management.rb +21 -0
  53. data/lib/climatic/proxy.rb +27 -0
  54. data/lib/climatic/script/base.rb +65 -0
  55. data/lib/climatic/script/simple.rb +31 -0
  56. data/lib/climatic/script/unimplemented_processor.rb +19 -0
  57. data/lib/climatic/script.rb +4 -0
  58. data/lib/climatic/utils/error.rb +5 -0
  59. data/lib/climatic/utils/input.rb +65 -0
  60. data/lib/climatic/utils/safe_exec.rb +28 -0
  61. data/lib/climatic/utils/script_helper.rb +18 -0
  62. data/lib/climatic/version.rb +3 -0
  63. data/lib/climatic.rb +45 -0
  64. metadata +162 -0
@@ -0,0 +1,87 @@
1
+ module Climatic
2
+
3
+ module Initializer
4
+
5
+ include Climatic::Utils::ScriptHelper
6
+
7
+ attr_reader :config
8
+
9
+ def bootstrap(cmd_line_args: ARGV.dup,
10
+ command_manager: Climatic::ConfigLayers::CommandLineLayer.default_command_line_manager)
11
+ raise Climatic::Error, 'You cannot bootstrap Climatic framework twice !' if climatic_bootstrapped?
12
+ @climatic_status = :bootstrapping
13
+ setup_initial_logger
14
+ Climatic.logger.debug 'Starting Climatic framework setup...'
15
+ # Get the config first to correctly setup the definitive logger
16
+ setup_config_manager cmd_line_args, command_manager
17
+ # Now we can setup the definitive logger
18
+ setup_logger
19
+ Climatic.logger.debug 'Climatic framework setup complete.'
20
+ @climatic_status = :bootstrapped
21
+ rescue Slop::UnknownOption
22
+ Climatic.logger.debug 'Climatic initialization failed (wrong command line options) !'
23
+ end
24
+
25
+ def climatic_bootstrapping?
26
+ @climatic_status == :bootstrapping
27
+ end
28
+
29
+ def climatic_bootstrapped?
30
+ @climatic_status == :bootstrapped
31
+ end
32
+
33
+ private
34
+
35
+ def setup_config_manager(cmd_line_args, command_manager)
36
+ @config = Climatic::LayersManager.new
37
+ config.command_line_layer.command_line_manager = command_manager
38
+ config.command_line_layer.cmd_line_args = cmd_line_args
39
+ end
40
+
41
+ def setup_initial_logger
42
+ @logger = Climatic::Logger::Accumulator.new
43
+ UltraCommandLine.logger = @logger
44
+ end
45
+
46
+ def setup_logger
47
+ if config[:debug]
48
+ log_device = if config[:'log-file']
49
+ if File.exists? config[:'log-file']
50
+ if File.writable? config[:'log-file']
51
+ if config[:'truncate-log-file']
52
+ File.open(config[:'log-file'], 'w') do |log_file|
53
+ log_file.puts "Log truncated on #{Time.now}"
54
+ end
55
+ end
56
+ config[:'log-file']
57
+ else
58
+ STDERR.puts "WARNING: Log file '#{config[:'log-file']}' is not writable. Switching to STDERR..."
59
+ config[:'log-file'] = nil
60
+ STDERR
61
+ end
62
+ else
63
+ if File.writable? File.dirname(config[:'log-file'])
64
+ config[:'log-file']
65
+ else
66
+ STDERR.puts "WARNING: Cannot write log file in '#{File.dirname config[:'log-file']}'. Switching to STDERR..."
67
+ config[:'log-file'] = nil
68
+ STDERR
69
+ end
70
+ end
71
+ elsif config[:'debug-on-stderr']
72
+ STDERR
73
+ else
74
+ STDOUT
75
+ end
76
+
77
+ new_logger = ::Logger.new log_device
78
+ new_logger.level = config[:'log-level'] || Climatic::Logger::Manager::DEFAULT_LOG_LEVEL
79
+ self.logger = new_logger
80
+ else
81
+ self.logger = user_defined_logger
82
+ end
83
+ end
84
+
85
+ end
86
+
87
+ end
@@ -0,0 +1,81 @@
1
+ module Climatic
2
+ class LayersManager < SuperStack::Manager
3
+
4
+ include Climatic::ConfigLayers::ProgramDescriptionHelper
5
+
6
+ attr_reader :system_layer, :global_layer, :executable_gem_layer, :user_layer, :env_layer,
7
+ :command_line_layer, :provided_config_file_layer, :project_layer
8
+
9
+ def initialize
10
+ super
11
+ self.merge_policy = SuperStack::MergePolicies::FullMergePolicy
12
+ setup_layers
13
+ end
14
+
15
+
16
+ def self.default_config_file_base_name
17
+ File.basename($PROGRAM_NAME).gsub /\.[^\.]+$/, ''
18
+ end
19
+
20
+ def include_project_layer(file_or_directory, project_file_basename=nil, priority = 65)
21
+ @project_layer = Climatic::ConfigLayers::ProjectLayer.new file_or_directory, project_file_basename
22
+ project_layer.name = 'Project level'
23
+ project_layer.priority = priority
24
+ self << project_layer
25
+ end
26
+
27
+ def include_env_layer(filter = nil, priority = 70)
28
+ @env_layer = Climatic::ConfigLayers::EnvLayer.new filter
29
+ env_layer.name = 'Environment variables level'
30
+ env_layer.priority = priority
31
+ self << env_layer
32
+ end
33
+
34
+ def include_gem_layer_for(gem_name, priority = 30)
35
+ gem_layer = Climatic::ConfigLayers::GemLayer.new
36
+ gem_layer.gem_name = gem_name
37
+ raise "No config found in gem #{gem_name}" if gem_layer.file_name.nil?
38
+ gem_layer.name = "#{gem_name} Gem configuration level"
39
+ gem_layer.priority = priority
40
+ self << gem_layer
41
+ end
42
+
43
+ private
44
+
45
+ def setup_layers
46
+ # The command line level.
47
+ @command_line_layer = setup_layer Climatic::ConfigLayers::CommandLineLayer, 'Command line configuration level', 100
48
+
49
+ # The system level
50
+ @system_layer = setup_layer Climatic::ConfigLayers::SystemLayer, 'System-wide configuration level', 10
51
+
52
+ # The executable gem level
53
+ @executable_gem_layer = setup_layer Climatic::ConfigLayers::ExecutableGemLayer, 'Gem associated to the executable running configuration level', 20
54
+
55
+ # The global level
56
+ @global_layer = setup_layer Climatic::ConfigLayers::GlobalLayer, 'Global configuration level', 40
57
+
58
+ # The user level
59
+ @user_layer = setup_layer Climatic::ConfigLayers::UserLayer, 'User configuration level', 50
60
+
61
+ # The specifically provided config file level
62
+ @provided_config_file_layer = setup_layer Climatic::ConfigLayers::ProvidedConfigFileLayer, 'Specific config file configuration level', 60
63
+
64
+ # The layer to write something
65
+ override_layer = setup_layer SuperStack::Layer, 'Overridden configuration level', 1000
66
+ self.write_layer = override_layer
67
+
68
+ reload_layers
69
+ end
70
+
71
+ def setup_layer(class_type, name, priority)
72
+ layer = class_type.new
73
+ layer.name = name
74
+ layer.priority = priority
75
+ self << layer
76
+ layer
77
+ end
78
+
79
+
80
+ end
81
+ end
@@ -0,0 +1,49 @@
1
+ module Climatic
2
+ module Logger
3
+
4
+ class Accumulator
5
+
6
+ STACK_OPS = %i(debug info warn error fatal unknown).freeze
7
+
8
+ attr_accessor :level
9
+
10
+ def initialize
11
+ @log_lines = [{op: :debug, args: ['Starting special temporary "accumulator" logger...']}]
12
+ end
13
+
14
+ def stack(op, *args)
15
+ log_lines << {op: op, args: args}
16
+ end
17
+
18
+ def transfer_content_to(other_logger)
19
+ debug "Transferring accumulated logs to logger '#{other_logger.inspect}'"
20
+ if other_logger.nil?
21
+ @log_lines = []
22
+ return
23
+ end
24
+ log_lines.each do |log_line|
25
+ other_logger.send log_line[:op], *log_line[:args]
26
+ end
27
+ @log_lines = []
28
+ end
29
+
30
+ def method_missing(method_name, *args)
31
+ if STACK_OPS.include? method_name
32
+ stack method_name, *args
33
+ else
34
+ super
35
+ end
36
+ end
37
+
38
+ def respond_to_missing?(method_name, include_private = false)
39
+ STACK_OPS.include?(method_name) || super
40
+ end
41
+
42
+ private
43
+
44
+ attr_reader :log_lines
45
+
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,50 @@
1
+ require 'logger'
2
+
3
+
4
+ module Climatic
5
+ module Logger
6
+
7
+ module Manager
8
+
9
+ attr_reader :logger
10
+
11
+ def puts_and_logs(*args)
12
+ logger.puts_and_logs *args
13
+ end
14
+
15
+ def logger=(new_logger)
16
+
17
+ new_logger ||= DEVNULL_LOGGER
18
+
19
+ unless climatic_bootstrapped? or climatic_bootstrapping?
20
+ @user_defined_logger = new_logger
21
+ return
22
+ end
23
+
24
+ if climatic_bootstrapped?
25
+ new_logger.level = config[:'log-level'].nil? ? Climatic::Logger::Manager::DEFAULT_LOG_LEVEL : config[:'log-level']
26
+ end
27
+
28
+ new_logger.extend Climatic::Logger::Wrapper
29
+
30
+ if self.logger.respond_to? :transfer_content_to
31
+ self.logger.transfer_content_to new_logger
32
+ end
33
+
34
+ UltraCommandLine.logger = new_logger
35
+ @logger = new_logger
36
+ end
37
+
38
+ protected
39
+
40
+ attr_reader :user_defined_logger
41
+
42
+ private
43
+
44
+ DEFAULT_LOG_LEVEL = ::Logger::Severity::WARN
45
+ DEVNULL_LOGGER = UltraCommandLine::Utils::BasicLogger::NullLogger.new
46
+
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,12 @@
1
+ module Climatic
2
+ module Logger
3
+
4
+ module Wrapper
5
+ def puts_and_logs(*args)
6
+ puts *args if Climatic.config[:verbose]
7
+ info(*args)
8
+ end
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,51 @@
1
+ require 'open3'
2
+
3
+ module Climatic
4
+ module Processes
5
+
6
+ class Base
7
+
8
+ include Climatic::Processes::Command
9
+ include Climatic::Processes::TimeManagement
10
+
11
+ attr_reader :process_state, :exit_status, :last_pid, :mode
12
+ attr_accessor :show_output, :log_output
13
+
14
+ def initialize(command = nil, mode = :synchronous)
15
+ self.command = command
16
+ self.process_state = :not_started
17
+ self.mode = mode
18
+ self.creation_time = Time.now
19
+ end
20
+
21
+ def mode=(mode)
22
+ mode_processor = Object.const_get "Climatic::Processes::#{mode.to_s.capitalize}"
23
+ self.extend mode_processor
24
+ @mode = mode.to_sym
25
+ rescue
26
+ raise "Invalid process mode '#{mode}'"
27
+ end
28
+
29
+ private
30
+
31
+ attr_writer :process_state, :exit_status, :last_pid
32
+
33
+ def report(message, to_stdout = true)
34
+ if show_output
35
+ to_stdout ? puts(message) : STDERR.puts(message)
36
+ end
37
+ if log_output
38
+ log_line = "[subprocess #{last_pid}] - #{message}"
39
+ if to_stdout
40
+ Climatic.logger.debug log_line
41
+ else
42
+ Climatic.logger.warn log_line
43
+ end
44
+ end
45
+
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,15 @@
1
+ module Climatic
2
+ module Processes
3
+
4
+ module Command
5
+
6
+ attr_accessor :command
7
+
8
+ def valid?
9
+ File.exists? self.command.split(' ').first
10
+ end
11
+
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,51 @@
1
+ module Climatic
2
+ module Processes
3
+
4
+ module Synchronous
5
+
6
+ def execute
7
+ self.exit_status = nil
8
+ self.last_pid = nil
9
+ self.process_state = :running
10
+ self.start_time = Time.now
11
+ Open3.popen3(command) do |stdin, stdout, stderr, wait_thread|
12
+ stdin.close
13
+ self.last_pid = wait_thread.pid
14
+ begin
15
+ monitored_streams = [stdout, stderr]
16
+ loop do
17
+ begin
18
+ readables, writables = IO.select(monitored_streams)
19
+ writables.each(&:close)
20
+ readables.each do |io|
21
+ begin
22
+ buffer = ''
23
+ buffer << io.read_nonblock(1) while buffer[-1] != "\n"
24
+ report buffer, io == stdout
25
+ rescue IO::WaitReadable
26
+ next
27
+ rescue EOFError => e
28
+ monitored_streams.delete io
29
+ monitored_streams.empty? ? raise(e) : next
30
+ end
31
+ end
32
+ rescue EOFError
33
+ report "End of process #{wait_thread.value.pid}"
34
+ break
35
+ end
36
+ end
37
+ rescue Errno::EAGAIN
38
+ retry
39
+ end
40
+ self.exit_status = wait_thread.value
41
+ return self.exit_status
42
+ end
43
+ ensure
44
+ self.end_time = Time.now
45
+ self.process_state = :terminated
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,21 @@
1
+ require 'open3'
2
+
3
+ module Climatic
4
+ module Processes
5
+
6
+ module TimeManagement
7
+
8
+ attr_reader :creation_time, :start_time, :end_time
9
+
10
+ def duration
11
+ end_time - start_time
12
+ end
13
+
14
+ private
15
+
16
+ attr_writer :creation_time, :start_time, :end_time
17
+
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,27 @@
1
+ module Climatic
2
+
3
+ module Proxy
4
+
5
+ def config
6
+ Climatic.config
7
+ end
8
+
9
+ def logger
10
+ Climatic.logger
11
+ end
12
+
13
+ def command_line_manager
14
+ config.command_line_layer.command_line_manager
15
+ end
16
+
17
+ def help
18
+ config.command_line_layer.help
19
+ end
20
+
21
+ def puts_and_logs(*args)
22
+ Climatic.logger.puts_and_logs *args
23
+ end
24
+
25
+ end
26
+
27
+ end
@@ -0,0 +1,65 @@
1
+ module Climatic
2
+ module Script
3
+
4
+ module Base
5
+
6
+ include Climatic::Utils::ScriptHelper
7
+
8
+ module ClassMethods
9
+
10
+ def start
11
+ script = new
12
+ script.run
13
+ end
14
+
15
+ end
16
+
17
+ def run
18
+ # logging startup configuration
19
+ Climatic.config.command_line_layer.cmd_line_args = ARGV.dup
20
+ Climatic.logger.debug "Config layers ->\n#{Climatic.config.detailed_layers_info}"
21
+ Climatic.logger.debug "Merged config -> #{Climatic.config[].to_yaml}"
22
+ # Displaying (and exiting) command line help
23
+ display_help_and_exit if Climatic.config[:help]
24
+ check_config
25
+ Climatic.logger.info 'Application is starting...'
26
+ do_process
27
+ Climatic.logger.info 'Application ended normally...'
28
+ rescue => e
29
+ display_exit_error e
30
+ exit_code = e.respond_to?(:exit_code) ? e.exit_code : 1
31
+ exit exit_code
32
+ ensure
33
+ Climatic.logger.info 'Exiting...'
34
+ end
35
+
36
+ def self.included(base)
37
+ base.extend ClassMethods
38
+ end
39
+
40
+ private
41
+
42
+ def display_help_and_exit
43
+ puts Climatic.config.command_line_help
44
+ exit 0
45
+ end
46
+
47
+ def do_process
48
+ cmd_line_mngr.processor.execute
49
+ end
50
+
51
+ def cmd_line_mngr
52
+ Climatic.config.command_line_layer.command_line_manager
53
+ end
54
+
55
+ def check_config
56
+ # Check options validity in terms of dependencies
57
+ cmd_line_mngr.command.valid? raise_error: true
58
+ # Delegates to the processor the functional checks of the config
59
+ cmd_line_mngr.processor.check_params cmd_line_mngr.cmd_line_args_for_command(cmd_line_mngr.command)
60
+ end
61
+
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,31 @@
1
+ module Climatic
2
+
3
+ module Script
4
+
5
+ class Simple
6
+
7
+ include Climatic::Script::UnimplementedProcessor
8
+ include Climatic::Script::Base
9
+
10
+ def initialize
11
+ register_processor
12
+ end
13
+
14
+ def register_processor(commands = cmd_line_mngr.commands, processor = self)
15
+ commands = [commands] unless commands.is_a? Array
16
+ commands.each do |command|
17
+ cmd_line_mngr.register_processor command, processor
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def cmd_line_mngr
24
+ Climatic.config.command_line_layer.command_line_manager
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+ end
31
+
@@ -0,0 +1,19 @@
1
+ module Climatic
2
+
3
+ module Script
4
+
5
+ module UnimplementedProcessor
6
+
7
+ def check_params(command_args)
8
+ Climatic.logger.debug Climatic.config[].inspect
9
+ true
10
+ end
11
+
12
+ def execute
13
+ raise Climatic::Error, 'Not yet implemented !'
14
+ end
15
+
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,4 @@
1
+ require 'climatic/script/unimplemented_processor'
2
+ require 'climatic/script/base'
3
+ require 'climatic/script/simple'
4
+
@@ -0,0 +1,5 @@
1
+ module Climatic
2
+
3
+ class Error < StandardError; end
4
+
5
+ end
@@ -0,0 +1,65 @@
1
+ module Climatic
2
+ module Utils
3
+
4
+ module Input
5
+
6
+ DEFAULT_CONFIRMATION_CHOICES = {
7
+ true => %w(Yes y),
8
+ false => %w(No n)
9
+ }
10
+
11
+ def get_user_confirmation(choices: DEFAULT_CONFIRMATION_CHOICES,
12
+ default_choice: 'No',
13
+ prompt: 'Are you sure ?',
14
+ strict: false)
15
+
16
+ raise Climatic::Error, 'Invalid choices !' unless choices.is_a? Hash
17
+ values = choices.values.flatten
18
+ raise Climatic::Error, "Invalid default choice '#{default_choice}' !" unless values.include? default_choice
19
+ if Climatic.config[:auto]
20
+ yield if block_given?
21
+ return true
22
+ end
23
+ full_prompt = '%s (%s): ' % [prompt, choices_string(values, default_choice)]
24
+ STDOUT.print full_prompt
25
+ STDOUT.flush
26
+ input = nil
27
+ until values.include? input
28
+ input = STDIN.gets.chomp
29
+ input = default_choice if input.nil? || input.empty?
30
+ unless strict
31
+ input = default_choice unless values.include? input
32
+ end
33
+ end
34
+ choices.each_pair do |res, possible_choices|
35
+ if possible_choices.include? input
36
+ if res and block_given?
37
+ yield
38
+ end
39
+ return res
40
+ end
41
+ end
42
+ raise 'Something wrong happened !'
43
+ end
44
+
45
+
46
+ def get_user_input(prompt, default=nil)
47
+ full_prompt = (default.nil? or default.empty?) ? "#{prompt}: " : "#{prompt} (default: #{default}): "
48
+ STDOUT.print full_prompt
49
+ STDOUT.flush
50
+ STDIN.gets.chomp
51
+ end
52
+
53
+ private
54
+
55
+
56
+ def choices_string(choices, default_choice, highlight= %w([ ]))
57
+ choices
58
+ .map { |choice| choice == default_choice ? "#{highlight.first}#{choice}#{highlight.last}" : choice }
59
+ .join '/'
60
+ end
61
+
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,28 @@
1
+ module Climatic
2
+ module Utils
3
+
4
+ module SafeExec
5
+
6
+ def safely_exec_code(*args, message: nil, &block)
7
+ if self.config[:simulate]
8
+ Climatic.logger.puts_and_logs "[SIMULATION MODE]: #{message}" unless message.nil?
9
+ else
10
+ Climatic.logger.puts_and_logs message
11
+ block.call *args
12
+ end
13
+ end
14
+
15
+ def safely_exec_command(command, message: nil, show_output: false, log_output: true)
16
+ safely_exec_code command, message: message do |cmd|
17
+ process = Climatic::Processes::Base.new cmd
18
+ process.show_output = show_output
19
+ process.log_output = log_output
20
+ process.execute
21
+ process
22
+ end
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,18 @@
1
+ module Climatic
2
+ module Utils
3
+
4
+ module ScriptHelper
5
+
6
+ def display_exit_error(e)
7
+ puts "Program aborted with message: '#{e.message}'."
8
+ if Climatic.config[:debug]
9
+ Climatic.logger.fatal "#{e.message}\nBacktrace:\n#{e.backtrace.join("\n\t")}"
10
+ else
11
+ puts ' Use --debug option for more detail (see --help).'
12
+ end
13
+ end
14
+
15
+ end
16
+
17
+ end
18
+ end