Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The Ruby Programming Language

The Ruby Programming Language

An introduction to the Ruby programming language for developers.

Guido Marucci Blas

May 05, 2013
Tweet

More Decks by Guido Marucci Blas

Other Decks in Programming

Transcript

  1. The Ruby Programming Language 100% Object Oriented extremely dynamic developer

    friendly sometimes a little bit magical Monday, May 6, 13
  2. 100% Object Oriented NO primitive types irb(main):002:0> 1.class => Fixnum

    irb(main):003:0> 1.methods => [:to_s, :-@, :+, :-, :*, :/, :div, ... ] irb(main):004:0> nil.class => NilClass irb(main):005:0> true.class => TrueClass irb(main):006:0> true.hash => 3677975328212232893 irb(main):007:0> true.methods => [:to_s, :&, :|, :^, :nil?, :===, ...] Monday, May 6, 13
  3. Scope: the current object $>irb irb(main):001:0> self => main irb(main):002:0>

    self.class => Object irb(main):003:0> def foo; puts "hello"; end => nil irb(main):003:0> methods.include?(:foo) => true irb(main):003:0> self.methods.include?(:foo) => nil Monday, May 6, 13
  4. Scope: local variables v1 = 1 class MyClass v2 =

    2 local_variables # => [:v2] def my_method v3 = 3 local_variables end local_variables # => [:v2] end obj = MyClass.new obj.my_method # => [:v3] Monday, May 6, 13
  5. Methods def my_method puts "hello" end def my_method(arg1) puts "hello

    #{arg1}" end def my_method(*args) args.each {|a| puts a} end my_method(1, 2, 3) Simple method definition Method definition with argument Method definition with variable arguments Monday, May 6, 13
  6. Methods def my_method(arg1 = Time.now, options = {}) puts "arg1

    #{arg1}" options.each { |k,v| puts "#{k} => #{v}" } end options = {foo: "hello", bar: "bye"} my_method("cool!", options) my_method("cool!", foo: "hello", bar: "bye") my_method("cool!", {foo: "hello", bar: "bye"}) my_method("cool!") my_method Default values & hash arguments Monday, May 6, 13
  7. Methods def my_method(arg1, *args) puts "arg1 #{arg1}" options.each { |a|

    puts "#{a}" } end args = [1, 2, 3] my_method("cool!", *args) my_method("cool!", 1, 2, 3) my_method("cool!") Variable length arguments Monday, May 6, 13
  8. Methods def my_method(foo: "hello", bar: "bye", **options) puts "foo #{foo}"

    puts "bar #{bar}" options.each { |k,v| puts "#{k} => #{v}" } end my_method(foo: "a", bar: 2) my_method(bar: 2, foo: "a") my_method(bar: "a") Keyword arguments Ruby 2.0 Monday, May 6, 13
  9. Methods class Fixnum def +(other) super + 1 end end

    2 + 2 => 5 Warning!: Do NOT try this at home. There are NO operators, just methods (well almost no operators) Monday, May 6, 13
  10. Methods def end_of_the_world? Date.today == Date.parse("2012-12-22") end def notify!(message) puts

    message SMSNotifier.notify(message) EMailNotifier.notify(message) end def my_property=(property) @property = property end Setter Caution Boolean Monday, May 6, 13
  11. Methods def foo puts "calling block" yield if block_given? end

    foo => calling block foo { puts "inside block" } => calling block => inside block Monday, May 6, 13
  12. Methods def bar(&block) block.call foo(&block) end bar { puts "inside

    block" } => inside block => calling block => inside block def baz(callable) callable.call() foo(&callable) end baz(Proc.new { puts "inside block" }) => inside block => calling block => inside block Monday, May 6, 13
  13. Methods a = "hello" a.upcase # => "HELLO" a.send(:upcase) #

    => "HELLO" a.__send__(:upcase) # => "HELLO" class Foo private def my_secret puts "I love Ruby" end end f = Foo.new f.my_secret # => NoMethodError f.send(:my_secret) # => "I love Ruby" Monday, May 6, 13
  14. Methods def foo(num) return if num % 2 == 0

    2 + num end a = foo(3) a # => 5 a = foo(2) a # => nil Monday, May 6, 13
  15. Blocks are not objects (:-o but you said ... yeah

    I know) Blocks, Procs & Lambdas Monday, May 6, 13
  16. They are like closures. They do not define a new

    scope. def foo(x) yield x + 1 end my_var = 2 foo(3) {|x| puts x + my_var} # => 6 Blocks, Procs & Lambdas Monday, May 6, 13
  17. You can yield them fewer arguments than expected def foo(x)

    yield x + 1 end foo(3) do |x, y| puts "x = #{x}" puts "y = #{y}" end # => x = 4 # => y = Blocks, Procs & Lambdas Monday, May 6, 13
  18. You can yield them more arguments than expected def foo(*args)

    yield *args end foo(3,4,5,6) do |x| puts "x = #{x}" end # => x = 3 Blocks, Procs & Lambdas Monday, May 6, 13
  19. Procs are the object representation of blocks proc = Proc.new

    { |x,y| x + y } proc.call(1,2) # => 3 proc.(1,2) # => 3 proc[1, 2] # => 3 Blocks, Procs & Lambdas Monday, May 6, 13
  20. Blocks, Procs & Lambdas Lambdas are special instances of Proc

    l = lambda { |x,y| x + y } l.is_a?(Proc) # => true l = ->(x,y) { x + y } l.call(1,2) # => 3 l.(1,2) # => 3 l[1, 2] # => 3 Monday, May 6, 13
  21. Blocks, Procs & Lambdas One di erence with procs is

    that arity MUST be respected. Monday, May 6, 13
  22. Blocks, Procs & Lambdas The other di erence is with

    the return keyword. def double(callable_object) callable_object.call * 2 end l = lambda { return 10 } double(l) # => 20 def another_double p = Proc.new { return 10 } result = p.call return result * 2 # unreachable code end another_double # => 10 Monday, May 6, 13
  23. Blocks, Procs & Lambdas def foo yield * 2 end

    foo { 10 } # => 20 foo { return 10 } # => LocalJumpError class Bar def bar Proc.new { return 10 } end end p = Bar.new.bar foo(&p) #=> LocalJumpError Monday, May 6, 13
  24. Strings Interpolation a = "My dynamic string, now is #{Time.now}"

    b = 'This is not dynamic, now is #{Time.now}' puts a => My dynamic string, now is 2013-04-01 16:57:30 -0300 puts b => This is not dynamic, now is #{Time.now} %q(Interpolationless string 'foo', "bar" & #{Time.now}) => "Interpolationless string 'foo', \"bar\" & \#{Time.now}" %Q(Interpolated string 'foo', "bar" & #{Time.now}) => "Interpolated string 'foo', \"bar\" & 2013-04-01 17:02:02 -0300" Monday, May 6, 13
  25. Strings Multiline a = <<-EOS Hello my little world! How

    are you? EOS => "Hello my little\n world!\n How are\nyou?\n" Monday, May 6, 13
  26. Strings are MUTABLE a = "HELLO" a[1] = "A" a

    # => "HALLO" a.downcase! a #=> "hello" a = "HELLO" b = "HELLO" a == b # => true a.object_id == b.object_id # => false a.equal?(b) # => false Monday, May 6, 13
  27. Strings Common pitfall class Foo def initialize(key) @key = key

    end def test(value) @key == value end end key = "foo" foo = Foo.new(key) foo.test("foo") # => true key.upcase! foo.test("foo") # => false Monday, May 6, 13
  28. Strings Some structures save you the trouble key = "foo"

    map = {} map[key] = 10 map["foo"] # => 10 key.upcase! key # => "FOO" map["foo"] # => 10 map["FOO"] # => nil m_key = map.keys.first m_key == key # => false m_key.frozen? # => true m_key.upcase! # => RuntimeError Monday, May 6, 13
  29. Symbols are immutable a = :foo b = :foo c

    = "foo".to_sym a.equal?(b) # => true b.equal?(c) # => true a.equal?(c) # => true are unique are NOT garbage collected Monday, May 6, 13
  30. Symbols random_words = Set.new STDIN.each_with_index do |line, index| words =

    line.split(/\s+/) break if %w(quit q exit).include?(words.first) random_words.add(words.sample.to_sym) end puts random_words Monday, May 6, 13
  31. Symbols random_words = Set.new STDIN.each_with_index do |line, index| words =

    line.split(/\s+/) break if %w(quit q exit).include?(words.first) random_words.add(words.sample.to_sym) end puts random_words A possible out of memory attack could be done Monday, May 6, 13
  32. Equality equal? Tests if two values refers exactly to the

    same object. == Tests if two values are equals. eql? Alias for equal?. Used as a stricted version of ==. === Case equality. Used in case statement to check if target matches any of the when clauses Monday, May 6, 13
  33. Equality a = "foo" b = "foo" a == b

    # => true a.equal?(b) # => false a === b # => true /^hola(\d)+chau$/ === "hola123chau" => true /^hola(\d)+chau$/ === "holaGATOchau" => false foo = Foo.new Foo === foo => true Foo === 120 => false 1 == 1.0 => true 1.eql?(1.0) => false Monday, May 6, 13
  34. Hashes a = Hash.new a = {} a[:foo] = 10

    a[:foo] # => 10 hash1 = { "key1" => "foo", "key2" => 120 } hash2 = { key1 => "foo", key2 => 120 } hash3 = { key1: "foo", key2: 120 } Ruby 1.9+ hash1.each do |key, value| puts "#{key} => #{value}" end Monday, May 6, 13
  35. Arrays a = Array.new a = [] a[0] = 10

    a[0] # => 10 a = [1,4,5,6] a.each do |elem| puts elem end a.map { |elem| elem * 2 } %w(hola chau como estas).map(&:upcase) a.select { |elem| elem % 2 == 0 } a.reduce(0) { |sum, elem| sum + elem } Monday, May 6, 13
  36. Lazy enumerators a = [1,3,56,3,5,6,243,212,123,5465,6432,13] a.lazy.map { |n| n *

    3 }.select { |n| n % 2 == 0}.each do |n| # Process element n end Ruby 2.0 Monday, May 6, 13
  37. Constants PI = 3.14 MyClass = Class.new PI = 3.18

    # => warning: already initialized constant PI class Foo BAR = 20 end Foo::BAR # => 20 Foo.const_defined?(:BAR) # => true Foo.const_set(:BAZ, 30) Foo::BAZ # => 30 Monday, May 6, 13
  38. Classes class Person attr_accessor :first_name attr_accessor :last_name def initialize(first_name, last_name)

    @first_name = first_name @last_name = last_name @birth_date = birth_date end def full_name "#{@first_name} #{@last_name}" end end constructor instance method Monday, May 6, 13
  39. Classes class Person attr_accessor :first_name attr_accessor :last_name def initialize(first_name, last_name)

    @first_name = first_name @last_name = last_name @birth_date = birth_date end def full_name "#{@first_name} #{@last_name}" end end setter & getters constructor instance method Monday, May 6, 13
  40. Classes class Person attr_accessor :first_name attr_accessor :last_name def initialize(first_name, last_name)

    @first_name = first_name @last_name = last_name @birth_date = birth_date end def full_name "#{@first_name} #{@last_name}" end end setter & getters constructor instance method def first_name @first_name end def first_name=(first_name) @first_name = first_name end Monday, May 6, 13
  41. Classes class Foo @@class_variable = 3 @class_instance_variable = 4 def

    initialize @instance_variable = 1 end def foo puts @@class_variable puts @class_instance_variable puts @instance_variable end end Variables Foo.new.foo #=> 3 #=> #=> 1 Monday, May 6, 13
  42. Classes class A @@a = 1 @b = 1 def

    self.foo puts @@a puts @b end end Variables A.foo #=> 2 #=> 1 B.foo #=> 2 #=> 2 class B < A @@a = 2 @b = 2 end Monday, May 6, 13
  43. Classes class A def self.class_method puts "Class Method" end def

    instance_method puts "Instance method" end end Methods class B class << self def class_method_1 puts "Class Method 1" end def class_method_2 puts "Class Method 2" end end end Monday, May 6, 13
  44. Classes class A def foo puts "Foo from A" end

    def bar puts "Bar from A" end end class B < A def foo super puts "Foo from B" end def bar puts "Bar from B" end end a = A.new b = B.new a.foo a.bar # => Foo from A # => Bar from A b.foo b.bar # => Foo from A # => Foo from B # => Bar from B Monday, May 6, 13
  45. Classes foo = Foo.new foo.singleton_class class << foo; self; end

    Singleton Class Eigenclass Metaclass “They are the UFOs of the Ruby world” Monday, May 6, 13
  46. Classes f1 = Foo.new class << foo def my_method_1 puts

    "m1" end def my_method_2 puts "m2" end end f1 = Foo.new f2 = Foo.new def f1.foo puts "foo! from f1" end f1.foo # => foo! from f1 f2.foo # => NoMethodError Singleton Methods Monday, May 6, 13
  47. Classes MyClass instance_method #obj custom_method obj Eigenclass Class instance class

    singleton_class #MyClass class_method class MyClass def self.class_method puts 1 end def instance_method puts 2 end end obj = MyClass.new def obj.custom_method puts 3 end singleton_class Monday, May 6, 13
  48. Classes Modules module MyModule def foo puts "Foo from Module"

    end def bar puts "Bar from Module" end end class MyClass include MyModule include MyOtherModule def foo puts "Foo from #{self}" super end end obj = MyClass.new obj.bar # => Bar from Module obj.foo # => Foo from MyClass # => Foo from OtherModule # => Foo from Module module MyOtherModule def foo puts "Foo from OtherModule" super end end Monday, May 6, 13
  49. Classes Modules module MyModule def foo puts "Foo from Module"

    end def bar puts "Bar from Module" end end class MyClass include MyModule prepend MyOtherModule def foo puts "Foo from #{self}" super end end obj = MyClass.new obj.bar # => Bar from Module obj.foo # => Foo from OtherModule # => Foo from MyClass # => Foo from Module module MyOtherModule def foo puts "Foo from OtherModule" super end end Ruby 2.0 Monday, May 6, 13
  50. Classes BasicObject Kernel Object A B #BasicObject #Kernel #Object #A

    #B #obj obj Eigenclass Class Module instance Monday, May 6, 13
  51. Classes Open Classes class String def encrypt chars.map(&:succ).join end end

    "hello".encrypt # => "ifmmp" Monkey Patching Monday, May 6, 13
  52. Classes Refinements module StringExtensions refine String do def encrypt chars.map(&:succ).join

    end end end "hello".encrypt # => NoMethodError using StringExtensions "hello".encrypt # => "ifmmp" Ruby 2.0 Monday, May 6, 13
  53. Dynamic Methods class AuditablePerson def first_name logger.info("First name was fetched

    at #{Time.now}") @first_name end def first_name=(first_name) logger.info("First name was changed from #{@first_name} to #{first_name}") @first_name = first_name end def last_name logger.info("Last name was fetched at #{Time.now}") @last_name end def last_name=(last_name) logger.info("Last name was changed from #{@last_name} to #{last_name}") @last_name = last_name end end Monday, May 6, 13
  54. Dynamic Methods module Auditable def self.included(receiver) receiver.extend(ClassMethods) receiver.send(:include, InstanceMethods) end

    module InstanceMethods end module ClassMethods def attr_auditable(*attributes) attributes.each do |attribute| define_setter(attribute) define_getter(attribute) end end ... end end Monday, May 6, 13
  55. Dynamic Methods module Auditable def self.included(receiver) receiver.extend(ClassMethods) receiver.send(:include, InstanceMethods) end

    module InstanceMethods end module ClassMethods def attr_auditable(*attributes) attributes.each do |attribute| define_setter(attribute) define_getter(attribute) end end ... end end private def define_setter(attribute) var_name = "@#{attribute}".to_sym define_method("#{attribute}=") do |value| old_value = instance_variable_get(var_name) puts("#{attribute} was changed from #{old_value} to #{value} at #{Time.now}") instance_variable_set(var_name, value) end end def define_getter(attribute) var_name = "@#{attribute}".to_sym define_method(attribute) do puts("#{attribute} was fetched at #{Time.now}") instance_variable_get(var_name) end end Monday, May 6, 13
  56. Method Missing class Recorder def method_missing(method, *args) puts "Method #{method}

    called with arguments #{args}" end end repository = UserRepository.new(Recorder.new) repository.find_by_name("john") # => Method execute called with arguments SELECT * FROM users WHERE name = "john" Monday, May 6, 13
  57. Method Missing module Delegator def self.included(receiver) receiver.extend(ClassMethod) receiver.send(:include, InstanceMethod) end

    module InstanceMethod def method_missing(method, *args) delegate = send(@@delegate) delegate.send(method, *args) end end module ClassMethod def delegate_to(delegate) @@delegate = delegate end end end Monday, May 6, 13
  58. Method Missing class Boss include Delegator attr_reader :secretary delegate_to :secretary

    def initialize(secretary) @secretary = secretary end end class Secretary def make_finance_report Report.new end end secretary = Secretary.new boss = Boss.new(secretary) report = boss.make_finance_report Monday, May 6, 13
  59. Method Missing Always overwrite respond_to? if you define a method

    missing def respond_to?(method) super || send(@@delegate).respond_to?(method) end Monday, May 6, 13
  60. eval class Foo def initialize @var = 30 end end

    obj = Foo.new var = obj.instance_eval { @var } # => 30 obj.instance_eval do def var @var end end foo.var # => 30 Foo.new.var # => NoMethodError Foo.class_eval do def var=(var) @var = var end end foo.var = 40 foo.var # => 40 Foo.new.var = 50 Monday, May 6, 13
  61. eval class Foo def initialize @var = 30 end end

    obj = Foo.new var = obj.instance_eval { @var } # => 30 obj.instance_eval do def var @var end end foo.var # => 30 Foo.new.var # => NoMethodError Foo.class_eval do def var=(var) @var = var end end foo.var = 40 foo.var # => 40 Foo.new.var = 50 Monday, May 6, 13
  62. class MyClass attr_accessor :attribute_1 attr_accessor :attribute_2 def my_method_1(arg1, arg2) ...

    end def my_method_2 ... end end obj = Mocker::create(MyClass) obj.attribute_1 # => "foo" obj.attribute_2 # => "bar" obj.attribute_1 = 30 obj.my_method_1(1,2) # => 3 obj.my_method_2 obj.method_called?(:my_method_2) # => true obj.my_method_3 # => NoMethodError mock MyClass do attribute_1 "foo" attribute_2 "bar" when :my_method_1 do |arg1, arg2| arg1 + arg2 end end Monday, May 6, 13