Wednesday, December 19, 2007

Installing and running Railsbench for your Ruby on Rails application

Installation of railsbench on Windows

  1. run from any directory (no download required) gem install hoe (this installs dependency gem hoe).
  2. Download railsbench gem from RubyForge and run gem install railsbench-0.9.2.gem from the directory where it is downloaded.
  3. Run gem list to see railsbench version 0.9.2 listed.

Running railsbench on Windows
  1. Set environment variable RAILS_ROOT through control panel to point to your app directory (here guitarati)
    RAILS_ROOT set to C:\InstantRails\rails_apps\guitarati
  2. Railsbench writes the result of tests in RAILS_PERF_DATA directory. Set environment variable RAILS_PERF_DATA through control panel. I chose to save the data in a newly created directory perf_data under RAILS_ROOT.
    RAILS_PERF_DATA set to C:\InstantRails\rails_apps\guitarati\perf_data
  3. Copy benchmarks.yml and benchmarking.rb from C:\InstantRails\ruby\lib\ruby\gems\1.8\gems\railsbench-0.9.2\config to RAILS_ROOT\config
  4. Copy benchmarking.rb from C:\InstantRails\ruby\lib\ruby\gems\1.8\gems\railsbench-0.9.2\config to RAILS_ROOT\config\environments
  5. run railsbench install from C:\InstantRails\ruby\lib\ruby\gems\1.8\gems\railsbench-0.9.2 directory. Should output .. creating database configuration: benchmarking
  6. Edit environment.rb to specify the plugins to be loaded:
    config.plugins = %W( simple_captcha acts_as_ferret ...etc...). Without this, running railsbench perf_run gives undefined method errors for me.
  7. Run the simplest railsbench command
    C:\...\1.8\gems\railsbench-0.9.2>railsbench perf_run 100
  8. The above command prints something like this



  9. Understanding the railsbench results is a topic in itself to be covered in a later blog post.

Sunday, December 2, 2007

Raising and Handling Custom Exceptions in Ruby on Rails

  1. Suppose we decide to catch access to a forbidden part of our application by raising and rescuing AreaAccessDenied exception.
  2. For this, we can subclass StandardError to create a custom error that can be handled using rescue_action. Looks like only classes subclassing from StandardError get handled using rescue. Add the following line in a separate file or perhaps in application.rb if you don't mind living with it.
    class AreaAccessDenied subclass StandardError; end
    (replace subclass with a 'smaller than' symbol before use)
  3. We need to handle those exceptions for which we want to show the user some output, in rescue_action_in_public.
  4. Edit config/development.rb to mark all requests non-local. This is necessary for rescue_action_in_public to catch exceptions in development mode. (for a clear idea, look at rescue_action code; all non-local requests are sent to rescue_action_in_public and others are sent to rescue_action_locally). (Do NOT FORGET to restart your development server for the environment settings to take effect!) config.action_controller.consider_all_requests_local = false #true
  5. Edit application.rb (Notice the double-colon prefix in ActionController in RoutingError and UnknownAction)
    def rescue_action_in_public(e)
    case e
    when ActiveRecord::RecordNotFound, ::ActionController::RoutingError, ::ActionController::UnknownAction
    logger.error "Exception: #{e.class}: #{e.message}" render :text => "Ooops!"
    when AreaAccessDenied
    logger.error "Exception: #{e.class}: #{e.message}" head :forbidden # status 403
    else logger.error "Exception: #{e.class}: #{e.message}" render :file => "#{RAILS_ROOT}/public/500.html", :status => 500
    end
    end
  6. Using the above method alone doesn't cut it in development mode. We also need to redefine local_request in application.rb to always return false. This is because rescue_action decides based on the return value of method local_request? whether to handle the exception using rescue_action_in_public or rescue_action_locally. controllers/application.rb
    def local_request?
    false
    end
  7. I did not see 'uninitialized constant ActionWebService::Dispatcher::ActionController::RoutingError'. (what are you talking about?). But routing errors were not getting caught through my rescue_action_in_public method without a double-colon.