My favorites | Sign in
Logo
                
Search
for
Updated Dec 13, 2009 by lazy.gopher
Labels: Featured
SinatraReloaded  
Getting Started guide for Sinatra with focus on development environment

Introduction

This guide is based on GettingStarted guide, but extends it with templating changed to HAML and gem management changed to bundler. The Rack::Reloader is used for reloading development environment.

Local installation

It's important to understand that development environment for the appengine and the Jruby contain 3 different places of ruby gems:

System ruby gems installation

First, make sure that you have the latest version of gem installed

$ gem --version
1.3.5
$ # If you have the older version perform upgrade
$ gem update --system
$ # Install the required system gems
$ gem install --no-rdoc --no-ri google-appengine bundler rack

Application gems installation

Create the folder for application. In this guide it's assumed to be ~/guestbook/.

$ mkdir guestbook
$ cd guestbook

The original guide recommends using appcfg.rb gem for application gem installation, but will be using bundler. In order to manage gems one need to create the Gemfile control file. Ours will be pretty simple.

$ cat >Gemfile <<EOF

disable_system_gems
disable_rubygems
bundle_path ".gems/bundler_gems"

gem "sinatra"
gem "dm-core"
gem "dm-appengine"
gem "haml"

EOF

After the file is there, let's install gems

$ gem bundle

Note, this all done with you local MRI ruby. Now, you should have .gems folder in you application directory with all required gems and dependencies. As you can see, you don't need sinatra or haml installed in your system gems. It's bundled (frozen) in your application.

Creating application

First let's create the config.ru, which will configure application and reloading for development environment. The simple use Rack::Reloader will not work as we need to reload the Sinatra framework.

$ cat >config.ru <<EOF 
require 'appengine-rack'
require 'guestbook'

AppEngine::Rack.configure_app(:application => "guestbook", :version => 1)

configure :development do
  class Sinatra::Reloader < ::Rack::Reloader
    def safe_load(file, mtime, stderr)
      if File.expand_path(file) == File.expand_path(::Sinatra::Application.app_file)
        ::Sinatra::Application.reset!
        stderr.puts "#{self.class}: reseting routes"
      end
      super
    end
  end
  use Sinatra::Reloader
end

run Sinatra::Application
EOF

The reloader example, above, assumes that application is Sinatra traditional style application and all routes defined in the 'guestbook.rb' file, or main file of the application.

If application has a separate file for routes, for example 'route.rb', then ::Sinatra::Application.app_file should be replaced with './route.rb' or similar. What is meant here is that you need to do .reset! only in case when file with routes changes. If you routes scattered in different files you need to write more complex reloading strategy.

If you are using 'sinatra/base' instead of the traditional Sinatra application, you, probably, already found out, that you should take care about everything yourself. And the reloader is no exception. Everywhere, where ::Sinatra::Application mentioned, you need to replace it with your class. Assume following example:

require 'sinatra/base'
class Simple < Sinatra::Base
  get '/' do
     'Hello World!'
  end
end

then your reloader should do Simple.reset! for file with routes. And, of course, you need to change run Sinatra::Application to run Simple.new.

Now, let's get back and put the application itself

$ cat >guestbook.rb <<EOF 
require 'sinatra'
require 'dm-core'
require 'haml'

# Configure DataMapper to use the App Engine datastore 
DataMapper.setup(:default, "appengine://auto")

# Create your model class
class Shout
  include DataMapper::Resource

  property :id, Serial
  property :message, Text
end

# Set Haml output format and enable escapes
set :haml, {:format => :html5 , :escape_html => true }

# Main board
get '/' do
  # Just list all the shouts
  @shouts = Shout.all
  haml :index
end

post '/' do
  # Create a now shout and redirect back to the list
  shout = Shout.create(:message => params[:message])
  redirect '/'
end
EOF

Let's add some templates as well

$ mkdir views

First, the above-mentioned index.haml

$ cat >views/index.haml <<EOF
%h2 Messages
- for shout in @shouts
  %p<
    Someone wrote,
    %q= shout.message

%form{:method => :post}  
  %textarea{:name => :message}  
  %br/
  %input{:type => :submit, :method => :Shout}
EOF

And the layout for the application

$ cat >views/layout.haml <<EOF
!!!
%html
  %head
    %title Guestbook
  %body
    != yield
EOF

Install deployment gems and create local structure

First we need to install the jruby and appengine. Thanks to this project it's dead easy

$ # AppEngine gems version < 0.0.5 :
$ appcfg.rb gem bundle
$ # ppEngine gems version = 0.0.5 :
$ appcfg.rb bundle .

In case you changed the Gemfile to add new gems to your environment, just rerun bundler in your local and jruby environment. The appcfg.rb does not always correctly check what's deployed, it doesn't know that we are using bundler, so just delete gems.jar.

$ # Install gems in the application .gems folder
$ gem bundle
$ # Remove the deployment gems
$ rm WEB-INF/lib/gems.jar
$ # Package the deployment gems (appengine gems version < 0.0.5)
$ appcfg.rb gem bundle
$ # Package the deployment gems (appengine gems version = 0.0.5)
$ appcfg.rb bundle .

Running servers

Now, finally we are ready to run the development with Sinatra reloading.

$ dev_appserver.rb .

The Rack::Reloader uses mtime on your system, so if the reloader is not doing its job try touch guestbook.rb.

Enjoy!

all question and improvements are welcome in the google group


Comment by richard.ibarra, Nov 03, 2009

I tried to use your reloader but it fails because I have a diferent file for all my models. When I save any file in my application, the application starts sending a not found in every in route... any advice?

Comment by lazy.gopher, Nov 07, 2009

Richard, how do you include the code in your application? Reloader works only with "require" and does not with "load". Do you see in the log the "reloaded" message ?

Comment by khelllS, Nov 09, 2009

when running appcfg.rb bundle . i get:

/usr/lib/ruby/site_ruby/1.8/rubygems.rb:280:in `activate': can't activate bundler (= 0.6.0, runtime) for ["appengine-tools-0.0.5"], already activated bundler-0.7.0 for (Gem::LoadError?)

from /usr/lib/ruby/site_ruby/1.8/rubygems.rb:296:in `activate' from /usr/lib/ruby/site_ruby/1.8/rubygems.rb:295:in `each' from /usr/lib/ruby/site_ruby/1.8/rubygems.rb:295:in `activate' from /usr/lib/ruby/site_ruby/1.8/rubygems.rb:68:in `gem' from /usr/bin/appcfg.rb:18

Trying to overcome it usging JRuby:

jruby -S appcfg.rb bundle .

i get nothing and gems.jar is not even generated....

Comment by lazy.gopher, Nov 10, 2009

this is bug in current version - see bug 36. Uninstall bundler v 0.7.0 with your MRI ruby and install v 0.6.0

gem uninstall bundler

gem install bundler -v 0.6.0

or, alternatively, don't install bundler manually as mentioned above, then correct version will be installed with google-appengine gem.

Comment by tomgullo, Nov 18, 2009

I'm getting this error on Ubuntu. I think it's a user permission issue with the library. All my other ruby gems work fine, so it may be an issue with appengine-jruby and Ubuntu.

/usr/lib/ruby/1.8/rubygems/custom_require.rb:36:in `gem_original_require': no such file to load -- appengine-tools/dev_appserver (LoadError?)

from /usr/lib/ruby/1.8/rubygems/custom_require.rb:36:in `require' from /var/lib/gems/1.8/gems/appengine-tools-0.0.5/bin/dev_appserver.rb:17 from /var/lib/gems/1.8/bin/dev_appserver.rb:19:in `load' from /var/lib/gems/1.8/bin/dev_appserver.rb:19

Comment by pftg.sof, Nov 19, 2009

Also, bundler do not install gems for java platform. May be better install gems through old solutions while builder do not updated.

Comment by jSlim180, Dec 21, 2009

In the last section, under "Install deployment gems and create local structure", "appcfg.rb gem bundle" is deprecated. The new syntax is displayed, however, when that is used a classpath exception is thrown. This happened on Cygwin.

See: http://groups.google.com/group/appengine-jruby/browse_thread/thread/bae51f86fc5b5063


Sign in to add a comment