Monday May 04, 2009

Rails Conf 2009 - Day 1 Trip Report


Rails Conf 2009 started this morning. The first day consists of morning and afternoon tutorials.

I attended Nick Sieger's JRuby on Rails tutorial, the slides are available. A survey in the room showed:
  • 95% comfortable with Ruby/Rails
  • 80% have used JRuby
  • 10% use JRuby actively
Here are some of the key points highlighted in the tutorial:

Why JRuby ?
  • JRuby is "Less Bitter Java", after all Java is a great platform.
  • Concurrency (Native threading)
  • Reliability (well-behaved because of Hotspot compiler, no process monitoring, etc)
  • Encapsulation (take a Rails application, bundle it as a single deployable artifact that is fully contained)
  • Choice (Any Java application server, huge breadth of Java libraries, and can write thin Ruby wrappers around Java libraries)
Download JDK 5 minimum, JDK 6 preferred, MySQL 5.x, JRuby 1.2 (1.3.0 RC1 OK too), GlassFish v2.1 b60e

Common options
  • --server: Run with server VM, better performance
  • --headless: No UI
  • --properties: Show tweaks for compiler, JIT compiling,  thread pooling etc
  • -J<java-opt>: Pass any Java properties
  • -J-Xmx1G: Increase memory to 1G
Drawbacks: No fork(), No native extensions (for example ParseTree, EventMachine, RMagic cannot be used), No tty for subprocesses, Startup time slow for short scripts

Advantages: Improved versions of some Ruby APIs (tempfile, mutex, thread, timeout), 1.8 and 1.9 in a single install (jruby --1.9), Wrap Java libraries and APIs in Ruby

The slides have much more details in terms of deployment options (WAR-based, GlassFish Gem), and many other interesting details Scroll to slide #68 to understand all the guts of kenai.com - a real life application running using JRuby, Rails, and GlassFish.

The afternoon tutorial for me was A Hat Full of Tricks with Sinatra. The tutorial was completely code driven with no slides, just love that format!

The tutorial started with a brief introduction to Rack. A basic Rack application can be "config.ru" or "app.rb", lets start with "config.ru" Hello World:

run lambda { |env|
  [
    200,
    {
    'Content-Length' => '2',
    'Content-Type' => 'text/html',
    },
    ["hi"]
  ]
}

Run it as ...

~/samples/railsconf/sinatra/basic-rack >~/tools/jruby/bin/jruby -S rackup
[2009-05-04 13:40:18] INFO  WEBrick 1.3.1
[2009-05-04 13:40:18] INFO  ruby 1.8.6 (2009-03-16) [java]
[2009-05-04 13:40:18] INFO  WEBrick::HTTPServer#start: pid=90964 port=9292
127.0.0.1 - - [04/May/2009 13:40:27] "GET / HTTP/1.1" 200 2 0.0160
127.0.0.1 - - [04/May/2009 13:40:27] "GET /favicon.ico HTTP/1.1" 200 2 0.0060
127.0.0.1 - - [04/May/2009 13:40:30] "GET /favicon.ico HTTP/1.1" 200 2 0.0100

"config.ru" is the default Rackup script, otherwise need to specify the name. And now "app.rb" ..

App = lambda { |env|
  [
    200,
    {
    'Content-Length' => '2',
    'Content-Type' => 'text/html',
    },
    ["hi"]
  ]
}

And run it as ...

~/samples/railsconf/sinatra/basic-rack >~/tools/jruby/bin/jruby -S rackup app.rb
[2009-05-04 13:43:57] INFO  WEBrick 1.3.1
[2009-05-04 13:43:57] INFO  ruby 1.8.6 (2009-03-16) [java]
[2009-05-04 13:43:57] INFO  WEBrick::HTTPServer#start: pid=90990 port=9292
127.0.0.1 - - [04/May/2009 13:44:09] "GET / HTTP/1.1" 200 2 0.0110

In both cases, the application is accessible at "http://localhost:9292".

Change the basic "config.ru" to convert into a class as ...

class BasicRack
     def call(env)
      body = "Hello from a class"
      [
        200,
        {
        'Content-Length' => body.size.to_s,
        'Content-Type' => 'text/html',
        },
        [body]
      ]
    end
end

run BasicRack.new

and run the same way as earlier.

Change body to "env.inspect" to see an output as:



Sinatra allows reloading of application but that "feature" will be removed soon. Instead install shotgun (which does not work with JRuby yet!). Anyway, install the gem:

~/samples/railsconf/sinatra/basic-rack >~/tools/jruby/bin/jruby -S gem install shotgun
JRuby limited openssl loaded. gem install jruby-openssl for full support.
http://wiki.jruby.org/wiki/JRuby_Builtin_OpenSSL
Successfully installed configuration-0.0.5
Successfully installed launchy-0.3.3
Successfully installed shotgun-0.2
3 gems installed
Installing ri documentation for launchy-0.3.3...
Installing RDoc documentation for launchy-0.3.3...

And run as:

~/samples/railsconf/sinatra/basic-rack >~/tools/jruby/bin/jruby -J-Djruby.fork.enabled=true -S shotgun
[2009-05-04 13:55:46] INFO  WEBrick 1.3.1
[2009-05-04 13:55:46] INFO  ruby 1.8.6 (2009-03-16) [java]
== Shotgun starting Rack::Handler::WEBrick on localhost:9393
[2009-05-04 13:55:46] INFO  WEBrick::HTTPServer#start: pid=91089 port=9393

Process separate bodies depending upon the info:

class BasicRack
     def call(env)
      body = if env["PATH_INFO"] == "/foo"
        "in foo"
      else
       "in other"
      end
      [
        200,
        {
        'Content-Length' => body.size.to_s,
        'Content-Type' => 'text/html',
        },
        [body]
      ]
    end
end

run BasicRack.new

Accessing "http://localhost:9292/foo" shows "in foo" and accessing "http://localhost:9393" shows "in other".

Target application is the last application specified by "run".

Rack supports middleware which are like filters, they can applied before/after a message is processed.

Rack will initialize middleware at load, so hold on to that application as shown:

class BasicRackApp
     def call(env)
      body = "hello from app"
      [
        200,
        {
        'Content-Length' => body.size.to_s,
        'Content-Type' => 'text/html',
        },
        [body]
      ]
    end
end

class MyMiddleware
    def initialize(app)
        @app = app
    end
   
    def call(env)
        @app.call(env)
    end
end

use MyMiddleware

run BasicRackApp.new

@app.call calls the next middleware in the chain.

Rack comes with couple of standard middleware, e.g.:

use Rack::CommonLogger

Example of an after filter:

    def call(env)
        status, headers, body = @app.call(env)
        body.map! { |part| part.upcase}
        [status, headers, body]
    end

Lots of other filters available.

With a basic Rack understanding, lets build a Sinatra app:

require 'sinatra'

is the simplest Sinatra application. Save it in a file "basic-sinatra.rb" and run it as:

~/samples/railsconf/sinatra/basic-sinatra >~/tools/jruby/bin/jruby -rubygems basic-sinatra.rb
== Sinatra/0.9.1.1 has taken the stage on 4567 for development with backup from WEBrick
[2009-05-04 14:40:14] INFO  WEBrick 1.3.1
[2009-05-04 14:40:14] INFO  ruby 1.8.6 (2009-03-16) [java]
[2009-05-04 14:40:14] INFO  WEBrick::HTTPServer#start: pid=91396 port=4567

The application is now available at "http://localhost:4567". BTW, this app can easily be run using GlassFish Gem as explained  in TOTD #79. Add a simple GET method and "not_found" handler as:

require 'rubygems'
require 'sinatra'

not_found do
  'hi from other'
end

get '/foo' do
    'hi from foo'
end

Every time a request comes in, it builds a request context, instance evals lambda and finds the one that hits.

Sinatra takes care of status and headers, the application needs to process the body.

Another one ...

require 'rubygems'
require 'sinatra'

get '/env' do
    env.inspect
end

And it shows Rack environment hash at 'http://localhost:4567".

Another one ...

require 'rubygems'
require 'sinatra'

get '/' do
end

post '/' do
end 

put '/' do
end

delete '/' do
end

This adds 4 HTTP methods with different routes.

No explicit render method, e.g.

require 'rubygems'
require 'sinatra'

get '/' do
  content_type "application/json"
  { "foo" => "goo" }.to_json
end

No ".rhtml.erb" or ".json.erb", instead it's just ".erb". Add "views/index.erb" as:

<html>
  <body>
  Hello form Sinatra + ERB
  </body>
  </html>

And change GET method to:

require 'rubygems'
require 'sinatra'

get '/' do
  erb :index
end

And the application now uses ERB templating.

Using HAML templates is simple, change to:

require 'rubygems'
require 'sinatra'
require 'haml'

get '/' do
  haml :index
end

And add "views/index.haml" as:

%html
  %body
    %h1 Hello from HAML

And now the application is using HAML templates.

__END__ is the end of Ruby, can be anything after that and it'll not barf. Sinatra uses it for in file templates:

require 'rubygems'
require 'sinatra'
require 'haml'

get '/' do
  erb :index
end

use_in_file_templates!

__END__

@@ index

<html>
  <body>
  Hello form Sinatra + ERB in file
  </body>
  </html>

Start with in-file templates, and then move out to separate directory ("views") once grows big. But no syntax highlighting etc.

Add your custom template as:

require 'rubygems'
require 'sinatra'
require 'haml'

get '/' do
  erb :index
end

get '/foo' do
  erb :foo
end

use_in_file_templates!

__END__

@@ index

<html>
  <body>
  Hello form Sinatra + ERB in file
  </body>
  </html>
 
@@ foo
<h1>FOO!</h1>

With this file "http://localhost:4567/" uses ERB template, and "http://localhost:4567/foo" uses "foo" template.

Sinatra defines on Main. The before filters work before every single request, executed in the same context as lambda. Can be used if every request needs to do some setup.

Helpers can be defined as:

require 'rubygems'
require 'sinatra'
require 'haml'

helpers do
 
end

without defining on Main. Or ...

require 'rubygems'
require 'sinatra'
require 'haml'

module helpers
    def self.dosomething(arg)
    end
end

get '/' do
    Helpers.dosomething
end

Don't define something on Main, it's a bad practice.

Extension is a nice package that can be shared for other Sinatra developers to use, like Rails plugins but does not have boilerplate, much easier to do.

Rest of tutorial was quite fast paced so the code samples could not be captured. But there is boatload of information available at sinatrarb.com.

Check out the pictures from Day 1:


The evening concluded with dinner at Burger Bar at Mandalay Bay along with Project Kenai team.

And check the evolving album at:



On to GlassFish talk tomorrow, and running with @railsConfRunner in the morning before that :)

Technorati: conf railsconf lasvegas jruby rubyonrails sinatra glassfish

Tuesday Apr 28, 2009

TOTD #80: Sinatra CRUD application using Haml templates with JRuby and GlassFish Gem


TOTD #79 showed how to run a trivial Sinatra application using GlassFish Gem. Sinatra provides support for Haml, Erb, Builder, Sass, and Inline templates as described here. This TOTD will show how to get started with creating a Sinatra CRUD application using Haml templates.

Haml is based on one primary principle - Markup should be beautiful because beauty makes you faster.

Get started by installing the Haml gem as:

/tools/jruby-1.2.0 >./bin/jruby -S gem install haml --no-ri --no-rdoc
JRuby limited openssl loaded. gem install jruby-openssl for full support.
http://wiki.jruby.org/wiki/JRuby_Builtin_OpenSSL
Successfully installed haml-2.0.9
1 gem installed

And follow the tutorial, documentation, and reference page for more details.

Sinatra is ORM-agnostic and so any Ruby ORM framework such as ActiveRecord, DataMapper, Sequel, and others. DataMapper with JRuby requires work so this TOTD will show how to use ActiveRecord instead. There is sinatras-hat which allows to create RESTy CRUD apps with Sinatra. There probably are mutiple other ways to create this application but I prefer to understanding the wiring so this blog will use a bare minimal structure.

Anyway, lets get started!
  1. Create the database as:

    ~/tools/jruby/samples/sinatra-sample >mysql --user root
    Welcome to the MySQL monitor.  Commands end with ; or \\g.
    Your MySQL connection id is 664
    Server version: 5.1.30 MySQL Community Server (GPL)

    Type 'help;' or '\\h' for help. Type '\\c' to clear the buffer.

    mysql> create database hello_development;
    Query OK, 1 row affected (0.00 sec)

    mysql> use hello_development;
    Database changed
    mysql> CREATE TABLE `runners` (`id` int(11) DEFAULT NULL auto_increment PRIMARY KEY, `distance` float, `minutes` int(11), `created_at` datetime, `updated_at` datetime);             
    Query OK, 0 rows affected (0.06 sec)

  2. Update "hello.rb" from TOTD #79 such that it looks like:

    require 'rubygems'
    require 'sinatra'
    require 'activerecord'

    ## Setup

    ActiveRecord::Base.establish_connection(
      :adapter  => "jdbcmysql",
      :host     => "localhost",
      :username => "root",
      :password => "",
      :database => "hello_development"
    )

    ## Models

    class Runner < ActiveRecord::Base
    end

    ## Controller Actions

    get '/hi' do
      "Hello World!"
    end

    get '/' do
      @runner = Runner.find(:all)
      haml :index
    end

    get '/new' do
      haml :new
    end

    get '/:id' do
      @runner = Runner.find(params[:id])
      if (@runner)
        haml :show
      else
        redirect '/'
      end
    end

    post '/' do
      @runner = Runner.new(:distance => params[:distance], :minutes => params[:minutes])
      if @runner.save
        redirect "/#{@runner.id}"
      else
        redirect '/'
      end
    end

    Firstly, it pulls in the ActiveRecord dependency. Then "ActiveRecord::Base.establish_connection" is used to establish a connection with the previously created database. "Runner" is tagged as a model class by inheriting from "ActiveRecord::Base" and uses the default table name ("runners" in this case). Add four new methods:
    1. Three GET methods to show all the runners, a form to enter new data, and show a particular log entry. Each method requires a HAML template (will be created in next step) to render the information.
    2. One POST method to save the newly created log entry in the database.
  3. Create a new directory "views" and create the following files in that directory. Each file serves as a view and rendered from an action in "hello.rb".
    1. "index.haml": Show all the runners

      %h1 Listing all runners ...
      %table
        %tr
          %th Distance
          %th Minutes
        - @runner.each do |r|
          %tr
            %td= r.distance
            %td= r.minutes
      %br
      %a{:href=>"/new"}
        New Runner

    2. "new.haml": Enter a new entry

      %h1 Adding a new runner log ...
      %form{:method=>"post", :action=>"/"}
        Distance:
        %input{:type=>"text", :name=>"distance"}
        %br
        Minutes:
        %input{:type=>"text", :name=>"minutes"}
        %br
        %input{:type=>"submit", :value=>"Submit"}
        %br

    3. "show.haml": Show a particular log entry

      %h1 Showing a runner log ...
      Distance:
      = @runner.distance
      %br
      Minutes:
      = @runner.minutes
      %br
      %br
      %a{:href=>"/"}= "Show All!"

      The complete directory structure looks like:

      .
      ./hello.rb
      ./views
      ./views/index.haml
      ./views/new.haml
      ./views/show.haml
That's it, now run the application as:

~/tools/jruby/samples/sinatra-sample >../../bin/jruby -S glassfish
Starting GlassFish server at: 192.168.1.145:3000 in development environment...
Writing log messages to: /Users/arungupta/tools/jruby-1.2.0/samples/sinatra-sample/log/development.log.
Press Ctrl+C to stop.

The main page is available at "http://localhost:3000/" and looks like:



Clicking on "New Runner" gives ...



Enter the data, and click on "Submit" to show ...



Click on "Show All!" to see all the entries added so far ...



And after adding few entries the main page looks like ...



This application shows Create and Read from the CRUD, it's fairly easy to add Update and Delete functionality as well but that's an excercise left for the readers :-)

You'll hear all about it at Develop with Pleasure, Deploy with Fun: GlassFish and NetBeans for a Better Rails Experience at Rails Conf next week.

Technorati: totd glassfish jruby sinatra crud

Monday Apr 27, 2009

TOTD #79: Getting Started with Sinatra applications on JRuby and GlassFish Gem


Sinatra is a DSL for quickly creating web-applications in Ruby with minimal effort. Like Rails and Merb, Sinatra is not an MVC framework and basically follows a flat-file structure instead. The framework define conventions such as location of static files and views, bootstrap, dev/production/test environment variables, filters, helpers, TDD, and much more.  Read Getting Started for complete details. Even though Sinatra is not a MVC framework but sinatra-gen may be used to generate new Sinatra projects.

GlassFish Gem can easily run Rails, Merb, Sinatra, and any other Ruby framework applications based upon Rack. TOTD #70 shows how to run Rails applications and TOTD #53 shows to run Merb applications. This TOTD will explain how to run a trivial Sinatra application. A later blog will describe how to plug a generic Rack-based framework.

Lets see how to get started with Sinatra using JRuby and GlassFish gem.
  1. Install Sinatra gem as:

    ~/tools/jruby >./bin/jruby -S gem install sinatra
    JRuby limited openssl loaded. gem install jruby-openssl for full support.
    http://wiki.jruby.org/wiki/JRuby_Builtin_OpenSSL
    Successfully installed sinatra-0.9.1.1
    1 gem installed
    Installing ri documentation for sinatra-0.9.1.1...
    Installing RDoc documentation for sinatra-0.9.1.1...

  2. Create a directory "sinatra-sample", create a file "hello.rb" in that directory with the contents shown below:

    require 'rubygems'
    require 'sinatra'
    get '/hi' do
      "Hello World!"
    end

  3. Run your sample using GlassFish gem as:

    ~/tools/jruby/samples/sinatra-sample >../../bin/jruby -S glassfish
    Log file /Users/arungupta/tools/jruby-1.2.0/samples/sinatra-sample/log/development.log does not exist. Creating a new one...
    Starting GlassFish server at: 192.168.1.145:3000 in development environment...
    Writing log messages to: /Users/arungupta/tools/jruby-1.2.0/samples/sinatra-sample/log/development.log.
    Press Ctrl+C to stop.

    And then the output is available at "http://localhost:3000/hi" and looks like:


Neat and simple!

You'll hear all about it at Develop with Pleasure, Deploy with Fun: GlassFish and NetBeans for a Better Rails Experience at Rails Conf next week.

Here is the order in which I'll seek any help:
The next blog will show how to create a Sinatra CRUD application and run it using GlassFish.

Please leave suggestions on other TOTD (Tip Of The Day) that you'd like to see. A complete archive of all the tips is available here.
Technorati: totd glassfish jruby sinatra
About

profile image
Arun Gupta is a technology enthusiast, a passionate runner, author, and a community guy who works for Oracle Corp.


Java EE 7 Samples

Stay Connected

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today