[MacRuby-devel] ControlTower in MacRuby

Nick Ludlam nick at recoil.org
Wed Jul 28 14:26:35 PDT 2010


On 28 Jul 2010, at 20:03, Joshua Ballanco wrote:

> Apologies for being so late to reply...
> 
> On Jul 21, 2010, at 11:44 AM, Nick Ludlam wrote:
> 
>> On 21 Jul 2010, at 15:31, Alexander von Below wrote:
>> 
>> Hi Alexander,
>> I've got a MacRuby app happily running very happily with Control Tower. First I cloned the code from github, and built and installed the macgem package. Then I also installed rack with
>> 
>> sudo macgem install rack
> 
> ControlTower currently has a version of rack that is vendored into the gem. Currently it's the same as rack v1.2.1, but with one particularly annoying bug fixed (unrelated to Ruby vs. MacRuby). Originally we made rack a gem dependency, but we decided that vendoring might work better for now, since there may still be MacRuby specific bugs lurking. There's also the possibility that we could optimize parts of rack for MacRuby, though there's nothing like that in there now. I'll do my best to keep the vendored version up to date with the latest rack release.
> 
> tl;dr -- You shouldn't have to install rack to use ControlTower, but there's not any harm if you do.

Hi Joshua,
Thanks for pointing that out. For some reason I had missed CT coming with Rack, so had assumed otherwise. I've just tested omitting that in my code and it works like a charm.


>> in order to get some of Rack's built in server functionality. Then a short script would look like:
>> 
>> 
>> require 'rubygems'
>> require 'control_tower'
>> require 'rack/utils'
>> 
>> server_options = { :port => 3001, :host => '0.0.0.0', :concurrent => false }
>> 
>> app = Rack::Builder.new do
>> map "/" do run Rack::File.new("/Users/nick/Sites/") end
>> end.to_app
>> 
>> @s = ControlTower::Server.new(app, server_options)
>> if @s
>> puts 'Starting control tower webserver'
>> @s.start
>> else
>> puts "ERROR: Couldnt build server"
>> end
> 
> This will work...however...
> 
> There are two ways to use a Rack based server to run a web app (this is true for more than just ControlTower):
> 
> 1. Use a rack-up file (e.g. config.ru) and pass it as an argument to the server start script (or, in the case of thin, if you're running a Rails app thin is nice enough to go find the file for you). The contents of this file are then eval'd in the context of Rack::Builder.new
> 
> 2. Programmatically, create the app (as above) using Rack::Builder.new and pass it into one of the rack handlers. If you look at the standard rack distribution, you'll see that there are handlers for all of the common rack servers. Eventually, we should submit a pull request for rack to include our handler, but for the time being ours is located in the control_tower gem. So, the example above would look like:
> 
> -----
> require 'rubygems'
> require 'control_tower'
> require 'rack/utils'
> require 'rack/handler/control_tower'
> 
> server_options = { :port => 3001, :host => '0.0.0.0', :concurrent => false }
> 
> app = Rack::Builder.new do
> map "/" do run Rack::File.new("/Users/nick/Sites/") end
> end.to_app
> 
> Rack::Handler::ControlTower.run(app, server_options) do |s|
>  puts "ERROR: Couldnt build server" unless s
> end
> -----
> 
> The reason to use the handler, instead of calling Server.new directly (even though, that's pretty much all the handler does), is that this is part of the rack-protocol, and guaranteed not to change. At some point in the future, we might add some more complicated logic around how to start a server, at which point your code that directly calls Server.new might break.

Great, I have amended my code to reflect the correct API. Incidentally I have had great success in getting concurrency working with MacRuby HEAD (0.7) for serving up my XML feeds and the raw files.

I also notice you're explicitly requiring 'rack/handler/control_tower.rb'. Is that going to remain the case too? Or could this be folded into the main require 'control_tower' call, if you're going to indicate that this is good practice.

Lastly, I just wanted to check with you on threading. I'm wrapping that whole sequence above in a simple method, and using the following to spin it out from the main thread:

@webserverThread = NSThread.alloc.initWithTarget self, selector:'startServerThread', object:nil
@webserverThread.start

But I found myself needing to create an autorelease pool (as I would with writing obj-c) at the beginning of #startServerThread otherwise I saw some really funky corruption and crashes.  Now I'm a little fuzzy on how GC works, so is this a necessary requirement when spawning new threads within a GC run environment?

Nick



More information about the MacRuby-devel mailing list