The Enterprise Strikes Back

Post on 27-Jan-2015

115 views 1 download

Tags:

description

Mostly the same talk as my earlier version with the same title for SDEC11, but this one was updated a little bit for Prairie Dev Con in November 2011.

Transcript of The Enterprise Strikes Back

The EnterpriseStrikes Back

The EnterpriseStrikes Back

BurkeLibbey

StefanPenner

Wednesday, 23 November, 11

BurkeLibbey

StefanPenner

Wednesday, 23 November, 11

Burke Libbey@burkelibbey

Stefan Penner@stefanpenner

Wednesday, 23 November, 11

Burke Libbey@burkelibbey

Stefan Penner@stefanpenner

Wednesday, 23 November, 11

Burke Libbey@burkelibbey

Stefan Penner@stefanpenner

Wednesday, 23 November, 11

Overview• Core concepts

• Ruby on the JVM (and the CLR, sort of)

• Data

• Integration

• Deployment

• Sysadmin

• The Cloud

Wednesday, 23 November, 11

Ruby’s weaknesses

• Poor performance

• Type system sometimes causes headaches in large codebases

• No compiler to catch certain bugsBut your tests catch these, right?

Wednesday, 23 November, 11

Why ruby is slow

• Late method lookup

• Lots of context tracking to allow for eval

• Stop-the-world Mark & Sweep GC

• Runtime modification of code

Wednesday, 23 November, 11

Ruby’s strengths

• Developing working code quickly

• Reducing incidental complexityEspecially when best practices are adhered to

• Ease of developing powerful libraries and succinct DSLs

Wednesday, 23 November, 11

Uniform Access Principle

All services o!ered by a module should be available through a uniform notation, which does not betray whether they are implemented through storage or through computation.

- Bertrand Meyer

Wednesday, 23 November, 11

Two Rules

• Everything is an object

• Objects expose methods and only methods

Wednesday, 23 November, 11

Wednesday, 23 November, 11

</background>

Wednesday, 23 November, 11

Ruby Java

+ Productive- Scary

+ Not Scary- Less productive

Wednesday, 23 November, 11

Ruby in Java

+ Not Scary+ Productive

Wednesday, 23 November, 11

• Full ruby implementation on the JVM

• Access to both ruby and Java libraries

• Can be deployed to existing Java infrastructure

Wednesday, 23 November, 11

The many flavours of ruby

• MRI (and YARV)

• JRuby

• IronRuby

• MacRuby

• Rubinius

• ...and several more...

Wednesday, 23 November, 11

java.lang.System.out.println("Hello World")

Wednesday, 23 November, 11

The Magic Sauce

require 'java'(contains up to 30% midichlorians)

Wednesday, 23 November, 11

Calling Java from ruby

f = javax.swing.JFrame.newf.getContentPane.add(javax.swing.JLabel.new("Hello World"))close_operation = javax.swing.JFrame::EXIT_ON_CLOSEf.setDefaultCloseOperation(close_operation)f.packf.setVisible(true)

Wednesday, 23 November, 11

Impedance Mismatch

f = javax.swing.JFrame.new

close_operation = javax.swing.JFrame::EXIT_ON_CLOSEf.setDefaultCloseOperation(close_operation)f.packf.setVisible(true)

Let’s break this down.

f.getContentPane.add(javax.swing.JLabel.new("Hello World"))

Wednesday, 23 November, 11

Impedance Mismatch

f = javax.swing.JFrame.newf.getContentPane.add(javax.swing.JLabel.new("Hello World"))close_operation = javax.swing.JFrame::EXIT_ON_CLOSEf.setDefaultCloseOperation(close_operation)f.packf.setVisible(true)

getContentPane

“Getters” and “Setters” are non-idiomatic in ruby.

snake_case is generally preferred to CamelCase

Wednesday, 23 November, 11

Impedance Mismatch

f = javax.swing.JFrame.newf. .add(javax.swing.JLabel.new("Hello World"))close_operation = javax.swing.JFrame::EXIT_ON_CLOSEf.setDefaultCloseOperation(close_operation)f.packf.setVisible(true)

content_pane

JavaBean properties can be accessed like this:

“get” disappears, CamelCase changes to snake_case(UA P in action!)

Wednesday, 23 November, 11

Impedance Mismatch

f = javax.swing.JFrame.newf. .add(javax.swing.JLabel.new("Hello World"))close_operation = javax.swing.JFrame::EXIT_ON_CLOSEf. close_operationf.packf. true

content_pane

default_close_operation =

visible =

“set” is replaced by “=”

“setDefaultCloseOperation(x)”becomes

“default_close_operation = x”

Wednesday, 23 November, 11

Ugliness Abounds

f = javax.swing.JFrame.newf.content_pane.add(javax.swing.JLabel.new("Hello World"))close_operation = javax.swing.JFrame::EXIT_ON_CLOSEf.default_close_operation = close_operationf.packf.visible = true

Namespaces everywhere!

javax.swing. javax.swing. javax.swing.

Wednesday, 23 November, 11

Ugliness Abounds

f = JFrame.newf.content_pane.add(JLabel.new("Hello World"))close_operation = JFrame::EXIT_ON_CLOSEf.default_close_operation = close_operationf.packf.visible = true

java_import adds classes to a ruby context

java_import 'javax.swing.JFrame'java_import 'javax.swing.JLabel' JFrame.new JLabel.new JFrame::EXIT_ON_CLOSE

Wednesday, 23 November, 11

100% Rubified™

java_import 'javax.swing.JFrame'java_import 'javax.swing.JLabel'

JFrame.new.tap do |f| f.content_pane.add JLabel.new("Hello World") f.default_close_operation = JFrame::EXIT_ON_CLOSE f.pack f.visible = trueend

Wednesday, 23 November, 11

Method Rubification™

• In general, CamelCase Java methods can optionally be transliterated to snake case.

Wednesday, 23 November, 11

Method Rubification™

JavaRuby

System.currentTimeMillis()

System.current_time_millis

Wednesday, 23 November, 11

Method Rubification™

JavaRuby

person.getName()

person.name

Seriously though, not actually trademarked.

Wednesday, 23 November, 11

Method Rubification™

JavaRuby

person.setAge(42)

person.age = 42

Someone shouldget on that.

Wednesday, 23 November, 11

Method Rubification™

Java person.isJedi()

person.jedi?Ruby

FALSE! TRUE!

Wednesday, 23 November, 11

Is ruby an acceptable Java?

• Most Java code can literally be written as ruby with few high-level changes.

• A more relaxed type system and syntax often has productivity benefits.

(http://bit.ly/pfNluA)

Wednesday, 23 November, 11

Exhibit A// interfaces = HashMap{ label => NetworkInterface }Collection c = interfaces.values();Iterator itr = c.iterator();

while(itr.hasNext()) { NetworkInterface interface = itr.next(); if (interface.isLoopback()) { return interface.getDisplayName(); }}

# interfaces = {label => NetworkInterface}interfaces.values.find(&:loopback?).display_name

VS.

Java

Ruby

Wednesday, 23 November, 11

Exhibit A// interfaces = HashMap{ label => NetworkInterface }Collection c = interfaces.values();Iterator itr = c.iterator();

while(itr.hasNext()) { NetworkInterface interface = itr.next(); if (interface.isLoopback()) { return interface.getDisplayName(); }}

# interfaces = {label => NetworkInterface}interfaces.values.find(&:loopback?).display_name

VS.

Java

Ruby 1 2 3 4 5 6 7

Only 7 characters of syntactic support!

Wednesday, 23 November, 11

“Are you suggesting I write all my Java in ruby?!”

• Not really...

• JRuby is much slower than Java (doesn’t matter as often as you’d think)

• Ruby’s added expressiveness makes it easier to shoot yourself in the foot.(or, in fact, to lazily clone an infinite number of your feet every planck length between your gun and your target)

Wednesday, 23 November, 11

• ...but maybe sometimes.

• One possibility:

• Encode high-level logic in expressive and concise ruby code

• Supporting code in fast, safe Java

• Mix and match as appropriate

“Are you suggesting I write all my Java in ruby?!”

Wednesday, 23 November, 11

Ruby as Glue

• Ruby is great for:

• wiring existing codebases together

• other miscellaneous tasks

Wednesday, 23 November, 11

PerlThe original

“Swiss Army Knife”

Wednesday, 23 November, 11

Perl• “glue”

• “duct tape”

• “swiss army knife”

Pretty much mean the same thing.

Wednesday, 23 November, 11

Perl Ruby

Wednesday, 23 November, 11

Subcategories of “Glue”

• Wiring stu! together

• Sysadmin tasks

Wednesday, 23 November, 11

Wiring stu! together

• There’s a lot we could cover here, but:

Wednesday, 23 November, 11

Nokogiri

• An extremely user-friendly XML library

• fast! (wraps libxml2)

Wednesday, 23 November, 11

Nokogiri

require 'open-uri'require 'nokogiri'html = open("http://slashdot.org").readdoc = Nokogiri::XML(html)(doc/".story a").each do |link| puts "#{link.text} (#{link.attr('href')})"end

# Britain's Broadband Censors: a Bunch of Students (//yro.slashdot...# ...

Wednesday, 23 November, 11

Don’t do this.

(Nokogiri::XML(open("http://slashdot.org").read)/".story a").each{|a|puts "#{a.text} (#{a.attr("href")})"}

Wednesday, 23 November, 11

Sysadmin with Ruby

• Nice system APIs(quite similar to perl’s)

• System provisioning libraries/DSLs

Wednesday, 23 November, 11

Provisioning systems

• Puppet

• Chef

• Vagrant

Wednesday, 23 November, 11

Puppet

• Describe system, and puppet sets it up

• Nontrivial, but the general idea is:

• I want mysql

• I want nginx < 0.8

• I want this cron job: “....”

• Go.

http://puppetlabs.com/

Wednesday, 23 November, 11

Puppetclass postgres-server { package { postgresql-server: ensure => latest } group { postgres: gid => 26 } user { postgres: comment => "PostgreSQL Server", uid => 26, gid => 26, home => "/var/lib/pgsql", shell => "/bin/bash" } service { postgresql: running => true, pattern => "/usr/bin/postmaster", require => package["postgresql-server"] }}

Wednesday, 23 November, 11

Puppet

• Fantastic for defining a reproducible production environment

Wednesday, 23 November, 11

Chef

• Same idea as puppet

• Somewhat less powerful

• Slightly more approachable to most ruby developers

http://www.opscode.com/chef/

Wednesday, 23 November, 11

Chefpackage "sudo" do action :upgradeend

template "/etc/sudoers" do source "sudoers.erb" mode 0440 owner "root" group "root" variables( :sudoers_groups => node['authorization']['sudo']['groups'], :sudoers_users => node['authorization']['sudo']['users'], :passwordless => node['authorization']['sudo']['passwordless'] )end

Wednesday, 23 November, 11

Puppet Chef

Configuration Language custom ruby

Targeted at production production

“Powerfulness” Lots Mostly lots

Verdict Good Di!erent

Written in ruby ruby

Wednesday, 23 November, 11

Vagrant

• Uses puppet and/or chef

• End product is a virtual machine for development use

• Consistent system for all developers, per project

Wednesday, 23 November, 11

Vagrant

• If you use puppet or chef, vagrant lets you test production locally

Wednesday, 23 November, 11

Suggestions

• As a non-ruby-dev:

• Use puppet

• Consider vagrant

Wednesday, 23 November, 11

Suggestions

• As a ruby developer:

• Consider chef and puppet, use whichever suits your taste and needs

• Consider vagrant

Wednesday, 23 November, 11

Databases

Wednesday, 23 November, 11

In the Real World,

Wednesday, 23 November, 11

In the Real World,

We have Data

Wednesday, 23 November, 11

In the Real World,

We have Data

Which lives in Databases

Wednesday, 23 November, 11

Databases Are Always simple

Wednesday, 23 November, 11

All our data is

ALWAYS in the same DBMS

Wednesday, 23 November, 11

Reality Check

Wednesday, 23 November, 11

LuckilyWe have

ODBC + JDBC

Wednesday, 23 November, 11

LuckilyWe have

ODBC + JDBC

+Ruby

Wednesday, 23 November, 11

Ruby Gives you Options

• Active Record

• Sequel

• DataMapper

• more

Wednesday, 23 November, 11

Active RecordDesign pattern coined by Martin Fowler in “Patterns of enterprise application architecture”.

Also, Ruby on Rails’s default ORM.

https://github.com/rails/rails/tree/master/activerecord

• Lots of Power• Lots of Opinion• Might fight with you (for non-standard uses)

Wednesday, 23 November, 11

Active Record Syntax (Raw)

require 'active_record'

ActiveRecord::Base.establish_connection({ :adapter => 'mysql', :database => 'test_database', :username => 'tester', :password => 'test22'})

connection = ActiveRecord::Base.connection

connection.tables> ['users', 'products', 'ducks', 'oranges']

users = [] connection.execute('SELECT * FROM users').each_hash do |user| users << userend

users> .... array of users, each user as a hash.

users.first> { :id => 1, :username => 'stefan', :password => 'is_super_secure' }

Wednesday, 23 November, 11

require 'active_record'

ActiveRecord::Base.establish_connection({ :adapter => 'mysql', :database => 'test_database', :username => 'tester', :password => 'test22'})

# class <camel_case_singular_table_name> < ActiveRecord::Baseclass User < ActiveRecord::Base # if the table's name does not fit convention, it can be manually overridden. # table_name :users_tableend

User.first> #<User id: 2, :name => 'stefan', :password => 'is_super_secure' >

User.find(2)> #<User id: 2, :name => 'stefan', :password => 'is_super_secure' >

User.where(:name => 'stefan')> #<User id: 2, :name => 'stefan', :password => 'is_super_secure' >

Active Record Syntax (ORM)

Wednesday, 23 November, 11

Active Record Syntax (AREL)

User.first> #<User id: 2, :name => 'stefan', :password => 'is_super_secure' >

User.find(2)> #<User id: 2, :name => 'stefan', :password => 'is_super_secure' >

User.where(:name => 'stefan')> #<User id: 2, :name => 'stefan', :password => 'is_super_secure' >

User.where(:name => 'stefan').order('id ASC')> #<User id: 2, :name => 'stefan', :password => 'is_super_secure' >

Wednesday, 23 November, 11

SequelElegant Full featured database toolkit for ruby.

http://sequel.rubyforge.org/

Supports- ADO, Amalgalite, DataObjects, DB2, DBI, DO, Firebird, ibmdb, Informix, JDBC, MySQL, Mysql2, ODBC, OpenBase, Oracle, PostgreSQL, SQLite3, Swift, and tinytds

• Supports a ton of DBMS’s• DSL• ORM

Wednesday, 23 November, 11

Sequel Examplerequire "sequel"

# connect to an in-memory databaseDB = Sequel.sqlite

# create an items tableDB.create_table :items do primary_key :id String :name Float :priceend

# create a dataset from the items tableitems = DB[:items]

# populate the tableitems.insert(:name => 'abc', :price => rand * 100)items.insert(:name => 'def', :price => rand * 100)items.insert(:name => 'ghi', :price => rand * 100)

# print out the number of recordsputs "Item count: #{items.count}"

# print out the average priceputs "The average price is: #{items.avg(:price)}"

Wednesday, 23 November, 11

Sequel ORMrequire "sequel"

# connect to an in-memory databaseDB = Sequel.sqlite

# create an items tableDB.create_table :items do primary_key :id String :name Float :priceend

# create a dataset from the items tableclass Item < Sequel::Model(:items) # associations # validations end

# populate the tableItem.create(:name => 'abc', :price => rand * 100)Item.create(:name => 'def', :price => rand * 100)Item.create(:name => 'ghi', :price => rand * 100)

# print out the number of recordsputs "Item count: #{Item.count}"

# print out the average priceputs "The average price is: #{Item.avg(:price)}"

Wednesday, 23 November, 11

ready out of the box

Sequel ORM

+jRuby

Wednesday, 23 November, 11

apparently works?

Sequel ORM

+IRONRuby

Wednesday, 23 November, 11

Case Study (Access)

• Access (top 2 solutions)

• Solution 1 (Cross Platform)

• jRuby

• JDBC

• HXTT’s driver (www.hxtt.com)

• Sequel

• Solution 2 (Windows Only)

• Ruby

• ADO via WIN32OLE

• Sequel

Wednesday, 23 November, 11

Case Study (Access)

Wednesday, 23 November, 11

1. Download jRuby http://jruby.org/

Case Study (Access)

Wednesday, 23 November, 11

1. Download jRuby http://jruby.org/

2. Install Sequel gem install Sequel

Case Study (Access)

Wednesday, 23 November, 11

1. Download jRuby http://jruby.org/

2. Install Sequel gem install Sequel

3. Download JDBC Access Driverhttp://www.hxtt.com/access.html

Case Study (Access)

Wednesday, 23 November, 11

Case Study (Access)require 'rubygems'require 'sequel'require 'sequel/adapters/jdbc'require 'sequel/jdbc_hxtt_adapter'require '../Support/Access_JDBC40.jar'

root = File.expand_path "../../", __FILE__path_to_db = root + "/Support/AccessThemeDemo.mdb"DB = Sequel.connect("jdbc:access:////#{path_to_db}")puts DB.tables

class Notes < Sequel::Model(:Notes)end

class ThemeImages < Sequel::Model(:tbl_ThemeImages)end

class Users < Sequel::Model(:tbl_Users)end

Wednesday, 23 November, 11

ba-da-bing

Wednesday, 23 November, 11

But our new appand our old database

have di!erent system requirements.

Wednesday, 23 November, 11

We Want Isolation

Wednesday, 23 November, 11

We Want Isolation-_NeedWednesday, 23 November, 11

Power by.. Sinatra

used at linkedInhttp://engineering.linkedin.com/44/linkedin-app-end-end-jruby-frontier-and-voldemort

Web DSL

get '/pictures' do ['picture1','picture2','picture2']end

post '/pictures' {}put '/pictures/:id' {}delete '/pictures/:id' {}

Wednesday, 23 November, 11

sooo... big deal?

Wednesday, 23 November, 11

RestServer

• any jdbc database into a restful json service

• https://github.com/stefanpenner/restserver

Wednesday, 23 November, 11

Portability/Bundling

• Warbler

http://github.com/nicksieger/warbler

• single jar/war file

• compiled (if needed)

Wednesday, 23 November, 11

it’s an app!it’s an app!

Wednesday, 23 November, 11

Putting it all together (p1)

ScreenShotrdownload:

https://github.com/stefanpenner/screenshotr/zipball/master

git@github.com:stefanpenner/screenshotr

Wednesday, 23 November, 11

Sorry, contrived example!

Wednesday, 23 November, 11

anyways...Wednesday, 23 November, 11

Java Gives Us• Portability

Ruby Gives Us• Happiness

Wednesday, 23 November, 11

ScreenShotr

• screen capture

• GUI

Normally Annoying re: Portability

but thx jRubyWednesday, 23 November, 11

Lets Shoot some Screensrequire 'java'

java_import 'java.awt.Robot'java_import 'java.awt.Toolkit'java_import 'java.awt.Rectangle'java_import 'java.awt.Image'java_import 'java.awt.image.BufferedImage'java_import 'javax.imageio.ImageIO'

screenRect = Rectangle.new(Toolkit.default_toolkit.getScreenSize())capture = Robot.new.createScreenCapture(screenRect)

ImageIO.write(capture,'jpg',java.io.File.new('some_tmp_file.jpg'))

Wednesday, 23 November, 11

Hold UP

Wednesday, 23 November, 11

Hold UPjava_import ?

Wednesday, 23 November, 11

Hold UPjava_import ?

why not require?

Wednesday, 23 November, 11

Hold UPjava_import ?

why not require?requiring classes just makes them discoverable later.

Wednesday, 23 November, 11

Hold UPjava_import ?

why not import?

why not require?requiring classes just makes them discoverable later.

Wednesday, 23 November, 11

Hold UPjava_import ?

why not import?Rake defines import

why not require?requiring classes just makes them discoverable later.

Wednesday, 23 November, 11

Hold UPjava_import ?

why not import?Rake defines import

why not require?requiring classes just makes them discoverable later.

weird...Wednesday, 23 November, 11

And fill some clipboards

require 'java'

java_import 'java.awt.Toolkit'java_import 'javax.imageio.ImageIO'

ss = StringSelection.new(public_url) Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss, nil)

Wednesday, 23 November, 11

upload to servergem install rest-client

require 'rubygems'

require 'rest-client'

RestClient.post('http://../resource', :file => File.new('path/to/file'))

Wednesday, 23 November, 11

upload to servergem install rest-client

require 'rubygems'

require 'rest-client'

RestClient.post('http://../resource', :file => File.new('path/to/file'))

Wednesday, 23 November, 11

<Insert Segue Here>

Wednesday, 23 November, 11

...and Vice Versa

• We’ve done a lot of calling Java from ruby.

• The reverse is possible as well.

Wednesday, 23 November, 11

...and Vice Versa

http://en.wikipedia.org/wiki/Jruby

import org.jruby.embed.InvokeFailedException;import org.jruby.embed.ScriptingContainer;

// ...ScriptingContainer container = new ScriptingContainer();container.runScriptlet("puts \"That's no Moon\"");

Wednesday, 23 November, 11

“i find your lack of tests disturbing.”

Testing Java with Ruby

Wednesday, 23 November, 11

Testing Java with Ruby

• JtestR is wonderful

• Includes most of ruby’s leading testing libraries

• supports ant and maven

• easy to add to a project

• takes advantage of jruby/java bridge

Wednesday, 23 November, 11

JtestR Installation

• Download jarfile from jtestr.codehaus.org

• Add to classpath

• Add a task to maven/ant

• Create tests in ./test or ./spec

• Run task

Wednesday, 23 November, 11

import java.util.HashMap

describe "An empty", HashMap do before :each do @hash_map = HashMap.new end

it "accepts new entries" do @hash_map.put "foo", "bar" @hash_map.get("foo").should == "bar" end

it "returns a keyset iterator that throws an exception on next" do proc do @hash_map.key_set.iterator.next end.should raise_error(java.util.NoSuchElementException) endend

Example!

Wednesday, 23 November, 11

BDD Any language

• Cucumber

• http://cukes.info

Wednesday, 23 November, 11

“i saw a city in the clouds”

Wednesday, 23 November, 11

MYTH: Ruby can’t scale.

Wednesday, 23 November, 11

FACT: Ruby scales like a BOSS

Wednesday, 23 November, 11

FACT: Ruby scales like a BOSS(because it has to)

Wednesday, 23 November, 11

Fog

• “The Ruby Cloud Services Library”

• Lets you upload to S3, provision instances in EC2, set DNS records in DNSimple...

• ...and much more.

Wednesday, 23 November, 11

whargarblbgarblgr-garblgragh

Fog

• Chewbacca-approved.

“ ”Wednesday, 23 November, 11

whargarblbgarblgr-garblgragh

Fog

• Chewbacca-approved. (chewbaccaproved?)

“ ”Wednesday, 23 November, 11

2.5 imperial tons of providers

Wednesday, 23 November, 11

2.5 imperial tons of providersHeh.( )

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Sinatra Example

#config.rurequire './lib/screen_shotr/server'run ScreenShotr::Server.new

Wednesday, 23 November, 11

require 'sinatra'require 'fog'require 'digest/sha1'

Wednesday, 23 November, 11

module ScreenShotr class Server < Sinatra::Base def storage #@storage ||= Fog::Storage.new({ # :provider => 'AWS', # :aws_access_key_id => ACCESS_KEY_ID, # :aws_secret_access_key => SECRET_ACCESS_KEY})

@storage ||= Fog::Storage.new({ :local_root => '~/fog', :provider => 'Local' })

def directory storage.directories.find("data").first or storage.directories.create(:key => 'data' ) end

# snip ....

Wednesday, 23 November, 11

get '/' do "hello, world!"end

Wednesday, 23 November, 11

post '/picture/create' do file = params[:file] data = file[:tempfile]

#super secure filename filename = file[:filename]

key = Digest::SHA1.hexdigest("super random seed"+Time.now.to_s) key << '.jpg'

file = directory.files.create( :body => data.read, :key => key ) file.public_url or "http://0.0.0.0:9292/picture/#{key}"end

Wednesday, 23 November, 11

get '/picture/:key' do file = directory.files.get(params[:key]) send_file file.send(:path)end

Wednesday, 23 November, 11

rackup

Wednesday, 23 November, 11

and...

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Fog Storage: Kind of cool.

Wednesday, 23 November, 11

Fog Compute: Wicked cool.

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Wednesday, 23 November, 11

Heroku

Cedar Stack (run “anything”)Classic Stack (run rack/rails)

Wednesday, 23 November, 11

Heroku

Classic Stack (run rack/rails)

$ heroku createCreated sushi.herokuapp.com | git@heroku.com:sushi.git

$ git push heroku master-----> Heroku receiving push-----> Rails app detected-----> Compiled slug size is 8.0MB-----> Launching... done, v1http://sushi.herokuapp.com deployed to Heroku

Wednesday, 23 November, 11

Heroku

$ cat Procfileweb: bundle exec rails server -p $PORTworker: bundle exec rake resque:work QUEUE=*urgentworker: bundle exec rake resque:work QUEUE=urgentclock: bundle exec clockwork clock.rb

$ heroku scale web=4 worker=2 urgentworker=1 clock=1Scaling processes... done

Cedar Stack (run “anything”)

Ruby, Node.js,Clojure, Java, Python, and Scala

“O!cially Everything”:

Wednesday, 23 November, 11

Recap• Core concepts

• Ruby on the JVM (and the CLR, sort of)

• Data

• Integration

• Deployment

• Sysadmin

• The Cloud

Wednesday, 23 November, 11

“help me ruby... you’re my only hope!”“help me ruby...

you’re my only hope!”Wednesday, 23 November, 11

“good... the force is strong with you.

a powerful rubyist you will become.”

Thanks!

Wednesday, 23 November, 11