invoker 0.0.3 → 0.1.1.pre

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.
data/.travis.yml CHANGED
@@ -2,5 +2,6 @@ language: ruby
2
2
  rvm:
3
3
  - 1.9.3
4
4
  - 2.0.0
5
+
5
6
  script: bundle exec rake spec
6
7
 
data/invoker.gemspec CHANGED
@@ -29,6 +29,7 @@ Gem::Specification.new do |s|
29
29
  s.add_dependency("slop")
30
30
  s.add_dependency("iniparse")
31
31
  s.add_dependency("colored")
32
+ s.add_dependency("formatador")
32
33
  s.add_development_dependency("bacon")
33
34
  s.add_development_dependency("mocha")
34
35
  s.add_development_dependency("mocha-on-bacon")
@@ -7,26 +7,37 @@ module Invoker
7
7
  end
8
8
 
9
9
  def read_and_execute
10
- command_info = client_socket.read()
10
+ command_info = client_socket.gets()
11
+ command_info && command_info.strip!
12
+
11
13
  if command_info && !command_info.empty?
12
14
  worker_command, command_label, rest_args = command_info.strip.split(" ")
13
- if worker_command && command_label
15
+ worker_command.strip!
16
+ if worker_command
14
17
  run_command(worker_command, command_label, rest_args)
15
18
  end
16
19
  end
17
- client_socket.close()
18
20
  end
19
21
 
20
22
  def run_command(worker_command, command_label, rest_args = nil)
21
23
  case worker_command
22
24
  when 'add'
23
- Invoker::COMMANDER.add_command_by_label(command_label)
25
+ Invoker::COMMANDER.on_next_tick(command_label) { |b_command_label|
26
+ add_command_by_label(b_command_label)
27
+ }
28
+ when 'list'
29
+ json = Invoker::COMMANDER.list_commands()
30
+ client_socket.puts(json)
24
31
  when 'remove'
25
- Invoker::COMMANDER.remove_command(command_label, rest_args)
32
+ Invoker::COMMANDER.on_next_tick(command_label, rest_args) { |b_command_label,b_rest_args|
33
+ remove_command(b_command_label, b_rest_args)
34
+ }
26
35
  when 'reload'
27
- Invoker::COMMANDER.reload_command(command_label)
36
+ Invoker::COMMANDER.on_next_tick(command_label, rest_args) { |b_command_label, b_rest_args|
37
+ reload_command(b_command_label, b_rest_args)
38
+ }
28
39
  else
29
- $stdout.puts("\n Invalid command".red)
40
+ Invoker::Logger.puts("\n Invalid command".red)
30
41
  end
31
42
  end
32
43
  end
@@ -6,13 +6,13 @@ module Invoker
6
6
  SOCKET_PATH = "/tmp/invoker"
7
7
  def initialize
8
8
  @open_clients = []
9
- clean_old_socket()
10
- UNIXServer.open(SOCKET_PATH) do |client|
11
- loop do
12
- client_socket = client.accept
13
- process_client(client_socket)
9
+ Socket.unix_server_loop(SOCKET_PATH) {|sock, client_addrinfo|
10
+ begin
11
+ process_client(sock)
12
+ ensure
13
+ sock.close
14
14
  end
15
- end
15
+ }
16
16
  end
17
17
 
18
18
  def clean_old_socket
@@ -19,12 +19,16 @@ module Invoker
19
19
  end
20
20
 
21
21
  def unbind
22
- # $stdout.print(".")
22
+ Invoker::Logger.print(".")
23
23
  end
24
24
 
25
25
  # Print the lines received over the network
26
26
  def receive_line(line)
27
- $stdout.puts "#{@command_label.send(color)} : #{line}"
27
+ Invoker::Logger.puts "#{@command_label.send(color)} : #{line}"
28
+ end
29
+
30
+ def to_h
31
+ {:command_label => command_label, :pid => pid.to_s}
28
32
  end
29
33
  end
30
34
  end
@@ -1,11 +1,13 @@
1
1
  require "io/console"
2
2
  require 'pty'
3
+ require "json"
3
4
 
4
5
  module Invoker
5
6
  class Commander
6
7
  MAX_PROCESS_COUNT = 10
7
8
  LABEL_COLORS = ['green', 'yellow', 'blue', 'magenta', 'cyan']
8
9
  attr_accessor :reactor, :workers, :thread_group, :open_pipes
10
+ attr_accessor :event_manager, :runnables
9
11
 
10
12
  def initialize
11
13
  # mapping between open pipes and worker classes
@@ -16,10 +18,16 @@ module Invoker
16
18
 
17
19
  @thread_group = ThreadGroup.new()
18
20
  @worker_mutex = Mutex.new()
21
+
22
+ @event_manager = Invoker::Event::Manager.new()
23
+ @runnables = []
24
+
19
25
  @reactor = Invoker::Reactor.new
20
26
  Thread.abort_on_exception = true
21
27
  end
22
28
 
29
+ # Start the invoker process supervisor. This method starts a unix server
30
+ # in separate thread that listens for incoming commands.
23
31
  def start_manager
24
32
  if !Invoker::CONFIG.processes || Invoker::CONFIG.processes.empty?
25
33
  raise Invoker::Errors::InvalidConfig.new("No processes configured in config file")
@@ -28,9 +36,12 @@ module Invoker
28
36
  unix_server_thread = Thread.new { Invoker::CommandListener::Server.new() }
29
37
  thread_group.add(unix_server_thread)
30
38
  Invoker::CONFIG.processes.each { |process_info| add_command(process_info) }
31
- reactor.start
39
+ start_event_loop()
32
40
  end
33
41
 
42
+ # Start given command and start a background thread to wait on the process
43
+ #
44
+ # @param process_info [OpenStruct(command, directory)]
34
45
  def add_command(process_info)
35
46
  m, s = PTY.open
36
47
  s.raw! # disable newline conversion.
@@ -39,14 +50,20 @@ module Invoker
39
50
 
40
51
  s.close()
41
52
 
42
- selected_color = LABEL_COLORS.shift()
43
- LABEL_COLORS.push(selected_color)
44
- worker = Invoker::CommandWorker.new(process_info.label, m, pid, selected_color)
53
+ worker = Invoker::CommandWorker.new(process_info.label, m, pid, select_color())
45
54
 
46
55
  add_worker(worker)
47
56
  wait_on_pid(process_info.label,pid)
48
57
  end
49
58
 
59
+ # List currently running commands
60
+ def list_commands
61
+ Invoker::ProcessPrinter.to_json(workers)
62
+ end
63
+
64
+ # Start executing given command by their label name.
65
+ #
66
+ # @param command_label [String] Command label of process specified in config file.
50
67
  def add_command_by_label(command_label)
51
68
  process_info = Invoker::CONFIG.processes.detect {|pconfig|
52
69
  pconfig.label == command_label
@@ -56,30 +73,86 @@ module Invoker
56
73
  end
57
74
  end
58
75
 
59
- def reload_command(command_label)
60
- remove_command(command_label)
61
- add_command_by_label(command_label)
76
+ # Reload a process given by command label
77
+ #
78
+ # @params command_label [String] Command label of process specified in config file.
79
+ def reload_command(command_label, rest_args)
80
+ if remove_command(command_label, rest_args)
81
+ event_manager.schedule_event(command_label, :worker_removed) {
82
+ add_command_by_label(command_label)
83
+ }
84
+ else
85
+ add_command_by_label(command_label)
86
+ end
62
87
  end
63
88
 
89
+ # Remove a process from list of processes managed by invoker supervisor.It also
90
+ # kills the process before removing it from the list.
91
+ #
92
+ # @param command_label [String] Command label of process specified in config file
93
+ # @param rest_args [Array] Additional option arguments, such as signal that can be used.
94
+ # @return [Boolean] if process existed and was removed else false
64
95
  def remove_command(command_label, rest_args)
65
96
  worker = workers[command_label]
97
+ return false unless worker
66
98
  signal_to_use = rest_args ? Array(rest_args).first : 'INT'
67
99
 
68
- if worker
69
- $stdout.puts("Removing #{command_label} with signal #{signal_to_use}".red)
70
- process_kill(worker.pid, signal_to_use)
71
- end
100
+ Invoker::Logger.puts("Removing #{command_label} with signal #{signal_to_use}".red)
101
+ kill_or_remove_process(worker.pid, signal_to_use, command_label)
72
102
  end
73
103
 
104
+ # Given a file descriptor returns the worker object
105
+ #
106
+ # @param fd [IO] an IO object with valid file descriptor
107
+ # @return [Invoker::CommandWorker] The worker object which is associated with this fd
74
108
  def get_worker_from_fd(fd)
75
109
  open_pipes[fd.fileno]
76
110
  end
77
111
 
112
+ # Given a command label returns the associated worker object
113
+ #
114
+ # @param label [String] Command label of the command
115
+ # @return [Invoker::CommandWorker] The worker object which is associated with this command
78
116
  def get_worker_from_label(label)
79
117
  workers[label]
80
118
  end
119
+
120
+ def on_next_tick(*args, &block)
121
+ @worker_mutex.synchronize do
122
+ @runnables << OpenStruct.new(:args => args, :block => block)
123
+ end
124
+ end
125
+
126
+ def run_runnables
127
+ @runnables.each do |runnable|
128
+ instance_exec(*runnable.args, &runnable.block)
129
+ end
130
+ @runnables = []
131
+ end
81
132
 
82
133
  private
134
+ def start_event_loop
135
+ loop do
136
+ reactor.watch_on_pipe()
137
+ run_runnables()
138
+ run_scheduled_events()
139
+ end
140
+ end
141
+
142
+ def run_scheduled_events
143
+ event_manager.run_scheduled_events do |event|
144
+ event.block.call()
145
+ end
146
+ end
147
+
148
+ def kill_or_remove_process(pid, signal_to_use, command_label)
149
+ process_kill(pid, signal_to_use)
150
+ true
151
+ rescue Errno::ESRCH
152
+ remove_worker(command_label, false)
153
+ false
154
+ end
155
+
83
156
  def process_kill(pid, signal_to_use)
84
157
  if signal_to_use.to_i == 0
85
158
  Process.kill(signal_to_use, pid)
@@ -87,29 +160,37 @@ module Invoker
87
160
  Process.kill(signal_to_use.to_i, pid)
88
161
  end
89
162
  end
163
+
164
+ def select_color
165
+ selected_color = LABEL_COLORS.shift()
166
+ LABEL_COLORS.push(selected_color)
167
+ selected_color
168
+ end
90
169
 
91
170
  # Remove worker from all collections
92
- def remove_worker(command_label)
93
- @worker_mutex.synchronize do
94
- worker = @workers[command_label]
95
- if worker
96
- @open_pipes.delete(worker.pipe_end.fileno)
97
- @reactor.remove_from_monitoring(worker.pipe_end)
98
- @workers.delete(command_label)
99
- end
171
+ def remove_worker(command_label, trigger_event = true)
172
+ worker = @workers[command_label]
173
+ if worker
174
+ @open_pipes.delete(worker.pipe_end.fileno)
175
+ @workers.delete(command_label)
176
+ end
177
+ if trigger_event
178
+ event_manager.trigger(command_label, :worker_removed)
100
179
  end
101
180
  end
102
181
 
103
182
  # add worker to global collections
104
183
  def add_worker(worker)
105
- @worker_mutex.synchronize do
106
- @open_pipes[worker.pipe_end.fileno] = worker
107
- @workers[worker.command_label] = worker
108
- @reactor.add_to_monitor(worker.pipe_end)
109
- end
184
+ @open_pipes[worker.pipe_end.fileno] = worker
185
+ @workers[worker.command_label] = worker
186
+ @reactor.add_to_monitor(worker.pipe_end)
110
187
  end
111
188
 
112
189
  def run_command(process_info, write_pipe)
190
+ command_label = process_info.label
191
+
192
+ event_manager.schedule_event(command_label, :exit) { remove_worker(command_label) }
193
+
113
194
  if defined?(Bundler)
114
195
  Bundler.with_clean_env do
115
196
  spawn(process_info.cmd,
@@ -125,12 +206,13 @@ module Invoker
125
206
 
126
207
  def wait_on_pid(command_label,pid)
127
208
  raise Invoker::Errors::ToomanyOpenConnections if @thread_group.enclosed?
209
+
128
210
  thread = Thread.new do
129
211
  Process.wait(pid)
130
212
  message = "Process with command #{command_label} exited with status #{$?.exitstatus}"
131
- $stdout.puts("\n#{message}".red)
213
+ Invoker::Logger.puts("\n#{message}".red)
132
214
  notify_user(message)
133
- remove_worker(command_label)
215
+ event_manager.trigger(command_label, :exit)
134
216
  end
135
217
  @thread_group.add(thread)
136
218
  end
@@ -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,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
@@ -0,0 +1,29 @@
1
+ require 'iniparse'
2
+
3
+ module Invoker
4
+ module Parsers
5
+ class Config
6
+ attr_accessor :processes
7
+ def initialize(filename)
8
+ @ini_content = File.read(filename)
9
+ @processes = process_ini(@ini_content)
10
+ end
11
+
12
+ private
13
+ def process_ini(ini_content)
14
+ document = IniParse.parse(ini_content)
15
+ document.map do |section|
16
+ check_directory(section["directory"])
17
+ OpenStruct.new(label: section.key, dir: section["directory"], cmd: section["command"])
18
+ end
19
+ end
20
+
21
+ def check_directory(app_dir)
22
+ if app_dir && !app_dir.empty? && !File.directory?(app_dir)
23
+ raise Invoker::Errors::InvalidConfig.new("Invalid directory #{app_dir}")
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,86 @@
1
+ require "slop"
2
+ require "ostruct"
3
+
4
+ module Invoker
5
+ module Parsers
6
+ class OptionParser
7
+ def self.parse(args)
8
+ selected_command = nil
9
+
10
+ opts = Slop.parse(args, help: true) do
11
+ on :v, "Print the version" do
12
+ Invoker::Logger.puts Invoker::VERSION
13
+ end
14
+
15
+ command 'start' do
16
+ banner "Usage : invoker start config.ini \n Start Invoker Process Manager"
17
+ run do |cmd_opts, cmd_args|
18
+ selected_command = OpenStruct.new(:command => 'start', :file => cmd_args.first)
19
+ end
20
+ end
21
+
22
+ command 'list' do
23
+ banner "Usage : invoker list\n List processes managed by invoker"
24
+ run do |cmd_opts, cmd_args|
25
+ selected_command = OpenStruct.new(:command => 'list')
26
+ end
27
+ end
28
+
29
+ command 'add' do
30
+ banner "Usage : invoker add process_label \n Start the process with given process_label"
31
+ run do |cmd_opts, cmd_args|
32
+ selected_command = OpenStruct.new(:command => 'add', :command_key => cmd_args.first)
33
+ end
34
+ end
35
+
36
+ command 'remove' do
37
+ banner "Usage : invoker remove process_label \n Stop the process with given label"
38
+ on :s, :signal=, "Signal to send for killing the process, default is SIGINT", as: String
39
+
40
+ run do |cmd_opts, cmd_args|
41
+ signal_to_use = cmd_opts.to_hash[:signal] || 'INT'
42
+ selected_command = OpenStruct.new(
43
+ :command => 'remove',
44
+ :command_key => cmd_args.first,
45
+ :signal => signal_to_use
46
+ )
47
+ end
48
+ end
49
+
50
+ command 'reload' do
51
+ banner "Usage : invoker reload process_label \n Restart the process with given label"
52
+ on :s, :signal=, "Signal to send for killing the process, default is SIGINT", as: String
53
+
54
+ run do |cmd_opts, cmd_args|
55
+ signal_to_use = cmd_opts.to_hash[:signal] || 'INT'
56
+ selected_command = OpenStruct.new(
57
+ :command => 'reload',
58
+ :command_key => cmd_args.first,
59
+ :signal => signal_to_use
60
+ )
61
+ end
62
+ end
63
+ end
64
+
65
+ selected_command || create_default_command(args, opts)
66
+ end
67
+
68
+
69
+ # If user specifies no command either show help message or start the invoker
70
+ # process supervisor.
71
+ #
72
+ # @param args [Array] command line arguments
73
+ # @param opts [Slop::Options] Processed slop options
74
+ # @return [OpenStruct, false] returns default command or nil
75
+ def self.create_default_command(args, opts)
76
+ if args.first && File.exists?(args.first) && File.file?(args.first)
77
+ OpenStruct.new(:command => "start", :file => args.first)
78
+ else
79
+ Invoker::Logger.puts opts.inspect
80
+ false
81
+ end
82
+ end
83
+
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,53 @@
1
+ module Invoker
2
+ class ProcessPrinter
3
+ MAX_COLUMN_WIDTH = 40
4
+ def self.to_json(workers)
5
+ final_json = []
6
+ Invoker::CONFIG.processes.each do |process|
7
+ if worker = workers[process.label]
8
+ final_json << {
9
+ :command => process.cmd, :command_label => process.label,
10
+ :dir => process.dir, :pid => worker.pid
11
+ }
12
+ else
13
+ final_json << {
14
+ :command => process.cmd, :command_label => process.label,
15
+ :dir => process.dir
16
+ }
17
+ end
18
+ end
19
+
20
+ final_json.to_json
21
+ end
22
+
23
+ def self.print_table(json_data)
24
+ final_json = JSON.parse(json_data)
25
+ if final_json && !final_json.empty?
26
+ json_for_printing = []
27
+ final_json.each do |json_row|
28
+ if json_row["pid"]
29
+ json_for_printing << colorize_hash(json_row, "green")
30
+ else
31
+ json_row["pid"] = "[light_black]not running[/]"
32
+ json_for_printing << colorize_hash(json_row, "light_black")
33
+ end
34
+ end
35
+ Formatador.display_compact_table(json_for_printing)
36
+ end
37
+ end
38
+
39
+ private
40
+ def self.colorize_hash(hash, color)
41
+ hash.inject({}) do |mem,(key,obj)|
42
+ if obj.to_s.length > MAX_COLUMN_WIDTH
43
+ short_command = "#{obj.to_s[0..MAX_COLUMN_WIDTH]}.."
44
+ mem[key] = "[#{color}]#{short_command}[/]"
45
+ else
46
+ mem[key] = "[#{color}]#{obj}[/]"
47
+ end
48
+ mem
49
+ end
50
+ end
51
+
52
+ end
53
+ end
@@ -13,12 +13,6 @@ module Invoker
13
13
  @monitored_fds.delete(fd)
14
14
  end
15
15
 
16
- def start
17
- loop do
18
- watch_on_pipe
19
- end
20
- end
21
-
22
16
  def watch_on_pipe
23
17
  ready_read_fds,ready_write_fds,read_error_fds = select(monitored_fds,[],[],0.05)
24
18
 
@@ -38,6 +32,7 @@ module Invoker
38
32
  data = read_data(ready_fd)
39
33
  command_worker.receive_data(data)
40
34
  rescue Invoker::Errors::ProcessTerminated
35
+ remove_from_monitoring(command_worker.pipe_end)
41
36
  command_worker.unbind()
42
37
  end
43
38
  end
@@ -5,45 +5,8 @@ require "socket"
5
5
  module Invoker
6
6
  class Runner
7
7
  def self.run(args)
8
-
9
- selected_command = nil
10
-
11
- opts = Slop.parse(args, help: true) do
12
- on :v, "Print the version" do
13
- $stdout.puts Invoker::VERSION
14
- end
15
-
16
- command 'start' do
17
- banner "Usage : invoker start config.ini \n Start Invoker Process Manager"
18
- run do |cmd_opts, cmd_args|
19
- selected_command = OpenStruct.new(:command => 'start', :file => cmd_args.first)
20
- end
21
- end
22
-
23
- command 'add' do
24
- banner "Usage : invoker add process_label \n Start the process with given process_label"
25
- run do |cmd_opts, cmd_args|
26
- selected_command = OpenStruct.new(:command => 'add', :command_key => cmd_args.first)
27
- end
28
- end
29
-
30
- command 'remove' do
31
- banner "Usage : invoker remove process_label \n Stop the process with given label"
32
- on :s, :signal=, "Signal to send for killing the process, default is SIGINT", as: String
33
-
34
- run do |cmd_opts, cmd_args|
35
- signal_to_use = cmd_opts.to_hash[:signal] || 'INT'
36
- selected_command = OpenStruct.new(
37
- :command => 'remove',
38
- :command_key => cmd_args.first,
39
- :signal => signal_to_use
40
- )
41
- end
42
- end
43
- end
44
- unless selected_command
45
- $stdout.puts opts.inspect
46
- else
8
+ selected_command = Invoker::Parsers::OptionParser.parse(args)
9
+ if selected_command
47
10
  run_command(selected_command)
48
11
  end
49
12
  end
@@ -55,15 +18,19 @@ module Invoker
55
18
  start_server(selected_command)
56
19
  when 'add'
57
20
  add_command(selected_command)
21
+ when 'reload'
22
+ refresh_command(selected_command)
23
+ when 'list'
24
+ list_commands(selected_command)
58
25
  when 'remove'
59
26
  remove_command(selected_command)
60
27
  else
61
- $stdout.puts "Invalid command"
28
+ Invoker::Logger.puts "Invalid command"
62
29
  end
63
30
  end
64
31
 
65
32
  def self.start_server(selected_command)
66
- config = Invoker::Config.new(selected_command.file)
33
+ config = Invoker::Parsers::Config.new(selected_command.file)
67
34
  Invoker.const_set(:CONFIG, config)
68
35
  warn_about_terminal_notifier()
69
36
  commander = Invoker::Commander.new()
@@ -72,31 +39,39 @@ module Invoker
72
39
  end
73
40
 
74
41
  def self.add_command(selected_command)
75
- socket = UNIXSocket.open(Invoker::CommandListener::Server::SOCKET_PATH)
76
- socket.puts("add #{selected_command.command_key}")
77
- socket.flush()
78
- socket.close()
42
+ Socket.unix(Invoker::CommandListener::Server::SOCKET_PATH) do |socket|
43
+ socket.puts("add #{selected_command.command_key}")
44
+ socket.flush()
45
+ end
79
46
  end
80
47
 
81
48
  def self.remove_command(selected_command)
82
- socket = UNIXSocket.open(Invoker::CommandListener::Server::SOCKET_PATH)
83
- socket.puts("remove #{selected_command.command_key} #{selected_command.signal}")
84
- socket.flush()
85
- socket.close()
49
+ Socket.unix(Invoker::CommandListener::Server::SOCKET_PATH) do |socket|
50
+ socket.puts("remove #{selected_command.command_key} #{selected_command.signal}")
51
+ socket.flush()
52
+ end
86
53
  end
87
54
 
88
55
  def self.refresh_command(selected_command)
89
- socket = UNIXSocket.open(Invoker::CommandListener::Server::SOCKET_PATH)
90
- socket.puts("reload #{selected_command.command_key}")
91
- socket.flush()
92
- socket.close()
56
+ Socket.unix(Invoker::CommandListener::Server::SOCKET_PATH) do |socket|
57
+ socket.puts("reload #{selected_command.command_key} #{selected_command.signal}")
58
+ socket.flush()
59
+ end
60
+ end
61
+
62
+ def self.list_commands(selected_command)
63
+ Socket.unix(Invoker::CommandListener::Server::SOCKET_PATH) {|sock|
64
+ sock.puts("list")
65
+ data = sock.gets()
66
+ Invoker::ProcessPrinter.print_table(data)
67
+ }
93
68
  end
94
69
 
95
70
  def self.warn_about_terminal_notifier
96
71
  if RUBY_PLATFORM.downcase.include?("darwin")
97
72
  command_path = `which terminal-notifier`
98
73
  if !command_path || command_path.empty?
99
- $stdout.puts("You can enable OSX notification for processes by installing terminal-notification gem".red)
74
+ Invoker::Logger.puts("You can enable OSX notification for processes by installing terminal-notifier gem".red)
100
75
  end
101
76
  end
102
77
  end
@@ -1,3 +1,3 @@
1
1
  module Invoker
2
- VERSION = "0.0.3"
2
+ VERSION = "0.1.1.pre"
3
3
  end
data/lib/invoker.rb CHANGED
@@ -1,16 +1,18 @@
1
1
  $: << File.dirname(__FILE__) unless $:.include?(File.expand_path(File.dirname(__FILE__)))
2
2
 
3
3
  require "colored"
4
- require_relative "invoker/version"
5
- require_relative "invoker/runner"
6
- require_relative "invoker/command_listener"
7
- require_relative "invoker/errors"
8
- require_relative "invoker/config"
9
- require_relative "invoker/commander"
10
- require_relative "invoker/command_worker"
11
- require_relative "invoker/reactor"
12
-
13
-
14
-
15
-
16
-
4
+ require "formatador"
5
+ require "ostruct"
6
+ require "invoker/version"
7
+ require "invoker/logger"
8
+ require "invoker/runner"
9
+ require "invoker/command_listener/server"
10
+ require "invoker/command_listener/client"
11
+ require "invoker/errors"
12
+ require "invoker/parsers/config"
13
+ require "invoker/parsers/option_parser"
14
+ require "invoker/commander"
15
+ require "invoker/command_worker"
16
+ require "invoker/reactor"
17
+ require "invoker/event/manager"
18
+ require "invoker/process_printer"
data/readme.md CHANGED
@@ -22,7 +22,6 @@ You need to start by creating a `ini` file which will define processes you want
22
22
  directory = /home/gnufied/god_particle
23
23
  command = zsh -c 'bundle exec ruby script/delayed_job'
24
24
 
25
-
26
25
  [events]
27
26
  directory = /home/gnufied/god_particle
28
27
  command = zsh -c 'bundle exec ruby script/event_server'
@@ -44,7 +43,16 @@ Now additionally you can control individual process by,
44
43
  # add and start running
45
44
  ~> invoker add dj
46
45
 
47
- You can also enable OSX notifications for crashed processes by installing `terminal-notification` gem. It is not a dependency, but can be useful if something crashed and you weren't paying attention.
46
+ # List currently running processes managed by invoker
47
+ ~> invoker list
48
+
49
+ # Restart process given by command Label
50
+ ~> invoker reload dj
51
+
52
+ # Restart process given by command label using specific signal for killing
53
+ ~> invoker reload dj -s 9
54
+
55
+ You can also enable OSX notifications for crashed processes by installing `terminal-notifier` gem. It is not a dependency, but can be useful if something crashed and you weren't paying attention.
48
56
 
49
57
  ## Using with rbenv or rvm ##
50
58
 
@@ -58,7 +66,7 @@ If you are running `invoker` with Ruby version x, but your application requires
58
66
  * Unless version of Ruby using which you are running `invoker` command and version of Ruby you are using in the application is same, you almost always will want to use
59
67
  `zsh -c` or `bash -c`. `RVM` in particular requires a login shell and hence sometimes you may have to use `bash -lc`. For example:
60
68
 
61
- command = bash -lc "rvm use 2.0.0-p0 && bundle exec rails s"
69
+ command = bash -lc "rvm 2.0.0-p0 do bundle exec rails s"
62
70
 
63
71
 
64
72
  ## Bug reports and Feature requests
@@ -8,9 +8,8 @@ describe Invoker::CommandListener::Client do
8
8
  end
9
9
 
10
10
  it "should run if read from socket" do
11
- invoker_commander.expects(:add_command_by_label).with("foo")
12
- @client_socket.expects(:read).returns("add foo\n")
13
- @client_socket.expects(:close)
11
+ invoker_commander.expects(:on_next_tick).with("foo")
12
+ @client_socket.expects(:gets).returns("add foo\n")
14
13
 
15
14
  @client.read_and_execute()
16
15
  end
@@ -23,17 +22,15 @@ describe Invoker::CommandListener::Client do
23
22
  end
24
23
 
25
24
  it "with specific signal" do
26
- invoker_commander.expects(:remove_command).with("foo", "9")
27
- @client_socket.expects(:read).returns("remove foo 9\n")
28
- @client_socket.expects(:close)
25
+ invoker_commander.expects(:on_next_tick).with("foo", "9")
26
+ @client_socket.expects(:gets).returns("remove foo 9\n")
29
27
 
30
28
  @client.read_and_execute()
31
29
  end
32
30
 
33
31
  it "with default signal" do
34
- invoker_commander.expects(:remove_command).with("foo",nil)
35
- @client_socket.expects(:read).returns("remove foo\n")
36
- @client_socket.expects(:close)
32
+ invoker_commander.expects(:on_next_tick).with("foo",nil)
33
+ @client_socket.expects(:gets).returns("remove foo\n")
37
34
 
38
35
  @client.read_and_execute()
39
36
  end
@@ -46,10 +43,8 @@ describe Invoker::CommandListener::Client do
46
43
  end
47
44
 
48
45
  it "should print error if read from socket" do
49
- invoker_commander.expects(:remove_command).never()
50
- invoker_commander.expects(:add_command_by_label).never()
51
- @client_socket.expects(:read).returns("eugh foo\n")
52
- @client_socket.expects(:close)
46
+ invoker_commander.expects(:on_next_tick).never()
47
+ @client_socket.expects(:gets).returns("eugh foo\n")
53
48
 
54
49
  @client.read_and_execute
55
50
  end
@@ -1,4 +1,15 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe "Command Worker" do
4
+ describe "converting workers hash to json" do
5
+ before do
6
+ @workers = {}
7
+ @workers["foo"] = Invoker::CommandWorker.new("foo", 89, 1023, "red")
8
+ @workers["bar"] = Invoker::CommandWorker.new("bar", 99, 1024, "blue")
9
+ end
10
+
11
+ it "should print json" do
12
+ @workers.values.map {|worker| worker.to_h }.to_json.should.not.be.empty
13
+ end
14
+ end
4
15
  end
@@ -39,16 +39,27 @@ describe "Invoker::Commander" do
39
39
  describe "if a signal is specified" do
40
40
  it "should use that signal to kill the worker" do
41
41
  @commander.expects(:process_kill).with("bogus", "HUP").returns(true)
42
- @commander.remove_command("resque", "HUP")
42
+ @commander.remove_command("resque", "HUP").should.be.true
43
43
  end
44
44
  end
45
45
 
46
46
  describe "if no signal is specified" do
47
47
  it "should use INT signal" do
48
48
  @commander.expects(:process_kill).with("bogus", "INT").returns(true)
49
- @commander.remove_command("resque", nil)
49
+ @commander.remove_command("resque", nil).should.be.true
50
50
  end
51
51
  end
52
+
53
+ # describe "when a worker is not found" do
54
+ # before do
55
+ # @commander = Invoker::Commander.new()
56
+ # @commander.workers.expects(:[]).returns(OpenStruct.new(:pid => "bogus"))
57
+ # end
58
+
59
+ # it "should return false" do
60
+
61
+ # end
62
+ # end
52
63
  end
53
64
 
54
65
  describe "when no worker is found" do
@@ -69,10 +80,15 @@ describe "Invoker::Commander" do
69
80
  before do
70
81
  invoker_config.stubs(:processes).returns([OpenStruct.new(:label => "sleep", :cmd => "sleep 4", :dir => ENV['HOME'])])
71
82
  @commander = Invoker::Commander.new()
83
+ Invoker.const_set(:COMMANDER, @commander)
84
+ end
85
+
86
+ after do
87
+ Invoker.send(:remove_const,:COMMANDER)
72
88
  end
73
89
 
74
90
  it "should populate workers and open_pipes" do
75
- @commander.reactor.expects(:start).returns(true)
91
+ @commander.expects(:start_event_loop)
76
92
  @commander.start_manager()
77
93
  @commander.open_pipes.should.not.be.empty
78
94
  @commander.workers.should.not.be.empty
@@ -89,4 +105,30 @@ describe "Invoker::Commander" do
89
105
  end
90
106
  end
91
107
 
108
+ describe "#runnables" do
109
+ before do
110
+ @commander = Invoker::Commander.new()
111
+ end
112
+
113
+ it "should run runnables in reactor tick with one argument" do
114
+ @commander.on_next_tick("foo") { |cmd| add_command_by_label(cmd) }
115
+ @commander.expects(:add_command_by_label).returns(true)
116
+ @commander.run_runnables()
117
+ end
118
+
119
+ it "should run runnables with multiple args" do
120
+ @commander.on_next_tick("foo", "bar", "baz") { |t1,*rest|
121
+ remove_command(t1, rest)
122
+ }
123
+ @commander.expects(:remove_command).with("foo", ["bar", "baz"]).returns(true)
124
+ @commander.run_runnables()
125
+ end
126
+
127
+ it "should run runnable with no args" do
128
+ @commander.on_next_tick() { hello() }
129
+ @commander.expects(:hello).returns(true)
130
+ @commander.run_runnables()
131
+ end
132
+ end
133
+
92
134
  end
@@ -16,7 +16,7 @@ command = ruby try_sleep.rb
16
16
  file.write(config_data)
17
17
  file.close
18
18
  lambda {
19
- Invoker::Config.new(file.path)
19
+ Invoker::Parsers::Config.new(file.path)
20
20
  }.should.raise(Invoker::Errors::InvalidConfig)
21
21
  ensure
22
22
  file.unlink()
@@ -0,0 +1,72 @@
1
+ require "spec_helper"
2
+
3
+ describe Invoker::Event::Manager do
4
+ describe "Run scheduled events" do
5
+ before do
6
+ @event_manager = Invoker::Event::Manager.new()
7
+ end
8
+
9
+ it "should run matched events" do
10
+ @event_manager.schedule_event("foo", :exit) { 'exit foo' }
11
+ @event_manager.trigger("foo", :exit)
12
+
13
+ @event_manager.run_scheduled_events do |event|
14
+ event.block.call.should.equal("exit foo")
15
+ end
16
+
17
+ @event_manager.scheduled_events.should.be.empty
18
+ @event_manager.triggered_events.should.be.empty
19
+ end
20
+
21
+ it "should remove triggrered and scheduld events on run" do
22
+ @event_manager.schedule_event("foo", :exit) { 'exit foo' }
23
+ @event_manager.schedule_event("bar", :entry) { "entry bar"}
24
+ @event_manager.trigger("foo", :exit)
25
+ @event_manager.trigger("baz", :exit)
26
+
27
+ @event_manager.run_scheduled_events do |event|
28
+ event.block.call.should.equal("exit foo")
29
+ end
30
+
31
+ @event_manager.scheduled_events.should.not.be.empty
32
+ @event_manager.triggered_events.should.not.be.empty
33
+
34
+ baz_containing_event = lambda do |events|
35
+ events.detect {|event| event.command_label == "baz" }
36
+ end
37
+
38
+ bar_containing_scheduled_event = lambda do |events|
39
+ events.keys.detect {|event_key| event_key == "bar" }
40
+ end
41
+
42
+ @event_manager.triggered_events.should.be.a baz_containing_event
43
+ @event_manager.scheduled_events.should.be.a bar_containing_scheduled_event
44
+ end
45
+
46
+ it "should handle multiple events for same command" do
47
+ @event_manager.schedule_event("foo", :exit) { 'exit foo' }
48
+ @event_manager.schedule_event("foo", :entry) { "entry bar"}
49
+ @event_manager.trigger("foo", :exit)
50
+
51
+ @event_manager.run_scheduled_events { |event| }
52
+
53
+
54
+ @event_manager.schedule_event("foo", :exit) { 'exit foo' }
55
+ @event_manager.trigger("foo", :exit)
56
+
57
+ @event_manager.scheduled_events.should.not.be.empty
58
+ @event_manager.triggered_events.should.not.be.empty
59
+ end
60
+
61
+ it "should not run unmatched events" do
62
+ @event_manager.schedule_event("bar", :entry) { "entry bar"}
63
+ @event_manager.trigger("foo", :exit)
64
+
65
+ events_ran = false
66
+ @event_manager.run_scheduled_events do |event|
67
+ events_ran = true
68
+ end
69
+ events_ran.should.be.false
70
+ end
71
+ end
72
+ end
data/spec/spec_helper.rb CHANGED
@@ -7,6 +7,7 @@ $: << __LIB_PATH__
7
7
  require "pry"
8
8
  require "invoker"
9
9
 
10
+ ENV["INVOKER_TESTS"] = "true"
10
11
 
11
12
  def invoker_config
12
13
  if Invoker.const_defined?(:CONFIG)
metadata CHANGED
@@ -1,7 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: invoker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.1.1.pre
5
+ prerelease: 6
5
6
  platform: ruby
6
7
  authors:
7
8
  - Hemant Kumar
@@ -11,99 +12,129 @@ cert_chain: []
11
12
  date: 2013-05-04 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
- version_requirements: !ruby/object:Gem::Requirement
15
+ name: slop
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
15
18
  requirements:
16
19
  - - ! '>='
17
20
  - !ruby/object:Gem::Version
18
21
  version: '0'
19
22
  type: :runtime
20
23
  prerelease: false
21
- name: slop
22
- requirement: !ruby/object:Gem::Requirement
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
23
26
  requirements:
24
27
  - - ! '>='
25
28
  - !ruby/object:Gem::Version
26
29
  version: '0'
27
30
  - !ruby/object:Gem::Dependency
28
- version_requirements: !ruby/object:Gem::Requirement
31
+ name: iniparse
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
29
34
  requirements:
30
35
  - - ! '>='
31
36
  - !ruby/object:Gem::Version
32
37
  version: '0'
33
38
  type: :runtime
34
39
  prerelease: false
35
- name: iniparse
36
- requirement: !ruby/object:Gem::Requirement
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
37
42
  requirements:
38
43
  - - ! '>='
39
44
  - !ruby/object:Gem::Version
40
45
  version: '0'
41
46
  - !ruby/object:Gem::Dependency
42
- version_requirements: !ruby/object:Gem::Requirement
47
+ name: colored
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
43
50
  requirements:
44
51
  - - ! '>='
45
52
  - !ruby/object:Gem::Version
46
53
  version: '0'
47
54
  type: :runtime
48
55
  prerelease: false
49
- name: colored
50
- requirement: !ruby/object:Gem::Requirement
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
51
58
  requirements:
52
59
  - - ! '>='
53
60
  - !ruby/object:Gem::Version
54
61
  version: '0'
55
62
  - !ruby/object:Gem::Dependency
56
- version_requirements: !ruby/object:Gem::Requirement
63
+ name: formatador
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
57
66
  requirements:
58
67
  - - ! '>='
59
68
  - !ruby/object:Gem::Version
60
69
  version: '0'
61
- type: :development
70
+ type: :runtime
62
71
  prerelease: false
63
- name: bacon
64
- requirement: !ruby/object:Gem::Requirement
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
65
74
  requirements:
66
75
  - - ! '>='
67
76
  - !ruby/object:Gem::Version
68
77
  version: '0'
69
78
  - !ruby/object:Gem::Dependency
70
- version_requirements: !ruby/object:Gem::Requirement
79
+ name: bacon
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
71
82
  requirements:
72
83
  - - ! '>='
73
84
  - !ruby/object:Gem::Version
74
85
  version: '0'
75
86
  type: :development
76
87
  prerelease: false
77
- name: mocha
78
- requirement: !ruby/object:Gem::Requirement
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
79
90
  requirements:
80
91
  - - ! '>='
81
92
  - !ruby/object:Gem::Version
82
93
  version: '0'
83
94
  - !ruby/object:Gem::Dependency
84
- version_requirements: !ruby/object:Gem::Requirement
95
+ name: mocha
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
85
98
  requirements:
86
99
  - - ! '>='
87
100
  - !ruby/object:Gem::Version
88
101
  version: '0'
89
102
  type: :development
90
103
  prerelease: false
91
- name: mocha-on-bacon
92
- requirement: !ruby/object:Gem::Requirement
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
93
106
  requirements:
94
107
  - - ! '>='
95
108
  - !ruby/object:Gem::Version
96
109
  version: '0'
97
110
  - !ruby/object:Gem::Dependency
98
- version_requirements: !ruby/object:Gem::Requirement
111
+ name: mocha-on-bacon
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
99
114
  requirements:
100
115
  - - ! '>='
101
116
  - !ruby/object:Gem::Version
102
117
  version: '0'
103
118
  type: :development
104
119
  prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
105
127
  name: rake
106
128
  requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
107
138
  requirements:
108
139
  - - ! '>='
109
140
  - !ruby/object:Gem::Version
@@ -123,13 +154,16 @@ files:
123
154
  - bin/invoker
124
155
  - invoker.gemspec
125
156
  - lib/invoker.rb
126
- - lib/invoker/command_listener.rb
127
157
  - lib/invoker/command_listener/client.rb
128
158
  - lib/invoker/command_listener/server.rb
129
159
  - lib/invoker/command_worker.rb
130
160
  - lib/invoker/commander.rb
131
- - lib/invoker/config.rb
132
161
  - lib/invoker/errors.rb
162
+ - lib/invoker/event/manager.rb
163
+ - lib/invoker/logger.rb
164
+ - lib/invoker/parsers/config.rb
165
+ - lib/invoker/parsers/option_parser.rb
166
+ - lib/invoker/process_printer.rb
133
167
  - lib/invoker/reactor.rb
134
168
  - lib/invoker/runner.rb
135
169
  - lib/invoker/version.rb
@@ -138,35 +172,37 @@ files:
138
172
  - spec/invoker/command_worker_spec.rb
139
173
  - spec/invoker/commander_spec.rb
140
174
  - spec/invoker/config_spec.rb
175
+ - spec/invoker/event/manager_spec.rb
141
176
  - spec/spec_helper.rb
142
177
  homepage: http://github.com/code-mancers/invoker
143
178
  licenses:
144
179
  - MIT
145
- metadata: {}
146
180
  post_install_message:
147
181
  rdoc_options: []
148
182
  require_paths:
149
183
  - lib
150
184
  required_ruby_version: !ruby/object:Gem::Requirement
185
+ none: false
151
186
  requirements:
152
187
  - - ! '>='
153
188
  - !ruby/object:Gem::Version
154
189
  version: '0'
155
190
  required_rubygems_version: !ruby/object:Gem::Requirement
191
+ none: false
156
192
  requirements:
157
193
  - - ! '>='
158
194
  - !ruby/object:Gem::Version
159
195
  version: '0'
160
196
  requirements: []
161
197
  rubyforge_project:
162
- rubygems_version: 2.0.3
198
+ rubygems_version: 1.8.23
163
199
  signing_key:
164
- specification_version: 4
200
+ specification_version: 3
165
201
  summary: Something small for Process management
166
202
  test_files:
167
203
  - spec/invoker/command_listener/client_spec.rb
168
204
  - spec/invoker/command_worker_spec.rb
169
205
  - spec/invoker/commander_spec.rb
170
206
  - spec/invoker/config_spec.rb
207
+ - spec/invoker/event/manager_spec.rb
171
208
  - spec/spec_helper.rb
172
- has_rdoc:
checksums.yaml DELETED
@@ -1,15 +0,0 @@
1
- ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- MGY0NmQ0YjY1MzZiOWNiYjJkNzE3NjI1YTQxOTI3YTEwMGQxM2FhZQ==
5
- data.tar.gz: !binary |-
6
- NGNiZDNlMTgxZjliOWVmMTdlNjI0YmFkNjUyODcwNjViNGQ5MTJlMA==
7
- !binary "U0hBNTEy":
8
- metadata.gz: !binary |-
9
- MGQxYzdhY2MxYWRhMjExM2NlNDdiYTM0OTc1YjAxZmIyYTFjMjM2YWJiMmUx
10
- NTE3OWFiMWNiZDEwOGE0NTA2ZmUyOGI1NGYwYTAxYzYwNjM2ZTQzMzU2ODkw
11
- Yjc5MGQxZDg4ZmQ2MTRlYWJjMTE3ZDkwODIzODYxOTkyYjM2ZmE=
12
- data.tar.gz: !binary |-
13
- NmIxY2Q3NzkzM2Y0OTYxMTk4ZmJmZTIxMmI2NGRjZTkyZjdkOGU2NGZiZjhh
14
- YmNlZDZmODdmYjE4OWIxMzY5ZDUyNDhjODU2NmIwYTA0ZjQ5YmQxNzQxZTM2
15
- MjUyMjI1MTAyYTZlZGVjZjRkYmEwMjRjOTgwNWE4ZjBmYjIwYTE=
@@ -1,3 +0,0 @@
1
- require_relative "command_listener/server"
2
- require_relative "command_listener/client"
3
-
@@ -1,28 +0,0 @@
1
- require "yaml"
2
- require 'iniparse'
3
-
4
- module Invoker
5
- class Config
6
- attr_accessor :processes
7
- def initialize(filename)
8
- @ini_content = File.read(filename)
9
- @processes = process_ini(@ini_content)
10
- end
11
-
12
- private
13
- def process_ini(ini_content)
14
- document = IniParse.parse(ini_content)
15
- document.map do |section|
16
- check_directory(section["directory"])
17
- OpenStruct.new(label: section.key, dir: section["directory"], cmd: section["command"])
18
- end
19
- end
20
-
21
- def check_directory(app_dir)
22
- if app_dir && !app_dir.empty? && !File.directory?(app_dir)
23
- raise Invoker::Errors::InvalidConfig.new("Invalid directory #{app_dir}")
24
- end
25
- end
26
-
27
- end
28
- end