Cool Ruby Features

Flip Flop Operator

in ruby since the dawn of time

Example

(1..15).each do |x|
  if (x == 5) .. (x == 10)
    puts "true: #{x}"
  else
    puts "false: #{x}"
  end
end
# false: 1
# false: 2
# false: 3
# false: 4
(1..15).each do |x|
  if (x == 5) .. (x == 10)
    puts "true: #{x}"
  else
    puts "false: #{x}"
  end
end
# false: 1
# false: 2
# false: 3
# false: 4
# true: 5
# true: 6
# true: 7
# true: 8
# true: 9
# true: 10
(1..15).each do |x|
  if (x == 5) .. (x == 10)
    puts "true: #{x}"
  else
    puts "false: #{x}"
  end
end
# false: 1
# false: 2
# false: 3
# false: 4
# true: 5
# true: 6
# true: 7
# true: 8
# true: 9
# true: 10
# false: 11
# false: 12
# false: 13
# false: 14
# false: 15

Method Arguments

2.0+

Optional arguments

def some_method(a, b, *p, q)
end

some_method(25, 35, 45, 55)
#=> a=25, b=35, p=[45], q=55

some_method(25, 35, 45)
#=> a=25, b=35, p=[], q=45

some_method(25, 35, 45, 55, 65, 75)
#=> a=25, b=35, p=[45,55,65], q=75


def some_method(a, b, c=5, *p, q)
end

some_method(25, 35, 45) 
#=> a=25, b=35, c=5, p=[], q=45

some_method(25, 35, 45, 55)
#=>a=25, b=35, c=45, p=[], q=55

some_method(25, 35, 45, 55, 65)
#=> a=25, b=35, c=45, p=[55], q=65

some_method(25, 35, 45, 55, 65, 75)
#=> a=25, b=35, c=45, p=[55,65], q=75

Keyrest Argument

def create_party(place,time,**options)
  puts "place: #{place}, time: #{time}, options: #{options}"
end
def create_party(place,time,**options)
  puts "place: #{place}, time: #{time}, options: #{options}"
end

create_party("Club","Evening",{vodka: 'lots'})
# place: Club, time: Evening, options: {:vodka=>"lots"}

def create_party(place,time,**options)
  puts "place: #{place}, time: #{time}, options: #{options}"
end

create_party("Club","Evening",{vodka: 'lots'})
# place: Club, time: Evening, options: {:vodka=>"lots"}

create_party("Club","Evening",{music: 'loud', party: 'hard'})
# place: Club, time: Evening, options: {:music=>"loud", :party=>"hard"}
def create_party(place,time,**options)
  puts "place: #{place}, time: #{time}, options: #{options}"
end

create_party("Club","Evening",{vodka: 'lots'})
# place: Club, time: Evening, options: {:vodka=>"lots"}

create_party("Club","Evening",{music: 'loud', party: 'hard'})
# place: Club, time: Evening, options: {:music=>"loud", :party=>"hard"}

create_party("Church","Morning")
# place: Church, time: Morning, options: {}
def create_party(place,time,**options)
  puts "place: #{place}, time: #{time}, options: #{options}"
end

create_party("Club","Evening",{vodka: 'lots'})
# place: Club, time: Evening, options: {:vodka=>"lots"}

create_party("Club","Evening",{music: 'loud', party: 'hard'})
# place: Club, time: Evening, options: {:music=>"loud", :party=>"hard"}

create_party("Church","Morning")
# place: Church, time: Morning, options: {}

create_party("Home", "Tomorrow", "Crasher")
# ArgumentError: wrong number of arguments (3 for 2)
# from (irb):4:in `create_party'
# from (irb):10
# from /Users/bartek/.rvm/rubies/ruby-2.0.0-p576/bin/irb:12:in `<main>'

Keyword Arguments

def m(foo: 1, bar: 2)
  [foo, bar]
end
def m(foo: 1, bar: 2)
  [foo, bar]
end

m 
# => [1, 2]
def m(foo: 1, bar: 2)
  [foo, bar]
end

m 
# => [1, 2]

m(1, 2)
# => ArgumentError
def m(foo: 1, bar: 2)
  [foo, bar]
end

m 
# => [1, 2]

m(1, 2)
# => ArgumentError

m(foo: 2) 
# => [2, 2]

m(bar: 1) 
# => [1, 1]

m(foo: 3, bar: 3) 
# => [3, 3]

Keyword Arguments

def m(foo: 1, **opts)
  [foo, opts]
end

def m(foo: 1, **opts)
  [foo, opts]
end

m(foo: 1, bar: 2) 
# => [1, {:bar=>2}]

m(bar: 2) 
# => [1, {:bar=>2}]

def m(foo: 1, **opts)
  [foo, opts]
end

m(foo: 1, bar: 2) 
# => [1, {:bar=>2}]

m(bar: 2) 
# => [1, {:bar=>2}]

def m(foo: 1)
  foo
end

m(bar: 2) 
# => ArgumentError
def m(foo: 1, **opts)
  [foo, opts]
end

m(foo: 1, bar: 2) 
# => [1, {:bar=>2}]

m(bar: 2) 
# => [1, {:bar=>2}]

def m(foo: 1)
  foo
end

m(bar: 2) 
# => ArgumentError

def m(foo, *bar, baz, qux: 1, **opts)
  [foo, bar, baz, qux, opts]
end

m(1, 2, 3, 4, 5, quux: 6) 
# => [1, [2, 3, 4], 5, 1, {:quux=>6}]

def m(foo: 1, **opts)
  [foo, opts]
end

m(foo: 1, bar: 2) 
# => [1, {:bar=>2}]

m(bar: 2) 
# => [1, {:bar=>2}]

def m(foo: 1)
  foo
end

m(bar: 2) 
# => ArgumentError

def m(foo, *bar, baz, qux: 1, **opts)
  [foo, bar, baz, qux, opts]
end

m(1, 2, 3, 4, 5, quux: 6) 
# => [1, [2, 3, 4], 5, 1, {:quux=>6}]

m(1, 2, 3, 4, 5, 6) 
# => [1, [2, 3, 4, 5], 6, 1, {}]

def m(foo: 1, **opts)
  [foo, opts]
end

m(foo: 1, bar: 2) 
# => [1, {:bar=>2}]

m(bar: 2) 
# => [1, {:bar=>2}]

def m(foo: 1)
  foo
end

m(bar: 2) 
# => ArgumentError

def m(foo, *bar, baz, qux: 1, **opts)
  [foo, bar, baz, qux, opts]
end

m(1, 2, 3, 4, 5, quux: 6) 
# => [1, [2, 3, 4], 5, 1, {:quux=>6}]

m(1, 2, 3, 4, 5, 6) 
# => [1, [2, 3, 4, 5], 6, 1, {}]

m(1, 2, qux: 3) 
# => [1, [], 2, 3, {}]

Keyword Arguments

# Block parameters can use keyword arguments:
def m
  yield bar: 3
end

m do |foo: 1, bar: 2|
  [foo, bar]
end # => [1, 3]

# Block parameters can use keyword arguments:
def m
  yield bar: 3
end

m do |foo: 1, bar: 2|
  [foo, bar]
end # => [1, 3]

# Putting it all together:

define_method(:m) do |foo, *bar, baz, qux: 1, **opts, &blk; a, b|
  [foo, bar, baz, qux, opts, blk]
end

method(:m).parameters # => [[:req, :foo], [:rest, :bar], [:req, :baz], [:key, :qux], [:keyrest, :opts], [:block, :blk]]
m(1, 2, 3, qux: 4, quux: 5) { } # => [1, [2], 3, 4, {:quux=>5}, #<Proc:0x83bbfc@kwargs.rb:67>]

Symbol Literal Syntax

2.0+

%w(kiedy swieca zgasnie)
#=> ['kiedy','swieca','zgasnie']

%i(to jest ciemno)
#=> [:to, :jest, :ciemno]

%i like %w and others

Improved Numbers

2.1+

Complex Numbers

# 2.0
Complex(2, 3)
#=> (2+3i)

(2+3i)
#=> SyntaxError

# 2.1
(2+3i)
#=> (2+3i)

(2+3i) + Complex(5, 4i)
#=> (3+3i)

Rational Shorthand

# ruby 2.0

2/3.0 + 5/4.0
# => 1.9166666666666665

# ruby 2.1

2/3r + 5/4r
# => (23/12)

Method Definitions

2.1+

def's return value

# ruby 2.0
def foo; end
# => nil

# ruby 2.1
def foo; end
# => :foo

Useful with private methods

class Foo
  # ruby 2.0
  def public
  end

  def private_1
  end

  private :private_1

  private

  def private_2
  end
end
class Foo
  # ruby 2.1
  def public
  end

  private def private_1
  end

end

Refinements

2.1+

Good bye monkey patching

# party.rb

module Party
  refine String do
    def bartek_other
      "bartek other"
    end
  end
end
# refinements.rb

require './party'

module Party
  refine String do
    def bartek
      "bartek"
    end
  end
end

class Test
  using Party

  def initialize
    puts "test".bartek
    puts "test".bartek_other
  end
end
# file_scope.rb

require './refinements.rb'

class Test
  def string_party
    "string".bartek
  end
end

test = Test.new

# => 'bartek'
# => 'bartek_other'

puts test.string_party
# => `string_party': undefined method `bartek' for "string":String (NoMethodError)

Works only within current class:

# refinements_parent.rb

require './party'

class BaseTest
  using Party
end

# refinements_inheritance.rb

require './refinements_parent'

class Test < BaseTest
  def initialize
    puts "test".bartek_other
  end
end

Test.new
# => undefined method `bartek_other' for "test":String (NoMethodError)

No nesting - clarity first

# refinements_nesting.rb

require './bigger_party'

class Test
  using BiggerParty

  def initialize
    puts "test".bigger_bartek
    puts "test".bartek_other
  end
end

Test.new
# => faaaaaat
# => `initialize': undefined method `bartek_other' for "test":String (NoMethodError)
# bigger_party.rb

require './party'

module BiggerParty
  using Party
  refine String do
    def bigger_bartek
      'faaaaaat'
    end
  end
end

Static Typing

3.0 proposal

Soft Typing

a = 1 # the type of a is now Integer
a = 1 # the type of a is now Integer

def foo(x)
  x.to_int  # now all x must have .to_int
end

foo(1)   # OK
foo('a') # no good! doesn't have to_int!

Questions?

Cool Ruby Features

By Bartek Kruszczyński