SlideShare a Scribd company logo
1 of 14
Download to read offline
a jar-nORM-ous task
                   simple data-backed web app in a single jar

                                    Ian Dees
                          www.ian.dees.name ● @undees
                                JRubyConf 2009




Hi. I’m Ian, a JRuby user. This talk is about deploying a Ruby app in a single .jar file. When you’re
leaning on big frameworks like ActiveRecord, there are a few things to remember when you package
your app.
CRUD - CUD



Imagine you’re handed some database credentials, and you want to whip up a web front end in
Sinatra to browse around the data. Let’s say the schema is simple and fairly normalized: the sweet
spot for ActiveRecord.

Photo (cc) flickr.com/photos/treehouse1977/1425246464
define “success”
                                     images and stylesheets
                        A
                        B            single .jar

                        C            app.jar + jruby.jar

                        D            “Install Ruby....”

                        F            “Install....”


MRI and MySQL present some deployment challenges, even when everyone is on the same platform.
With JRuby, you can just hand out a couple of .jar files and have people run “java -jar
app.jar” (assuming everyone has a recent JVM). For bigger deployments, you can use Warbler to
make a .war file—but that requires configuring an application server: definitely overkill for small
projects.
1-2-3-4

             1. vendor everything
             2. extract jars from your binary gems
             3. let rawr do the heavy lifting
             4. combine your jar with jruby-complete




The “rawr” project provides some recipes for packing Ruby code into a .jar, with a small Java
bootstrapper. Here are the four basic steps involved.
1. vendor everything
   # This file is auto-generated;
   # use 'rake gems:vendor' to update it.

                                                              TE
   %w(activerecord-2.3.4

                                  L
       activerecord-jdbc-adapter-0.9.2                      E
       activesupport-2.3.4
                                SO
       activerecord-jdbcsqlite3-adapter-0.9.2

       haml-2.2.13

                      O
       jdbc-sqlite3-3.6.3.054
       rack-1.0.1
                              B
       sinatra-0.9.4).each do |dir|
     $: << File.dirname(__FILE__) + "/#{dir}/lib"
   end

Trying to disentangle the notion of a GEM_HOME from the weird file paths inside .jars is... nontrivial.
You can avoid the fuss by just putting your dependencies into the .jar as plain old Ruby libraries, a
bit like Rails’ vendored gems.
1 (revised). use Bundler
              bundle_path 'lib/ruby'
              bin_path    'lib/ruby/bin'

              gem     'activerecord'
              gem     'activerecord-jdbc-adapter'
              gem     'activerecord-jdbcsqlite3-adapter'
              gem     'activesupport'
              gem     'haml'
              gem     'jdbc-sqlite3'
              gem     'rack'
              gem     'sinatra'



The Bundler library can automate this step for you.
2. binary gems
  desc 'Extract jars from our gems into staging area'

  task :unjar => 'package/classes' do
    Dir['lib/ruby/**/*.jar'].each do |jar|
      path = File.expand_path(jar)

      Dir.chdir 'package/classes' do
        sh "jar -xf #{path}"
      end
    end
  end



Some gems, notably including ActiveRecord database adapters, contain .jars. Java can’t load these
from within another .jar. One workaround is to extract the .class files out of the sub-.jars for
inclusion in your project. (Another approach is to use something like JarJar.)
3. rawr
  # build_configuration.rb
  configuration do |c|
    # ...

     c.source_dirs = ['src', 'lib/ruby']
     c.source_exclude_filter = Dir['lib/ruby/**/*.java'].map do
       |file|

       Regexp.new(
         File.basename(file).gsub(".", ".") + "$")
     end

    # ...
  end



The default configuration of rawr gets confused by gems that include .java files. The easiest thing to
do is tell it to skip these files. It would be nice if rawr’s configuration file took a glob-style file
pattern, but for now, you can make do with an array of regular expressions.
4a. unjar
desc 'Extract app and jruby-complete 
for later combining'

task :stage => 'package/bigjar/contents' do
  Dir.chdir('package/bigjar/contents') do
    sh 'jar -xf ../../jar/appapp.jar'
    sh 'jar -xf ../../jar/lib/java/jruby-complete.jar'
  end
end




At this point, you’ve got your app.jar and a stock jruby-complete.jar. That fits the “C” grade on our
success criteria, so we could stop here. But it’s not too big of an extra step to combine those into a
single .jar. That should earn us at least a “B.” First, extract them into the same directory.
4b. manifest
desc 'Point the big jar manifest at our app'

task :manifest do
  manifest = IO.read 
    'package/bigjar/contents/META-INF/MANIFEST.MF'

   manifest.gsub! /^Main-Class: .+$/,
                  'Main-Class: org.rubyforge.rawr.Main'

  File.open('package/bigjar/manifest', 'w') do
    |f| f.write manifest
  end
end

Then, change the main class in the manifest from the JRuby interpreter to the rawr-generated
bootstrapper.
4c. combine

 desc 'Combine staged app and 
 jruby-complete files into one jar'

 task :package do
   Dir.chdir('package/bigjar') do
     sh 'jar -cfm appapp.jar manifest -C contents/ .'
   end
 end




Finally, combine them all into one jar.
5-6-7-8

             task :default => %w(
               gems:bundle
               gems:unjar
               rawr:jar
               app:stage app:manifest app:package)




Just to run this idea into the ground, here are those four basic steps again, expressed as Rake
tasks.
}            /undees/appapp



An example of this technique is available on GitHub and Bitbucket. The demo app is a silly web front
end around some iPhone app store data. To run it, you’ll need to download the app store CSV data
provided by busted-loop.com and run a couple of Rake tasks to populate your database first. See
the Rakefile for details.
overall grade:



                                    B-
A single .jar, with no embedded stylesheets or images, is nearly enough to earn a solid B according
to our success criteria. All that’s left to do is fix one minor quirk (the app currently has to be run
from a subfolder of the working directory).

More Related Content

What's hot

FOSDEM 2012: MySQL synchronous replication in practice with Galera
FOSDEM 2012: MySQL synchronous replication in practice with GaleraFOSDEM 2012: MySQL synchronous replication in practice with Galera
FOSDEM 2012: MySQL synchronous replication in practice with Galera
FromDual GmbH
 
Ecos基础应用介绍
Ecos基础应用介绍Ecos基础应用介绍
Ecos基础应用介绍
wanglei999
 
RestfulX “The RESTful Way to develop Adobe Flex and AIR applications”
RestfulX “The RESTful Way to develop Adobe Flex and AIR applications”RestfulX “The RESTful Way to develop Adobe Flex and AIR applications”
RestfulX “The RESTful Way to develop Adobe Flex and AIR applications”
elliando dias
 
Perl Stored Procedures for MySQL (2009)
Perl Stored Procedures for MySQL (2009)Perl Stored Procedures for MySQL (2009)
Perl Stored Procedures for MySQL (2009)
Antony T Curtis
 
Harmonious Development: Standardizing The Deployment Process via Vagrant and ...
Harmonious Development: Standardizing The Deployment Process via Vagrant and ...Harmonious Development: Standardizing The Deployment Process via Vagrant and ...
Harmonious Development: Standardizing The Deployment Process via Vagrant and ...
Acquia
 
Apache hadoop 2_installation
Apache hadoop 2_installationApache hadoop 2_installation
Apache hadoop 2_installation
sushantbit04
 
Macruby& Hotcocoa presentation by Rich Kilmer
Macruby& Hotcocoa presentation by Rich KilmerMacruby& Hotcocoa presentation by Rich Kilmer
Macruby& Hotcocoa presentation by Rich Kilmer
Matt Aimonetti
 

What's hot (20)

[Spring Camp 2013] Java Configuration 없인 못살아!
[Spring Camp 2013] Java Configuration 없인 못살아![Spring Camp 2013] Java Configuration 없인 못살아!
[Spring Camp 2013] Java Configuration 없인 못살아!
 
FOSDEM 2012: MySQL synchronous replication in practice with Galera
FOSDEM 2012: MySQL synchronous replication in practice with GaleraFOSDEM 2012: MySQL synchronous replication in practice with Galera
FOSDEM 2012: MySQL synchronous replication in practice with Galera
 
Ecos基础应用介绍
Ecos基础应用介绍Ecos基础应用介绍
Ecos基础应用介绍
 
RestfulX “The RESTful Way to develop Adobe Flex and AIR applications”
RestfulX “The RESTful Way to develop Adobe Flex and AIR applications”RestfulX “The RESTful Way to develop Adobe Flex and AIR applications”
RestfulX “The RESTful Way to develop Adobe Flex and AIR applications”
 
Drupal Pilipinas Apprentice: LAMP Administration, CSS, and Vagrant
Drupal Pilipinas Apprentice: LAMP Administration, CSS, and VagrantDrupal Pilipinas Apprentice: LAMP Administration, CSS, and Vagrant
Drupal Pilipinas Apprentice: LAMP Administration, CSS, and Vagrant
 
JRuby @ Boulder Ruby
JRuby @ Boulder RubyJRuby @ Boulder Ruby
JRuby @ Boulder Ruby
 
Puppet Camp Boston 2014: Greenfield Puppet: Getting it right from the start (...
Puppet Camp Boston 2014: Greenfield Puppet: Getting it right from the start (...Puppet Camp Boston 2014: Greenfield Puppet: Getting it right from the start (...
Puppet Camp Boston 2014: Greenfield Puppet: Getting it right from the start (...
 
RubyEnRails2007 - Dr Nic Williams - DIY Syntax
RubyEnRails2007 - Dr Nic Williams - DIY SyntaxRubyEnRails2007 - Dr Nic Williams - DIY Syntax
RubyEnRails2007 - Dr Nic Williams - DIY Syntax
 
RubyEnRails2007 - Dr Nic Williams - Keynote
RubyEnRails2007 - Dr Nic Williams - KeynoteRubyEnRails2007 - Dr Nic Williams - Keynote
RubyEnRails2007 - Dr Nic Williams - Keynote
 
RubyGems 3 & 4
RubyGems 3 & 4RubyGems 3 & 4
RubyGems 3 & 4
 
MySQLドライバの改良と軽量O/Rマッパーの紹介
MySQLドライバの改良と軽量O/Rマッパーの紹介MySQLドライバの改良と軽量O/Rマッパーの紹介
MySQLドライバの改良と軽量O/Rマッパーの紹介
 
Practical Testing of Ruby Core
Practical Testing of Ruby CorePractical Testing of Ruby Core
Practical Testing of Ruby Core
 
High Performance tDiary
High Performance tDiaryHigh Performance tDiary
High Performance tDiary
 
Perl Stored Procedures for MySQL (2009)
Perl Stored Procedures for MySQL (2009)Perl Stored Procedures for MySQL (2009)
Perl Stored Procedures for MySQL (2009)
 
RubyGems 3 & 4
RubyGems 3 & 4RubyGems 3 & 4
RubyGems 3 & 4
 
Harmonious Development: Standardizing The Deployment Process via Vagrant and ...
Harmonious Development: Standardizing The Deployment Process via Vagrant and ...Harmonious Development: Standardizing The Deployment Process via Vagrant and ...
Harmonious Development: Standardizing The Deployment Process via Vagrant and ...
 
Apache hadoop 2_installation
Apache hadoop 2_installationApache hadoop 2_installation
Apache hadoop 2_installation
 
Herd your chickens: Ansible for DB2 configuration management
Herd your chickens: Ansible for DB2 configuration managementHerd your chickens: Ansible for DB2 configuration management
Herd your chickens: Ansible for DB2 configuration management
 
Php on Windows
Php on WindowsPhp on Windows
Php on Windows
 
Macruby& Hotcocoa presentation by Rich Kilmer
Macruby& Hotcocoa presentation by Rich KilmerMacruby& Hotcocoa presentation by Rich Kilmer
Macruby& Hotcocoa presentation by Rich Kilmer
 

Viewers also liked

Write Your Own JVM Compiler
Write Your Own JVM CompilerWrite Your Own JVM Compiler
Write Your Own JVM Compiler
Erin Dees
 

Viewers also liked (8)

A World of Playfulness
A World of PlayfulnessA World of Playfulness
A World of Playfulness
 
We are playful
We are playfulWe are playful
We are playful
 
Playfulness at Work
Playfulness at WorkPlayfulness at Work
Playfulness at Work
 
Logic Lessons That Last Generations
Logic Lessons That Last GenerationsLogic Lessons That Last Generations
Logic Lessons That Last Generations
 
JRuby, Not Just For Hard-Headed Pragmatists Anymore
JRuby, Not Just For Hard-Headed Pragmatists AnymoreJRuby, Not Just For Hard-Headed Pragmatists Anymore
JRuby, Not Just For Hard-Headed Pragmatists Anymore
 
Your Own Metric System
Your Own Metric SystemYour Own Metric System
Your Own Metric System
 
Thnad's Revenge
Thnad's RevengeThnad's Revenge
Thnad's Revenge
 
Write Your Own JVM Compiler
Write Your Own JVM CompilerWrite Your Own JVM Compiler
Write Your Own JVM Compiler
 

Similar to A jar-nORM-ous Task

JRuby on Rails Deployment: What They Didn't Tell You
JRuby on Rails Deployment: What They Didn't Tell YouJRuby on Rails Deployment: What They Didn't Tell You
JRuby on Rails Deployment: What They Didn't Tell You
elliando dias
 
JRuby + Rails = Awesome Java Web Framework at Jfokus 2011
JRuby + Rails = Awesome Java Web Framework at Jfokus 2011JRuby + Rails = Awesome Java Web Framework at Jfokus 2011
JRuby + Rails = Awesome Java Web Framework at Jfokus 2011
Nick Sieger
 
Jruby synergy-of-ruby-and-java
Jruby synergy-of-ruby-and-javaJruby synergy-of-ruby-and-java
Jruby synergy-of-ruby-and-java
Keith Bennett
 
把鐵路開進視窗裡
把鐵路開進視窗裡把鐵路開進視窗裡
把鐵路開進視窗裡
Wei Jen Lu
 

Similar to A jar-nORM-ous Task (20)

Buildr In Action @devoxx france 2012
Buildr In Action @devoxx france 2012Buildr In Action @devoxx france 2012
Buildr In Action @devoxx france 2012
 
Gradle talk, Javarsovia 2010
Gradle talk, Javarsovia 2010Gradle talk, Javarsovia 2010
Gradle talk, Javarsovia 2010
 
JRuby on Rails Deployment: What They Didn't Tell You
JRuby on Rails Deployment: What They Didn't Tell YouJRuby on Rails Deployment: What They Didn't Tell You
JRuby on Rails Deployment: What They Didn't Tell You
 
Crank Up Your Apps With TorqueBox
Crank Up Your Apps With TorqueBoxCrank Up Your Apps With TorqueBox
Crank Up Your Apps With TorqueBox
 
Jaoo Michael Neale 09
Jaoo Michael Neale 09Jaoo Michael Neale 09
Jaoo Michael Neale 09
 
Ruby on Rails survival guide of an aged Java developer
Ruby on Rails survival guide of an aged Java developerRuby on Rails survival guide of an aged Java developer
Ruby on Rails survival guide of an aged Java developer
 
JRuby + Rails = Awesome Java Web Framework at Jfokus 2011
JRuby + Rails = Awesome Java Web Framework at Jfokus 2011JRuby + Rails = Awesome Java Web Framework at Jfokus 2011
JRuby + Rails = Awesome Java Web Framework at Jfokus 2011
 
Practical JRuby
Practical JRubyPractical JRuby
Practical JRuby
 
Практики применения JRuby
Практики применения JRubyПрактики применения JRuby
Практики применения JRuby
 
Leaner microservices with Java 10
Leaner microservices with Java 10Leaner microservices with Java 10
Leaner microservices with Java 10
 
The Future of Dependency Management for Ruby
The Future of Dependency Management for RubyThe Future of Dependency Management for Ruby
The Future of Dependency Management for Ruby
 
JRuby, Ruby, Rails and You on the Cloud
JRuby, Ruby, Rails and You on the CloudJRuby, Ruby, Rails and You on the Cloud
JRuby, Ruby, Rails and You on the Cloud
 
Jruby synergy-of-ruby-and-java
Jruby synergy-of-ruby-and-javaJruby synergy-of-ruby-and-java
Jruby synergy-of-ruby-and-java
 
Monkeybars in the Manor
Monkeybars in the ManorMonkeybars in the Manor
Monkeybars in the Manor
 
把鐵路開進視窗裡
把鐵路開進視窗裡把鐵路開進視窗裡
把鐵路開進視窗裡
 
Getting Started with Rails on GlassFish (Hands-on Lab) - Spark IT 2010
Getting Started with Rails on GlassFish (Hands-on Lab) - Spark IT 2010Getting Started with Rails on GlassFish (Hands-on Lab) - Spark IT 2010
Getting Started with Rails on GlassFish (Hands-on Lab) - Spark IT 2010
 
Gradle: The Build system you have been waiting for
Gradle: The Build system you have been waiting forGradle: The Build system you have been waiting for
Gradle: The Build system you have been waiting for
 
PuppetConf 2016: The Challenges with Container Configuration – David Lutterko...
PuppetConf 2016: The Challenges with Container Configuration – David Lutterko...PuppetConf 2016: The Challenges with Container Configuration – David Lutterko...
PuppetConf 2016: The Challenges with Container Configuration – David Lutterko...
 
Challenges of container configuration
Challenges of container configurationChallenges of container configuration
Challenges of container configuration
 
How to build your query engine in spark
How to build your query engine in sparkHow to build your query engine in spark
How to build your query engine in spark
 

Recently uploaded

Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
panagenda
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(☎️+971_581248768%)**%*]'#abortion pills for sale in dubai@
 

Recently uploaded (20)

How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
Manulife - Insurer Innovation Award 2024
Manulife - Insurer Innovation Award 2024Manulife - Insurer Innovation Award 2024
Manulife - Insurer Innovation Award 2024
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 
HTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesHTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation Strategies
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live StreamsTop 5 Benefits OF Using Muvi Live Paywall For Live Streams
Top 5 Benefits OF Using Muvi Live Paywall For Live Streams
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 

A jar-nORM-ous Task

  • 1. a jar-nORM-ous task simple data-backed web app in a single jar Ian Dees www.ian.dees.name ● @undees JRubyConf 2009 Hi. I’m Ian, a JRuby user. This talk is about deploying a Ruby app in a single .jar file. When you’re leaning on big frameworks like ActiveRecord, there are a few things to remember when you package your app.
  • 2. CRUD - CUD Imagine you’re handed some database credentials, and you want to whip up a web front end in Sinatra to browse around the data. Let’s say the schema is simple and fairly normalized: the sweet spot for ActiveRecord. Photo (cc) flickr.com/photos/treehouse1977/1425246464
  • 3. define “success” images and stylesheets A B single .jar C app.jar + jruby.jar D “Install Ruby....” F “Install....” MRI and MySQL present some deployment challenges, even when everyone is on the same platform. With JRuby, you can just hand out a couple of .jar files and have people run “java -jar app.jar” (assuming everyone has a recent JVM). For bigger deployments, you can use Warbler to make a .war file—but that requires configuring an application server: definitely overkill for small projects.
  • 4. 1-2-3-4 1. vendor everything 2. extract jars from your binary gems 3. let rawr do the heavy lifting 4. combine your jar with jruby-complete The “rawr” project provides some recipes for packing Ruby code into a .jar, with a small Java bootstrapper. Here are the four basic steps involved.
  • 5. 1. vendor everything # This file is auto-generated; # use 'rake gems:vendor' to update it. TE %w(activerecord-2.3.4 L activerecord-jdbc-adapter-0.9.2 E activesupport-2.3.4 SO activerecord-jdbcsqlite3-adapter-0.9.2 haml-2.2.13 O jdbc-sqlite3-3.6.3.054 rack-1.0.1 B sinatra-0.9.4).each do |dir| $: << File.dirname(__FILE__) + "/#{dir}/lib" end Trying to disentangle the notion of a GEM_HOME from the weird file paths inside .jars is... nontrivial. You can avoid the fuss by just putting your dependencies into the .jar as plain old Ruby libraries, a bit like Rails’ vendored gems.
  • 6. 1 (revised). use Bundler bundle_path 'lib/ruby' bin_path 'lib/ruby/bin' gem 'activerecord' gem 'activerecord-jdbc-adapter' gem 'activerecord-jdbcsqlite3-adapter' gem 'activesupport' gem 'haml' gem 'jdbc-sqlite3' gem 'rack' gem 'sinatra' The Bundler library can automate this step for you.
  • 7. 2. binary gems desc 'Extract jars from our gems into staging area' task :unjar => 'package/classes' do Dir['lib/ruby/**/*.jar'].each do |jar| path = File.expand_path(jar) Dir.chdir 'package/classes' do sh "jar -xf #{path}" end end end Some gems, notably including ActiveRecord database adapters, contain .jars. Java can’t load these from within another .jar. One workaround is to extract the .class files out of the sub-.jars for inclusion in your project. (Another approach is to use something like JarJar.)
  • 8. 3. rawr # build_configuration.rb configuration do |c| # ... c.source_dirs = ['src', 'lib/ruby'] c.source_exclude_filter = Dir['lib/ruby/**/*.java'].map do |file| Regexp.new( File.basename(file).gsub(".", ".") + "$") end # ... end The default configuration of rawr gets confused by gems that include .java files. The easiest thing to do is tell it to skip these files. It would be nice if rawr’s configuration file took a glob-style file pattern, but for now, you can make do with an array of regular expressions.
  • 9. 4a. unjar desc 'Extract app and jruby-complete for later combining' task :stage => 'package/bigjar/contents' do Dir.chdir('package/bigjar/contents') do sh 'jar -xf ../../jar/appapp.jar' sh 'jar -xf ../../jar/lib/java/jruby-complete.jar' end end At this point, you’ve got your app.jar and a stock jruby-complete.jar. That fits the “C” grade on our success criteria, so we could stop here. But it’s not too big of an extra step to combine those into a single .jar. That should earn us at least a “B.” First, extract them into the same directory.
  • 10. 4b. manifest desc 'Point the big jar manifest at our app' task :manifest do manifest = IO.read 'package/bigjar/contents/META-INF/MANIFEST.MF' manifest.gsub! /^Main-Class: .+$/, 'Main-Class: org.rubyforge.rawr.Main' File.open('package/bigjar/manifest', 'w') do |f| f.write manifest end end Then, change the main class in the manifest from the JRuby interpreter to the rawr-generated bootstrapper.
  • 11. 4c. combine desc 'Combine staged app and jruby-complete files into one jar' task :package do Dir.chdir('package/bigjar') do sh 'jar -cfm appapp.jar manifest -C contents/ .' end end Finally, combine them all into one jar.
  • 12. 5-6-7-8 task :default => %w( gems:bundle gems:unjar rawr:jar app:stage app:manifest app:package) Just to run this idea into the ground, here are those four basic steps again, expressed as Rake tasks.
  • 13. } /undees/appapp An example of this technique is available on GitHub and Bitbucket. The demo app is a silly web front end around some iPhone app store data. To run it, you’ll need to download the app store CSV data provided by busted-loop.com and run a couple of Rake tasks to populate your database first. See the Rakefile for details.
  • 14. overall grade: B- A single .jar, with no embedded stylesheets or images, is nearly enough to earn a solid B according to our success criteria. All that’s left to do is fix one minor quirk (the app currently has to be run from a subfolder of the working directory).