Successfully reported this slideshow.
Tasty Burgers
ZOMG GOOD
 MORNING!
HAPPY
WEDNESDAY!
PEW PEW PEW!
http://tenderlovemaking.com/
       railsconf2010.pdf
Aaron Patterson
@tenderlove
Seattle.rb
Ruby-Core Team
AT&T, AT&T logo and all AT&T related marks are trademarks of AT&T Intellectual Property and/or AT&T affiliated companies.
We’re Hiring!
Rails 4*
Rails 4*

• Ponies
• Unicorns
• Venture Capital
*Disclaimer



Some of the things I say may not be true.
Why I’m Here*
*The following events may not have actually
                 happened
One Day...
I
was
out
jogging
Hello! This is
@tenderlove,
who is this?
This is Chad
  Fowler!
AH! Chad
Flower, the
  famous
  florist?
Famous Florist: Chad Flower
No.
Chad Fouler
the Baseball
  player?
Baseball Player: Chad Fouler
No.
Chad
  Flounder the
Fish Whisperer?
Fish Whisperer: Chad Flounder
No.
Wait. Yes this is
Chad Flounder,
Fish Whisperer.
But I'm calling on
behalf of Chad Fowler,
 Ruby Programmer.
To invite you to
  RailsConf.
I accept.
Nom.
The Next Day
At Work
I'll need your bio, affiliation, and
so on. I would also love a recent
  head shot of you. 300 dpi, 3 "
      minimum is pr...
Professional
2 min later...
Erm. Nice mullet. Is
  that really you?
Yes
You know that means I'm
 going to require you to
   have that mullet at
       RailsConf.
Time to get
Professional
Rails
This is RailsConf right?
Ingredients
ERb
SQLITE         RACK
       RAILS
YAML
Using our
Ingredients
Twisting Software
FEAR
CONFUSION
INTRIGUE
INSPIRED
Better ingredients,
 better burgers.
   Guaranteed
TOPICS OF DOOM

• SQLite
• ERb
• Rack
• YAML
SQLite3-Ruby
SQLite3-Ruby

• Adapter for SQLite3 database
• Originally by Jamis Buck
• SWIG
Relationship with
      Rails
Default Database
class Foo < AR::Base; end

Foo.find(:all, :conditions => {...})

Foo.find(:all, :conditions => [
  ‘x = ?’, 20
])
ActionView


ActionController


 ActiveRecord


 SQLite3-Ruby
ActiveRecord
SQLite3-Ruby
AR::Base       SQLite3 Adapter



Connection Pool
SQLite3Adapter



SQLite3-Ruby
Outside Rails
Establishing a
           Connection

require 'sqlite3'

connection =
 SQLite3::Database.new('file.sqlite3')
In Memory
               Database
                                     Pro Tip


require 'sqlite3'

connection =
 SQLite3:...
In Memory
               Database
                                     Pro Tip


require 'sqlite3'

connection =
 SQLite3:...
database.yml


development:
 adapter: sqlite3
 database: ':memory:'
Making a Query


connection.execute('CREATE TABLE foo(id INTEGER)')


connection.execute('INSERT INTO foo (id) VALUES (?)'...
Preparing a Statement

stmt = connection.prepare('SELECT * FROM foo WHERE id
= ?')

stmt.bind_param(1, 10)

stmt.each do |...
Why Prepare a
 Statement?
Statements can be
     cached
Can improve
 database
performance
Rails Caveats
Parameter Substitution
User.find(10)
SELECT * FROM users WHERE id = ?
stmt = get_cached_stmt ||
 connection.prepare(
   'SELECT * FROM users WHERE id = ?'
 )
stmt.execute(10)
WRONG
stmt = get_cached_stmt ||
 connection.prepare(
   'SELECT * FROM users WHERE id = ?'
 )
stmt.execute(10)
connection.execute(
  'SELECT * FROM users WHERE id = 10'
)
No Prepared
Statement Support
        :-(
Room for Improvement?
Twisting SQLite3
• Step 1: Read the API
• Step 2: Write your code
• Step 3: Annoy your friends (or profit)
Step 1: Read the API
struct sqlite3_vfs {
  int iVersion;        /* Structure version number */
  int szOsFile;         /*...
IO Hooks
Step 2: Write our Code
static int rbFile_close(sqlite3_file * ctx)
{
  rubyFilePtr rfile = (rubyFilePtr)ctx;
  VALUE file = rfile->file;
  rb_funcall(...
class VFS < SQLite3::VFS
 def open(name, flags)
   OurFile.new(name, flags)
 end
end

class OurFile
 def read(...); ... end
...
Anything can store
a SQLite Database
__END__ + DATA
class EvilVFS < SQLite3::VFS
 def open name, flags
   DATABase.new name, flags
 end
end

class DATABase < SQLite3::VFS::File...
SQLite3.vfs_register(EvilVFS.new)
db = SQLite3::Database.new(__FILE__, nil, 'EvilVFS')

db.execute(<<-eosql)
 create table...
Step 3: Profit?
Get the Code!
http://gist.github.com/319224/
What's New in
SQLite3-Ruby?
Fun Stuff!!


• Ruby 1.9 Encoding support
• 1000x faster (or something)
Encodings
require 'sqlite3'

db = SQLite3::Database.new(':memory:')

db.execute('CREATE TABLE foo(id INTEGER, name VARCHAR...
Encodings
require 'sqlite3'

db = SQLite3::Database.new(':memory:')

db.execute('CREATE TABLE foo(id INTEGER, name VARCHAR...
Encodings
require 'sqlite3'

db = SQLite3::Database.new(':memory:')

db.execute('CREATE TABLE foo(id INTEGER, name VARCHAR...
Speed! (on selects)
                    1.2.5             1.3.0

1000

 100

  10

   1

 0.1

0.01
       500   1000     ...
ERb
ERb

• Masatoshi SEKI
• Template System
• Ruby 1.8 and 1.9
Relationship with
      Rails
app/views/**/*.html.erb
render('users/show')
Template Registration
class Something::ERB < AV::Template::Handler
 def render(template, *args)
   ...
   body
 end
end

A...
AV Resolver
           render()


Something::ERB
Something::ERB



     ERb
Outside Rails
require 'erb'

erb = ERB.new(File.read(__FILE__))
puts erb.result(binding)

__END__
Hello world! <%= 10 + 100 %>
Output

require 'erb'

erb = ERB.new(File.read(__FILE__))
puts erb.result(binding)

__END__
Hello world! 110
Output

require 'erb'

erb = ERB.new(File.read(__FILE__))
puts erb.result(binding)

__END__
Hello world! 110
require 'erb'

erb = ERB.new(File.read(__FILE__))
puts erb.result(binding)

__END__
Hello world! <%= 10 + 100 %>
Template Source

require 'erb'

erb = ERB.new(File.read(__FILE__))
puts erb.src

__END__
Hello world! <%= 10 + 100 %>
Template Source

require 'erb'

erb = ERB.new(File.read(__FILE__))
puts erb.src

__END__
Hello world! <%= 10 + 100 %>
Output
_erbout = ''; _erbout.concat "require 'erb'nnerb
= ERB.new(File.read(__FILE__))nputs erb.srcn
n__END__nHello world!...
Output Buffer

require 'erb'

erb = ERB.new(File.read(__FILE__), nil, nil, '@foo')
puts erb.src

__END__
Hello world! <%= ...
Output Buffer

require 'erb'

erb = ERB.new(File.read(__FILE__), nil, nil, '@foo')
puts erb.src

__END__
Hello world! <%= ...
Output
@foo = ''; @foo.concat "require 'erb'nnerb =
ERB.new(File.read(__FILE__), nil, nil, '@foo')nputs
erb.srcnn__END__nH...
Output
@foo = ''; @foo.concat "require 'erb'nnerb =
ERB.new(File.read(__FILE__), nil, nil, '@foo')nputs
erb.srcnn__END__nH...
Twisting ERb
ERb Memory
 Footprint
IO Input
mmap
allocate memory, or map files or devices into
                 memory
gem install mmap
Mmap.new(__FILE__)
IO input
require 'erb'
require 'rubygems'
require 'mmap'

erb = ERB.new(Mmap.new(__FILE__), nil, nil, '@foo')
erb.result(b...
IO Output
Our Problem

def set_eoutvar(compiler, eoutvar)
 ...
 cmd = []
 cmd.push "#{eoutvar} = ''"

 compiler.pre_cmd = cmd
 ...
e...
Our Problem

def set_eoutvar(compiler, eoutvar)
 ...
 cmd = []
 cmd.push "#{eoutvar} = ''"

 compiler.pre_cmd = cmd
 ...
e...
A Solution


class ERB::Compiler
 def pre_cmd= x; end
end
Source Output (before)
@foo = ''; @foo.concat "require 'erb'nrequire
'rubygems'nrequire 'mmap'nnclass
ERB::Compilern def p...
Source Output (after)
@foo.concat "require 'erb'nrequire
'rubygems'nrequire 'mmap'nnclass
ERB::Compilern def pre_cmd= x; e...
Source Output (before)
@foo = ''; @foo.concat "require 'erb'nrequire
'rubygems'nrequire 'mmap'nnclass
ERB::Compilern def p...
Source Output
@foo.concat "require 'erb'nrequire
'rubygems'nrequire 'mmap'nnclass
ERB::Compilern def pre_cmd= x; endnend
n...
Adapting to "concat"


class IOAdapter < Struct.new(:io)
 def concat(string)
   io.write string
 end
end
require 'erb'
require 'mmap'

class ERB::Compiler
 def pre_cmd= x; end
end

class IOAdapter < Struct.new(:io)
 def concat(...
Performance Results*


• Slower compiler
• Similar output
• Lower memory overhead
*YMMV
  :-(
rack
Ruby webserver interface ACK!
not racc
Ryet Another Compiler Compiler
Rack


• Interface between app code and
  servers

• By Christian Neukirchen
Relationship with
      Rails
Unicorn (etc)

   Rack

    Rails
Unicorn (etc)

   Rack

Rack Adapter
   RAILS
SLIDE MISSING
Rack Outside Rails
Handler#call(env)
class MyRack
 def call env
   [
     200,
     {'X-RailsConf' => 'yay'},
     "hello world!"
   ]
 end
end
HTTP/1.1 200 OK
Connection: close
Date: Thu, 03 Jun 2010 03:57:05 GMT
Server: WEBrick/1.3.1 (Ruby/
1.8.7/2009-06-08)
Conte...
HTTP/1.1 200 OK
Connection: close
Date: Thu, 03 Jun 2010 03:57:05 GMT
Server: WEBrick/1.3.1 (Ruby/
1.8.7/2009-06-08)
Conte...
body#each(&blk)
block must yield a
      String
class MyRack
 def call env
   [
     200,
     {'X-RailsConf' => 'yay'},
     ['hello ', 'America!']
   ]
 end
end
GET / HTTP/1.0

HTTP/1.1 200 OK
Connection: close
Date: Thu, 03 Jun 2010 04:06:08 GMT
Server: WEBrick/1.3.1 (Ruby/
1.8.7/2...
body#close
class MyRack
 def call env
   [
     200,
     {'X-RailsConf' => 'yay'},
     File.open(__FILE__, 'rb')
   ]
 end
end
HTTP/1.1 200 OK
Connection: close
Date: Thu, 03 Jun 2010 04:08:21 GMT
Server: WEBrick/1.3.1 (Ruby/1.8.7/2009-06-08)
Conten...
Great Flexibility
Twisting Rack
ERB Handler
class ERBHandler
 def initialize
   @root =
    File.join(File.dirname(__FILE__), 'htdocs')
 end

 def call env
  erb =
  ...
<%= env['SERVER_SOFTWARE'] %>

<%
 def fib n
  n < 2 ? n : fib(n - 1) + fib(n - 2)
 end
%>
<% 1.upto(30) do |i| %>
 <%= fib(i)...
Connection Times (ms)
         min mean[+/-sd] median max
Connect:     0 0 0.0     0    0
Processing: 3239 3312 91.8 3274 ...
Requests per second: 0.30 [#/sec] (mean)
Time per request:   3312.112 [ms] (mean)
Template Caching
class ERBHandler
 def initialize
   @root = File.join(File.dirname(__FILE__), 'htdocs')
   @cache = Hash.new do |h,file|
  ...
class ERBHandler
 def initialize
   @root = File.join(File.dirname(__FILE__), 'htdocs')
   @cache = Hash.new do |h,file|
  ...
Connection Times (ms)
         min mean[+/-sd] median max
Connect:     0 0 0.0     0    0
Processing: 3230 3267 30.6 3269 ...
Requests per second: 0.31 [#/sec] (mean)
Time per request:   3266.990 [ms] (mean)
Leverage "each"
rd, wr = IO.pipe
ERB Hacks

class ERB::Compiler
 def pre_cmd= x; end
end

class IO
 alias :concat :write
end
class ERBHandler
 def initialize
   @root = File.join(File.dirname(__FILE__), 'htdocs')
   @cache = Hash.new do |h,file|
  ...
class ERBHandler
 def initialize
   @root = File.join(File.dirname(__FILE__), 'htdocs')
   @cache = Hash.new do |h,file|
  ...
class ERBHandler
 def initialize
   @root = File.join(File.dirname(__FILE__), 'htdocs')
   @cache = Hash.new do |h,file|
  ...
class ERBHandler
 def initialize
   @root = File.join(File.dirname(__FILE__), 'htdocs')
   @cache = Hash.new do |h,file|
  ...
class ERBHandler
 def initialize
   @root = File.join(File.dirname(__FILE__), 'htdocs')
   @cache = Hash.new do |h,file|
  ...
Connection Times (ms)
         min mean[+/-sd] median max
Connect:     0 0 0.0     0    0
Processing: 3273 3312 22.0 3322 ...
Connection Times (ms)
         min mean[+/-sd] median max
Connect:     0 0 0.0     0    0
Processing: 3273 3312 22.0 3322 ...
Rails Improvements
YAML
Psych and Syck
Syck

• Ruby 1.8 and 1.9
• YAML 1.0 (mostly) parser / emitter
• Written by Why
Psych

• Ruby >= 1.9.2 only
• Wraps libyaml
• YAML 1.1 parser / emitter
• Written by me
Relationship with
      Rails
database.yml

development: &defaults
 adapter: sqlite3
 database: db/development.sqlite3

test:
 <<: *defaults
 database: ...
i18n
ja:
  calendars:
    gregorian:
     days:
      format:
       abbreviated:
         fri:
       mon:
       sat:
  ...
fixtures


default:
 user: default
 text: hello world
 created_at: <%= Time.now.to_formatted_s(:db)
%>
Serialized Attributes


# Serialize a preferences attribute
class User < AR::Base
 serialize :preferences # ZOMG YAML!
end
YAML Outside Rails
Parsing YAML
Ruby 1.8 / 1.9


require 'yaml'

YAML.load_file('some_file.yml')
YAML.load('--- hello world!')
Ruby 1.9.2 / Psych


require 'psych'

Psych.load_file('some_file.yml')
Psych.load('--- hello world!')
1.9.2 Opt-in

require 'yaml'

YAML::ENGINE.yamler = 'psych'

YAML.load('--- this is psych')
Dumping YAML
require 'psych'

Psych.dump { :foo => 'bar' }
{ :hello => 'world' }.to_yaml
Dumping JSON?
YAML Map


---
foo: bar
YAML Map


---
'foo': 'bar'
YAML Map


---
{ 'foo': 'bar' }
require 'psych'

Psych.to_json { :foo => 'bar' }
Twisting Psych
Custom Emitter
class Foo
 def initialize
   @greeting = 'world'
 end

 def encode_with(coder)
  coder['hello'] = @greeting
 end

 def ini...
Psych.dump


--- !ruby/object:Foo
hello: world
Psych.load


#<Foo:0x000001009f6770
@greeting="world">
Format Agnostic
class Foo
 def initialize
   @greeting = 'world'
 end

 def encode_with(coder)
  coder['hello'] = @greetin...
YAML & JSON


Psych.dump Foo.new
Psych.to_json Foo.new
class XMLDumper < Psych::Handler
 def self.dump(object)
   dumper = new
   viz = Psych::Visitors::YAMLTree.new({}, dumper)...
YAML & JSON &
         XML

Psych.dump    Foo.new
Psych.to_json Foo.new
XMLDumper.dump Foo.new
class HTMLDumper < XMLDumper
 def initialize
   @doc      =
Nokogiri::HTML::Document.new
   @doc.root = @doc.create_elemen...
YAML & JSON &
      XML & HTML

Psych.dump    Foo.new
Psych.to_json Foo.new
XMLDumper.dump Foo.new
HTMLDumper.dump Foo.new
module Marshalable
 module ClassMethods
  def _load data
   x = allocate
   x.init_with Marshal.load data
   x
  end
 end
...
YAML & JSON &
     XML & HTML &
        Marshal
Psych.dump    Foo.new
Psych.to_json Foo.new
XMLDumper.dump Foo.new
HTMLDum...
5 formats in 2
   methods!
Rails is made up of
 interesting things
Investigate those
     things
Improve those
   things
But most of all,
HAVE FUN!
THE END!
Bonus Round!
Twitter Stream
It's JSON!

512
{"in_reply_to_screen_name":null,...}

419
{"in_reply_to_screen_name":"tenderlove"...}
Still JSON!

---
{"in_reply_to_screen_name":null,...}
...
---
{"in_reply_to_screen_name":"tenderlove"...}
...
Event Output


stream = Psych::JSON::Stream.new $stdout
loop do
  stream << { :hello => "world" }
end
---   {'hello': 'world'}
...
---   {'hello': 'world'}
...
---   {'hello': 'world'}
...
---   {'hello': 'world'}
...
Psych + Rack
class StreamAPI
 def call(env)
   reader, writer = IO.pipe

  Thread.new(writer) do |wr|
   stream = Psych::JSON::Stream.n...
class StreamAPI
 def call(env)
   reader, writer = IO.pipe

  Thread.new(writer) do |wr|
   stream = Psych::JSON::Stream.n...
Ruby on Rails: Tasty Burgers
Ruby on Rails: Tasty Burgers
Ruby on Rails: Tasty Burgers
Ruby on Rails: Tasty Burgers
Ruby on Rails: Tasty Burgers
Ruby on Rails: Tasty Burgers
Ruby on Rails: Tasty Burgers
Ruby on Rails: Tasty Burgers
Ruby on Rails: Tasty Burgers
Ruby on Rails: Tasty Burgers
Ruby on Rails: Tasty Burgers
Upcoming SlideShare
Loading in …5
×

Ruby on Rails: Tasty Burgers

6,465 views

Published on

My talk at RailsConf 2010

Published in: Technology

Ruby on Rails: Tasty Burgers

  1. Tasty Burgers
  2. ZOMG GOOD MORNING!
  3. HAPPY WEDNESDAY!
  4. PEW PEW PEW!
  5. http://tenderlovemaking.com/ railsconf2010.pdf
  6. Aaron Patterson
  7. @tenderlove
  8. Seattle.rb
  9. Ruby-Core Team
  10. AT&T, AT&T logo and all AT&T related marks are trademarks of AT&T Intellectual Property and/or AT&T affiliated companies.
  11. We’re Hiring!
  12. Rails 4*
  13. Rails 4* • Ponies • Unicorns • Venture Capital
  14. *Disclaimer Some of the things I say may not be true.
  15. Why I’m Here* *The following events may not have actually happened
  16. One Day...
  17. I
  18. was
  19. out
  20. jogging
  21. Hello! This is @tenderlove, who is this?
  22. This is Chad Fowler!
  23. AH! Chad Flower, the famous florist?
  24. Famous Florist: Chad Flower
  25. No.
  26. Chad Fouler the Baseball player?
  27. Baseball Player: Chad Fouler
  28. No.
  29. Chad Flounder the Fish Whisperer?
  30. Fish Whisperer: Chad Flounder
  31. No.
  32. Wait. Yes this is Chad Flounder, Fish Whisperer.
  33. But I'm calling on behalf of Chad Fowler, Ruby Programmer.
  34. To invite you to RailsConf.
  35. I accept.
  36. Nom.
  37. The Next Day
  38. At Work
  39. I'll need your bio, affiliation, and so on. I would also love a recent head shot of you. 300 dpi, 3 " minimum is preferable.
  40. Professional
  41. 2 min later...
  42. Erm. Nice mullet. Is that really you?
  43. Yes
  44. You know that means I'm going to require you to have that mullet at RailsConf.
  45. Time to get Professional
  46. Rails This is RailsConf right?
  47. Ingredients
  48. ERb SQLITE RACK RAILS YAML
  49. Using our Ingredients
  50. Twisting Software
  51. FEAR
  52. CONFUSION
  53. INTRIGUE
  54. INSPIRED
  55. Better ingredients, better burgers. Guaranteed
  56. TOPICS OF DOOM • SQLite • ERb • Rack • YAML
  57. SQLite3-Ruby
  58. SQLite3-Ruby • Adapter for SQLite3 database • Originally by Jamis Buck • SWIG
  59. Relationship with Rails
  60. Default Database
  61. class Foo < AR::Base; end Foo.find(:all, :conditions => {...}) Foo.find(:all, :conditions => [ ‘x = ?’, 20 ])
  62. ActionView ActionController ActiveRecord SQLite3-Ruby
  63. ActiveRecord SQLite3-Ruby
  64. AR::Base SQLite3 Adapter Connection Pool
  65. SQLite3Adapter SQLite3-Ruby
  66. Outside Rails
  67. Establishing a Connection require 'sqlite3' connection = SQLite3::Database.new('file.sqlite3')
  68. In Memory Database Pro Tip require 'sqlite3' connection = SQLite3::Database.new(':memory:')
  69. In Memory Database Pro Tip require 'sqlite3' connection = SQLite3::Database.new(':memory:')
  70. database.yml development: adapter: sqlite3 database: ':memory:'
  71. Making a Query connection.execute('CREATE TABLE foo(id INTEGER)') connection.execute('INSERT INTO foo (id) VALUES (?)', [10])
  72. Preparing a Statement stmt = connection.prepare('SELECT * FROM foo WHERE id = ?') stmt.bind_param(1, 10) stmt.each do |row| p row end
  73. Why Prepare a Statement?
  74. Statements can be cached
  75. Can improve database performance
  76. Rails Caveats
  77. Parameter Substitution
  78. User.find(10)
  79. SELECT * FROM users WHERE id = ?
  80. stmt = get_cached_stmt || connection.prepare( 'SELECT * FROM users WHERE id = ?' ) stmt.execute(10)
  81. WRONG stmt = get_cached_stmt || connection.prepare( 'SELECT * FROM users WHERE id = ?' ) stmt.execute(10)
  82. connection.execute( 'SELECT * FROM users WHERE id = 10' )
  83. No Prepared Statement Support :-(
  84. Room for Improvement?
  85. Twisting SQLite3
  86. • Step 1: Read the API • Step 2: Write your code • Step 3: Annoy your friends (or profit)
  87. Step 1: Read the API struct sqlite3_vfs { int iVersion; /* Structure version number */ int szOsFile; /* Size of subclassed sqlite3_file */ int mxPathname; /* Maximum file pathname length */ sqlite3_vfs *pNext; /* Next registered VFS */ const char *zName; /* Name of this virtual file system */ void *pAppData; /* Pointer to application-specific data */ int (*xOpen)(sqlite3_vfs*, const char *zName, sqlite3_file*, int flags, int *pOutFlags); int (*xDelete)(sqlite3_vfs*, const char *zName, int syncDir); int (*xAccess)(sqlite3_vfs*, const char *zName, int flags, int *pResOut); int (*xFullPathname)(sqlite3_vfs*, const char *zName, int nOut, char *zOut); void *(*xDlOpen)(sqlite3_vfs*, const char *zFilename); void (*xDlError)(sqlite3_vfs*, int nByte, char *zErrMsg); void (*(*xDlSym)(sqlite3_vfs*,void*, const char *zSymbol))(void); void (*xDlClose)(sqlite3_vfs*, void*); int (*xRandomness)(sqlite3_vfs*, int nByte, char *zOut); int (*xSleep)(sqlite3_vfs*, int microseconds); int (*xCurrentTime)(sqlite3_vfs*, double*); int (*xGetLastError)(sqlite3_vfs*, int, char *); /* New fields may be appended in figure versions. The iVersion ** value will increment whenever this happens. */ };
  88. IO Hooks
  89. Step 2: Write our Code
  90. static int rbFile_close(sqlite3_file * ctx) { rubyFilePtr rfile = (rubyFilePtr)ctx; VALUE file = rfile->file; rb_funcall(file, rb_intern("close"), 0); return SQLITE_OK; }
  91. class VFS < SQLite3::VFS def open(name, flags) OurFile.new(name, flags) end end class OurFile def read(...); ... end def write(...); ... end end
  92. Anything can store a SQLite Database
  93. __END__ + DATA
  94. class EvilVFS < SQLite3::VFS def open name, flags DATABase.new name, flags end end class DATABase < SQLite3::VFS::File def initialize name, flags super @store = File.open(name, File::RDWR | File::CREAT) @offset = 0 if File.expand_path(__FILE__) == name @store.seek DATA.pos @offset = DATA.pos end end def close @store.close end def read amt, offset @store.seek(offset + @offset) @store.read amt end def write data, offset @store.seek(offset + @offset) @store.write data end def sync opts @store.fsync end def file_size
  95. SQLite3.vfs_register(EvilVFS.new) db = SQLite3::Database.new(__FILE__, nil, 'EvilVFS') db.execute(<<-eosql) create table if not exists users(id integer primary key, name string) eosql 100.times { db.execute(<<-eosql, 'tenderlove') insert into users(name) values (?) eosql } p db.execute('select count(*) from users') __END__
  96. Step 3: Profit?
  97. Get the Code! http://gist.github.com/319224/
  98. What's New in SQLite3-Ruby?
  99. Fun Stuff!! • Ruby 1.9 Encoding support • 1000x faster (or something)
  100. Encodings require 'sqlite3' db = SQLite3::Database.new(':memory:') db.execute('CREATE TABLE foo(id INTEGER, name VARCHAR)') db.execute('INSERT INTO foo (id, name) VALUES (?,?)',[10,'hello world']) rows = db.execute('SELECT name FROM foo') p rows.first.first.encoding # => #<Encoding:UTF-8> Encoding.default_internal = 'EUC-JP' rows = db.execute('SELECT name FROM foo') p rows.first.first.encoding # => #<Encoding:EUC-JP>
  101. Encodings require 'sqlite3' db = SQLite3::Database.new(':memory:') db.execute('CREATE TABLE foo(id INTEGER, name VARCHAR)') db.execute('INSERT INTO foo (id, name) VALUES (?,?)',[10,'hello world']) rows = db.execute('SELECT name FROM foo') p rows.first.first.encoding # => #<Encoding:UTF-8> Encoding.default_internal = 'EUC-JP' rows = db.execute('SELECT name FROM foo') p rows.first.first.encoding # => #<Encoding:EUC-JP>
  102. Encodings require 'sqlite3' db = SQLite3::Database.new(':memory:') db.execute('CREATE TABLE foo(id INTEGER, name VARCHAR)') db.execute('INSERT INTO foo (id, name) VALUES (?,?)',[10,'hello world']) rows = db.execute('SELECT name FROM foo') p rows.first.first.encoding # => #<Encoding:UTF-8> Encoding.default_internal = 'EUC-JP' rows = db.execute('SELECT name FROM foo') p rows.first.first.encoding # => #<Encoding:EUC-JP>
  103. Speed! (on selects) 1.2.5 1.3.0 1000 100 10 1 0.1 0.01 500 1000 1500 2000 2500 3000 3500
  104. ERb
  105. ERb • Masatoshi SEKI • Template System • Ruby 1.8 and 1.9
  106. Relationship with Rails
  107. app/views/**/*.html.erb
  108. render('users/show')
  109. Template Registration class Something::ERB < AV::Template::Handler def render(template, *args) ... body end end ActionView::Template.register_template_handler( :erb, Something::ERB )
  110. AV Resolver render() Something::ERB
  111. Something::ERB ERb
  112. Outside Rails
  113. require 'erb' erb = ERB.new(File.read(__FILE__)) puts erb.result(binding) __END__ Hello world! <%= 10 + 100 %>
  114. Output require 'erb' erb = ERB.new(File.read(__FILE__)) puts erb.result(binding) __END__ Hello world! 110
  115. Output require 'erb' erb = ERB.new(File.read(__FILE__)) puts erb.result(binding) __END__ Hello world! 110
  116. require 'erb' erb = ERB.new(File.read(__FILE__)) puts erb.result(binding) __END__ Hello world! <%= 10 + 100 %>
  117. Template Source require 'erb' erb = ERB.new(File.read(__FILE__)) puts erb.src __END__ Hello world! <%= 10 + 100 %>
  118. Template Source require 'erb' erb = ERB.new(File.read(__FILE__)) puts erb.src __END__ Hello world! <%= 10 + 100 %>
  119. Output _erbout = ''; _erbout.concat "require 'erb'nnerb = ERB.new(File.read(__FILE__))nputs erb.srcn n__END__nHello world! " ; _erbout.concat(( 10 + 100 ).to_s); _erbout.concat "n" ; _erbout
  120. Output Buffer require 'erb' erb = ERB.new(File.read(__FILE__), nil, nil, '@foo') puts erb.src __END__ Hello world! <%= 10 + 100 %>
  121. Output Buffer require 'erb' erb = ERB.new(File.read(__FILE__), nil, nil, '@foo') puts erb.src __END__ Hello world! <%= 10 + 100 %>
  122. Output @foo = ''; @foo.concat "require 'erb'nnerb = ERB.new(File.read(__FILE__), nil, nil, '@foo')nputs erb.srcnn__END__nHello world! " ; @foo.concat(( 10 + 100 ).to_s); @foo.concat "n" ; @foo
  123. Output @foo = ''; @foo.concat "require 'erb'nnerb = ERB.new(File.read(__FILE__), nil, nil, '@foo')nputs erb.srcnn__END__nHello world! " ; @foo.concat(( 10 + 100 ).to_s); @foo.concat "n" ; @foo
  124. Twisting ERb
  125. ERb Memory Footprint
  126. IO Input
  127. mmap allocate memory, or map files or devices into memory
  128. gem install mmap
  129. Mmap.new(__FILE__)
  130. IO input require 'erb' require 'rubygems' require 'mmap' erb = ERB.new(Mmap.new(__FILE__), nil, nil, '@foo') erb.result(binding) puts @foo __END__ Hello world! <%= 10 + 100 %>
  131. IO Output
  132. Our Problem def set_eoutvar(compiler, eoutvar) ... cmd = [] cmd.push "#{eoutvar} = ''" compiler.pre_cmd = cmd ... end
  133. Our Problem def set_eoutvar(compiler, eoutvar) ... cmd = [] cmd.push "#{eoutvar} = ''" compiler.pre_cmd = cmd ... end
  134. A Solution class ERB::Compiler def pre_cmd= x; end end
  135. Source Output (before) @foo = ''; @foo.concat "require 'erb'nrequire 'rubygems'nrequire 'mmap'nnclass ERB::Compilern def pre_cmd= x; endnend nnerb = ERB.new(Mmap.new(__FILE__), nil, nil, '@foo')nputs erb.srcnn__END__nHello world! " ; @foo.concat(( 10 + 100 ).to_s); @foo.concat "n" ; @foo
  136. Source Output (after) @foo.concat "require 'erb'nrequire 'rubygems'nrequire 'mmap'nnclass ERB::Compilern def pre_cmd= x; endnend nnerb = ERB.new(Mmap.new(__FILE__), nil, nil, '@foo')nputs erb.srcnn__END__nHello world! " ; @foo.concat(( 10 + 100 ).to_s); @foo.concat "n" ; @foo
  137. Source Output (before) @foo = ''; @foo.concat "require 'erb'nrequire 'rubygems'nrequire 'mmap'nnclass ERB::Compilern def pre_cmd= x; endnend nnerb = ERB.new(Mmap.new(__FILE__), nil, nil, '@foo')nputs erb.srcnn__END__nHello world! " ; @foo.concat(( 10 + 100 ).to_s); @foo.concat "n" ; @foo
  138. Source Output @foo.concat "require 'erb'nrequire 'rubygems'nrequire 'mmap'nnclass ERB::Compilern def pre_cmd= x; endnend nnerb = ERB.new(Mmap.new(__FILE__), nil, nil, '@foo')nputs erb.srcnn__END__nHello world! " ; @foo.concat(( 10 + 100 ).to_s); @foo.concat "n" ; @foo
  139. Adapting to "concat" class IOAdapter < Struct.new(:io) def concat(string) io.write string end end
  140. require 'erb' require 'mmap' class ERB::Compiler def pre_cmd= x; end end class IOAdapter < Struct.new(:io) def concat(string) io.write string end end @foo = IOAdapter.new($stdout) erb = ERB.new(Mmap.new(__FILE__), nil, nil, '@foo') erb.result(binding) __END__ Hello world! <%= 10 + 100 %>
  141. Performance Results* • Slower compiler • Similar output • Lower memory overhead
  142. *YMMV :-(
  143. rack Ruby webserver interface ACK!
  144. not racc Ryet Another Compiler Compiler
  145. Rack • Interface between app code and servers • By Christian Neukirchen
  146. Relationship with Rails
  147. Unicorn (etc) Rack Rails
  148. Unicorn (etc) Rack Rack Adapter RAILS
  149. SLIDE MISSING
  150. Rack Outside Rails
  151. Handler#call(env)
  152. class MyRack def call env [ 200, {'X-RailsConf' => 'yay'}, "hello world!" ] end end
  153. HTTP/1.1 200 OK Connection: close Date: Thu, 03 Jun 2010 03:57:05 GMT Server: WEBrick/1.3.1 (Ruby/ 1.8.7/2009-06-08) Content-Length: 11 X-Railsconf: yay hello world
  154. HTTP/1.1 200 OK Connection: close Date: Thu, 03 Jun 2010 03:57:05 GMT Server: WEBrick/1.3.1 (Ruby/ 1.8.7/2009-06-08) Content-Length: 11 X-Railsconf: yay hello world
  155. body#each(&blk)
  156. block must yield a String
  157. class MyRack def call env [ 200, {'X-RailsConf' => 'yay'}, ['hello ', 'America!'] ] end end
  158. GET / HTTP/1.0 HTTP/1.1 200 OK Connection: close Date: Thu, 03 Jun 2010 04:06:08 GMT Server: WEBrick/1.3.1 (Ruby/ 1.8.7/2009-06-08) Content-Length: 14 X-Railsconf: yay hello America!
  159. body#close
  160. class MyRack def call env [ 200, {'X-RailsConf' => 'yay'}, File.open(__FILE__, 'rb') ] end end
  161. HTTP/1.1 200 OK Connection: close Date: Thu, 03 Jun 2010 04:08:21 GMT Server: WEBrick/1.3.1 (Ruby/1.8.7/2009-06-08) Content-Length: 215 X-Railsconf: yay class MyRack def call env [ 200, {'X-RailsConf' => 'yay'}, File.open(__FILE__, 'rb') ] end end
  162. Great Flexibility
  163. Twisting Rack
  164. ERB Handler
  165. class ERBHandler def initialize @root = File.join(File.dirname(__FILE__), 'htdocs') end def call env erb = ERB.new(File.read("#{@root}#{env['REQUEST_PATH']}")) [ 200, {'X-RailsConf' => 'yay'}, erb.result(binding) ] end end
  166. <%= env['SERVER_SOFTWARE'] %> <% def fib n n < 2 ? n : fib(n - 1) + fib(n - 2) end %> <% 1.upto(30) do |i| %> <%= fib(i) %> <% end %>
  167. Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.0 0 0 Processing: 3239 3312 91.8 3274 3483 Waiting: 3238 3311 91.2 3274 3482 Total: 3239 3312 91.8 3274 3483
  168. Requests per second: 0.30 [#/sec] (mean) Time per request: 3312.112 [ms] (mean)
  169. Template Caching
  170. class ERBHandler def initialize @root = File.join(File.dirname(__FILE__), 'htdocs') @cache = Hash.new do |h,file| h[file] = ERB.new(File.read("#{@root}#{file}")) end end def call env [ 200, {'X-RailsConf' => 'yay'}, @cache[env['REQUEST_PATH']].result(binding) ] end end
  171. class ERBHandler def initialize @root = File.join(File.dirname(__FILE__), 'htdocs') @cache = Hash.new do |h,file| h[file] = ERB.new(File.read("#{@root}#{file}")) end end def call env [ 200, {'X-RailsConf' => 'yay'}, @cache[env['REQUEST_PATH']].result(binding) ] end end
  172. Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.0 0 0 Processing: 3230 3267 30.6 3269 3315 Waiting: 3229 3267 30.5 3269 3315 Total: 3230 3267 30.7 3269 3315
  173. Requests per second: 0.31 [#/sec] (mean) Time per request: 3266.990 [ms] (mean)
  174. Leverage "each"
  175. rd, wr = IO.pipe
  176. ERB Hacks class ERB::Compiler def pre_cmd= x; end end class IO alias :concat :write end
  177. class ERBHandler def initialize @root = File.join(File.dirname(__FILE__), 'htdocs') @cache = Hash.new do |h,file| h[file] = ERB.new( File.read("#{@root}#{file}"), nil, nil, 'wr') end end def call env rd, wr = IO.pipe Thread.new do @cache[env['REQUEST_PATH']].result(binding) wr.close end [ 200, {'X-RailsConf' => 'yay'}, rd ] end end
  178. class ERBHandler def initialize @root = File.join(File.dirname(__FILE__), 'htdocs') @cache = Hash.new do |h,file| h[file] = ERB.new( File.read("#{@root}#{file}"), nil, nil, 'wr') end end def call env rd, wr = IO.pipe Thread.new do @cache[env['REQUEST_PATH']].result(binding) wr.close end [ 200, {'X-RailsConf' => 'yay'}, rd ] end end
  179. class ERBHandler def initialize @root = File.join(File.dirname(__FILE__), 'htdocs') @cache = Hash.new do |h,file| h[file] = ERB.new( File.read("#{@root}#{file}"), nil, nil, 'wr') end end def call env rd, wr = IO.pipe Thread.new do @cache[env['REQUEST_PATH']].result(binding) wr.close end [ 200, {'X-RailsConf' => 'yay'}, rd ] end end
  180. class ERBHandler def initialize @root = File.join(File.dirname(__FILE__), 'htdocs') @cache = Hash.new do |h,file| h[file] = ERB.new( File.read("#{@root}#{file}"), nil, nil, 'wr') end end def call env rd, wr = IO.pipe Thread.new do @cache[env['REQUEST_PATH']].result(binding) wr.close end [ 200, {'X-RailsConf' => 'yay'}, rd ] end end
  181. class ERBHandler def initialize @root = File.join(File.dirname(__FILE__), 'htdocs') @cache = Hash.new do |h,file| h[file] = ERB.new( File.read("#{@root}#{file}"), nil, nil, 'wr') end end def call env rd, wr = IO.pipe Thread.new do @cache[env['REQUEST_PATH']].result(binding) wr.close end [ 200, {'X-RailsConf' => 'yay'}, rd ] end end
  182. Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.0 0 0 Processing: 3273 3312 22.0 3322 3338 Waiting: 0 1 0.5 0 2 Total: 3273 3313 22.0 3322 3338
  183. Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.0 0 0 Processing: 3273 3312 22.0 3322 3338 Waiting: 0 1 0.5 0 2 Total: 3273 3313 22.0 3322 3338
  184. Rails Improvements
  185. YAML Psych and Syck
  186. Syck • Ruby 1.8 and 1.9 • YAML 1.0 (mostly) parser / emitter • Written by Why
  187. Psych • Ruby >= 1.9.2 only • Wraps libyaml • YAML 1.1 parser / emitter • Written by me
  188. Relationship with Rails
  189. database.yml development: &defaults adapter: sqlite3 database: db/development.sqlite3 test: <<: *defaults database: db/test.sqlite3
  190. i18n ja: calendars: gregorian: days: format: abbreviated: fri: mon: sat: sun: thu: tue: wed:
  191. fixtures default: user: default text: hello world created_at: <%= Time.now.to_formatted_s(:db) %>
  192. Serialized Attributes # Serialize a preferences attribute class User < AR::Base serialize :preferences # ZOMG YAML! end
  193. YAML Outside Rails
  194. Parsing YAML
  195. Ruby 1.8 / 1.9 require 'yaml' YAML.load_file('some_file.yml') YAML.load('--- hello world!')
  196. Ruby 1.9.2 / Psych require 'psych' Psych.load_file('some_file.yml') Psych.load('--- hello world!')
  197. 1.9.2 Opt-in require 'yaml' YAML::ENGINE.yamler = 'psych' YAML.load('--- this is psych')
  198. Dumping YAML
  199. require 'psych' Psych.dump { :foo => 'bar' } { :hello => 'world' }.to_yaml
  200. Dumping JSON?
  201. YAML Map --- foo: bar
  202. YAML Map --- 'foo': 'bar'
  203. YAML Map --- { 'foo': 'bar' }
  204. require 'psych' Psych.to_json { :foo => 'bar' }
  205. Twisting Psych
  206. Custom Emitter
  207. class Foo def initialize @greeting = 'world' end def encode_with(coder) coder['hello'] = @greeting end def init_with(coder) @greeting = coder['hello'] end end
  208. Psych.dump --- !ruby/object:Foo hello: world
  209. Psych.load #<Foo:0x000001009f6770 @greeting="world">
  210. Format Agnostic class Foo def initialize @greeting = 'world' end def encode_with(coder) coder['hello'] = @greeting end def init_with(coder) @greeting = coder['hello'] end end
  211. YAML & JSON Psych.dump Foo.new Psych.to_json Foo.new
  212. class XMLDumper < Psych::Handler def self.dump(object) dumper = new viz = Psych::Visitors::YAMLTree.new({}, dumper) viz << object dumper.doc.to_xml end attr_reader :doc def initialize @doc = Nokogiri::XML::Document.new @doc.root = @doc.create_element 'root' @tags = [] @stack = [@doc.root] end def start_mapping(anchor, tag, implicit, style) tag = @doc.create_element('table') @stack.last << tag @stack << tag end def end_mapping @stack.pop end def scalar(value, anchor, tag, *args) @stack.last << @doc.create_element('tr').tap do |tag| tag << @doc.create_element('td', value) end end end
  213. YAML & JSON & XML Psych.dump Foo.new Psych.to_json Foo.new XMLDumper.dump Foo.new
  214. class HTMLDumper < XMLDumper def initialize @doc = Nokogiri::HTML::Document.new @doc.root = @doc.create_element('html') @doc.root << @doc.create_element('body') @tags = [] @stack = [@doc.root.children.first] end end
  215. YAML & JSON & XML & HTML Psych.dump Foo.new Psych.to_json Foo.new XMLDumper.dump Foo.new HTMLDumper.dump Foo.new
  216. module Marshalable module ClassMethods def _load data x = allocate x.init_with Marshal.load data x end end def self.included klass klass.extend ClassMethods end def _dump o coder = {} encode_with coder Marshal.dump coder end end class Foo; include Marshalable; end
  217. YAML & JSON & XML & HTML & Marshal Psych.dump Foo.new Psych.to_json Foo.new XMLDumper.dump Foo.new HTMLDumper.dump Foo.new Marshal.dump Foo.new
  218. 5 formats in 2 methods!
  219. Rails is made up of interesting things
  220. Investigate those things
  221. Improve those things
  222. But most of all,
  223. HAVE FUN!
  224. THE END!
  225. Bonus Round!
  226. Twitter Stream
  227. It's JSON! 512 {"in_reply_to_screen_name":null,...} 419 {"in_reply_to_screen_name":"tenderlove"...}
  228. Still JSON! --- {"in_reply_to_screen_name":null,...} ... --- {"in_reply_to_screen_name":"tenderlove"...} ...
  229. Event Output stream = Psych::JSON::Stream.new $stdout loop do stream << { :hello => "world" } end
  230. --- {'hello': 'world'} ... --- {'hello': 'world'} ... --- {'hello': 'world'} ... --- {'hello': 'world'} ...
  231. Psych + Rack
  232. class StreamAPI def call(env) reader, writer = IO.pipe Thread.new(writer) do |wr| stream = Psych::JSON::Stream.new writer loop { stream << { :hello => "world" } } end [200, {}, reader] end end
  233. class StreamAPI def call(env) reader, writer = IO.pipe Thread.new(writer) do |wr| stream = Psych::JSON::Stream.new writer loop { stream << { :hello => "world" } } end [200, {}, reader] end end

×