the enterprise strikes back
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
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
http://jruby.org
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
[email protected]: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 | [email protected]: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