HELLO!!!
ZOMG HAPPY
 SATURDAY!
PEW PEW PEW~!!!
Aaron Patterson
@tenderlove
google 'tenderlove'
      Might be NSFW
AT&T, AT&T logo and all AT&T related marks are trademarks of AT&T Intellectual Property and/or AT&T affiliated companies.
Enterprise
       Developer
http://github.com/tenderlove/enterprise
ruby core
committer
rails core
committer
rails
committer
My Failures
rails core
committer
rails
committer
Presenting Last
I like Ryan Davis
My Slides Suck
    (Sorry Shane)
minitest
RFC 2119
 common.rspec
No Parens on
Method Definitions
def foo bar, baz
  ...
end
Ruby + JavaScript
Ruby + C
My Failures
  (as a presenter)
Fun
Practical
I am a nerd
I like boring things
2 Presentations
Practical
Fun!
The Fun
Your Guide to
Presentation
 Popularity!
Your Guide to
Presentation
  Notoriety!
•Provocative Title
• Risqué Photos

•Ruby Code?
Provocative Title:
Use Ruby 1.9 like an
     Engineer
Use Ruby 1.9 like a
 SEXY Engineer
Risqué Photos
America's Next Top
      Model
America's Next Top
    Engineer
Confident
Elegant
Sultry
Sexy
Thoughtful
Fierce
Playful
Powerful
Provocative
Ruby Code?
protected

    def method_missing(method, *args, &block)
      if Array.method_defined?(method)
        to_a.send(method, *args, &block)
      elsif @klass.scopes[method]
        merge(@klass.send(method, *args, &block))
      elsif @klass.respond_to?(method)
        scoping { @klass.send(method, *args, &block) }
      elsif arel.respond_to?(method)
        arel.send(method, *args, &block)
      elsif match = DynamicFinderMatch.match(method)
        attributes = match.attribute_names
        super unless @klass.send(:all_attributes_exists?, attributes)

        if match.finder?
           find_by_attributes(match, attributes, *args)
        elsif match.instantiator?
           find_or_instantiator_by_attributes(match, attributes, *args, &block)
        end
      else
        super
      end
    end

    private

    def references_eager_loaded_tables?
      # always convert table names to downcase as in Oracle quoted table names are in uppercase
      joined_tables = (tables_in_string(arel.joins(arel)) + [table.name, table.table_alias]).compact.map{ |t|
t.downcase }.uniq
      (tables_in_string(to_sql) - joined_tables).any?
    end
TL;DR
The Practical
Hidden Gems of
   Ruby 1.9
Ruby 1.9 PSA
minitest
require 'minitest/autorun'

class FooTest < MiniTest::Unit::TestCase
  WIN32 = true

 def test_foo
   assert_equal 'foo', 'foo'
 end

 def test_refutation
   refute_equal 'foo', 'bar'
 end

  def test_skip
    return skip if WIN32
    assert_equal 'fun!', 'fun!'
  end
end
require 'minitest/autorun'
Test::Unit::TestCase
           =>
MiniTest::Unit::TestCase
class FooTest < MiniTest::Unit::TestCase
end
assert_not_*
     =>
  refute_*
def test_refutation
  refute_equal 'foo', 'bar'
end
skip
class FooTest < MiniTest::Unit::TestCase
  WIN32 = true

  def test_skip
    skip if WIN32
    assert_equal 'fun!', 'fun!'
  end
end
Loaded suite footest
Started
.S.
Finished in 0.000682 seconds.

  1) Skipped:
test_skip(FooTest) [footest.rb:15]:
Skipped, no message given

3 tests, 2 assertions, 0 failures, 0 errors, 1 skips
randomization
class FailTest < MiniTest::Unit::TestCase
  @@foos = %w{ hello }

 def test_equality
   assert_equal @@foos, %w{ hello }
 end

  def test_append
    @@foos << "world"
    assert_equal @@foos, %w{ hello world }
  end
end
Test run options: --seed 31149

Loaded suite failtest
Started
..
Finished in 0.000604 seconds.

2 tests, 2 assertions, 0 failures, 0 errors, 0 skips

Test run options: --seed 31149
Test run options: --seed 29650

Loaded suite failtest
Started
.F
Finished in 0.000637 seconds.

  1) Failure:
test_equality(FailTest) [failtest.rb:7]:
Expected ["hello", "world"], not ["hello"].

2 tests, 2 assertions, 1 failures, 0 errors, 0 skips

Test run options: --seed 29650
Test run options: --seed 29650

Loaded suite failtest
Started
.F
Finished in 0.000637 seconds.

  1) Failure:
test_equality(FailTest) [failtest.rb:7]:
Expected ["hello", "world"], not ["hello"].

2 tests, 2 assertions, 1 failures, 0 errors, 0 skips

Test run options: --seed 29650
--seed 29650
-v
ruby failtest.rb --seed 29650 -v
Test run options: --seed 29650 --verbose

Loaded suite failtest
Started
FailTest#test_append: 0.00 s: .
FailTest#test_equality: 0.00 s: F

Finished in 0.000735 seconds.

  1) Failure:
test_equality(FailTest) [failtest.rb:7]:
Expected ["hello", "world"], not ["hello"].

2 tests, 2 assertions, 1 failures, 0 errors, 0 skips

Test run options: --seed 29650 --verbose
Test run options: --seed 29650 --verbose

Loaded suite failtest
Started
FailTest#test_append: 0.00 s: .
FailTest#test_equality: 0.00 s: F

Finished in 0.000735 seconds.

  1) Failure:
test_equality(FailTest) [failtest.rb:7]:
Expected ["hello", "world"], not ["hello"].

2 tests, 2 assertions, 1 failures, 0 errors, 0 skips

Test run options: --seed 29650 --verbose
Test Performance
class FooTest < MiniTest::Unit::TestCase
  def test_foo
    assert_equal 'foo', 'foo'
  end

 def test_refutation
   refute_equal 'foo', 'bar'
 end

  def test_slow
    sleep 10
  end
end
class FooTest < MiniTest::Unit::TestCase
  def test_foo
    assert_equal 'foo', 'foo'
  end

 def test_refutation
   refute_equal 'foo', 'bar'
 end

  def test_slow
    sleep 10
  end
end
Test run options: --seed 33095 --verbose

Loaded suite footest
Started
FooTest#test_slow: 10.00 s: .
FooTest#test_refutation: 0.00 s: .
FooTest#test_foo: 0.00 s: .

Finished in 10.001114 seconds.

3 tests, 2 assertions, 0 failures, 0 errors, 0 skips

Test run options: --seed 33095 --verbose
Test run options: --seed 33095 --verbose

Loaded suite footest
Started
FooTest#test_slow: 10.00 s: .
FooTest#test_refutation: 0.00 s: .
FooTest#test_foo: 0.00 s: .

Finished in 10.001114 seconds.

3 tests, 2 assertions, 0 failures, 0 errors, 0 skips

Test run options: --seed 33095 --verbose
With Rake:
rake test TESTSOPTS='-v'
rspec
describe 'Awesome' do
  describe 'Class' do
    it 'discovers something AMAZING' do
      (10 + 10).must_equal 20
    end

    it 'matches something AMAZING' do
      "vuvuzela".must_match /vuvu/
    end

    it 'raises something AMAZING' do
      lambda { raise }.must_raise(RuntimeError)
    end
  end
end
rspec
minitest/spec
require 'minitest/spec'
require 'minitest/autorun'
require 'minitest/spec'
require 'minitest/autorun'

describe 'Awesome' do
  describe 'Class' do
    it 'discovers something AMAZING' do
      (10 + 10).must_equal 20
    end

    it 'matches something AMAZING' do
      "vuvuzela".must_match /vuvu/
    end

    it 'must raise something' do
      lambda { raise }.must_raise(RuntimeError)
    end
  end
end
ObjectSpace
ObjectSpace.each_object do |obj|
  p obj
end
require 'objspace'

• count_objects_size
• memsize_of
• count_nodes
• count_tdata_objects
count_object_size
require 'objspace'

hash = {}
ObjectSpace.count_objects_size(hash)
p hash # =>
{:T_CLASS=>291520, :T_MODULE=>42512,
:T_STRING=>26133, :T_REGEXP=>11501,
:T_ARRAY=>5896, :T_HASH=>1088, :T_FILE=>9056,
:T_DATA=>1144348, :TOTAL=>1532054}
count_object_size
require 'objspace'

hash = {}
ObjectSpace.count_objects_size(hash)
p hash # =>
{:T_CLASS=>291520, :T_MODULE=>42512,
:T_STRING=>26133, :T_REGEXP=>11501,
:T_ARRAY=>5896, :T_HASH=>1088, :T_FILE=>9056,
:T_DATA=>1144348, :TOTAL=>1532054}
memsize_of


require 'objspace'
require 'fiddle'

cl = Fiddle::Closure.new(0, [1])
p ObjectSpace.memsize_of(cl) # => 232
memsize_of


require 'objspace'
require 'fiddle'

cl = Fiddle::Closure.new(0, [1])
p ObjectSpace.memsize_of(cl) # => 232
Implementation
struct rb_data_type_struct

struct rb_data_type_struct {
    const char *wrap_struct_name;
    struct {
   void (*dmark)(void*);
   void (*dfree)(void*);
   size_t (*dsize)(const void *);
   void *reserved[2]; /* For future extension.
                This array *must* be filled with ZERO. */
    } function;
    const rb_data_type_t *parent;
    void *data;        /* This area can be used for any purpose
                          by a programmer who define the type. */
};
struct rb_data_type_struct

struct rb_data_type_struct {
    const char *wrap_struct_name;
    struct {
   void (*dmark)(void*);
   void (*dfree)(void*);
   size_t (*dsize)(const void *);
   void *reserved[2]; /* For future extension.
                This array *must* be filled with ZERO. */
    } function;
    const rb_data_type_t *parent;
    void *data;        /* This area can be used for any purpose
                          by a programmer who define the type. */
};
static size_t my_memsize(const void *p) {
    return 10;
}

const rb_data_type_t my_data_type = {
    "my_extension",
    {NULL, NULL, my_memsize,},
};

static VALUE allocate(VALUE klass) {
    struct something * cif;
    return TypedData_Make_Struct(
      klass, something, &my_data_type, cif);
}
count_nodes


require 'objspace'

p ObjectSpace.count_nodes #=>
{:NODE_SCOPE=>50, :NODE_BLOCK=>168,
:NODE_IF=>27, :NODE_ITER=>7, ... }
count_nodes


require 'objspace'

p ObjectSpace.count_nodes #=>
{:NODE_SCOPE=>50, :NODE_BLOCK=>168,
:NODE_IF=>27, :NODE_ITER=>7, ... }
count_nodes

require 'objspace'

10.times do
  p ObjectSpace.count_nodes[:NODE_IF]
  eval 'if true; end'
end
$ ruby objectspace.rb
27
28
29
30
31
32
33
34
35
36
$
count_tdata_objects

require 'objspace'

p ObjectSpace.count_tdata_objects # =>
{RubyVM::InstructionSequence=>64,
false=>13, ... }
count_tdata_objects

require 'objspace'

p ObjectSpace.count_tdata_objects # =>
{RubyVM::InstructionSequence=>64,
false=>13, ... }
count_tdata_objects


require 'objspace'
require 'fiddle'

10.times do
  Fiddle::Closure.new(0, [1])
  p ObjectSpace.count_tdata_objects[Fiddle::Closure]
end
$ ruby objectspace.rb
1
2
3
4
5
6
7
8
9
10
$
Fiddle
libffi wrapper
fiddle + dl
Fiddle


• Function calls
• Closure allocation
DL


• dlopen() wrapper
• memory management
Calling Functions
• Open dynamic library
• Locate function pointer
• Wrap function pointer
• Call function
Wrapping "sin"
require 'fiddle'

libm = DL.dlopen('libm.dylib')

function = Fiddle::Function.new(
  libm['sin'],
  [Fiddle::TYPE_DOUBLE],
  Fiddle::TYPE_DOUBLE
)

puts function.call(90 * Math::PI / 180)
Wrapping "sin"
require 'fiddle'

libm = DL.dlopen('libm.dylib')

function = Fiddle::Function.new(
  libm['sin'],
  [Fiddle::TYPE_DOUBLE],
  Fiddle::TYPE_DOUBLE
)

puts function.call(90 * Math::PI / 180)
Wrapping "sin"
require 'fiddle'

libm = DL.dlopen('libm.dylib')

function = Fiddle::Function.new(
  libm['sin'],
  [Fiddle::TYPE_DOUBLE],
  Fiddle::TYPE_DOUBLE
)

puts function.call(90 * Math::PI / 180)
Wrapping "sin"
require 'fiddle'

libm = DL.dlopen('libm.dylib')

function = Fiddle::Function.new(
  libm['sin'],
  [Fiddle::TYPE_DOUBLE],
  Fiddle::TYPE_DOUBLE
)

puts function.call(90 * Math::PI / 180)
Wrapping "sin"
require 'fiddle'

libm = DL.dlopen('libm.dylib')

function = Fiddle::Function.new(
  libm['sin'],
  [Fiddle::TYPE_DOUBLE],
  Fiddle::TYPE_DOUBLE
)

puts function.call(90 * Math::PI / 180)
Creating Closures
double (func *)(double)
require 'fiddle'

class MySin < Fiddle::Closure
  def call number
    Math.sin(number)
  end
end

function = MySin.new(
  Fiddle::TYPE_DOUBLE,
  [Fiddle::TYPE_DOUBLE]
)

puts function.call(90 * Math::PI / 180)
require 'fiddle'

class MySin < Fiddle::Closure
  def call(number)
    Math.sin(number)
  end
end

function = MySin.new(
  Fiddle::TYPE_DOUBLE,
  [Fiddle::TYPE_DOUBLE]
)

puts function.call(90 * Math::PI / 180)
require 'fiddle'

class MySin < Fiddle::Closure
  def call(number)
    Math.sin(number)
  end
end

function = MySin.new(
  Fiddle::TYPE_DOUBLE,
  [Fiddle::TYPE_DOUBLE]
)

puts function.call(90 * Math::PI / 180)
require 'fiddle'

class MySin < Fiddle::Closure
  def call(number)
    Math.sin(number)
  end
end

function = MySin.new(
  Fiddle::TYPE_DOUBLE,
  [Fiddle::TYPE_DOUBLE]
)

puts function.call(90 * Math::PI / 180)
Using our Closure
class MySin < Fiddle::Closure
  def call number
    Math.sin(number)
  end
end

function = MySin.new(
  Fiddle::TYPE_DOUBLE,
  [Fiddle::TYPE_DOUBLE]
)

cfunc = Fiddle::Function.new(
  function,
  [Fiddle::TYPE_DOUBLE],
  Fiddle::TYPE_DOUBLE
)

puts cfunc.call(90 * Math::PI / 180)
class MySin < Fiddle::Closure
  def call number
    Math.sin(number)
  end
end

function = MySin.new(
  Fiddle::TYPE_DOUBLE,
  [Fiddle::TYPE_DOUBLE]
)

cfunc = Fiddle::Function.new(
  function,
  [Fiddle::TYPE_DOUBLE],
  Fiddle::TYPE_DOUBLE
)

puts cfunc.call(90 * Math::PI / 180)
Fiddle Masquerade
ruby-ffi code
module Tidy
  extend FFI::Library

 ffi_lib "libtidy.dylib"

  attach_function   :tidyFileExists, [:string], :int
  attach_function   :tidyCreate, [], :pointer
  attach_function   :tidyParseString, [:pointer, :string], :int
  attach_function   :tidySaveStdout, [:pointer], :int
end

tdoc = Tidy.tidyCreate
Tidy.tidyParseString tdoc, "<title>Foo</title"
Tidy.tidySaveStdout tdoc
In terms of Fiddle
module FFI
  module Library
    TYPE_MAP = {
      :string => DL::TYPE_VOIDP,
      :pointer => DL::TYPE_VOIDP,
    }

   DL.constants.each do |const|
     next unless const.to_s =~ /^TYPE_/

     name = const.to_s.split('_', 2).last.downcase.to_sym
     TYPE_MAP[name] = DL.const_get(const)
   end

   def ffi_lib(lib)
     @lib = DL::Handle.new lib
   end

   def attach_function(name, args, ret)
     func = Fiddle::Function.new(
       @lib[name.to_s], args.map { |x| TYPE_MAP[x] }, TYPE_MAP[ret]
     )

      define_singleton_method(name) { |*args| func.call(*args) }
    end
  end
end
ruby-ffi code
module Tidy
  extend FFI::Library

 ffi_lib "libtidy.dylib"

  attach_function   :tidyFileExists, [:string], :int
  attach_function   :tidyCreate, [], :pointer
  attach_function   :tidyParseString, [:pointer, :string], :int
  attach_function   :tidySaveStdout, [:pointer], :int
end

tdoc = Tidy.tidyCreate
Tidy.tidyParseString tdoc, "<title>Foo</title"
Tidy.tidySaveStdout tdoc
Fiddle code
module Tidy
  extend FFI::Library

 ffi_lib "libtidy.dylib"

  attach_function   :tidyFileExists, [:string], :int
  attach_function   :tidyCreate, [], :pointer
  attach_function   :tidyParseString, [:pointer, :string], :int
  attach_function   :tidySaveStdout, [:pointer], :int
end

tdoc = Tidy.tidyCreate
Tidy.tidyParseString tdoc, "<title>Foo</title"
Tidy.tidySaveStdout tdoc
Psych
YAML Parser

• 1.9.2 and up
• Wraps libyaml
• Replaces Syck
• Opt-in
Opt-in Process
$ irb
irb(main):001:0>   require 'yaml'
=> true
irb(main):002:0>   YAML::ENGINE.syck?
=> true
irb(main):003:0>   YAML::ENGINE.yamler = 'psych'
=> "psych"
irb(main):004:0>   YAML::ENGINE.syck?
=> false
irb(main):005:0>
require 'psych'
Parsing & Dumping

require 'psych'

Psych.load('--- hello world!') # => 'hello world!'
Psych.dump('hello world!')     # => '--- hello world!'
'hello world!'.to_yaml         # => '--- hello world!'
JSON

Psych.load("['hello', 'world!']n")
  # => ["hello", "world!"]

Psych.to_json(%w{ hello world! })
  # => "['hello', 'world!']n"
JSON Disclaimer
Evented Parsing
Evented Parsing
class MyHandler < Psych::Handler
  def start_sequence(*args)
    puts "open ["
  end

  def end_sequence(*args)
    puts "close ]"
  end

  def scalar(value, anchor, tag, plain, quoted, style)
    puts value
  end
end
Evented Parsing


parser = Psych::Parser.new(MyHandler.new)
parser.parse(StringIO.new("['foo', 'bar']"))
Evented Parsing

$ ruby yml.rb
open [
foo
bar
close ]
$
Psych::Parser#parse(io_or_string)
Evented Emitting
(the hard way)
Psych::Emitter

emitter = Psych::Emitter.new($stdout)

emitter.start_stream(Psych::Parser::UTF8)
emitter.start_document([], [], false)
emitter.start_sequence(nil, nil, false, 1)
10.times {
  emitter.scalar('hello world', nil, nil, false, true, 1)
}
emitter.end_sequence
emitter.end_document true
emitter.end_stream
---
- 'hello   world'
- 'hello   world'
- 'hello   world'
- 'hello   world'
- 'hello   world'
- 'hello   world'
- 'hello   world'
- 'hello   world'
- 'hello   world'
- 'hello   world'
Read
Psych::Handler
Streamed Emitting
(the easy way)
Psych::Stream

emitter = Psych::Stream.new($stdout)
emitter.start
emitter.push %w{ one two }
emitter.push %w{ three four }
emitter.finish
Psych::Stream

emitter = Psych::Stream.new($stdout)
emitter.start
emitter.push %w{ one two }
emitter.push %w{ three four }
emitter.finish
Psych::Stream
$ ruby yml.rb
---
- one
- two
...
---
- three
- four
...
Problem?
Streaming JSON
Psych::Stream


emitter = Psych::Stream.new($stdout)
emitter.start
emitter.push %w{ one two }
emitter.push %w{ three four }
emitter.finish
Psych::JSON::Stream


emitter = Psych::JSON::Stream.new($stdout)
emitter.start
emitter.push %w{ one two }
emitter.push %w{ three four }
emitter.finish
Psych::JSON::Stream


emitter = Psych::JSON::Stream.new($stdout)
emitter.start
emitter.push %w{ one two }
emitter.push %w{ three four }
emitter.finish
Psych::JSON::Stream


--- ['one', 'two']
...
--- ['three', 'four']
...
Uses?
More Info
THE END
Questions?
Coverage
Methods


• Coverage.start
• Coverage.result
a.rb
###
# It's my class!
class Foo
  def initialize
    @thought = 0
    10.times {
      @thought += 1 # thinking
    }
  end

  def rested?
    if @thought > 8
      false
    else
      true
    end
  end
end

Foo.new.rested?
a.rb
###
# It's my class!
class Foo
  def initialize
    @thought = 0
    10.times {
      @thought += 1 # thinking
    }
  end

  def rested?
    if @thought > 8
      false
    else
      true
    end
  end
end

Foo.new.rested?
a.rb
###
# It's my class!
class Foo
  def initialize
    @thought = 0
    10.times {
      @thought += 1 # thinking
    }
  end

  def rested?
    if @thought > 8
      false
    else
      true
    end
  end
end

Foo.new.rested?
a.rb
###
# It's my class!
class Foo
  def initialize
    @thought = 0
    10.times {
      @thought += 1 # thinking
    }
  end

  def rested?
    if @thought > 8
      false
    else
      true
    end
  end
end

Foo.new.rested?
require 'coverage'

Coverage.start
require 'a'
p Coverage.result
require 'coverage'

Coverage.start
require 'a'
p Coverage.result
Coverage.result


{"/Users/apatterson/git/code/a.rb"=>[nil,
nil, 1, 1, 1, 1, 10, nil, nil, nil, 1, 1, 1,
nil, 0, nil, nil, nil, nil, 1]}
We've Learned
We've Learned

• Lines executed
We've Learned

• Lines executed
• # times a line was executed
We've Learned

• Lines executed
• # times a line was executed
• Lines that can't be executed
We can deduce
We can deduce


• Coverage
We can deduce


• Coverage
• Hotspots (code heatmap)
SimpleCov
http://github.com/colszowka/simplecov
Read More Here!
   http://bit.ly/19coverage

Hidden Gems of Ruby 1.9