itrg-invoker 1.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.
Files changed (89) hide show
  1. checksums.yaml +7 -0
  2. data/bin/invoker +7 -0
  3. data/lib/invoker/cli/pinger.rb +23 -0
  4. data/lib/invoker/cli/question.rb +15 -0
  5. data/lib/invoker/cli/tail.rb +34 -0
  6. data/lib/invoker/cli/tail_watcher.rb +34 -0
  7. data/lib/invoker/cli.rb +197 -0
  8. data/lib/invoker/command_worker.rb +64 -0
  9. data/lib/invoker/commander.rb +101 -0
  10. data/lib/invoker/daemon.rb +126 -0
  11. data/lib/invoker/dns_cache.rb +23 -0
  12. data/lib/invoker/errors.rb +17 -0
  13. data/lib/invoker/event/manager.rb +79 -0
  14. data/lib/invoker/ipc/add_command.rb +12 -0
  15. data/lib/invoker/ipc/add_http_command.rb +10 -0
  16. data/lib/invoker/ipc/base_command.rb +24 -0
  17. data/lib/invoker/ipc/client_handler.rb +26 -0
  18. data/lib/invoker/ipc/dns_check_command.rb +17 -0
  19. data/lib/invoker/ipc/list_command.rb +11 -0
  20. data/lib/invoker/ipc/message/list_response.rb +35 -0
  21. data/lib/invoker/ipc/message/tail_response.rb +10 -0
  22. data/lib/invoker/ipc/message.rb +170 -0
  23. data/lib/invoker/ipc/ping_command.rb +10 -0
  24. data/lib/invoker/ipc/reload_command.rb +12 -0
  25. data/lib/invoker/ipc/remove_command.rb +12 -0
  26. data/lib/invoker/ipc/server.rb +26 -0
  27. data/lib/invoker/ipc/tail_command.rb +11 -0
  28. data/lib/invoker/ipc/unix_client.rb +60 -0
  29. data/lib/invoker/ipc.rb +45 -0
  30. data/lib/invoker/logger.rb +13 -0
  31. data/lib/invoker/parsers/config.rb +184 -0
  32. data/lib/invoker/parsers/procfile.rb +86 -0
  33. data/lib/invoker/power/balancer.rb +133 -0
  34. data/lib/invoker/power/config.rb +77 -0
  35. data/lib/invoker/power/dns.rb +38 -0
  36. data/lib/invoker/power/http_parser.rb +68 -0
  37. data/lib/invoker/power/http_response.rb +81 -0
  38. data/lib/invoker/power/port_finder.rb +49 -0
  39. data/lib/invoker/power/power.rb +3 -0
  40. data/lib/invoker/power/powerup.rb +29 -0
  41. data/lib/invoker/power/setup/distro/arch.rb +15 -0
  42. data/lib/invoker/power/setup/distro/base.rb +80 -0
  43. data/lib/invoker/power/setup/distro/debian.rb +11 -0
  44. data/lib/invoker/power/setup/distro/opensuse.rb +11 -0
  45. data/lib/invoker/power/setup/distro/redhat.rb +11 -0
  46. data/lib/invoker/power/setup/distro/ubuntu.rb +46 -0
  47. data/lib/invoker/power/setup/files/invoker_forwarder.sh.erb +17 -0
  48. data/lib/invoker/power/setup/files/socat_invoker.service +12 -0
  49. data/lib/invoker/power/setup/linux_setup.rb +97 -0
  50. data/lib/invoker/power/setup/osx_setup.rb +137 -0
  51. data/lib/invoker/power/setup.rb +93 -0
  52. data/lib/invoker/power/templates/400.html +40 -0
  53. data/lib/invoker/power/templates/404.html +40 -0
  54. data/lib/invoker/power/templates/503.html +40 -0
  55. data/lib/invoker/power/url_rewriter.rb +40 -0
  56. data/lib/invoker/process_manager.rb +201 -0
  57. data/lib/invoker/process_printer.rb +59 -0
  58. data/lib/invoker/reactor/reader.rb +65 -0
  59. data/lib/invoker/reactor.rb +37 -0
  60. data/lib/invoker/version.rb +47 -0
  61. data/lib/invoker.rb +151 -0
  62. data/spec/invoker/cli/pinger_spec.rb +22 -0
  63. data/spec/invoker/cli/tail_watcher_spec.rb +39 -0
  64. data/spec/invoker/cli_spec.rb +27 -0
  65. data/spec/invoker/command_worker_spec.rb +45 -0
  66. data/spec/invoker/commander_spec.rb +152 -0
  67. data/spec/invoker/config_spec.rb +361 -0
  68. data/spec/invoker/daemon_spec.rb +34 -0
  69. data/spec/invoker/event/manager_spec.rb +67 -0
  70. data/spec/invoker/invoker_spec.rb +71 -0
  71. data/spec/invoker/ipc/client_handler_spec.rb +54 -0
  72. data/spec/invoker/ipc/dns_check_command_spec.rb +32 -0
  73. data/spec/invoker/ipc/message/list_response_spec.rb +24 -0
  74. data/spec/invoker/ipc/message_spec.rb +49 -0
  75. data/spec/invoker/ipc/unix_client_spec.rb +29 -0
  76. data/spec/invoker/power/balancer_spec.rb +53 -0
  77. data/spec/invoker/power/config_spec.rb +18 -0
  78. data/spec/invoker/power/http_parser_spec.rb +32 -0
  79. data/spec/invoker/power/http_response_spec.rb +34 -0
  80. data/spec/invoker/power/port_finder_spec.rb +16 -0
  81. data/spec/invoker/power/setup/linux_setup_spec.rb +166 -0
  82. data/spec/invoker/power/setup/osx_setup_spec.rb +105 -0
  83. data/spec/invoker/power/setup_spec.rb +4 -0
  84. data/spec/invoker/power/url_rewriter_spec.rb +69 -0
  85. data/spec/invoker/power/web_sockets_spec.rb +61 -0
  86. data/spec/invoker/process_manager_spec.rb +130 -0
  87. data/spec/invoker/reactor_spec.rb +6 -0
  88. data/spec/spec_helper.rb +43 -0
  89. metadata +376 -0
@@ -0,0 +1,79 @@
1
+ module Invoker
2
+ module Event
3
+ class Manager
4
+ attr_accessor :scheduled_events, :triggered_events
5
+
6
+ def initialize
7
+ @scheduled_events = Hash.new {|h,k| h[k] = [] }
8
+ @triggered_events = []
9
+ @trigger_mutex = Mutex.new()
10
+ end
11
+
12
+ # Trigger an event. The event is not triggered immediately, but is just scheduled to be
13
+ # triggered.
14
+ #
15
+ # @param command_label [String] Command for which event should be triggered
16
+ # @param event_name [Symbol, nil] The optional event name
17
+ def trigger(command_label, event_name = nil)
18
+ @trigger_mutex.synchronize do
19
+ triggered_events << OpenStruct.new(
20
+ :command_label => command_label,
21
+ :event_name => event_name)
22
+ end
23
+ end
24
+
25
+ # Schedule an Event. The event will only trigger when a scheduled event matches
26
+ # a triggered event.
27
+ #
28
+ # @param command_label [String] Command for which the event should be triggered
29
+ # @param event_name [String, nil] Optional event name
30
+ # @param block The block to execute when event actually triggers
31
+ def schedule_event(command_label, event_name = nil, &block)
32
+ @trigger_mutex.synchronize do
33
+ scheduled_events[command_label] << OpenStruct.new(:event_name => event_name, :block => block)
34
+ end
35
+ end
36
+
37
+ # On next iteration of event loop, this method is called and we try to match
38
+ # scheduled events with events that were triggered.
39
+ def run_scheduled_events
40
+ filtered_events_by_name_and_command = []
41
+
42
+ triggered_events.each_with_index do |triggered_event, index|
43
+ matched_events = scheduled_events[triggered_event.command_label]
44
+ if matched_events && !matched_events.empty?
45
+ filtered_events_by_name_and_command, unmatched_events =
46
+ filter_matched_events(matched_events, triggered_event)
47
+ triggered_events[index] = nil
48
+ remove_scheduled_event(unmatched_events, triggered_event.command_label)
49
+ end
50
+ end
51
+ triggered_events.compact!
52
+
53
+ filtered_events_by_name_and_command.each {|event| yield event }
54
+ end
55
+
56
+ private
57
+ def filter_matched_events(matched_events, event)
58
+ matched_filtered_events = []
59
+
60
+ matched_events.each_with_index do |matched_event, index|
61
+ if !event.event_name || event.event_name == matched_event.event_name
62
+ matched_filtered_events << matched_event
63
+ matched_events[index] = nil
64
+ end
65
+ end
66
+ [matched_filtered_events, matched_events.compact]
67
+ end
68
+
69
+ def remove_scheduled_event(matched_events, command_label)
70
+ if !matched_events || matched_events.empty?
71
+ scheduled_events.delete(command_label)
72
+ else
73
+ scheduled_events[command_label] = matched_events
74
+ end
75
+ end
76
+
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,12 @@
1
+ module Invoker
2
+ module IPC
3
+ class AddCommand < BaseCommand
4
+ def run_command(message_object)
5
+ Invoker.commander.on_next_tick(message_object.process_name) do |process_name|
6
+ start_process_by_name(process_name)
7
+ end
8
+ true
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ module Invoker
2
+ module IPC
3
+ class AddHttpCommand < BaseCommand
4
+ def run_command(message_object)
5
+ Invoker.dns_cache.add(message_object.process_name, message_object.port, message_object.ip)
6
+ true
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,24 @@
1
+ module Invoker
2
+ module IPC
3
+ class BaseCommand
4
+ attr_accessor :client_socket
5
+ def initialize(client_socket)
6
+ @client_socket = client_socket
7
+ end
8
+
9
+ def send_data(message_object)
10
+ client_socket.write(message_object.encoded_message)
11
+ end
12
+
13
+ # Invoke the command that actual processes incoming message
14
+ # returning true from this message means, command has been processed
15
+ # and client socket can be closed. returning false means, it is a
16
+ # long running command and socket should not be closed immediately
17
+ # @param [Invoker::IPC::Message] incoming message
18
+ # @return [Boolean] true or false
19
+ def run_command(message_object)
20
+ raise "Not implemented"
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,26 @@
1
+ module Invoker
2
+ module IPC
3
+ class ClientHandler
4
+ attr_accessor :client_socket
5
+ def initialize(client_socket)
6
+ @client_socket = client_socket
7
+ end
8
+
9
+ def read_and_execute
10
+ client_handler, message_object = read_incoming_command
11
+ client_socket.close if client_handler.run_command(message_object)
12
+ rescue StandardError => error
13
+ Invoker::Logger.puts error.message
14
+ Invoker::Logger.puts error.backtrace
15
+ client_socket.close
16
+ end
17
+
18
+ private
19
+
20
+ def read_incoming_command
21
+ message_object = Invoker::IPC.message_from_io(client_socket)
22
+ [message_object.command_handler_klass.new(client_socket), message_object]
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,17 @@
1
+ module Invoker
2
+ module IPC
3
+ class DnsCheckCommand < BaseCommand
4
+ def run_command(message_object)
5
+ process_detail = Invoker.dns_cache[message_object.process_name]
6
+
7
+ dns_check_response = Invoker::IPC::Message::DnsCheckResponse.new(
8
+ process_name: message_object.process_name,
9
+ port: process_detail ? process_detail['port'] : nil,
10
+ ip: process_detail && process_detail['ip'] ? process_detail['ip'] : '0.0.0.0'
11
+ )
12
+ send_data(dns_check_response)
13
+ true
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ module Invoker
2
+ module IPC
3
+ class ListCommand < BaseCommand
4
+ def run_command(message_object)
5
+ list_response = Invoker.commander.process_list
6
+ send_data(list_response)
7
+ true
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,35 @@
1
+ module Invoker
2
+ module IPC
3
+ module Message
4
+ class ListResponse < Base
5
+ include Serialization
6
+ message_attributes :processes
7
+ def initialize(options)
8
+ self.processes = []
9
+ process_array = options[:processes] || options['processes']
10
+ process_array.each do |process_hash|
11
+ processes << Process.new(process_hash)
12
+ end
13
+ end
14
+
15
+ def self.from_workers(workers)
16
+ process_array = []
17
+ Invoker.config.processes.each do |process|
18
+ worker_attrs = {
19
+ shell_command: process.cmd,
20
+ process_name: process.label,
21
+ dir: process.dir,
22
+ port: process.port
23
+ }
24
+ if worker = workers[process.label]
25
+ worker_attrs.update(pid: worker.pid)
26
+ end
27
+ process_array << worker_attrs
28
+ end
29
+
30
+ new(processes: process_array)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,10 @@
1
+ module Invoker
2
+ module IPC
3
+ module Message
4
+ class TailResponse < Base
5
+ include Serialization
6
+ message_attributes :tail_line
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,170 @@
1
+ module Invoker
2
+ module IPC
3
+ module Message
4
+ module Serialization
5
+ def self.included(base)
6
+ base.extend ClassMethods
7
+ end
8
+
9
+ def as_json
10
+ attributes.merge(type: message_type)
11
+ end
12
+
13
+ def to_json
14
+ JSON.generate(as_json)
15
+ end
16
+
17
+ def message_attributes
18
+ self.class.message_attributes
19
+ end
20
+
21
+ def encoded_message
22
+ json_data = to_json
23
+ json_size = json_data.length.to_s
24
+ length_str = json_size.rjust(Invoker::IPC::INITIAL_PACKET_SIZE, '0')
25
+ length_str + json_data
26
+ end
27
+
28
+ def eql?(other)
29
+ other.class == self.class &&
30
+ compare_attributes(other)
31
+ end
32
+
33
+ def attributes
34
+ message_attribute_keys = message_attributes || []
35
+ message_attribute_keys.reduce({}) do |mem, obj|
36
+ value = send(obj)
37
+ if value.is_a?(Array)
38
+ mem[obj] = serialize_array(value)
39
+ elsif value.is_a?(Hash)
40
+ mem[obj] = serialize_hash(value)
41
+ else
42
+ mem[obj] = value.respond_to?(:as_json) ? value.as_json : encode_as_utf(value)
43
+ end
44
+ mem
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def compare_attributes(other)
51
+ message_attributes.all? do |attribute_name|
52
+ send(attribute_name).eql?(other.send(attribute_name))
53
+ end
54
+ end
55
+
56
+ def encode_as_utf(value)
57
+ return value unless value.is_a?(String)
58
+ value.encode("utf-8", invalid: :replace, undef: :replace, replace: '_')
59
+ end
60
+
61
+ def serialize_array(attribute_array)
62
+ attribute_array.map do |x|
63
+ x.respond_to?(:as_json) ? x.as_json : encode_as_utf(x)
64
+ end
65
+ end
66
+
67
+ def serialize_hash(attribute_hash)
68
+ attribute_hash.inject({}) do |temp_mem, (temp_key, temp_value)|
69
+ if temp_value.respond_to?(:as_json)
70
+ temp_mem[temp_key] = temp_value.as_json
71
+ else
72
+ temp_mem[temp_key] = encode_as_utf(temp_value)
73
+ end
74
+ end
75
+ end
76
+
77
+ module ClassMethods
78
+ def message_attributes(*incoming_attributes)
79
+ if incoming_attributes.empty? && defined?(@message_attributes)
80
+ @message_attributes
81
+ else
82
+ @message_attributes ||= []
83
+ new_attributes = incoming_attributes.flatten
84
+ @message_attributes += new_attributes
85
+ attr_accessor *new_attributes
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ class Base
92
+ def initialize(options)
93
+ options.each do |key, value|
94
+ if self.respond_to?("#{key}=")
95
+ send("#{key}=", value)
96
+ end
97
+ end
98
+ end
99
+
100
+ def message_type
101
+ Invoker::IPC.underscore(self.class.name).split("/").last
102
+ end
103
+
104
+ def command_handler_klass
105
+ Invoker::IPC.const_get("#{IPC.camelize(message_type)}Command")
106
+ end
107
+ end
108
+
109
+ class Add < Base
110
+ include Serialization
111
+ message_attributes :process_name
112
+ end
113
+
114
+ class Tail < Base
115
+ include Serialization
116
+ message_attributes :process_names
117
+ end
118
+
119
+ class AddHttp < Base
120
+ include Serialization
121
+ message_attributes :process_name, :port, :ip
122
+ end
123
+
124
+ class Reload < Base
125
+ include Serialization
126
+ message_attributes :process_name, :signal
127
+
128
+ def remove_message
129
+ Remove.new(process_name: process_name, signal: signal)
130
+ end
131
+ end
132
+
133
+ class List < Base
134
+ include Serialization
135
+ end
136
+
137
+ class Process < Base
138
+ include Serialization
139
+ message_attributes :process_name, :shell_command, :dir, :pid, :port
140
+ end
141
+
142
+ class Remove < Base
143
+ include Serialization
144
+ message_attributes :process_name, :signal
145
+ end
146
+
147
+ class DnsCheck < Base
148
+ include Serialization
149
+ message_attributes :process_name
150
+ end
151
+
152
+ class DnsCheckResponse < Base
153
+ include Serialization
154
+ message_attributes :process_name, :port, :ip
155
+ end
156
+
157
+ class Ping < Base
158
+ include Serialization
159
+ end
160
+
161
+ class Pong < Base
162
+ include Serialization
163
+ message_attributes :status
164
+ end
165
+ end
166
+ end
167
+ end
168
+
169
+ require "invoker/ipc/message/list_response"
170
+ require "invoker/ipc/message/tail_response"
@@ -0,0 +1,10 @@
1
+ module Invoker
2
+ module IPC
3
+ class PingCommand < BaseCommand
4
+ def run_command(message_object)
5
+ pong = Invoker::IPC::Message::Pong.new(status: 'pong')
6
+ send_data(pong)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,12 @@
1
+ module Invoker
2
+ module IPC
3
+ class ReloadCommand < BaseCommand
4
+ def run_command(message_object)
5
+ Invoker.commander.on_next_tick(message_object) do |reload_message|
6
+ restart_process(reload_message)
7
+ end
8
+ true
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Invoker
2
+ module IPC
3
+ class RemoveCommand < BaseCommand
4
+ def run_command(message_object)
5
+ Invoker.commander.on_next_tick(message_object) do |remove_message|
6
+ stop_process(remove_message)
7
+ end
8
+ true
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,26 @@
1
+ require "fileutils"
2
+
3
+ module Invoker
4
+ module IPC
5
+ class Server
6
+ SOCKET_PATH = "/tmp/invoker"
7
+ def initialize
8
+ @open_clients = []
9
+ Socket.unix_server_loop(SOCKET_PATH) do |sock, client_addrinfo|
10
+ Thread.new { process_client(sock) }
11
+ end
12
+ end
13
+
14
+ def clean_old_socket
15
+ if File.exist?(SOCKET_PATH)
16
+ FileUtils.rm(SOCKET_PATH, :force => true)
17
+ end
18
+ end
19
+
20
+ def process_client(client_socket)
21
+ client = Invoker::IPC::ClientHandler.new(client_socket)
22
+ client.read_and_execute
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,11 @@
1
+ module Invoker
2
+ module IPC
3
+ class TailCommand < BaseCommand
4
+ def run_command(message_object)
5
+ Invoker::Logger.puts("Adding #{message_object.process_names.inspect}")
6
+ Invoker.tail_watchers.add(message_object.process_names, client_socket)
7
+ false
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,60 @@
1
+ module Invoker
2
+ module IPC
3
+ class UnixClient
4
+ def send_command(command, message = {})
5
+ message_object = get_message_object(command, message)
6
+ open_client_socket do |socket|
7
+ send_json_message(socket, message_object)
8
+ socket.flush
9
+ if block_given?
10
+ response_object = Invoker::IPC.message_from_io(socket)
11
+ yield response_object
12
+ end
13
+ end
14
+ end
15
+
16
+ def send_and_receive(command, message = {})
17
+ response = nil
18
+ message_object = get_message_object(command, message)
19
+ open_client_socket(false) do |socket|
20
+ send_json_message(socket, message_object)
21
+ socket.flush
22
+ response = Invoker::IPC.message_from_io(socket)
23
+ end
24
+ response
25
+ end
26
+
27
+ def send_and_wait(command, message = {})
28
+ begin
29
+ socket = Socket.unix(Invoker::IPC::Server::SOCKET_PATH)
30
+ rescue
31
+ abort("Invoker does not seem to be running".colorize(:red))
32
+ end
33
+ message_object = get_message_object(command, message)
34
+ send_json_message(socket, message_object)
35
+ socket.flush
36
+ socket
37
+ end
38
+
39
+ def self.send_command(command, message_arguments = {}, &block)
40
+ new.send_command(command, message_arguments, &block)
41
+ end
42
+
43
+ private
44
+
45
+ def get_message_object(command, message_arguments)
46
+ Invoker::IPC::Message.const_get(Invoker::IPC.camelize(command)).new(message_arguments)
47
+ end
48
+
49
+ def open_client_socket(abort_if_not_running = true)
50
+ Socket.unix(Invoker::IPC::Server::SOCKET_PATH) { |socket| yield socket }
51
+ rescue
52
+ abort_if_not_running && abort("Invoker does not seem to be running".colorize(:red))
53
+ end
54
+
55
+ def send_json_message(socket, message_object)
56
+ socket.write(message_object.encoded_message)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,45 @@
1
+ require "invoker/ipc/base_command"
2
+ require 'invoker/ipc/message'
3
+ require 'invoker/ipc/add_command'
4
+ require 'invoker/ipc/add_http_command'
5
+ require 'invoker/ipc/client_handler'
6
+ require 'invoker/ipc/dns_check_command'
7
+ require 'invoker/ipc/list_command'
8
+ require 'invoker/ipc/remove_command'
9
+ require 'invoker/ipc/server'
10
+ require "invoker/ipc/reload_command"
11
+ require 'invoker/ipc/tail_command'
12
+ require 'invoker/ipc/unix_client'
13
+ require "invoker/ipc/ping_command"
14
+
15
+ module Invoker
16
+ module IPC
17
+ INITIAL_PACKET_SIZE = 9
18
+ def self.message_from_io(io)
19
+ json_size = io.read(INITIAL_PACKET_SIZE)
20
+ json_string = io.read(json_size.to_i)
21
+ ruby_object_hash = JSON.parse(json_string)
22
+ command_name = camelize(ruby_object_hash['type'])
23
+ command_klass = Invoker::IPC::Message.const_get(command_name)
24
+ command_klass.new(ruby_object_hash)
25
+ end
26
+
27
+ # Taken from Rails without inflection support
28
+ def self.camelize(term)
29
+ string = term.to_s
30
+ string = string.sub(/^[a-z\d]*/) { $&.capitalize }
31
+ string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }
32
+ string.gsub!('/', '::')
33
+ string
34
+ end
35
+
36
+ def self.underscore(term)
37
+ word = term.to_s.gsub('::', '/')
38
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
39
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
40
+ word.tr!("-", "_")
41
+ word.downcase!
42
+ word
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,13 @@
1
+ module Invoker
2
+ class Logger
3
+ def self.puts(message)
4
+ return if ENV["INVOKER_TESTS"]
5
+ $stdout.puts(message)
6
+ end
7
+
8
+ def self.print(message)
9
+ return if ENV["INVOKER_TESTS"]
10
+ $stdout.print(message)
11
+ end
12
+ end
13
+ end