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