2
\$\begingroup\$

I wanted to write a script, which starts a rails app, and opens it in the browser.
I'm trying to implement this in a peculiar way though - I expect two things:

  1. the script must wait until the server is started, then open the browser within a short time
  2. when I stop the script (Ctrl+C), the server process must stop.

The script I've written actually works, but I was wondering if there is a simplerr/more standard/more correct way of writing it.

The script is written for linux, but it's not meant to be platform specific - or more precisely, anything except Windows.

PROJECT_DIR          = '/path/to/app'
POLLING_TIME         = 0.1
SERVER_STARTED_REGEX = /WEBrick::HTTPServer#start/
BROWSER_COMMAND      = 'browser http://localhost:3000 &'

Dir.chdir( PROJECT_DIR )

thread = Thread.new do
  IO.popen( 'script/rails server 2>&1' ) do | io |
    Thread.current[ :last_server_line ] = io.gets while true
  end
end

sleep POLLING_TIME while thread[ :last_server_line ] !~ SERVER_STARTED_REGEX

`#{ BROWSER_COMMAND }`

thread.join
\$\endgroup\$
  • \$\begingroup\$ Do you mean what anybody will edit you post, with better script, or make an answer below? \$\endgroup\$ – Малъ Скрылевъ Nov 5 '13 at 15:03
  • \$\begingroup\$ @majioa It's best to use the answer feature, so that others have the chance to see the original question and provide answers of their own. \$\endgroup\$ – CMW Nov 5 '13 at 20:13
1
\$\begingroup\$

This was an interesting one. Thank you :)

I dug around in the rails code and poked at webrick and rack for a while and this is what I came up with:

PROJECT_DIR     = ENV['PROJECT_DIR']     || '/path/to/app'
BROWSER_COMMAND = ENV['BROWSER_COMMAND'] || 'browser http://localhost:3000 &'
PORT            = ENV['PORT'].to_i       || 3000

require "#{PROJECT_DIR}/config/environment.rb"

thread = Thread.new do
  Rack::Handler::WEBrick.run Rails.application, Port: PORT
end

`#{ BROWSER_COMMAND }`

Rack::Handler::WEBrick.shutdown

thread.join

This approach is actually borrowed from rack's webrick handler.

I load the environment prior to actually starting the webrick server making any waits unnecessary for me. If that should be necessary for you, I'm sure you could add it.

Then I pretty much stuck to what you did, putting the actual server into a thread. The Handler already provides that neat shutdown method.

Oh, I also took the liberty of adding some ENV variables so you can put this script into a file and run it from where ever you want.

EDIT: In the comments Marcus pointed out, that in order to resolve the timing issue, one can set the closure (block variable) from WEBrick.run {|server|} to a thread-local variable that can be polled afterwards.

There might also be ways to exploit Celluloid for messaging between the different threads.

\$\endgroup\$
  • \$\begingroup\$ The logic is appropriate, but there is a bug! The completion of the application start is asynchronous, so that the browser starts before actually something is listening on the port. In addition to that, the webrick shutdown raises an error if the app is not fully started! \$\endgroup\$ – Marcus Nov 16 '13 at 15:17
  • \$\begingroup\$ I've actually found a workaround - the WEBrick.run {|server|} variable can be stored in the thread local storage. immediately afterwards, it can be polled at intervals, until it's not nil. at that point, the server is ready. \$\endgroup\$ – Marcus Nov 16 '13 at 15:25
  • \$\begingroup\$ Yeah, I mentioned that one in my answer. It was not necessary for me since my browser took a while to start and the server was ready by then. Glad you managed to figure it out. \$\endgroup\$ – CMW Nov 16 '13 at 20:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.