onrails.org home

Advanced Ruby - Day 2

I just missed the first half hour of the training due to a long commute due to a little snow in Denver. Chad completed the design discussion on Inheritance .vs. Composition, using mixins and modules. 1.class.ancestors => [Fixnum, Integer, Numeric, Comparable, Object, Kernel, BasicObject] 1.class.instance_methods(false) => [:to_s, :-@, :+, :-, :*, :/, :div, :%, :modulo, :divmod, :fdiv, :**, :abs, :magnitude, :==, :<=>, :>, :>=, :<, :<=, :~, :&, :|, :^, :[], :<<, :>>, :to_f, :size, :zero?, :odd?, :even?, :succ] m = 1.method(:inspect) => # We are moving on to performance.

...make it fast.

Rails doesn't scale and Ruby is slow. This said Ruby is arguably the slowest of the scripting languages with a naive implementation. But for 99% of the work we do, it's fast enough...Now let's talk about that last 1%. Premature optimization is the root of all evil - Donald Knuth.

Benchmarking

In 1.9 require 'benchmark' puts Benchmark.measure {(1..1000000).map{|num| ""+num.to_s}} 0.940000 0.050000 0.990000 ( 1.001714) In 1.8 puts Benchmark.measure {(1..1000000).map{|num| ""+num.to_s}} 1.800000 0.050000 1.850000 ( 1.850319) Yea, 1.9 is twice as fast. Now to compare approaches, here using map or inject...which one is the fastest: require 'benchmark' Benchmark.bm do |b| b.report("map") {(1..1000000).map{|num| ""+num.to_s}} b.report("inject") {(1..1000000).inject(""){|accum, num| ""+num.to_s}} end user system total real map 1.970000 0.070000 2.040000 ( 2.077786) inject 1.910000 0.010000 1.920000 ( 1.978541)

Don't Use Ruby

* C (or OCAML?) extensions * Sockets * DL * Don't use Ruby at all DL is "Pure Ruby" way of calling native code form shared libraries. require 'dl/import' require 'dl/struct'

memoization

def fib(n) @k||={} n<=2 ? 1 : (@k[n-1]||=fib(n-1))+(@k[n-2]||=fib(n-2)) end puts fib(200) 280571172992510140037611932413038677189525 Program exited with code #0 after 0.02 seconds Now the same code as above without the cluttering def fib(n) if n <= 2 1 else fib(n-1) + fib(n-2) end end alias :pre_memoized_fib :fib def fib(n) @cache ||= {} @cache[n] ||= pre_memoized_fib(n) end

The Ruby Object Model

Now we move onto the essence of an Object in Ruby. * self is the "current object" * self always as a value * two things change self: 1) method calls 2) class or module definition class << str # << opens up the singleton class. Arrows seem to go wrong way. p self # ghost class: > - class of the class String def speak puts "miaow" end end puts str.speak class Dave class << self # puts the methods into the singleton class and becomes class methods def do_something end end end class Person < Struct.new(:name, :age) def greet puts "Hello #{self.name}" end end f = Person.new("Chad", 28) module Logger def log(msg) puts msg end end class Album include Logger end Album.ancestors # => [Album, Logger, Object, Kernel] # Note that Logger becomes ancestor of Album...It's insert a generated class named after the module that shares the method table. Module can extend an object: module Speak def hello puts "hello" end end str = "cat" str.extend Speak puts str.hello

Metaprogramming

After the break we will be diving into metaprogramming. I saw his talk at the Advanced Rails Studio a while back and took some notes then. I will therefore sit back, relax, and code along his examples.

Library Organization

Fork me on GitHub