Thursday, June 4, 2009

How to Convert From Using Sinatra::Application to Using Sinatra::Base

This is based on conversation in the Sinatra google group here:
http://groups.google.com/group/sinatrarb/browse_thread/thread/b1b3a18897fc1009#

If you really want to convert from using Sinatra::Application to using Sinatra::Base, here are the changes you might want to make. Thanks to Jim for helping me with the first part! There is nothing wrong with using Sinatra::Application for the most part though. (As a sidenote though, Sinatra::Default is planned to be deprecated).

Here is the reasoning for Tanner Burson for why you'd want to use Sinatra::Base:

You don't HAVE to inherit from Sinatra::Base, but by doing require 'sinatra', you're going to pollute the top level namespace with methods that you're never using, because you're using subclasses. If you're using subclasses, inherit from Sinatra::Base, the differences between ::Base and ::Application for a subclass application are absolutely minimal, and are almost solely restricted to which default settings you have to change.

Here's what I did:

In your sinatra controller class (we'll call it whatever.rb), change from requiring sinatra to requiring sinatra/base

#require 'sinatra'
require 'sinatra/base'
Assuming you didn't even have a class defined, you'll want to define a (module and) class wrapper around everything under the requires, and within that throw the following stuff in that you see in sinatra's own base.rb that is included in Default (and Application which just extends Default in Sinatra 0.9.2):
module MyModule
  class Controller < Sinatra::Base
    # stuff that was in Sinatra::Default - some stuff commented per Tanner Burson
    # note: if you want the error handling block you create always to be called, 
    #       make sure :raise_errors is set to Proc.new { false } and that
    #       :show_exceptions is false
    set :raise_errors, Proc.new { test? }
    #set :dump_errors, true
    set :sessions, false
    #set :logging, Proc.new { ! test? }
    #set :methodoverride, true
    set :static, true
    #set :run, Proc.new { ! test? }
    
    # this should be false if you want to make sure that the error handler block
    # you define is called. Also make sure :raise_errors is set to Proc.new { false } 
    set :show_exceptions, false 
 
    # our static assets are stored in the public directory
    # this was in this controller prior to the conversion to Sinatra::Base
    set :public, "public"

    # stuff that was in Sinatra::Default - now commented per Tanner Burson
    #def self.register(*extensions, &block) #:nodoc:
    #  added_methods = extensions.map {|m| m.public_instance_methods }.flatten
    #  Delegator.delegate(*added_methods)
    #  super(*extensions, &block)
    #end

    ... (rest of the code) ...

  end
end   

I commented stuff out above per Tanner Burson:

(This) seems to be a bit overkill. For reference in my Base subclassed app the only settings I manually set are: :static, :public, :sessions. I certainly didn't have to redefine self.register. You just have to follow the documentation for using extensions with Base classes instead.

In the old rackup (config.ru) you might have a line like:

run Sinatra::Application
That should look like this:
#run Sinatra::Application

def app
  MyModule::Controller
end

map "/" do
  run MyModule::Controller
end

Tanner Burson notes:

Also, you can use a rackup file without having to use the URLMap method. You can just do run MyModule::Controller it will automatically map it to '/'. The only reason to use URLMap is if you want to map it to something other than the root, or you want to run multiple applications at different mount points."

Then in any rack tests you have setup, you'll want to comment out set :environment, :test (I think, because it won't work otherwise- maybe that should go somewhere else?) and change them from defining app as Sinatra::Application to defining MyModule::Controller (or whatever you called it).

Then in the tests:

...

#set :environment, :test

class DmwsControllerTest < Test::Unit::TestCase
   ...

   def app
     #Sinatra::Application
     MyModule::Controller
   end

   ...
end

Simon Rozet notes:

Basically, what happens when you `require "sinatra"` is that all your `get`, `post`, `before`, etc calls are delegated to Sinatra::Application.

This:

require "sinatra"
get "/" do
  "foo"
end
is equivalent to:
require "sinatra"
class Application
  get "/" do
    "foo"
  end
end
Here is the relevant code: http://github.com/sinatra/sinatra/blob/9de67b15c6040f9a5d864a4db2dc7f5c9665f462/lib/sinatra/base.rb#L1073 Sinatra::Delegator is then included into the main namespace.

`require "sinatra/base"` and Sinatra::Base are for when you don't want to pollute the main namespace and/or encapsulate your app in class. I hope that cleared it.

BTW, you might want to have a look at the options and documentation documentation: http://www.sinatrarb.com/configuration.html

No comments: