JRuby + Rails = Awesome Java Web Framework! Nick Sieger nsieger@engineyard.com Jfokus 2011
Ruby: Developer Happiness =
“Ruby is the glue that doesn't set” – Dave Thomas http://pragdave.pragprog.com/pragdave/2006/06/glue_that_doesn.html
Ruby = innovation http://www.flickr.com/photos/vermininc/2777441779/
Testing RSpec http://rspec.info/ http://cukes.info/
Ruby Dynamic language of the cloud
Ruby: Dynamic, Object-Oriented http://www.flickr.com/photos/listenmissy/4869202176/
Duck-Typing def area(width = 10, height = 2 * width) width * height end p area # => 200 p area 5 # => 50 p area 5, 20 # => 100 p area "10", 4 # => ?
Duck-Typing p area "10", 4 # => "10101010" # From Ruby API docs: # String#*(num) # # Returns a new String containing num copies of # the receiver. # # "Ho! " * 3 #=> "Ho! Ho! Ho! " area true, false # => NoMethodError: undefined method `*' for # true:TrueClass
Flexible Syntax def set_options(env, opts) end set_options(:production, {"caching" => "on", "debug" => "false"}) set_options(:production, "caching" => "on", "debug" => "false") set_options :production, {"caching" => "on", "debug" => "false"} set_options :production, "caching" => "on", "debug" => "false"
Blocks list = [1, 2, 3, 4] list.each {|n| puts n } Ruby list.each do |n| puts n end List<Integer> list = Arrays.asList(1, 2, 3, 4); Java for (Integer n : list) { System.out.println(n); }
Blocks File.open(__FILE__) do |file| file.each_line do |line| Ruby puts line end end BufferedReader file = new BufferedReader(new FileReader("Blocks.java")); try { String line; while ((line = buf.readLine()) != null) { Java System.out.println(line); } } finally { file.close(); }
Open Classes msg = "Scramble this so you can't read it!" msg.rot13! # => NoMethodError: undefined method `rot13!' for # "Scramble this so you can't read it!":String
Open Classes class String def rot13! 0.upto(length - 1) do |i| case self[i] when ?a..?z self[i] = ?a + ((self[i] - ?a) + 13) % 26 when ?A..?Z self[i] = ?A + ((self[i] - ?A) + 13) % 26 end end self end end
Open Classes puts msg.rot13! # => "Fpenzoyr guvf fb lbh pna'g ernq vg!" puts msg.rot13! # => "Scramble this so you can't read it!"
Rails 3 Dynamic framework of the cloud
Opinionated Framework Request- Convention Defaults Place for based over with everything Configuration MVC Choices
Place for everything application code configuration & environments routes (URL structure) database migrations static assets (images, js, css) tests
Request-based MVC Request Routing Controller Model Action Action ActiveRecord Dispatch Controller View Database Response ActionView
Convention over Configuration URL GET /people resources :people Routing #=> people#index # app/controllers/people_controller.rb class PeopleController < ApplicationController def index Controller @people = Person.all end end # app/models/person.rb Model class Person < ActiveRecord::Base end View app/views/people/index.html.erb
Defaults with Choices Default Alternatives DataMapper, MongoMapper, ORM ActiveRecord Sequel, Any object with ActiveModel HAML, XML Builder, View Templates ERb Markaby, RedCloth (Textile), BlueCloth (Markdown) JavaScript Prototype jQuery Framework MySQL, PostgreSQL, Database SQLite3 Oracle, more via JRuby + JDBC Test Framework Test::Unit RSpec, Cucumber
Why Rails? COMPARING JVM WEB FRAMEWORKS Matt Raible http://raibledesigns.com Images by Stuck in Customs - http://www.flickr.com/photos/stuckincustoms © 2010, Raible Designs © 2010 Raible Designs http://j.mp/raible-jvm-frameworks
Why Rails? Consider... Information Available Project Development Books, skilled maturity Docs speed developers
Installing Rails INSTALL gem install rails
New Application $ rails new coolapp -m http://jruby.org create create README create Rakefile ...
Dependencies with Bundler $ cd coolapp $ bundle install Fetching source index for http://rubygems.org/ Using rake (0.8.7) Using abstract (1.0.0) ... Using rails (3.0.3) Your bundle is complete!
Generate Scaffolding $ rails generate scaffold person email:string password:string invoke active_record create db/migrate/20101214020707_create_people.rb create app/models/person.rb invoke test_unit create test/unit/person_test.rb create test/fixtures/people.yml route resources :people ...
Migrate Database $ rake db:migrate (in /Users/nicksieger/Projects/rails/coolapp) == CreatePeople: migrating =========================== -- create_table(:people) -> 0.0040s -> 0 rows == CreatePeople: migrated (0.0040s) ==================
Start Dev Server $ rails server => Booting WEBrick => Rails 3.0.3 application starting in development on http://0.0.0.0:3000 => Call with -d to detach => Ctrl-C to shutdown server [2010-12-13 20:11:28] INFO WEBrick 1.3.1 [2010-12-13 20:11:28] INFO ruby 1.8.7 (2010-12-10) [java] [2010-12-13 20:11:28] INFO WEBrick::HTTPServer#start: pid=21022 port=3000
First Page
Controller class PeopleController < ApplicationController # GET /people # GET /people.xml def index @people = Person.all respond_to do |format| format.html # index.html.erb format.xml { render :xml => @people } end end def show; end def new; end def edit; end def create; end def update; end def destroy; end end
Model class Person < ActiveRecord::Base end
Console $ rails console Loading development environment (Rails 3.0.3) irb(main):001:0> Person.create :email => "nsieger@engineyard.com", ... => #<Person id: 1, email: "nsieger@engineyard.com", ...>
Rails 3 and JRuby http://weblog.rubyonrails.org/2010/8/29/rails-3-0-it-s-done
http://ci.jruby.org/
JRuby Dynamic toolkit of the cloud
Getting JRuby http://jruby.org/download
JRuby via Maven Group ID: org.jruby Artifact IDs: jruby, jruby-complete
JRuby drive java • embed • compile
Drive Java synth = javax.sound.midi.MidiSystem.synthesizer Ruby synth.open channel = synth.channels[0] import javax.sound.midi.*; Java Synthesizer synth = MidiSystem.getSynthesizer(); synth.open(); final MidiChannel channel = synth.getChannels()[0];
Drive Java frame = javax.swing.JFrame.new "Music Frame" Ruby frame.set_size 600, 100 frame.layout = java.awt.FlowLayout.new import java.awt.*; Java JFrame frame = new JFrame("Music Frame"); frame.setSize(600, 100); frame.setLayout(new java.awt.FlowLayout());
Drive Java KEYS.each do |value, char| button = javax.swing.JButton.new char button.add_action_listener do |e| Ruby channel.note_on value, 99 end frame.add button end for (Iterator i = keys.entrySet().iterator(); i.hasNext(); ) { Map.Entry entry = (Map.Entry) i.next(); final Integer value = (Integer) entry.getKey(); String name = (String) entry.getValue(); JButton button = new JButton(name); Java button.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent e) { channel.noteOn(value, 99); } }); frame.add(button); }
MIDI Swing
Embed import org.jruby.embed.ScriptingContainer; public class EmbedJRuby { public static void main(String[] args) { ScriptingContainer container = new ScriptingContainer(); container.runScriptlet("puts 'Hello from Ruby'"); } }
http://wiki.jruby.org/ RedBridge
Compile # engine.rb require 'java' java_package 'demo' class Engine java_implements 'java.lang.Runnable' java_signature 'void run()' def run puts "The #{self.inspect} is running." end end
Compile // Starter.java import demo.Engine; public class Starter { public static void main(String[] args) { Runnable engine = new Engine(); engine.run(); } }
Compile $ jrubyc --javac engine.rb Starter.java Generating Java class Engine to demo/Engine.java javac -d . -cp jruby.jar:. demo/Engine.java Starter.java
Compile // Engine.java package demo; public class Engine implements Runnable { public void run() { ... } }
Compile $ java -cp jruby.jar:. Starter The #<Engine:0x59c958af> is running.
JRuby 1.6 Release soon! ruby 1.9.2 • c-ext • perf • dynopt • java
activerecord-jdbc ActiveRecord with JDBC databases INSTALL gem install activerecord-jdbc-adapter
Warbler INSTALL gem install warbler • Create a Java EE .war file from a Rails application • “Looks like Java” to the ops staff deploy Rails warble app.war to java app appserver
JRuby Deployment Ruby servers WAR files Cloud WEBrick GlassFish EY AppCloud Trinidad Tomcat AppEngine TorqueBox JBoss AWS EB
Enterprise Software Evolving and adapting long-running projects with legacy codebases
Sagrada Família, Barcelona, Spain
passion facade nativity facade scaffolded interior
Ryugyuong Hotel, 2005 North Korea 2010
seismic retrofit
Szkieletor, Kraków, Poland
Hybrid Rails/Java App ActionDispatch Rails ActionController/ActionView MVC ActiveModel Java Java JDBC SOAP Backend POJOs DataSource interface
https://github.com/nicksieger/ spring-petclinic
Metaphor Use Ruby, JRuby, and Rails to... Sagrada • Build new facade faster Familia • Scaffolding during refactoring Ryugyong • Revive a project with a new face Hotel Seismic • Reinforce business rules with a DSL retrofit • Harden security Szkieletor • Find novel uses for abandoned code
engineyard.com/services
Resources Resources for Getting Started with JRuby.org Ruby on Rails JRubyConf 2010 Rails for Videos Zombies
Nick Sieger nsieger@engineyard.com Slides: http://j.mp/sieger-jfokus
Images http://en.wikipedia.org/wiki/File:Sagrada_Familia_01.jpg http://www.flickr.com/photos/gonzalvo/4257293127/ http://www.flickr.com/photos/mgrenner57/263392884/ http://www.flickr.com/photos/koocheekoo/38407225/ http://www.flickr.com/photos/27649557@N07/5000528445/ http://www.flickr.com/photos/gpaumier/446059442/ http://www.flickr.com/photos/ilm/12831049/ http://en.wikipedia.org/wiki/File:Ryugyong_Hotel_-_May_2005.JPG http://en.wikipedia.org/wiki/File:Ryugyong_Hotel_October_2010.jpg http://en.wikipedia.org/wiki/File:ExteiorShearTruss.jpg http://en.wikipedia.org/wiki/File:ExtReenfDetail.jpg http://en.wikipedia.org/wiki/File:Szkieleteor_in_krakow.JPG http://www.flickr.com/photos/bazylek/3194294047/
JRuby + Rails = Awesome Java Web Framework at Jfokus 2011

JRuby + Rails = Awesome Java Web Framework at Jfokus 2011