expedite 0.2.2 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a83d8e6d3ba40d5e35466b011f32538004da8cf4c680cbf7b7b8d492110d46b
4
- data.tar.gz: 8cbdb627f4c2cf7468be1d478fe3811a974a95a6ac429ace89bfea14ad09090a
3
+ metadata.gz: ece07855912fe5042042713eb78fecedc03cd06f5b495c0f17a1e7dab14ee99e
4
+ data.tar.gz: c94ea277e1a83968b187648d5917ed2e42181c81f3a3a04c4c6acafc1c52fbf5
5
5
  SHA512:
6
- metadata.gz: 875af703feeeae36c5f0986a6049dbd0867182298446aa6d9a6a1e2bce1ecc1914b7be8acfa367c6a140cb77743b1654fb7088c3913c8573f38e45cb21e6865b
7
- data.tar.gz: 589627e2e61a4dc7fef263da86ff44f24ac31b32f301063595f3d0ba979ff579ccd5d54365ecca608ce2c2dd7e4cd88aeda089573189500c0da495d84bf778f5
6
+ metadata.gz: eb9688825664432ab5e7f5ef60ee855715eabcf6e25ee9242c6eca816c99bb7431eb36ad12b1124e11401dd961b4d68868077c41c9dd8f6b5bb20ffc854a1e09
7
+ data.tar.gz: baf6f7c68c3f98eccf8651947b72dd7e3150580d886015038e7ccd68e0c63798db82e8b6977c1f974a1c027112a233dc0fc227b002cb763151c3916fbda58a5f
data/README.md CHANGED
@@ -16,7 +16,9 @@ This is the "parent" agent:
16
16
  ```
17
17
  Expedite.define do
18
18
  agent :parent do
19
- $parent_var = 1
19
+ before(:serve) do |name|
20
+ $parent_var = name
21
+ end
20
22
  end
21
23
  end
22
24
  ```
@@ -26,8 +28,12 @@ matchers.
26
28
 
27
29
  ```
28
30
  Expedite.define do
29
- agent "development/*", parent: :parent do |name|
30
- $development_var = name
31
+ agent "development/*" do
32
+ self.parent = :parent
33
+
34
+ before(:serve) do |name|
35
+ $child_var = name
36
+ end
31
37
  end
32
38
  end
33
39
  ```
@@ -37,10 +43,12 @@ The following defines an `info` action.
37
43
  ```
38
44
  Expedite.define do
39
45
  action :info do
40
- puts " Process.pid = #{Process.pid}"
41
- puts " Process.ppid = #{Process.ppid}"
42
- puts " $parent_var = #{$parent_var}"
43
- puts "$development_var = #{$development_var}"
46
+ {
47
+ "Process.pid" => Process.pid,
48
+ "Process.ppid" => Process.ppid,
49
+ "$parent_var" => $parent_var,
50
+ "$child_var" => $child_var,
51
+ }
44
52
  end
45
53
  end
46
54
  ```
@@ -55,7 +63,7 @@ the action; in that case, the return result is the exit code.
55
63
  ```
56
64
  require 'expedite'
57
65
 
58
- Expedite.agent("development/abc").invoke("info")
66
+ puts Expedite.agent("development/abc").invoke("info")
59
67
  ```
60
68
 
61
69
  When you run `main.rb`, the following output is produced. Note that `$sleep_parent`
@@ -3,7 +3,7 @@ module Expedite
3
3
  module Action
4
4
  class Block
5
5
  attr_reader :runs_in
6
-
6
+
7
7
  def initialize(runs_in: :application, &block)
8
8
  @runs_in = runs_in
9
9
  @block = block
@@ -13,6 +13,7 @@ module Expedite
13
13
  @block.call(*args)
14
14
  end
15
15
 
16
+ # Ignoring all streams
16
17
  def setup(_)
17
18
  end
18
19
  end
@@ -2,12 +2,12 @@ module Expedite
2
2
  module Action
3
3
  class Boot
4
4
  def call(*args)
5
- agent = args[0]
5
+ name = args[0]
6
6
 
7
7
  require "expedite/server/agent"
8
-
8
+
9
9
  Expedite::Server::Agent.new(
10
- agent: agent,
10
+ name: name,
11
11
  manager: UNIXSocket.for_fd(@child_socket.fileno),
12
12
  env: Expedite::Env.new(
13
13
  root: ENV['EXPEDITE_ROOT'],
@@ -16,9 +16,10 @@ module Expedite
16
16
  ).boot
17
17
  end
18
18
 
19
- def setup(client)
20
- @child_socket = client.recv_io
21
- @log_file = client.recv_io
19
+ # STDOUT, STDERR, STDIN, log, child_socket
20
+ def setup(streams)
21
+ @log_file = streams[3]
22
+ @child_socket = streams[4]
22
23
  end
23
24
 
24
25
  def runs_in
@@ -1,7 +1,10 @@
1
+ require 'expedite/hooks'
1
2
 
2
3
  module Expedite
3
4
  # Definition of a Agent
4
5
  class Agent
6
+ include Hooks
7
+
5
8
  ##
6
9
  # Name of the parent agent. This allows you to create agents from
7
10
  # an existing agent.
@@ -16,18 +19,20 @@ module Expedite
16
19
  ##
17
20
  # [parent] Name of parent agent.
18
21
  # [keep_alive] Specifies if the agent should be automatically restarted if it is terminated. Defaults to false.
19
- # [after_fork] Block is executed when agent is first preloaded.
20
- def initialize(parent: nil, keep_alive: false, &after_fork)
22
+ def initialize(parent: nil, keep_alive: false)
21
23
  @parent = parent
22
24
  @keep_alive = keep_alive
23
- @after_fork_proc = after_fork
24
25
  end
25
26
 
26
27
  ##
27
- # Called when agent if first preloaded. This version calls the after_fork
28
- # block provided in the initializer.
29
- def after_fork(agent)
30
- @after_fork_proc&.call(agent)
28
+ # Register a before event
29
+ # @params event [String] Allowed values: :run
30
+ def before(event, &block)
31
+ register_hook(:"before_#{event}", block)
32
+ end
33
+
34
+ def after(event, &block)
35
+ register_hook(:"after_#{event}", block)
31
36
  end
32
37
  end
33
38
 
@@ -54,8 +59,6 @@ module Expedite
54
59
  #
55
60
  # [matcher] Wildcard to match a name against.
56
61
  # [named_options] Agent options.
57
- # [after_fork] Optional block that is called when
58
- # agent is preloaded.
59
62
  #
60
63
  # = Example
61
64
  # Expedite::Agents.register('base' do |name|
@@ -64,8 +67,8 @@ module Expedite
64
67
  # Expedite::Agents.register('development/abc', parent: 'base') do |name|
65
68
  # puts "Agent #{name} started"
66
69
  # end
67
- def self.register(matcher, **named_options, &after_fork)
68
- self.current.register(matcher.to_s, **named_options, &after_fork)
70
+ def self.register(matcher, **named_options)
71
+ self.current.register(matcher.to_s, **named_options)
69
72
  end
70
73
 
71
74
  ##
@@ -86,11 +89,10 @@ module Expedite
86
89
  ret.agent
87
90
  end
88
91
 
89
- def register(matcher, **named_options, &after_fork)
90
- @registrations << Registration.new(
91
- matcher,
92
- Agent.new(**named_options, &after_fork)
93
- )
92
+ def register(matcher, **named_options)
93
+ agent = Agent.new(**named_options)
94
+ @registrations << Registration.new(matcher, agent)
95
+ agent
94
96
  end
95
97
 
96
98
  def reset
@@ -0,0 +1,27 @@
1
+ require 'expedite/client/exec'
2
+ require 'expedite/client/invoke'
3
+
4
+ module Expedite
5
+ module Client
6
+ class AgentProxy
7
+ attr_accessor :name, :env
8
+
9
+ ##
10
+ #
11
+ # @param name [String] Name of the agent
12
+ # @param env [Expedite::Env] Environment
13
+ def initialize(name, env:)
14
+ self.name = name
15
+ self.env = env
16
+ end
17
+
18
+ def exec(*args)
19
+ Client::Exec.new(env: env, agent: name).call(*args)
20
+ end
21
+
22
+ def invoke(*args)
23
+ Client::Invoke.new(env: env, agent: name).call(*args)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -59,11 +59,11 @@ module Expedite
59
59
  end
60
60
 
61
61
  def run_command(client, agent, args)
62
+ @null_socket ||= File.open(File::NULL, "a")
62
63
  log "sending command"
63
64
 
64
- agent.send_io STDOUT
65
- agent.send_io STDERR
66
- agent.send_io STDIN
65
+ # No child socket
66
+ agent.send_setup(@null_socket, env)
67
67
 
68
68
  agent.send_object({
69
69
  "args" => args,
@@ -85,7 +85,7 @@ module Expedite
85
85
  ## suspend_resume_on_tstp_cont(pid)
86
86
 
87
87
  ## forward_signals(application)
88
- result = agent.recv_object
88
+ result = agent.recv_object(env)
89
89
  if result.key?("exception")
90
90
  e = result["exception"]
91
91
  log "got exception #{e}"
@@ -106,7 +106,9 @@ module Expedite
106
106
  def boot_server
107
107
  env.socket_path.unlink if env.socket_path.exist?
108
108
 
109
- pid = Process.spawn(gem_env, env.server_command, out: File::NULL)
109
+ pid = Bundler.with_original_env do
110
+ Process.spawn(gem_env, env.server_command, out: File::NULL, chdir: env.root)
111
+ end
110
112
  timeout = Time.now + BOOT_TIMEOUT
111
113
 
112
114
  @server_booted = true
@@ -118,9 +120,9 @@ module Expedite
118
120
  # Server did not start
119
121
  raise ArgumentError, "Server exited: #{status.exitstatus}"
120
122
  elsif Time.now > timeout
121
- $stderr.puts "Starting Expedite server with `#{env.server_command}` " \
122
- "timed out after #{BOOT_TIMEOUT} seconds"
123
- exit 1
123
+ raise "Starting Expedite server with `#{env.server_command}` " \
124
+ "timed out after #{BOOT_TIMEOUT} seconds. Was waiting for #{env.socket_path} to appear."
125
+ #exit 1
124
126
  end
125
127
 
126
128
  sleep 0.1
data/lib/expedite/env.rb CHANGED
@@ -6,19 +6,31 @@ require 'expedite/server/application_manager'
6
6
  module Expedite
7
7
  class Env
8
8
  attr_accessor :root
9
- attr_accessor :application_id, :app_name, :log_file
9
+ attr_accessor :application_id, :app_name, :log_file, :bundler
10
10
  attr_reader :applications
11
11
 
12
- def initialize(root: nil, app_name: nil, log_file: nil)
13
- @root = root || Dir.pwd
12
+ ##
13
+ # The environment containing the target application.
14
+ #
15
+ # The root and app_name are used to derive the socket.
16
+ # @param root [String] Path to the root directory.
17
+ # @param app_name [String] The name of the application.
18
+ # @param log_file [IO] Path to log file. If nil, logs are discarded.
19
+ # @param bundler [Boolean] If true, `bundle exec` will be added in front
20
+ # of the server command. Defaults to true.
21
+ def initialize(root: nil, app_name: nil, log_file: nil, bundler: true)
22
+ # Use realpath so that directories that are symlinked end up with
23
+ # the same root. This is important for getting the correct socket.
24
+ @root = File.realpath(root || Dir.pwd)
14
25
  @app_name = app_name || File.basename(@root)
15
26
  @log_file = log_file || File.open(File::NULL, "a")
16
- @tmp_path = nil
27
+ @bundler = bundler
17
28
 
18
- @application_id = Digest::SHA1.hexdigest(@root)
29
+ @application_id = Digest::SHA1.hexdigest(@root + "|" + @app_name)
30
+ @tmp_path = nil
19
31
 
20
- env = self
21
- @applications = Server::ApplicationManager.new(env)
32
+ # TODO: @applications should only be available in the server
33
+ @applications = Server::ApplicationManager.new(self)
22
34
  end
23
35
 
24
36
  def version
@@ -49,7 +61,13 @@ module Expedite
49
61
  end
50
62
 
51
63
  def server_command
52
- "#{File.expand_path("../../../bin/expedite", __FILE__)} server --background"
64
+ bin_expedite = File.expand_path("../../../bin/expedite", __FILE__)
65
+ cmd = if bundler
66
+ "bundle exec #{bin_expedite}"
67
+ else
68
+ bin_expedite
69
+ end
70
+ "#{cmd} server --background"
53
71
  end
54
72
 
55
73
  def graceful_termination_timeout
@@ -11,4 +11,10 @@ module Expedite
11
11
 
12
12
  class AgentNotFoundError < Error
13
13
  end
14
+
15
+ ##
16
+ # Wraps exceptions from client calls.
17
+ #
18
+ class InvokeError < Error
19
+ end
14
20
  end
@@ -0,0 +1,46 @@
1
+ module Expedite
2
+ module Hooks
3
+ ##
4
+ # Register a new hook with the given block to name
5
+ # @param name [String] Name of the hook
6
+ def register_hook(name, block)
7
+ return clear_hooks(name) if block.nil?
8
+
9
+ block = Array(block)
10
+ all_hooks[name].concat(block)
11
+ end
12
+
13
+ ##
14
+ # Clears all hooks for the specified name
15
+ # @param name [String] Name of the hook
16
+ def clear_hooks(name)
17
+ all_hooks[name] = []
18
+ end
19
+
20
+ ##
21
+ # Returns all hooks for the specified name
22
+ # @param name [String] Name of the hook
23
+ def hooks(name)
24
+ all_hooks[name]
25
+ end
26
+
27
+ ##
28
+ # Returne all hooks as a hash
29
+ def all_hooks
30
+ @all_hooks ||= Hash.new { |h, k| h[k] = [] }
31
+ end
32
+
33
+ ##
34
+ # Runs all Procs registered for the specified name
35
+ # @param name [String] Name of the hook
36
+ # @param @args Arguments passed to the Procs
37
+ def run_hook(name, *args)
38
+ hks = hooks(name)
39
+ return if hks.empty?
40
+
41
+ hks.each do |hook|
42
+ args.any? ? hook.call(*args) : hook.call
43
+ end
44
+ end
45
+ end
46
+ end
@@ -8,14 +8,63 @@ module Expedite
8
8
 
9
9
  self.puts data.bytesize.to_i
10
10
  self.write data
11
+ self.write "$"
11
12
  end
12
13
 
13
- def recv_object
14
+ ##
15
+ # Result is an exception
16
+ #
17
+ def send_exception(e, env)
18
+ if !e.is_a?(Expedite::Error)
19
+ ie = Expedite::InvokeError.new("#{e.class}: #{e.message}")
20
+ ie.set_backtrace(e.backtrace)
21
+ e = ie
22
+ end
23
+ self.send_object({"exception" => e}, env)
24
+ end
25
+
26
+ ##
27
+ # Result is a normal return value
28
+ #
29
+ def send_return(obj, env)
30
+ self.send_object({"return" => obj}, env)
31
+ end
32
+
33
+ def recv_object(env)
14
34
  len = self.gets.to_i
15
35
  data = self.read(len)
36
+ e = self.read(1)
37
+ env.log "recv_object len=#{len} data=... e=#{e}"
38
+ raise "Unexpected end #{e}" if e != "$"
16
39
  Marshal.load(data)
17
40
  end
41
+
42
+
43
+ ##
44
+ # Send sockets for setup.
45
+ # Linux seems to require all fds to be sent first
46
+ #
47
+ def send_setup(child_socket, env)
48
+ self.send_io STDOUT
49
+ self.send_io STDERR
50
+ self.send_io STDIN
51
+ self.send_io env.log_file
52
+ self.send_io child_socket
53
+ end
54
+
55
+ ##
56
+ # Receive setup sockets.
57
+ # Returns the child_socket
58
+ #
59
+ def recv_setup(env)
60
+ streams = 5.times.map { self.recv_io }
61
+ [STDOUT, STDERR, STDIN].zip(streams[0..2]).each { |a, b| a.reopen(b) }
62
+ env.log_file = streams[3]
63
+ return streams
64
+ end
18
65
  end
19
66
  end
20
67
 
68
+ module ActiveRecord
69
+ end
21
70
  IO.include ::Expedite::Protocol
@@ -4,11 +4,11 @@ require 'pty'
4
4
  require 'set'
5
5
  require 'socket'
6
6
  require 'expedite/actions'
7
+ require 'expedite/agents'
7
8
  require 'expedite/env'
8
9
  require 'expedite/failsafe_thread'
9
10
  require 'expedite/protocol'
10
11
  require 'expedite/signals'
11
- require 'expedite/agents'
12
12
 
13
13
  module Expedite
14
14
  def self.agent
@@ -33,11 +33,12 @@ module Expedite
33
33
  class Agent
34
34
  include Signals
35
35
 
36
- attr_reader :agent
36
+ attr_reader :name
37
37
  attr_reader :manager, :env
38
38
 
39
- def initialize(agent:, manager:, env:)
40
- @agent = agent
39
+ # @params name [String] Name of agent
40
+ def initialize(name:, manager:, env:)
41
+ @name = name
41
42
  @manager = manager
42
43
  @env = env
43
44
  @mutex = Mutex.new
@@ -56,8 +57,8 @@ module Expedite
56
57
  Signal.trap("TERM") { terminate }
57
58
 
58
59
  env.load_helper
59
- eager_preload if false #if ENV.delete("SPRING_PRELOAD") == "1"
60
- run
60
+
61
+ serve
61
62
  end
62
63
 
63
64
  def state(val)
@@ -75,8 +76,12 @@ module Expedite
75
76
  env.app_name
76
77
  end
77
78
 
79
+ def agent
80
+ @agent ||= Expedite::Agents.lookup(name)
81
+ end
82
+
78
83
  def log(message)
79
- env.log "[application:#{agent}] #{message}"
84
+ env.log "[application:#{name}] #{message}"
80
85
  end
81
86
 
82
87
  def preloaded?
@@ -112,10 +117,10 @@ module Expedite
112
117
  with_pty { preload }
113
118
  end
114
119
 
115
- def run
116
- $0 = "expedite agent | #{app_name} | #{agent}"
120
+ def serve
121
+ $0 = "expedite agent | #{app_name} | #{name}"
117
122
 
118
- Expedite::Agents.lookup(agent).after_fork(agent)
123
+ agent.run_hook(:before_serve, name)
119
124
 
120
125
  state :running
121
126
  manager.puts
@@ -124,36 +129,37 @@ module Expedite
124
129
  IO.select [manager, @interrupt.first]
125
130
 
126
131
  if terminating? || preload_failed?
132
+ agent.run_hook(:after_serve, name)
127
133
  exit
128
134
  else
129
- serve manager.recv_io(UNIXSocket)
135
+ serve_request(manager.recv_io(UNIXSocket))
130
136
  end
131
137
  end
132
138
  end
133
139
 
134
- def serve(client)
140
+ def serve_request(client)
135
141
  puts "got client"
136
142
  manager.puts
137
143
 
138
- _stdout, stderr, _stdin = streams = 3.times.map { client.recv_io }
139
- [STDOUT, STDERR, STDIN].zip(streams).each { |a, b| a.reopen(b) }
144
+ streams = client.recv_setup(env)
140
145
 
141
146
  preload unless preloaded?
142
147
 
143
- args, env, method = client.recv_object.values_at("args", "env", "method")
144
- log "serve #{args} using #{method}"
148
+ cargs, cenv, cmethod = client.recv_object(env).values_at("args", "env", "method")
149
+ log "serve #{cargs} using #{cmethod}"
145
150
 
146
- exec_name = args.shift
151
+ exec_name = cargs.shift
147
152
  action = Expedite::Actions.lookup(exec_name)
148
- action.setup(client)
153
+ action.setup(streams)
154
+ # TODO: before(:request)
149
155
 
150
156
  connect_database # why are we connecting prior? is this for invoke?
151
- pid = case method
157
+ pid = case cmethod
152
158
  when "invoke"
153
159
  # TODO: Invoke in a worker process instead of the preloader
154
- serve_invoke(client, action, args, env)
160
+ serve_invoke(client, action, cargs, cenv)
155
161
  else
156
- serve_fork(client, action, args, env)
162
+ serve_fork(client, action, cargs, cenv)
157
163
  end
158
164
 
159
165
  disconnect_database
@@ -186,25 +192,25 @@ module Expedite
186
192
  end
187
193
 
188
194
  # Returns pid of the current process
189
- def serve_invoke(client, action, args, env)
195
+ def serve_invoke(client, action, cargs, cenv)
190
196
  begin
191
- ret = action.call(*args)
197
+ ret = action.call(*cargs)
192
198
  rescue Exception => e
193
- client.send_object({"exception" => e}, self.env)
199
+ client.send_exception(e, self.env)
194
200
  else
195
- client.send_object({"return" => ret}, self.env)
201
+ client.send_return(ret, self.env)
196
202
  end
197
203
  Process.pid
198
204
  end
199
205
 
200
- def serve_fork(client, action, args, env)
206
+ def serve_fork(client, action, cargs, cenv)
201
207
  fork do
202
208
  Process.setsid
203
209
  IGNORE_SIGNALS.each { |sig| trap(sig, "DEFAULT") }
204
210
  trap("TERM", "DEFAULT")
205
211
 
206
212
  # Load in the current env vars, except those which *were* changed when Spring started
207
- env.each { |k, v| ENV[k] = v }
213
+ cenv.each { |k, v| ENV[k] = v }
208
214
 
209
215
  # requiring is faster, so if config.cache_classes was true in
210
216
  # the environment's config file, then we can respect that from
@@ -221,11 +227,11 @@ module Expedite
221
227
  shush_backtraces
222
228
 
223
229
  begin
224
- ret = action.call(*args)
230
+ ret = action.call(*cargs)
225
231
  rescue => e
226
- client.send_object({"exception" => e}, self.env)
232
+ client.send_exception(e, self.env)
227
233
  else
228
- client.send_object({"return" => ret}, self.env)
234
+ client.send_return(ret, self.env)
229
235
  end
230
236
  end
231
237
  end
@@ -1,7 +1,7 @@
1
1
  require "expedite/server/agent"
2
2
 
3
3
  app = Expedite::Server::Agent.new(
4
- agent: ENV['EXPEDITE_VARIANT'],
4
+ name: ENV['EXPEDITE_VARIANT'],
5
5
  manager: UNIXSocket.for_fd(3),
6
6
  env: Expedite::Env.new(
7
7
  root: ENV['EXPEDITE_ROOT'],
@@ -82,7 +82,7 @@ module Expedite
82
82
  end
83
83
  rescue Exception => e
84
84
  # NotImplementedError is an Exception, not StandardError
85
- client.send_object({"exception" => e}, env)
85
+ client.send_exception(e, env)
86
86
  return Process.pid
87
87
  rescue Errno::ECONNRESET, Errno::EPIPE => e
88
88
  log "#{e} while reading from child; returning no pid"
@@ -95,9 +95,10 @@ module Expedite
95
95
  log "stopping"
96
96
  @state = :stopping
97
97
 
98
- if pid
99
- Process.kill('TERM', pid)
100
- Process.wait(pid)
98
+ _pid = self.pid
99
+ if _pid
100
+ Process.kill('TERM', _pid)
101
+ Process.wait(_pid)
101
102
  end
102
103
  rescue Errno::ESRCH, Errno::ECHILD
103
104
  # Don't care
@@ -123,30 +124,31 @@ module Expedite
123
124
 
124
125
  # Creates a child that is forked from a parent
125
126
  def fork_child(preload = false)
126
- @child, child_socket = UNIXSocket.pair
127
+ child_socket = nil
128
+ wr = nil
129
+ begin
130
+ @child, child_socket = UNIXSocket.pair(:STREAM)
127
131
 
128
- # Compose command
129
- wr, rd = UNIXSocket.pair
130
- wr.send_io STDOUT
131
- wr.send_io STDERR
132
- wr.send_io STDIN
132
+ # Compose command
133
+ wr, rd = UNIXSocket.pair(:STREAM)
133
134
 
134
- wr.send_object({
135
- 'args' => ['expedite/boot', name],
136
- 'env' => {},
137
- 'method' => "fork",
138
- }, env)
135
+ wr.send_setup(child_socket, env)
139
136
 
140
- wr.send_io child_socket
141
- wr.send_io env.log_file
142
- wr.close
137
+ wr.send_object({
138
+ 'args' => ['expedite/boot', name],
139
+ 'env' => {},
140
+ 'method' => "fork",
141
+ }, env)
143
142
 
144
- @pid = env.applications.with(parent) do |target|
145
- target.run(rd)
146
- end
143
+ @pid = env.applications.with(parent) do |target|
144
+ target.run(rd)
145
+ end
147
146
 
148
- start_wait_thread(pid, child) if child.gets
149
- child_socket.close
147
+ start_wait_thread(pid, child) if child.gets
148
+ ensure
149
+ wr&.close
150
+ child_socket&.close
151
+ end
150
152
  end
151
153
 
152
154
  # Creates a child that is started from scratch
@@ -7,6 +7,9 @@ require "expedite/signals"
7
7
 
8
8
  module Expedite
9
9
  module Server
10
+ ##
11
+ # Controls the `expedite server`.
12
+ #
10
13
  class Controller
11
14
  include Signals
12
15
 
@@ -99,7 +102,7 @@ module Expedite
99
102
 
100
103
  # Corresponds to Client::Invoke#connect_to_agent
101
104
  app_client = client.recv_io
102
- command = client.recv_object
105
+ command = client.recv_object(env)
103
106
 
104
107
  args, agent = command.values_at("args", "agent")
105
108
  cmd = args.first
@@ -111,9 +114,7 @@ module Expedite
111
114
  client.puts
112
115
 
113
116
  unix_socket = UNIXSocket.for_fd(app_client.fileno)
114
- _stdout = unix_socket.recv_io
115
- _stderr = unix_socket.recv_io
116
- _stdin = unix_socket.recv_io
117
+ _ = unix_socket.recv_setup(env)
117
118
 
118
119
  client.puts Process.pid
119
120
 
@@ -121,7 +122,7 @@ module Expedite
121
122
  env.applications.pools.each do |k, pool|
122
123
  application_pids.concat(pool.all.map(&:pid))
123
124
  end
124
- unix_socket.send_object({"return" => application_pids}, env)
125
+ unix_socket.send_return(application_pids, env)
125
126
 
126
127
  unix_socket.close
127
128
  client.close
@@ -139,18 +140,16 @@ module Expedite
139
140
  end
140
141
  rescue AgentNotFoundError => e
141
142
  unix_socket = UNIXSocket.for_fd(app_client.fileno)
142
- _stdout = unix_socket.recv_io
143
- _stderr = unix_socket.recv_io
144
- _stdin = unix_socket.recv_io
143
+ _ = unix_socket.recv_setup(env)
145
144
 
146
- args, env = unix_socket.recv_object.values_at("args", "env")
145
+ args, env = unix_socket.recv_object(env).values_at("args", "env")
147
146
 
148
147
  client.puts Process.pid
149
148
 
150
149
  # boot only
151
150
  #@child_socket = client.recv_io
152
151
  #@log_file = client.recv_io
153
- unix_socket.send_object({"exception" => e}, env)
152
+ unix_socket.send_exception(e, env)
154
153
 
155
154
  unix_socket.close
156
155
  client.close
@@ -13,7 +13,9 @@ module Expedite
13
13
  end
14
14
 
15
15
  def agent(name, parent:nil, &block)
16
- Expedite::Agents.register(name, parent: parent, &block)
16
+ agent = Expedite::Agents.register(name, parent: parent)
17
+ agent.instance_eval(&block) if !block.nil?
18
+ agent
17
19
  end
18
20
 
19
21
  def self.run(&block)
@@ -23,4 +25,4 @@ module Expedite
23
25
  end
24
26
 
25
27
  extend Syntax
26
- end
28
+ end
@@ -1,3 +1,3 @@
1
1
  module Expedite
2
- VERSION = '0.2.2'
2
+ VERSION = '0.2.4'
3
3
  end
data/lib/expedite.rb CHANGED
@@ -1,33 +1,14 @@
1
- require 'expedite/client/exec'
2
- require 'expedite/client/invoke'
1
+ require 'expedite/client/agent_proxy'
3
2
  require 'expedite/syntax'
4
3
 
5
- module Expedite
6
- class AgentProxy
7
- attr_accessor :env, :agent
8
-
9
- def initialize(env:, agent:)
10
- self.env = env
11
- self.agent = agent
12
- end
13
-
14
- def exec(*args)
15
- Client::Exec.new(env: env, agent: agent).call(*args)
16
- end
17
-
18
- def invoke(*args)
19
- Client::Invoke.new(env: env, agent: agent).call(*args)
20
- end
21
- end
22
- end
23
4
 
24
5
  module Expedite
25
6
  ##
26
7
  # Returns a client to dispatch actions to the specified agent
27
- def self.agent(agent)
28
- @clients ||= Hash.new do |h, k|
29
- AgentProxy.new(env: Env.new, agent: agent)
30
- end
31
- @clients[agent]
8
+ # @param name [String] Name of the agent that we want to talk to
9
+ # @param env [Expedite::Env] Defaults to an environment pointing to the
10
+ # current directory
11
+ def self.agent(name, env: Env.new)
12
+ Client::AgentProxy.new(name, env: env)
32
13
  end
33
14
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: expedite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bing-Chang Lai
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-12-21 00:00:00.000000000 Z
11
+ date: 2023-02-08 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Manages Ruby processes that can be used to spawn child processes faster.
14
14
  email: johnny.lai@me.com
@@ -29,6 +29,7 @@ files:
29
29
  - lib/expedite/cli/server.rb
30
30
  - lib/expedite/cli/status.rb
31
31
  - lib/expedite/cli/stop.rb
32
+ - lib/expedite/client/agent_proxy.rb
32
33
  - lib/expedite/client/base.rb
33
34
  - lib/expedite/client/exec.rb
34
35
  - lib/expedite/client/invoke.rb
@@ -36,6 +37,7 @@ files:
36
37
  - lib/expedite/errors.rb
37
38
  - lib/expedite/failsafe_thread.rb
38
39
  - lib/expedite/helper/rails.rb
40
+ - lib/expedite/hooks.rb
39
41
  - lib/expedite/protocol.rb
40
42
  - lib/expedite/server/agent.rb
41
43
  - lib/expedite/server/agent_boot.rb
@@ -65,7 +67,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
65
67
  - !ruby/object:Gem::Version
66
68
  version: '0'
67
69
  requirements: []
68
- rubygems_version: 3.3.26
70
+ rubygems_version: 3.4.1
69
71
  signing_key:
70
72
  specification_version: 4
71
73
  summary: Expedite startup of Ruby process