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?
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
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.
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