SlideShare a Scribd company logo
Dynamic languages
Reuven M. Lerner • reuven@lerner.co.il
Software Craftsmanship Group, Tel Aviv
          March 22nd, 2011




                                         1
Who am I?

• Web developer, software architect,
  consultant, trainer
• Linux Journal columnist since 1996
• Mostly Ruby on Rails + PostgreSQL, but
  also Python, PHP, jQuery, and lots more...



                                               2
Language wars!

• Static vs. dynamic languages
• I’m here representing the good guys
 • (Just kidding. Mostly.)
• Not just a different language — a different
  mindset and set of expectations



                                                3
What is a dynamic
       language?
• Dynamic typing
• Usually interpreted (or JIT compiled)
 • Interactive shell for experimenting
• Closures (anonymous code blocks)
• Flexible, “living” object model
• Result: Short, powerful, reusable code
                                           4
Who is in charge?

• Static language: The language is in charge,
  and it’s for your own good!
• Dynamic language: The programmer is in
  charge, and changes the language to suit his
  or her needs




                                                 5
6
Examples

• Lisp
• Smalltalk
• Python
• Ruby
• JavaScript

                          7
Examples

• Lisp
• Smalltalk
• Python
• Ruby
• JavaScript

                          7
Values, not variables,
     have types
x = 5
 => 5
x.class
 => Fixnum
x = [1,2,3]
 => [1, 2, 3]
x.class
 => Array


                         8
Values, not variables,
     have types
x = 5
 => 5
x.class
 => Fixnum
x = [1,2,3]
 => [1, 2, 3]
x.class
 => Array


                         8
Values, not variables,
     have types
x = 5
 => 5
x.class
 => Fixnum
x = [1,2,3]
 => [1, 2, 3]
x.class
 => Array


                         8
Values, not variables,
     have types
x = 5
 => 5
x.class
 => Fixnum
x = [1,2,3]
 => [1, 2, 3]
x.class
 => Array


                         8
Values, not variables,
     have types
x = 5
 => 5
x.class
 => Fixnum
x = [1,2,3]
 => [1, 2, 3]
x.class
 => Array


                         8
Less code!

• No need for variable declarations
• No function parameter declarations
• No function return-type declarations


                                         9
Ahhhh!

• Are you serious?
• Can real software be developed without a
  compiler and type checking?
• How can you possibly work this way?


                                             10
Ahhhh!

• Are you serious?
• Can real software be developed without a
  compiler and type checking?
• How can you possibly work this way?
 • Answer:Very well, thank you.

                                             10
11
Flexible collections
a = [1, 2, 'three', [4, 5, 6]]
 => [1, 2, "three", [4, 5, 6]]
a.length
 => 4
a << {first_name:'Reuven',
last_name:'Lerner'}
 => [1, 2, "three", [4, 5, 6],
{:first_name=>"Reuven",
:last_name=>"Lerner"}]



                                 12
Flexible collections
   Integer
a = [1, 2, 'three', [4, 5, 6]]
 => [1, 2, "three", [4, 5, 6]]
a.length
 => 4
a << {first_name:'Reuven',
last_name:'Lerner'}
 => [1, 2, "three", [4, 5, 6],
{:first_name=>"Reuven",
:last_name=>"Lerner"}]



                                 12
Flexible collections
   Integer   String
a = [1, 2, 'three', [4, 5, 6]]
 => [1, 2, "three", [4, 5, 6]]
a.length
 => 4
a << {first_name:'Reuven',
last_name:'Lerner'}
 => [1, 2, "three", [4, 5, 6],
{:first_name=>"Reuven",
:last_name=>"Lerner"}]



                                 12
Flexible collections
   Integer   String   Array
a = [1, 2, 'three', [4, 5, 6]]
 => [1, 2, "three", [4, 5, 6]]
a.length
 => 4
a << {first_name:'Reuven',
last_name:'Lerner'}
 => [1, 2, "three", [4, 5, 6],
{:first_name=>"Reuven",
:last_name=>"Lerner"}]



                                 12
Flexible collections
   Integer   String   Array
a = [1, 2, 'three', [4, 5, 6]]
 => [1, 2, "three", [4, 5, 6]]
a.length
 => 4
a << {first_name:'Reuven',
last_name:'Lerner'}          Hash
 => [1, 2, "three", [4, 5, 6],
{:first_name=>"Reuven",
:last_name=>"Lerner"}]



                                    12
Collections

• Built-in collections (arrays, hashes) are good
  for most basic data structures
• Only create a class when you need to!
• Learning to use arrays and hashes is a key
  part of dynamic programming, and can save
  you lots of time



                                                   13
Functional tricks

• collect (map)
• detect (find)
• select (find_all)
• reject
• inject

                          14
Sorted username list
File.readlines('/etc/passwd').
 reject {|line| line =~ /^#/}.
 map {|line| line.split(':').
 first}.
 sort



                                 15
Sorted username list
File.readlines('/etc/passwd').
 reject {|line| line =~ /^#/}.
 map {|line| line.split(':').
 first}.
 sort



                                 15
Sorted username list
File.readlines('/etc/passwd').
 reject {|line| line =~ /^#/}.
 map {|line| line.split(':').
 first}.
 sort



                                 15
Sorted username list
File.readlines('/etc/passwd').
 reject {|line| line =~ /^#/}.
 map {|line| line.split(':').
 first}.
 sort



                                 15
Sorted username list
File.readlines('/etc/passwd').
 reject {|line| line =~ /^#/}.
 map {|line| line.split(':').
 first}.
 sort



                                 15
Sorted username list
File.readlines('/etc/passwd').
 reject {|line| line =~ /^#/}.
 map {|line| line.split(':').
 first}.
 sort



                                 15
Domain counter
domains = Hash.new(0)
File.readlines(list_member_file).each do |line|
      email, domain = line.chomp.split('@')
      domains[domain.downcase] += 1
end

domains.sort_by {|d| -d[1] }.
  each {|d| puts "#{d[1]} #{d[0]}" }

domains.sort_by {|d| [-d[1], d[0]] }.
  each {|d| puts "#{d[1]} #{d[0]}" }




                                                  16
Domain counter
domains = Hash.new(0)
File.readlines(list_member_file).each do |line|
      email, domain = line.chomp.split('@')
      domains[domain.downcase] += 1
end

domains.sort_by {|d| -d[1] }.
  each {|d| puts "#{d[1]} #{d[0]}" }

domains.sort_by {|d| [-d[1], d[0]] }.
  each {|d| puts "#{d[1]} #{d[0]}" }




                                                  16
Domain counter
domains = Hash.new(0)
File.readlines(list_member_file).each do |line|
      email, domain = line.chomp.split('@')
      domains[domain.downcase] += 1
end

domains.sort_by {|d| -d[1] }.
  each {|d| puts "#{d[1]} #{d[0]}" }

domains.sort_by {|d| [-d[1], d[0]] }.
  each {|d| puts "#{d[1]} #{d[0]}" }




                                                  16
Domain counter
domains = Hash.new(0)
File.readlines(list_member_file).each do |line|
      email, domain = line.chomp.split('@')
      domains[domain.downcase] += 1
end

domains.sort_by {|d| -d[1] }.
  each {|d| puts "#{d[1]} #{d[0]}" }

domains.sort_by {|d| [-d[1], d[0]] }.
  each {|d| puts "#{d[1]} #{d[0]}" }




                                                  16
Domain counter
domains = Hash.new(0)
File.readlines(list_member_file).each do |line|
      email, domain = line.chomp.split('@')
      domains[domain.downcase] += 1
end

domains.sort_by {|d| -d[1] }.
  each {|d| puts "#{d[1]} #{d[0]}" }

domains.sort_by {|d| [-d[1], d[0]] }.
  each {|d| puts "#{d[1]} #{d[0]}" }




                                                  16
Domain counter
domains = Hash.new(0)
File.readlines(list_member_file).each do |line|
      email, domain = line.chomp.split('@')
      domains[domain.downcase] += 1
end

domains.sort_by {|d| -d[1] }.
  each {|d| puts "#{d[1]} #{d[0]}" }

domains.sort_by {|d| [-d[1], d[0]] }.
  each {|d| puts "#{d[1]} #{d[0]}" }




                                                  16
Domain counter
domains = Hash.new(0)
File.readlines(list_member_file).each do |line|
      email, domain = line.chomp.split('@')
      domains[domain.downcase] += 1
end

domains.sort_by {|d| -d[1] }.
  each {|d| puts "#{d[1]} #{d[0]}" }

domains.sort_by {|d| [-d[1], d[0]] }.
  each {|d| puts "#{d[1]} #{d[0]}" }




                                                  16
Total cost of order

• Total price of an order of books:
  total_price = books.
  inject(0) do |sum, b|
     sum + (b.price * b.quantity)
  end




                                      17
Real-time require
require 'jobs/a'
require 'jobs/b'
require 'jobs/c'

Dir["#{Rails.root}/app/jobs/
*.rb"].
 each { |file| require file }


                                18
Real-time require
require 'jobs/a'
require 'jobs/b'
require 'jobs/c'

Dir["#{Rails.root}/app/jobs/
*.rb"].
 each { |file| require file }


                                18
Real-time require
require 'jobs/a'
require 'jobs/b'
require 'jobs/c'

Dir["#{Rails.root}/app/jobs/
*.rb"].
 each { |file| require file }


                                18
Classes

• Classes allow you to abstract behavior
• Classes contain data and methods
• It’s easy and fast to create a class


                                           19
Defining classes
class Person
end
 => nil
p = Person.new
 => #<Person:0x00000102105740>
p.class
 => Person
p.class.ancestors
 => [Person, Object, Wirble::Shortcuts,
PP::ObjectMixin, Kernel, BasicObject]



                                          20
Defining classes
class Person
end
 => nil
p = Person.new
 => #<Person:0x00000102105740>
p.class
 => Person
p.class.ancestors
 => [Person, Object, Wirble::Shortcuts,
PP::ObjectMixin, Kernel, BasicObject]



                                          20
Defining classes
class Person
end
 => nil
p = Person.new
 => #<Person:0x00000102105740>
p.class
 => Person
p.class.ancestors
 => [Person, Object, Wirble::Shortcuts,
PP::ObjectMixin, Kernel, BasicObject]



                                          20
Defining classes
class Person
end
 => nil
p = Person.new
 => #<Person:0x00000102105740>
p.class
 => Person
p.class.ancestors
 => [Person, Object, Wirble::Shortcuts,
PP::ObjectMixin, Kernel, BasicObject]



                                          20
Defining classes
class Person
end
 => nil
p = Person.new
 => #<Person:0x00000102105740>
p.class
 => Person
p.class.ancestors
 => [Person, Object, Wirble::Shortcuts,
PP::ObjectMixin, Kernel, BasicObject]



                                          20
Reflection
> p.methods # or Person.instance_methods
 =>
[:po, :poc, :pretty_print, :pretty_print_cycle,
:pretty_print_instance_variables, :pretty_print_inspect,
:nil?, :, :, :!, :eql?, :hash, :=>, :class, :singleton_class,
:clone, :dup, :initialize_dup, :initialize_clone, :taint,
:tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze,
:frozen?, :to_s, :inspect, :methods, :singleton_methods,
:protected_methods, :private_methods, :public_methods,
:instance_variables, :instance_variable_get,
:instance_variable_set, :instance_variable_defined?,
:instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send,
:respond_to?, :respond_to_missing?, :extend, :display,
:method, :public_method, :define_singleton_method, :__id__,
:object_id, :to_enum, :enum_for, :pretty_inspect, :ri, :,
:equal?, :!, :!, :instance_eval, :instance_exec, :__send__]




                                                                21
Predicates
p.methods.grep(/?$/)
 =>
[:nil?, :eql?, :tainted?,
:untrusted?, :frozen?,
:instance_variable_defined?,
:instance_of?, :kind_of?, :is_a?,
:respond_to?, :respond_to_missing?,
:equal?]



                                      22
“Local” methods
class Person
  def blah
    "blah"
    end
  end
 => nil
p = Person.new
p.class.instance_methods -
p.class.superclass.instance_methods
 => [:blah]



                                      23
“Local” methods
class Person
  def blah
    "blah"
    end
  end
 => nil
p = Person.new
p.class.instance_methods -
p.class.superclass.instance_methods
 => [:blah]



                                      23
“Local” methods
class Person
  def blah
    "blah"
    end
  end
 => nil
p = Person.new
p.class.instance_methods -
p.class.superclass.instance_methods
 => [:blah]



                                      23
“Local” methods
class Person
  def blah
    "blah"
    end
  end
 => nil
p = Person.new
p.class.instance_methods -
p.class.superclass.instance_methods
 => [:blah]



                                      23
Calling methods

a.length
 => 5
a.send(:length)
 => 5
a.send("length".to_sym)
 => 5




                          24
Calling methods

a.length
 => 5
a.send(:length)
 => 5
a.send("length".to_sym)
 => 5




                          24
Calling methods

a.length
 => 5
a.send(:length)
 => 5
a.send("length".to_sym)
 => 5




                          24
Calling methods

a.length
 => 5
a.send(:length)
 => 5
a.send("length".to_sym)
 => 5




                          24
Preferences become
    method calls
def bid_or_ask
    is_ask? ? :ask : :bid
  end


trade_amount = net_basis *
(end_rate.send(bid_or_ask) -
start_rate.send(bid_or_ask))



                               25
Preferences become
    method calls
def bid_or_ask
    is_ask? ? :ask : :bid
  end


trade_amount = net_basis *
(end_rate.send(bid_or_ask) -
start_rate.send(bid_or_ask))



                               25
Preferences become
    method calls
def bid_or_ask
    is_ask? ? :ask : :bid
  end


trade_amount = net_basis *
(end_rate.send(bid_or_ask) -
start_rate.send(bid_or_ask))



                               25
Define similar methods
['preview', 'applet', 'info',
'procedures', 'discuss', 'files',
'history', 'tags', 'family', 'upload',
'permissions'].each do |tab_name|
  define_method(
      "browse_#{tab_name}_tab".to_sym ) do
        render :layout => 'browse_tab'
  end
end



                                             26
Define similar methods
['preview', 'applet', 'info',
'procedures', 'discuss', 'files',
'history', 'tags', 'family', 'upload',
'permissions'].each do |tab_name|
  define_method(
      "browse_#{tab_name}_tab".to_sym ) do
        render :layout => 'browse_tab'
  end
end



                                             26
Define similar methods
['preview', 'applet', 'info',
'procedures', 'discuss', 'files',
'history', 'tags', 'family', 'upload',
'permissions'].each do |tab_name|
  define_method(
      "browse_#{tab_name}_tab".to_sym ) do
        render :layout => 'browse_tab'
  end
end



                                             26
Define similar methods
['preview', 'applet', 'info',
'procedures', 'discuss', 'files',
'history', 'tags', 'family', 'upload',
'permissions'].each do |tab_name|
  define_method(
      "browse_#{tab_name}_tab".to_sym ) do
        render :layout => 'browse_tab'
  end
end



                                             26
Define similar methods
['preview', 'applet', 'info',
'procedures', 'discuss', 'files',
'history', 'tags', 'family', 'upload',
'permissions'].each do |tab_name|
  define_method(
      "browse_#{tab_name}_tab".to_sym ) do
        render :layout => 'browse_tab'
  end
end



                                             26
Searching by result
'a'.what?('A')
"a".upcase == "A"
"a".capitalize == "A"
"a".swapcase == "A"
"a".upcase! == "A"
"a".capitalize! == "A"
"a".swapcase! == "A"
[:upcase, :capitalize, :swapcase,
:upcase!, :capitalize!, :swapcase!]


                                      27
Searching by result
'a'.what?('A')
"a".upcase == "A"
"a".capitalize == "A"
"a".swapcase == "A"
"a".upcase! == "A"
"a".capitalize! == "A"
"a".swapcase! == "A"
[:upcase, :capitalize, :swapcase,
:upcase!, :capitalize!, :swapcase!]


                                      27
Searching by result
'a'.what?('A')
"a".upcase == "A"
"a".capitalize == "A"
"a".swapcase == "A"
"a".upcase! == "A"
"a".capitalize! == "A"
"a".swapcase! == "A"
[:upcase, :capitalize, :swapcase,
:upcase!, :capitalize!, :swapcase!]


                                      27
Searching by result
'a'.what?('A')
"a".upcase == "A"
"a".capitalize == "A"
"a".swapcase == "A"
"a".upcase! == "A"
"a".capitalize! == "A"
"a".swapcase! == "A"
[:upcase, :capitalize, :swapcase,
:upcase!, :capitalize!, :swapcase!]


                                      27
Monkey patching!
class Fixnum
  def *(other)
      self + other
  end
end


6*5
 => 11


                         28
Monkey patching!
class Fixnum
  def *(other)
      self + other
  end
end


6*5
 => 11


                         28
Monkey patching!
class Fixnum
  def *(other)
      self + other
  end
end


6*5
 => 11


                         28
Example: “Whiny nils”

• Try to invoke “id” on nil, and Rails will
  complain
  @y.id
  RuntimeError: Called id for nil,
  which would mistakenly be 4 -- if
  you really wanted the id of nil,
  use object_id



                                              29
Example: RSpec
it "should have a unique name" do
  c1 = Currency.new(@valid_attributes)
  c1.save!
  c2 =
      Currency.new(
        :name => @valid_attributes[:name],
        :abbreviation => 'XYZ')
  c1.should be_valid
  c2.should_not be_valid
end




                                             30
Example: RSpec
it "should have a unique name" do
  c1 = Currency.new(@valid_attributes)
  c1.save!
  c2 =
      Currency.new(
        :name => @valid_attributes[:name],
        :abbreviation => 'XYZ')
  c1.should be_valid
  c2.should_not be_valid
end




                                             30
Example: RSpec
it "should have a unique name" do
  c1 = Currency.new(@valid_attributes)
  c1.save!
  c2 =
      Currency.new(
        :name => @valid_attributes[:name],
        :abbreviation => 'XYZ')
  c1.should be_valid
  c2.should_not be_valid
end




                                             30
method_missing


• If no method exists, then method_missing
  is invoked, passing the method name, args




                                              31
Dynamic finders
Reading.find_all_by_longitude_and_d
evice_id(0, 3)
 => [#<Reading id: 46, longitude:
#<BigDecimal:24439a8,'0.0',9(18)>,
latitude: ... ]


Reading.instance_methods.grep(/
long/)
 => []


                                      32
Dynamic finders
Reading.find_all_by_longitude_and_d
evice_id(0, 3)
 => [#<Reading id: 46, longitude:
#<BigDecimal:24439a8,'0.0',9(18)>,
latitude: ... ]


Reading.instance_methods.grep(/
long/)
 => []


                                      32
Dynamic finders
Reading.find_all_by_longitude_and_d
evice_id(0, 3)
 => [#<Reading id: 46, longitude:
#<BigDecimal:24439a8,'0.0',9(18)>,
latitude: ... ]


Reading.instance_methods.grep(/
long/)
 => []


                                      32
Hash methods as keys
class Hash
  def method_missing(name, *args)
      if has_key?(name)
        self[name]
      end
  end
end



                                    33
Example
h = {a:1, b:2}        => 3
=> {:a=>1, :b=>2}     h.c
h[:a]                 => nil
=> 1                  h[:d] = 4
h.a                   => 4
=> 1                  h.d
h['c'] = 3            => 4


                                  34
Example
h = {a:1, b:2}        => 3
=> {:a=>1, :b=>2}     h.c
h[:a]                 => nil
=> 1                  h[:d] = 4
h.a                   => 4
=> 1                  h.d
h['c'] = 3            => 4


                                  34
Example
h = {a:1, b:2}        => 3
=> {:a=>1, :b=>2}     h.c
h[:a]                 => nil
=> 1                  h[:d] = 4
h.a                   => 4
=> 1                  h.d
h['c'] = 3            => 4


                                  34
Example
h = {a:1, b:2}        => 3
=> {:a=>1, :b=>2}     h.c
h[:a]                 => nil
=> 1                  h[:d] = 4
h.a                   => 4
=> 1                  h.d
h['c'] = 3            => 4


                                  34
Example
h = {a:1, b:2}        => 3
=> {:a=>1, :b=>2}     h.c
h[:a]                 => nil
=> 1                  h[:d] = 4
h.a                   => 4
=> 1                  h.d
h['c'] = 3            => 4


                                  34
Example
h = {a:1, b:2}        => 3
=> {:a=>1, :b=>2}     h.c
h[:a]                 => nil
=> 1                  h[:d] = 4
h.a                   => 4
=> 1                  h.d
h['c'] = 3            => 4


                                  34
Example
h = {a:1, b:2}        => 3
=> {:a=>1, :b=>2}     h.c
h[:a]                 => nil
=> 1                  h[:d] = 4
h.a                   => 4
=> 1                  h.d
h['c'] = 3            => 4


                                  34
Example
h = {a:1, b:2}        => 3
=> {:a=>1, :b=>2}     h.c
h[:a]                 => nil
=> 1                  h[:d] = 4
h.a                   => 4
=> 1                  h.d
h['c'] = 3            => 4


                                  34
Hash method access
class Hash
  def method_missing(method_name, *arguments)
      if has_key?(method_name)
        self[method_name]
      elsif has_key?(method_name.to_s)
        self[method_name.to_s]
      else
        nil
      end
  end
end




                                                35
Hash method access
class Hash
  def method_missing(method_name, *arguments)
      if has_key?(method_name)
        self[method_name]
      elsif has_key?(method_name.to_s)
        self[method_name.to_s]
      else
        nil
      end
  end
end




                                                35
Hash method access
class Hash
  def method_missing(method_name, *arguments)
      if has_key?(method_name)
        self[method_name]
      elsif has_key?(method_name.to_s)
        self[method_name.to_s]
      else
        nil
      end
  end
end




                                                35
Hash method access
class Hash
  def method_missing(method_name, *arguments)
      if has_key?(method_name)
        self[method_name]
      elsif has_key?(method_name.to_s)
        self[method_name.to_s]
      else
        nil
      end
  end
end




                                                35
Hash method access
class Hash
  def method_missing(method_name, *arguments)
      if has_key?(method_name)
        self[method_name]
      elsif has_key?(method_name.to_s)
        self[method_name.to_s]
      else
        nil
      end
  end
end




                                                35
Hash method access
class Hash
  def method_missing(method_name, *arguments)
      if has_key?(method_name)
        self[method_name]
      elsif has_key?(method_name.to_s)
        self[method_name.to_s]
      else
        nil
      end
  end
end




                                                35
xml.instruct! :xml, :version=>"1.0"
xml.rss(:version=>"2.0"){
  xml.channel{
    xml.title("Modeling Commons updates")
    xml.link("http://modelingcommons.org/")
    xml.description("NetLogo Modeling Commons")
    xml.language('en-us')
      for update in @updates
        xml.item do
          xml.title(post.title)
          xml.description(post.html_content)
          xml.author("Your Name Here")
          xml.pubDate(post.created_on.strftime("%a,
%d %b %Y %H:%M:%S %z"))
          xml.link(post.link)
          xml.guid(post.link)
        end
      end
  }
}

                                                      36
xml.instruct! :xml, :version=>"1.0"
xml.rss(:version=>"2.0"){
  xml.channel{
    xml.title("Modeling Commons updates")
    xml.link("http://modelingcommons.org/")
    xml.description("NetLogo Modeling Commons")
    xml.language('en-us')
      for update in @updates
        xml.item do
          xml.title(post.title)
          xml.description(post.html_content)
          xml.author("Your Name Here")
          xml.pubDate(post.created_on.strftime("%a,
%d %b %Y %H:%M:%S %z"))
          xml.link(post.link)
          xml.guid(post.link)
        end
      end
  }
}

                                                      36
xml.instruct! :xml, :version=>"1.0"
xml.rss(:version=>"2.0"){
  xml.channel{
    xml.title("Modeling Commons updates")
    xml.link("http://modelingcommons.org/")
    xml.description("NetLogo Modeling Commons")
    xml.language('en-us')
      for update in @updates
        xml.item do
          xml.title(post.title)
          xml.description(post.html_content)
          xml.author("Your Name Here")
          xml.pubDate(post.created_on.strftime("%a,
%d %b %Y %H:%M:%S %z"))
          xml.link(post.link)
          xml.guid(post.link)
        end
      end
  }
}

                                                      36
xml.instruct! :xml, :version=>"1.0"
xml.rss(:version=>"2.0"){
  xml.channel{
    xml.title("Modeling Commons updates")
    xml.link("http://modelingcommons.org/")
    xml.description("NetLogo Modeling Commons")
    xml.language('en-us')
      for update in @updates
        xml.item do
          xml.title(post.title)
          xml.description(post.html_content)
          xml.author("Your Name Here")
          xml.pubDate(post.created_on.strftime("%a,
%d %b %Y %H:%M:%S %z"))
          xml.link(post.link)
          xml.guid(post.link)
        end
      end
  }
}

                                                      36
xml.instruct! :xml, :version=>"1.0"
xml.rss(:version=>"2.0"){
  xml.channel{
    xml.title("Modeling Commons updates")
    xml.link("http://modelingcommons.org/")
    xml.description("NetLogo Modeling Commons")
    xml.language('en-us')
      for update in @updates
        xml.item do
          xml.title(post.title)
          xml.description(post.html_content)
          xml.author("Your Name Here")
          xml.pubDate(post.created_on.strftime("%a,
%d %b %Y %H:%M:%S %z"))
          xml.link(post.link)
          xml.guid(post.link)
        end
      end
  }
}

                                                      36
xml.instruct! :xml, :version=>"1.0"
xml.rss(:version=>"2.0"){
  xml.channel{
    xml.title("Modeling Commons updates")
    xml.link("http://modelingcommons.org/")
    xml.description("NetLogo Modeling Commons")
    xml.language('en-us')
      for update in @updates
        xml.item do
          xml.title(post.title)
          xml.description(post.html_content)
          xml.author("Your Name Here")
          xml.pubDate(post.created_on.strftime("%a,
%d %b %Y %H:%M:%S %z"))
          xml.link(post.link)
          xml.guid(post.link)
        end
      end
  }
}

                                                      36
Module includes
module MyStuff
      def self.included(klass)
        klass.extend(ClassMethods)
      end
      module ClassMethods
        def foo; end
      end
end


                                     37
Module includes
module MyStuff
      def self.included(klass)
        klass.extend(ClassMethods)
      end
      module ClassMethods
        def foo; end
      end
end


                                     37
Module includes
module MyStuff
      def self.included(klass)
        klass.extend(ClassMethods)
      end
      module ClassMethods
        def foo; end
      end
end


                                     37
Module includes
module MyStuff
      def self.included(klass)
        klass.extend(ClassMethods)
      end
      module ClassMethods
        def foo; end
      end
end


                                     37
Thanks!
     (Any questions?)

• Call me: 054-496-8405
• E-mail me: reuven@lerner.co.il
• Interrupt me: reuvenlerner (Skype/AIM)


                                           38

More Related Content

PDF
Programming in python Unit-1 Part-1
PDF
Puppet Language 4.0 - PuppetConf 2014
PPTX
Practically Functional
PPT
Sql
PDF
Scala In The Wild
PPT
Python tutorial
PPT
Python tutorialfeb152012
PDF
Php summary
Programming in python Unit-1 Part-1
Puppet Language 4.0 - PuppetConf 2014
Practically Functional
Sql
Scala In The Wild
Python tutorial
Python tutorialfeb152012
Php summary

What's hot (20)

PPTX
Python dictionary
KEY
1 the ruby way
PDF
functional groovy
PPTX
Web Application Development using PHP Chapter 3
PDF
DBIx::Class beginners
PDF
Python Dictionary
PPTX
Learn python - for beginners - part-2
PDF
DBIx::Class introduction - 2010
ODP
Database Programming with Perl and DBIx::Class
PPTX
Oracle Database - JSON and the In-Memory Database
PDF
Slides chapter3part1 ruby-forjavaprogrammers
PPTX
Hadoop Streaming Tutorial With Python
PDF
This Is Not Your Father's Java
PDF
UKOUG Tech14 - Getting Started With JSON in the Database
PPTX
Starting with JSON Path Expressions in Oracle 12.1.0.2
PDF
R for Pythonistas (PyData NYC 2017)
PDF
A quick python_tour
PDF
The Ring programming language version 1.5.4 book - Part 6 of 185
ODP
What's new, what's hot in PHP 5.3
PDF
DevNation'15 - Using Lambda Expressions to Query a Datastore
Python dictionary
1 the ruby way
functional groovy
Web Application Development using PHP Chapter 3
DBIx::Class beginners
Python Dictionary
Learn python - for beginners - part-2
DBIx::Class introduction - 2010
Database Programming with Perl and DBIx::Class
Oracle Database - JSON and the In-Memory Database
Slides chapter3part1 ruby-forjavaprogrammers
Hadoop Streaming Tutorial With Python
This Is Not Your Father's Java
UKOUG Tech14 - Getting Started With JSON in the Database
Starting with JSON Path Expressions in Oracle 12.1.0.2
R for Pythonistas (PyData NYC 2017)
A quick python_tour
The Ring programming language version 1.5.4 book - Part 6 of 185
What's new, what's hot in PHP 5.3
DevNation'15 - Using Lambda Expressions to Query a Datastore
Ad

Viewers also liked (6)

PDF
The Rise of Dynamic Languages
PPTX
Elements of dynamic programming
PPT
Dynamic pgmming
PPT
Dynamic programming
PPTX
Dynamic Programming
PPTX
Dynamic Programming
The Rise of Dynamic Languages
Elements of dynamic programming
Dynamic pgmming
Dynamic programming
Dynamic Programming
Dynamic Programming
Ad

Similar to Dynamic languages, for software craftmanship group (20)

KEY
Desarrollando aplicaciones web en minutos
PDF
Connecting the Worlds of Java and Ruby with JRuby
PDF
Ruby and Rails by example
PDF
Ruby — An introduction
PDF
Ruby on Rails
PDF
The Joy Of Ruby
PPTX
Why learn new programming languages
PDF
Introduction to Ruby
PDF
Let’s Talk About Ruby
PDF
Ruby is Awesome
PDF
Ruby and Rails by Example (GeekCamp edition)
PDF
Introduction to Ruby Programming Language
PDF
IJTC%202009%20JRuby
PDF
IJTC%202009%20JRuby
KEY
Intro to Ruby - Twin Cities Code Camp 7
KEY
KEY
An introduction to Ruby
ODP
Ruby Basics by Rafiq
PDF
Ruby on Rails Presentation
Desarrollando aplicaciones web en minutos
Connecting the Worlds of Java and Ruby with JRuby
Ruby and Rails by example
Ruby — An introduction
Ruby on Rails
The Joy Of Ruby
Why learn new programming languages
Introduction to Ruby
Let’s Talk About Ruby
Ruby is Awesome
Ruby and Rails by Example (GeekCamp edition)
Introduction to Ruby Programming Language
IJTC%202009%20JRuby
IJTC%202009%20JRuby
Intro to Ruby - Twin Cities Code Camp 7
An introduction to Ruby
Ruby Basics by Rafiq
Ruby on Rails Presentation

More from Reuven Lerner (20)

PDF
Technical training business talk.key
PDF
Big Data — Your new best friend
PDF
PostgreSQL, your NoSQL database
PDF
Python's magic methods
PDF
What can Ruby learn from Python (and vice versa)?
PDF
Functional Python Webinar from October 22nd, 2014
PDF
Web APIs: The future of software
PDF
Rails israel 2013
PDF
Intro to cloud computing — MegaCOMM 2013, Jerusalem
KEY
PostgreSQL
KEY
Rails traps
KEY
Modern Web technologies (and why you should care): Megacomm, Jerusalem, Febru...
KEY
Rails development environment talk
KEY
Git talk from Open 2011 conference in Israel
KEY
Modern Web Technologies — Jerusalem Web Professionals, January 2011
KEY
PostgreSQL talk, Database 2011 conference
PDF
ActiveRecord 2.3
KEY
Ruby objects
KEY
Rails console
KEY
Rails tools
Technical training business talk.key
Big Data — Your new best friend
PostgreSQL, your NoSQL database
Python's magic methods
What can Ruby learn from Python (and vice versa)?
Functional Python Webinar from October 22nd, 2014
Web APIs: The future of software
Rails israel 2013
Intro to cloud computing — MegaCOMM 2013, Jerusalem
PostgreSQL
Rails traps
Modern Web technologies (and why you should care): Megacomm, Jerusalem, Febru...
Rails development environment talk
Git talk from Open 2011 conference in Israel
Modern Web Technologies — Jerusalem Web Professionals, January 2011
PostgreSQL talk, Database 2011 conference
ActiveRecord 2.3
Ruby objects
Rails console
Rails tools

Recently uploaded (20)

PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PDF
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
PDF
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
PDF
KodekX | Application Modernization Development
PPTX
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
PPTX
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication
PPT
Teaching material agriculture food technology
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PDF
Machine learning based COVID-19 study performance prediction
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
DOCX
The AUB Centre for AI in Media Proposal.docx
PPTX
Understanding_Digital_Forensics_Presentation.pptx
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PDF
Electronic commerce courselecture one. Pdf
PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
PDF
Network Security Unit 5.pdf for BCA BBA.
PPTX
MYSQL Presentation for SQL database connectivity
PDF
[발표본] 너의 과제는 클라우드에 있어_KTDS_김동현_20250524.pdf
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
KodekX | Application Modernization Development
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication
Teaching material agriculture food technology
Mobile App Security Testing_ A Comprehensive Guide.pdf
Machine learning based COVID-19 study performance prediction
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
The AUB Centre for AI in Media Proposal.docx
Understanding_Digital_Forensics_Presentation.pptx
The Rise and Fall of 3GPP – Time for a Sabbatical?
Electronic commerce courselecture one. Pdf
Reach Out and Touch Someone: Haptics and Empathic Computing
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
Network Security Unit 5.pdf for BCA BBA.
MYSQL Presentation for SQL database connectivity
[발표본] 너의 과제는 클라우드에 있어_KTDS_김동현_20250524.pdf

Dynamic languages, for software craftmanship group

  • 1. Dynamic languages Reuven M. Lerner • reuven@lerner.co.il Software Craftsmanship Group, Tel Aviv March 22nd, 2011 1
  • 2. Who am I? • Web developer, software architect, consultant, trainer • Linux Journal columnist since 1996 • Mostly Ruby on Rails + PostgreSQL, but also Python, PHP, jQuery, and lots more... 2
  • 3. Language wars! • Static vs. dynamic languages • I’m here representing the good guys • (Just kidding. Mostly.) • Not just a different language — a different mindset and set of expectations 3
  • 4. What is a dynamic language? • Dynamic typing • Usually interpreted (or JIT compiled) • Interactive shell for experimenting • Closures (anonymous code blocks) • Flexible, “living” object model • Result: Short, powerful, reusable code 4
  • 5. Who is in charge? • Static language: The language is in charge, and it’s for your own good! • Dynamic language: The programmer is in charge, and changes the language to suit his or her needs 5
  • 6. 6
  • 7. Examples • Lisp • Smalltalk • Python • Ruby • JavaScript 7
  • 8. Examples • Lisp • Smalltalk • Python • Ruby • JavaScript 7
  • 9. Values, not variables, have types x = 5 => 5 x.class => Fixnum x = [1,2,3] => [1, 2, 3] x.class => Array 8
  • 10. Values, not variables, have types x = 5 => 5 x.class => Fixnum x = [1,2,3] => [1, 2, 3] x.class => Array 8
  • 11. Values, not variables, have types x = 5 => 5 x.class => Fixnum x = [1,2,3] => [1, 2, 3] x.class => Array 8
  • 12. Values, not variables, have types x = 5 => 5 x.class => Fixnum x = [1,2,3] => [1, 2, 3] x.class => Array 8
  • 13. Values, not variables, have types x = 5 => 5 x.class => Fixnum x = [1,2,3] => [1, 2, 3] x.class => Array 8
  • 14. Less code! • No need for variable declarations • No function parameter declarations • No function return-type declarations 9
  • 15. Ahhhh! • Are you serious? • Can real software be developed without a compiler and type checking? • How can you possibly work this way? 10
  • 16. Ahhhh! • Are you serious? • Can real software be developed without a compiler and type checking? • How can you possibly work this way? • Answer:Very well, thank you. 10
  • 17. 11
  • 18. Flexible collections a = [1, 2, 'three', [4, 5, 6]] => [1, 2, "three", [4, 5, 6]] a.length => 4 a << {first_name:'Reuven', last_name:'Lerner'} => [1, 2, "three", [4, 5, 6], {:first_name=>"Reuven", :last_name=>"Lerner"}] 12
  • 19. Flexible collections Integer a = [1, 2, 'three', [4, 5, 6]] => [1, 2, "three", [4, 5, 6]] a.length => 4 a << {first_name:'Reuven', last_name:'Lerner'} => [1, 2, "three", [4, 5, 6], {:first_name=>"Reuven", :last_name=>"Lerner"}] 12
  • 20. Flexible collections Integer String a = [1, 2, 'three', [4, 5, 6]] => [1, 2, "three", [4, 5, 6]] a.length => 4 a << {first_name:'Reuven', last_name:'Lerner'} => [1, 2, "three", [4, 5, 6], {:first_name=>"Reuven", :last_name=>"Lerner"}] 12
  • 21. Flexible collections Integer String Array a = [1, 2, 'three', [4, 5, 6]] => [1, 2, "three", [4, 5, 6]] a.length => 4 a << {first_name:'Reuven', last_name:'Lerner'} => [1, 2, "three", [4, 5, 6], {:first_name=>"Reuven", :last_name=>"Lerner"}] 12
  • 22. Flexible collections Integer String Array a = [1, 2, 'three', [4, 5, 6]] => [1, 2, "three", [4, 5, 6]] a.length => 4 a << {first_name:'Reuven', last_name:'Lerner'} Hash => [1, 2, "three", [4, 5, 6], {:first_name=>"Reuven", :last_name=>"Lerner"}] 12
  • 23. Collections • Built-in collections (arrays, hashes) are good for most basic data structures • Only create a class when you need to! • Learning to use arrays and hashes is a key part of dynamic programming, and can save you lots of time 13
  • 24. Functional tricks • collect (map) • detect (find) • select (find_all) • reject • inject 14
  • 25. Sorted username list File.readlines('/etc/passwd'). reject {|line| line =~ /^#/}. map {|line| line.split(':'). first}. sort 15
  • 26. Sorted username list File.readlines('/etc/passwd'). reject {|line| line =~ /^#/}. map {|line| line.split(':'). first}. sort 15
  • 27. Sorted username list File.readlines('/etc/passwd'). reject {|line| line =~ /^#/}. map {|line| line.split(':'). first}. sort 15
  • 28. Sorted username list File.readlines('/etc/passwd'). reject {|line| line =~ /^#/}. map {|line| line.split(':'). first}. sort 15
  • 29. Sorted username list File.readlines('/etc/passwd'). reject {|line| line =~ /^#/}. map {|line| line.split(':'). first}. sort 15
  • 30. Sorted username list File.readlines('/etc/passwd'). reject {|line| line =~ /^#/}. map {|line| line.split(':'). first}. sort 15
  • 31. Domain counter domains = Hash.new(0) File.readlines(list_member_file).each do |line| email, domain = line.chomp.split('@') domains[domain.downcase] += 1 end domains.sort_by {|d| -d[1] }. each {|d| puts "#{d[1]} #{d[0]}" } domains.sort_by {|d| [-d[1], d[0]] }. each {|d| puts "#{d[1]} #{d[0]}" } 16
  • 32. Domain counter domains = Hash.new(0) File.readlines(list_member_file).each do |line| email, domain = line.chomp.split('@') domains[domain.downcase] += 1 end domains.sort_by {|d| -d[1] }. each {|d| puts "#{d[1]} #{d[0]}" } domains.sort_by {|d| [-d[1], d[0]] }. each {|d| puts "#{d[1]} #{d[0]}" } 16
  • 33. Domain counter domains = Hash.new(0) File.readlines(list_member_file).each do |line| email, domain = line.chomp.split('@') domains[domain.downcase] += 1 end domains.sort_by {|d| -d[1] }. each {|d| puts "#{d[1]} #{d[0]}" } domains.sort_by {|d| [-d[1], d[0]] }. each {|d| puts "#{d[1]} #{d[0]}" } 16
  • 34. Domain counter domains = Hash.new(0) File.readlines(list_member_file).each do |line| email, domain = line.chomp.split('@') domains[domain.downcase] += 1 end domains.sort_by {|d| -d[1] }. each {|d| puts "#{d[1]} #{d[0]}" } domains.sort_by {|d| [-d[1], d[0]] }. each {|d| puts "#{d[1]} #{d[0]}" } 16
  • 35. Domain counter domains = Hash.new(0) File.readlines(list_member_file).each do |line| email, domain = line.chomp.split('@') domains[domain.downcase] += 1 end domains.sort_by {|d| -d[1] }. each {|d| puts "#{d[1]} #{d[0]}" } domains.sort_by {|d| [-d[1], d[0]] }. each {|d| puts "#{d[1]} #{d[0]}" } 16
  • 36. Domain counter domains = Hash.new(0) File.readlines(list_member_file).each do |line| email, domain = line.chomp.split('@') domains[domain.downcase] += 1 end domains.sort_by {|d| -d[1] }. each {|d| puts "#{d[1]} #{d[0]}" } domains.sort_by {|d| [-d[1], d[0]] }. each {|d| puts "#{d[1]} #{d[0]}" } 16
  • 37. Domain counter domains = Hash.new(0) File.readlines(list_member_file).each do |line| email, domain = line.chomp.split('@') domains[domain.downcase] += 1 end domains.sort_by {|d| -d[1] }. each {|d| puts "#{d[1]} #{d[0]}" } domains.sort_by {|d| [-d[1], d[0]] }. each {|d| puts "#{d[1]} #{d[0]}" } 16
  • 38. Total cost of order • Total price of an order of books: total_price = books. inject(0) do |sum, b| sum + (b.price * b.quantity) end 17
  • 39. Real-time require require 'jobs/a' require 'jobs/b' require 'jobs/c' Dir["#{Rails.root}/app/jobs/ *.rb"]. each { |file| require file } 18
  • 40. Real-time require require 'jobs/a' require 'jobs/b' require 'jobs/c' Dir["#{Rails.root}/app/jobs/ *.rb"]. each { |file| require file } 18
  • 41. Real-time require require 'jobs/a' require 'jobs/b' require 'jobs/c' Dir["#{Rails.root}/app/jobs/ *.rb"]. each { |file| require file } 18
  • 42. Classes • Classes allow you to abstract behavior • Classes contain data and methods • It’s easy and fast to create a class 19
  • 43. Defining classes class Person end => nil p = Person.new => #<Person:0x00000102105740> p.class => Person p.class.ancestors => [Person, Object, Wirble::Shortcuts, PP::ObjectMixin, Kernel, BasicObject] 20
  • 44. Defining classes class Person end => nil p = Person.new => #<Person:0x00000102105740> p.class => Person p.class.ancestors => [Person, Object, Wirble::Shortcuts, PP::ObjectMixin, Kernel, BasicObject] 20
  • 45. Defining classes class Person end => nil p = Person.new => #<Person:0x00000102105740> p.class => Person p.class.ancestors => [Person, Object, Wirble::Shortcuts, PP::ObjectMixin, Kernel, BasicObject] 20
  • 46. Defining classes class Person end => nil p = Person.new => #<Person:0x00000102105740> p.class => Person p.class.ancestors => [Person, Object, Wirble::Shortcuts, PP::ObjectMixin, Kernel, BasicObject] 20
  • 47. Defining classes class Person end => nil p = Person.new => #<Person:0x00000102105740> p.class => Person p.class.ancestors => [Person, Object, Wirble::Shortcuts, PP::ObjectMixin, Kernel, BasicObject] 20
  • 48. Reflection > p.methods # or Person.instance_methods => [:po, :poc, :pretty_print, :pretty_print_cycle, :pretty_print_instance_variables, :pretty_print_inspect, :nil?, :, :, :!, :eql?, :hash, :=>, :class, :singleton_class, :clone, :dup, :initialize_dup, :initialize_clone, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :to_s, :inspect, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :respond_to_missing?, :extend, :display, :method, :public_method, :define_singleton_method, :__id__, :object_id, :to_enum, :enum_for, :pretty_inspect, :ri, :, :equal?, :!, :!, :instance_eval, :instance_exec, :__send__] 21
  • 49. Predicates p.methods.grep(/?$/) => [:nil?, :eql?, :tainted?, :untrusted?, :frozen?, :instance_variable_defined?, :instance_of?, :kind_of?, :is_a?, :respond_to?, :respond_to_missing?, :equal?] 22
  • 50. “Local” methods class Person def blah "blah" end end => nil p = Person.new p.class.instance_methods - p.class.superclass.instance_methods => [:blah] 23
  • 51. “Local” methods class Person def blah "blah" end end => nil p = Person.new p.class.instance_methods - p.class.superclass.instance_methods => [:blah] 23
  • 52. “Local” methods class Person def blah "blah" end end => nil p = Person.new p.class.instance_methods - p.class.superclass.instance_methods => [:blah] 23
  • 53. “Local” methods class Person def blah "blah" end end => nil p = Person.new p.class.instance_methods - p.class.superclass.instance_methods => [:blah] 23
  • 54. Calling methods a.length => 5 a.send(:length) => 5 a.send("length".to_sym) => 5 24
  • 55. Calling methods a.length => 5 a.send(:length) => 5 a.send("length".to_sym) => 5 24
  • 56. Calling methods a.length => 5 a.send(:length) => 5 a.send("length".to_sym) => 5 24
  • 57. Calling methods a.length => 5 a.send(:length) => 5 a.send("length".to_sym) => 5 24
  • 58. Preferences become method calls def bid_or_ask is_ask? ? :ask : :bid end trade_amount = net_basis * (end_rate.send(bid_or_ask) - start_rate.send(bid_or_ask)) 25
  • 59. Preferences become method calls def bid_or_ask is_ask? ? :ask : :bid end trade_amount = net_basis * (end_rate.send(bid_or_ask) - start_rate.send(bid_or_ask)) 25
  • 60. Preferences become method calls def bid_or_ask is_ask? ? :ask : :bid end trade_amount = net_basis * (end_rate.send(bid_or_ask) - start_rate.send(bid_or_ask)) 25
  • 61. Define similar methods ['preview', 'applet', 'info', 'procedures', 'discuss', 'files', 'history', 'tags', 'family', 'upload', 'permissions'].each do |tab_name| define_method( "browse_#{tab_name}_tab".to_sym ) do render :layout => 'browse_tab' end end 26
  • 62. Define similar methods ['preview', 'applet', 'info', 'procedures', 'discuss', 'files', 'history', 'tags', 'family', 'upload', 'permissions'].each do |tab_name| define_method( "browse_#{tab_name}_tab".to_sym ) do render :layout => 'browse_tab' end end 26
  • 63. Define similar methods ['preview', 'applet', 'info', 'procedures', 'discuss', 'files', 'history', 'tags', 'family', 'upload', 'permissions'].each do |tab_name| define_method( "browse_#{tab_name}_tab".to_sym ) do render :layout => 'browse_tab' end end 26
  • 64. Define similar methods ['preview', 'applet', 'info', 'procedures', 'discuss', 'files', 'history', 'tags', 'family', 'upload', 'permissions'].each do |tab_name| define_method( "browse_#{tab_name}_tab".to_sym ) do render :layout => 'browse_tab' end end 26
  • 65. Define similar methods ['preview', 'applet', 'info', 'procedures', 'discuss', 'files', 'history', 'tags', 'family', 'upload', 'permissions'].each do |tab_name| define_method( "browse_#{tab_name}_tab".to_sym ) do render :layout => 'browse_tab' end end 26
  • 66. Searching by result 'a'.what?('A') "a".upcase == "A" "a".capitalize == "A" "a".swapcase == "A" "a".upcase! == "A" "a".capitalize! == "A" "a".swapcase! == "A" [:upcase, :capitalize, :swapcase, :upcase!, :capitalize!, :swapcase!] 27
  • 67. Searching by result 'a'.what?('A') "a".upcase == "A" "a".capitalize == "A" "a".swapcase == "A" "a".upcase! == "A" "a".capitalize! == "A" "a".swapcase! == "A" [:upcase, :capitalize, :swapcase, :upcase!, :capitalize!, :swapcase!] 27
  • 68. Searching by result 'a'.what?('A') "a".upcase == "A" "a".capitalize == "A" "a".swapcase == "A" "a".upcase! == "A" "a".capitalize! == "A" "a".swapcase! == "A" [:upcase, :capitalize, :swapcase, :upcase!, :capitalize!, :swapcase!] 27
  • 69. Searching by result 'a'.what?('A') "a".upcase == "A" "a".capitalize == "A" "a".swapcase == "A" "a".upcase! == "A" "a".capitalize! == "A" "a".swapcase! == "A" [:upcase, :capitalize, :swapcase, :upcase!, :capitalize!, :swapcase!] 27
  • 70. Monkey patching! class Fixnum def *(other) self + other end end 6*5 => 11 28
  • 71. Monkey patching! class Fixnum def *(other) self + other end end 6*5 => 11 28
  • 72. Monkey patching! class Fixnum def *(other) self + other end end 6*5 => 11 28
  • 73. Example: “Whiny nils” • Try to invoke “id” on nil, and Rails will complain @y.id RuntimeError: Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id 29
  • 74. Example: RSpec it "should have a unique name" do c1 = Currency.new(@valid_attributes) c1.save! c2 = Currency.new( :name => @valid_attributes[:name], :abbreviation => 'XYZ') c1.should be_valid c2.should_not be_valid end 30
  • 75. Example: RSpec it "should have a unique name" do c1 = Currency.new(@valid_attributes) c1.save! c2 = Currency.new( :name => @valid_attributes[:name], :abbreviation => 'XYZ') c1.should be_valid c2.should_not be_valid end 30
  • 76. Example: RSpec it "should have a unique name" do c1 = Currency.new(@valid_attributes) c1.save! c2 = Currency.new( :name => @valid_attributes[:name], :abbreviation => 'XYZ') c1.should be_valid c2.should_not be_valid end 30
  • 77. method_missing • If no method exists, then method_missing is invoked, passing the method name, args 31
  • 78. Dynamic finders Reading.find_all_by_longitude_and_d evice_id(0, 3) => [#<Reading id: 46, longitude: #<BigDecimal:24439a8,'0.0',9(18)>, latitude: ... ] Reading.instance_methods.grep(/ long/) => [] 32
  • 79. Dynamic finders Reading.find_all_by_longitude_and_d evice_id(0, 3) => [#<Reading id: 46, longitude: #<BigDecimal:24439a8,'0.0',9(18)>, latitude: ... ] Reading.instance_methods.grep(/ long/) => [] 32
  • 80. Dynamic finders Reading.find_all_by_longitude_and_d evice_id(0, 3) => [#<Reading id: 46, longitude: #<BigDecimal:24439a8,'0.0',9(18)>, latitude: ... ] Reading.instance_methods.grep(/ long/) => [] 32
  • 81. Hash methods as keys class Hash def method_missing(name, *args) if has_key?(name) self[name] end end end 33
  • 82. Example h = {a:1, b:2} => 3 => {:a=>1, :b=>2} h.c h[:a] => nil => 1 h[:d] = 4 h.a => 4 => 1 h.d h['c'] = 3 => 4 34
  • 83. Example h = {a:1, b:2} => 3 => {:a=>1, :b=>2} h.c h[:a] => nil => 1 h[:d] = 4 h.a => 4 => 1 h.d h['c'] = 3 => 4 34
  • 84. Example h = {a:1, b:2} => 3 => {:a=>1, :b=>2} h.c h[:a] => nil => 1 h[:d] = 4 h.a => 4 => 1 h.d h['c'] = 3 => 4 34
  • 85. Example h = {a:1, b:2} => 3 => {:a=>1, :b=>2} h.c h[:a] => nil => 1 h[:d] = 4 h.a => 4 => 1 h.d h['c'] = 3 => 4 34
  • 86. Example h = {a:1, b:2} => 3 => {:a=>1, :b=>2} h.c h[:a] => nil => 1 h[:d] = 4 h.a => 4 => 1 h.d h['c'] = 3 => 4 34
  • 87. Example h = {a:1, b:2} => 3 => {:a=>1, :b=>2} h.c h[:a] => nil => 1 h[:d] = 4 h.a => 4 => 1 h.d h['c'] = 3 => 4 34
  • 88. Example h = {a:1, b:2} => 3 => {:a=>1, :b=>2} h.c h[:a] => nil => 1 h[:d] = 4 h.a => 4 => 1 h.d h['c'] = 3 => 4 34
  • 89. Example h = {a:1, b:2} => 3 => {:a=>1, :b=>2} h.c h[:a] => nil => 1 h[:d] = 4 h.a => 4 => 1 h.d h['c'] = 3 => 4 34
  • 90. Hash method access class Hash def method_missing(method_name, *arguments) if has_key?(method_name) self[method_name] elsif has_key?(method_name.to_s) self[method_name.to_s] else nil end end end 35
  • 91. Hash method access class Hash def method_missing(method_name, *arguments) if has_key?(method_name) self[method_name] elsif has_key?(method_name.to_s) self[method_name.to_s] else nil end end end 35
  • 92. Hash method access class Hash def method_missing(method_name, *arguments) if has_key?(method_name) self[method_name] elsif has_key?(method_name.to_s) self[method_name.to_s] else nil end end end 35
  • 93. Hash method access class Hash def method_missing(method_name, *arguments) if has_key?(method_name) self[method_name] elsif has_key?(method_name.to_s) self[method_name.to_s] else nil end end end 35
  • 94. Hash method access class Hash def method_missing(method_name, *arguments) if has_key?(method_name) self[method_name] elsif has_key?(method_name.to_s) self[method_name.to_s] else nil end end end 35
  • 95. Hash method access class Hash def method_missing(method_name, *arguments) if has_key?(method_name) self[method_name] elsif has_key?(method_name.to_s) self[method_name.to_s] else nil end end end 35
  • 96. xml.instruct! :xml, :version=>"1.0" xml.rss(:version=>"2.0"){ xml.channel{ xml.title("Modeling Commons updates") xml.link("http://modelingcommons.org/") xml.description("NetLogo Modeling Commons") xml.language('en-us') for update in @updates xml.item do xml.title(post.title) xml.description(post.html_content) xml.author("Your Name Here") xml.pubDate(post.created_on.strftime("%a, %d %b %Y %H:%M:%S %z")) xml.link(post.link) xml.guid(post.link) end end } } 36
  • 97. xml.instruct! :xml, :version=>"1.0" xml.rss(:version=>"2.0"){ xml.channel{ xml.title("Modeling Commons updates") xml.link("http://modelingcommons.org/") xml.description("NetLogo Modeling Commons") xml.language('en-us') for update in @updates xml.item do xml.title(post.title) xml.description(post.html_content) xml.author("Your Name Here") xml.pubDate(post.created_on.strftime("%a, %d %b %Y %H:%M:%S %z")) xml.link(post.link) xml.guid(post.link) end end } } 36
  • 98. xml.instruct! :xml, :version=>"1.0" xml.rss(:version=>"2.0"){ xml.channel{ xml.title("Modeling Commons updates") xml.link("http://modelingcommons.org/") xml.description("NetLogo Modeling Commons") xml.language('en-us') for update in @updates xml.item do xml.title(post.title) xml.description(post.html_content) xml.author("Your Name Here") xml.pubDate(post.created_on.strftime("%a, %d %b %Y %H:%M:%S %z")) xml.link(post.link) xml.guid(post.link) end end } } 36
  • 99. xml.instruct! :xml, :version=>"1.0" xml.rss(:version=>"2.0"){ xml.channel{ xml.title("Modeling Commons updates") xml.link("http://modelingcommons.org/") xml.description("NetLogo Modeling Commons") xml.language('en-us') for update in @updates xml.item do xml.title(post.title) xml.description(post.html_content) xml.author("Your Name Here") xml.pubDate(post.created_on.strftime("%a, %d %b %Y %H:%M:%S %z")) xml.link(post.link) xml.guid(post.link) end end } } 36
  • 100. xml.instruct! :xml, :version=>"1.0" xml.rss(:version=>"2.0"){ xml.channel{ xml.title("Modeling Commons updates") xml.link("http://modelingcommons.org/") xml.description("NetLogo Modeling Commons") xml.language('en-us') for update in @updates xml.item do xml.title(post.title) xml.description(post.html_content) xml.author("Your Name Here") xml.pubDate(post.created_on.strftime("%a, %d %b %Y %H:%M:%S %z")) xml.link(post.link) xml.guid(post.link) end end } } 36
  • 101. xml.instruct! :xml, :version=>"1.0" xml.rss(:version=>"2.0"){ xml.channel{ xml.title("Modeling Commons updates") xml.link("http://modelingcommons.org/") xml.description("NetLogo Modeling Commons") xml.language('en-us') for update in @updates xml.item do xml.title(post.title) xml.description(post.html_content) xml.author("Your Name Here") xml.pubDate(post.created_on.strftime("%a, %d %b %Y %H:%M:%S %z")) xml.link(post.link) xml.guid(post.link) end end } } 36
  • 102. Module includes module MyStuff def self.included(klass) klass.extend(ClassMethods) end module ClassMethods def foo; end end end 37
  • 103. Module includes module MyStuff def self.included(klass) klass.extend(ClassMethods) end module ClassMethods def foo; end end end 37
  • 104. Module includes module MyStuff def self.included(klass) klass.extend(ClassMethods) end module ClassMethods def foo; end end end 37
  • 105. Module includes module MyStuff def self.included(klass) klass.extend(ClassMethods) end module ClassMethods def foo; end end end 37
  • 106. Thanks! (Any questions?) • Call me: 054-496-8405 • E-mail me: reuven@lerner.co.il • Interrupt me: reuvenlerner (Skype/AIM) 38