问题
I wonder how I could start a Ruby Rack application (such as Sinatra) and call it with Net::HTTP or similar in the same script. Of couse I could do something like...
require 'sinatra/base'
require 'net/http'
t = Thread.new do
class App < Sinatra::Base
get '/' do
'Hi!'
end
end
App.run! :host => 'localhost', :port => 1234
end
sleep 2
puts Net::HTTP.start('localhost', 1234) { |http| http.get('/') }.body
t.join
puts 'Bye!'
...but it doesn't feel optimal to sleep for two seconds, waiting for Thin to start. What I need is some kind of callback when the server has started or does anybody have any other suggestions?
回答1:
If you look at the run!
method in the sinatra source in base.rb you will see this:
def run!(options={})
...
handler.run self, :Host => bind, :Port => port do |server|
[:INT, :TERM].each { |sig| trap(sig) { quit!(server, handler_name) } }
set :running, true
end
...
end
There is no way to attach callbacks around here. BUT! as you see the :running
setting is changed once the server is up.
So, the simple solution seems to be to have a thread watch App.settings.running
in a small polling loop (every 500ms or something along those lines). Once running
is true you can safely do your stuff.
Edit: improved version, with a bit of monkey patching goodness.
Adding an after_running callback to Sinatra:
class Sinatra::Base
# Redefine the 'running' setting to support a threaded callback
def self.running=(isup)
metadef(:running, &Proc.new{isup})
return if !defined?(after_running)
return if !isup
Thread.new do
Thread.pass
after_running
end
end
end
class App < Sinatra::Base
set :after_running, lambda {
puts "We're up!"
puts Net::HTTP.start('localhost', 1234) { |http| http.get('/') }.body
puts "Done"
}
get '/' do
'Hi!'
end
end
App.run! :host => "localhost", :port => 1234
回答2:
run! in current Sinatra versions takes a block that is called when the app is started.
Using that callback you can do this:
require 'thread'
def sinatra_run_wait(app, opts)
queue = Queue.new
thread = Thread.new do
Thread.abort_on_exception = true
app.run!(opts) do |server|
queue.push("started")
end
end
queue.pop # blocks until the run! callback runs
end
sinatra_run_wait(TestApp, :port => 3000, :server => 'webrick')
This seems to be reliable for WEBrick, but when using Thin the callback is still sometimes called a little bit before the server accepts connections.
回答3:
I would use a semaphore (cf. Ruby Semaphores?) with a capacity of 1 for this task:
Main thread:
- Acquire the semaphore
- Spawn new thread
- Acquire semaphore (will block until released by the spawned thread)
Spawned web server thread:
- App.run!
- Release semaphore
来源:https://stackoverflow.com/questions/6557079/start-and-call-ruby-http-server-in-the-same-script