Ruby An introduction to the Ruby programming language LPOO — MIEIC — FEUP May 2011
@goncalossilva
Ruby Created by Yukihiru “Matz” Matsumoto, in Japan Perl + SmallTalk + Eiffel + Ada + Lisp = Ruby “I wanted a scripting language that was more powerful than Perl, and more object-oriented than Python.” — matz
Ruby Interpreted language, with many implementations: YARV , Rubinius , JRuby , etc Functional, object-oriented, imperative and reflective Dynamically typed, with automatic garbage collection, exception handling and built-in unit testing Optimized for programmer productivity and happiness
Basics
Methods def hello_world() return "Hello world!" end hello_world()
Methods def hello_world # no need for () "Hello world!" # implicit return end hello_world # no need for ()
Classes class HelloWorld def say "Hello world done right!" end end hello_world_object = HelloWorld.new hello_world_object.say
It's object-oriented class Lecture def initialize(name = "TBA", duration_in_minutes = 60) @name = name @duration = duration_in_minutes/60.0; end def description "Name: #{@name}nDuration: #{@duration} hours" end end lecture = Lecture.new("Ruby", 45) lecture.description
It's really object-oriented // Java maximum = Math.max(1, 3) # Ruby maximum = [1, 3].max
It's really object-oriented # Python positive = abs(num) # Ruby positive = num.abs
It's really object-oriented // C length = strlen(name) # Ruby length = name.length
It's really object-oriented In Ruby, all functions and most operators are methods of an object In Python, for instance, some functions are procedural Classes themselves are instances of Class
It's really object-oriented Ruby has a permanent notion of the current object : self self controls how Ruby accesses instance variables All method calls are made on some object, called the receiver If no receiver is specified, self is used — it is implicit!
It's really object-oriented # self is "main", an instance of Object class MyClass # self is MyClass def my_method # self will depend on the receiver for this method end end # self is "main" again, so my_object exists in the main scope my_object = MyClass.new # explicit receiver, so self will be my_object inside my_method my_object.my_method
Arrays Store indexed collections of objects accessible through an integer key Can contain objects with different classes simultaneously # array a = [1, "second", 3.14] a[2]
Hashes Store indexed collections of objects accessible through a key which can be any object Slightly less efficient but much more flexible # hash h = { "string" => [3,4,5], 2 => "everything can be a value!", [1,2] => "everything can be a key!" } h[[1,2]]
Symbols A significant name, generally a static variable // java static final int NORTH = 1; // ... more code move(NORTH); # ruby move(:north) No need to predeclare these constants, they are unique return true if direction == :north
Control Structures # if if score.between?(100, 199) puts "You rock!" elsif score < 50 puts "You suck!" else puts "Meh" end # while while score < 100 puts "You're almost there! Try again..." # ... end # more goodness with unless, until, case, for, etc # and awesome shortcuts like statement modifiers puts "You cheated!" if score >= 200
Blocks and Iterators
Blocks A chuck of code enclosed between { } or do/end keywords Similar to the concept of anonymous methods Can take parameters Can be passed to methods as arguments (at the end, like an extra parameter) sum = 0 (1..5).each do |n| # same as [1,2,3,4,5] sum += n end sum # same thing with { } sum = 0 (1..5).each { |n| sum += n } sum
Blocks as Objects Can be converted into objects Can be stored in variables, pass them around, invoke them whenever you want Great for implementing callbacks, dispatch tables, etc class BlockAsObject def store_block(&my_block) @stored_block = my_block # converted to Proc end def use_block(parameter) @stored_block.call(parameter) end end foo = BlockAsObject.new foo.store_block { |param| "The block was called with " + param } foo.use_block("delay")
Blocks as Closures They can use local variables from the surrounding scope def powers_of_2_proc value = 1 lambda { value += value } end powers_of_2 = powers_of_2_proc powers_of_2.call # 2 powers_of_2.call # 4 powers_of_2.call # will return 8! So, powers_of_2_proc returns a Proc that references value When the block is called, value is out of scope The block is still able to access it (and will be for the remaining life of this block)
Iterators In many languages, collections implement methods to generate external iterator objects // C++ for (std::vector<int>::iterator i=list.begin(); i!=list.end(); i++) { // code }
Iterators In many languages, collections implement methods to generate external iterator objects // C# IEnumerator<int> i = list.GetEnumerator(); while (i.MoveNext()) { // code }
Iterators In many languages, collections implement methods to generate external iterator objects // Java Iterator i = list.iterator(); while (i.hasNext()) { // code }
Iterators When coming from other languages, many people iterate collections like this: # familiar? for i in 0..2 number = array[i][0] word = array[i][1] puts "#{word}: #{number}" end
Iterators However, there's another approach: # the "ruby way", with a lot less coupling array.each do |word, number| puts "#{word}: #{number}" end The “Ruby way” is different: an iterator is internal to the collection... it's just a method that calls yield every time it generates a new value
Iterators Ruby provides a lot of useful iterators: each, map , inject , etc But you can build your own def fib(max) i1, i2 = 1, 1 # parallel assignment while i1 <= max yield i1 i1, i2 = i2, i1+i2 end end result = "" fib(1337) { |n| result += "#{n} " } result
Iterators Ruby's internal iterators aren't necessarily the best solution What if you need the iterator to be an object? What if you want to iterate multiple collections simultaneously?
Enumerators When iterators aren't suitable, you can resort to enumerators To put it simply, an enumerator is an external iterator array = ["my", 1337, "array"] enumerator = array.to_enum # same as "enumerator = array.each" enumerator.next # returns "my" and moves to the next element enumerator.next
Enumerators Most internal iterators are can be used as enumerators string = "le fu" enumerator = string.each_char enumerator.next # returns "l" and moves to the next char enumerator.next # returns "e" and moves to the next char
Containers, containers, containers! Blocks and iterators are core concepts of Ruby With practice, you'll start building classes that iterate over their contents instead of using the conventional looping constructs It might seem complicated at first, but you'll start using these features naturally Easy to read and maintain
Sharing Functionality
Inheritance class SuperClass def hello_world "Hello world!" end end class SubClass < SuperClass end superclass = SuperClass.new subclass = SubClass.new "Hello world from the superclass: #{superclass.hello_world}n" + "Hello world from the subclass : #{subclass.hello_world}"
Inheritance Single inheritance, unlike C++ Top-level classes are subclasses of Object , which is a subclass of BasicObject Classes have built-in functionality because they inherited it!
Modules Modules provide a namespace, avoiding name collisions module MyModule # a module has it all MY_CONSTANT = "my constant" # constants def my_method # methods end class MyClass # and classes end end namespaced = MyModule::MyClass.new Modules have another wonderful use: mixins
Mixins and Inheritance Some OO languages support multiple inheritance (C++, Lisp, Python, etc) This is very powerful, but can be troublesome because of inheritance ambiguity Ruby offers a great compromise: the simplicity of single inheritance and the power of multiple inheritance
Mixins include is used to mix modules into classes or other modules module ToBeIncluded def foo "bar" end end class MyClass include ToBeIncluded end object = MyClass.new object.foo
Mixins extend is used to add instance methods to a given object module ToBeIncluded def foo "bar" end end class MyClass end object = MyClass.new object.extend ToBeIncluded object.foo
Mixins extend is also used to mix instance methods into a class module ToBeIncluded def foo "bar" end end class MyClass extend ToBeIncluded end MyClass.foo
Mixins module StringHelpers class Over9000Number < Number def stringify include StringHelpers if self.value > 9000 "Over 9000!" def initialize(value) else super(value + 9000) "Meh" end end end def status end "Current status: "+stringify end class Number end attr_reader :value number = Over9000Number.new(42) def initialize(value) number.status @value = value # => "Current status: Over 9000!" end end Inherit from one class, include functionality from multiple modules — mixins !
Inheritance, Mixins and Design Ruby allows you to right code once and inject it into multiple places When to use each? Inheritance You should be able to replace a parent object with a child object, honoring its contract A child object is a kind of the parent (an apple is a fruit) In the real world, strict hierarchies are restrictive... we need composition! Mixins For composition: A has a B, or A uses a B Exclusively using mixins can be messy — both should be combined
Inheritance, Mixis and Design Each of them serves its purpose, our job is to use the appropriately None of these make any sense: class Person < DataWrapper end class Banana include FruitProperties end Think before you type
Reflection
Reflection Ruby is very powerful when it comes to examining the aspects of a program within itself It can discover information about: What objects exist All class hierarchies Attributes and methods of all objects Information about methods “When you introspect, you think about your thoughts and feelings. This is interesting because you're using thought to analyze thought.” — Dave Thomas
Reflection and Objects You can transverse all living objects: ObjectSpace.each_object(Float) { |x| puts x }
Reflection and Objects You can look inside objects: [1,2,3].methods[0..4] [1,2,3].respond_to?(:to_s) [1,2,3].kind_of?(Hash)
Reflection and Objects You can invoke any method by name using send : a = [1,2,3] a.send(a.private_methods.first.to_sym) # "initialize" a # now it's empty!
Reflection and Objects Another way is to use the Method class: a = [1,2,3] constructor = a.method(:initialize) constructor.call a You get a Method object which you can store, pass around and call anytime!
Reflection and Classes It's also possible to look inside classes: String.ancestors # all superclasses and mixed-in modules
Reflection and Classes It's also possible to look inside classes: klass = String result = klass.to_s begin klass = klass.superclass result += " < " + klass.to_s end while klass.superclass result
Reflection and Classes It's also possible to look inside classes: Fixnum.constants Fixnum.class_variables Fixnum.singleton_methods(false) Fixnum.instance_methods(false) # private/protected/public prefix
Reflection and the Program's Execution Ruby let's you look at the interpreter while it executes your code set_trace_func lambda { |event, file, line, id, binding, classname| printf "%8s %s:%s-2d %-15sn", event, file, line, classname, id } # all code is traced from now on And you can also get the current call stack by using calling caller on your methods
Reflection and the Program's Execution You can even get the current source file being executed with the __FILE__ special variable, leading to an interesting Quine : print File.read(__FILE__) A program that outputs itself!
Metaprogramming
Metaprogramming Ruby code can modify aspects of its own structure at runtime and it makes it all easy You've seen it before on this presentation: remember include and extend? Ruby is effectively injecting their code into their receiver
Singleton Methods Ruby lets you define methods specific to a particular object person = "person" # String object def person.say_hi "Hi!" end person.say_hi Magic? No. Ruby created an anonymous class (often called singleton class) based on String and added the method say_hi to it
Singleton Class There are other ways of creating methods in an object's singleton class person = "person" class << person def say_hi "Hi there!" end end person.say_hi
Inherited Visibility The visibility of an inherited method can be changed class SuperClass private def my_private_method "U can't touch this" end end class SubClass < SuperClass public :my_private_method end object = SubClass.new object.my_private_method # not so private anymore
Inherited Visibility What's really happening is that Ruby is inserting a hidden proxy method in the subclass that invokes the original method with super class SubClass < SuperClass def my_private_method super end public :my_private_method end super calls can access the parent's method regardless of its visibility
Defining Methods Ruby allows you to define methods at runtime using define_method class BabyInfo ["cry", "eat", "poop"].each do |baby_action| define_method(baby_action) do "Of course, babies #{baby_action}" end end end baby_info = BabyInfo.new baby_info.cry Methods can also be blocked/removed by calling undef_method and remove_method, respectively
Class-level Macros Ruby has a few class-level macros that generate code behind the scenes class Laptop attr_accessor :memory # injects a getter/setter for "memory" end If you've used Ruby on Rails, you've probably dealt with associations class Post < ActiveRecord::Base has_many :comments end
Eval Similarly to other languages, eval evaluates the passed Ruby expression(s) course = "LPOO" eval "'Hello ' + course + '!'"
Instance eval instance_eval allows you to evaluate Ruby expression(s) in the context of an instance string = "cool man cool" string.instance_eval "def shout; self.upcase; end" string.shout Remember: classes are instances of Class, so you can also use instance_eval with them: Fixnum.instance_eval "def ten; 10; end" Fixnum.ten
Class eval As the name implies, class_eval can only be used with classes It evaluates the code as if you were in the context of the class definition String.class_eval do def shout self.upcase end end string = "cool man cool" string.shout Note: eval, instance_eval and class_eval can take blocks as arguments as shown above
Eval evilness eval is slow eval is dangerous eval doesn't generally make sense, given all other metaprogramming facilities
Callbacks Ruby provides hook methods , called by the interpreter when a specific event occurs Among all available hook methods —also known as callbacks —are: Method-related hooks: method_added, method_missing, method_removed, etc Class/Module-related hooks: const_missing, extended, included, inherited, etc Object marshaling and coercion hooks
Callbacks: method_missing Ruby allows you to act upon calls to undefined methods by using method missing class BabyInfo ["cry", "eat", "poop"].each do |baby_action| define_method(baby_action) do "Of course, babies #{baby_action}" end end def method_missing(name, *args, &block) "Nope, babies don't #{name}" end end baby_info = BabyInfo.new baby_info.surf
Callbacks: inherited Ruby allows your classes to act when they're subclassed class SuperClass @children = [] # class variable def self.inherited(child) @children << child end def self.children @children end end
Callback: method calls Ruby allows you to intercept calls to specific methods: class Object alias_method :old_to_s, :to_s def to_s result = self.old_to_s "Your modified to_s returned this (should be '"+result+"')" end end object = Object.new object.to_s This isn't a direct hook: you're copying the original method and inserting a hook by yourself
Wrapping Up
Ruby Ruby is an incredibly powerful language Many successful projects/products use it at their core: Ruby on Rails, God, Redmine, etc You can add and remove code in a running process, redefine methods on the fly, change their scope You can even modify basic types like String, Float or even Class and Object After using it for a while, you'll notice the lack of flexibility on static languages like C++ or half-static languages like Java
Ruby This presentation, for instance, is running on a Sinatra application And most of the shown code was executed in real time No? Then check this out: class Lecture def initialize(name = "TBA") @name = name end def finish "Finished at #{(Time.now.utc+3600).strftime('%H:%M:%S')}!" end end lecture = Lecture.new("Ruby") lecture.finish
More Books: Programming Ruby 1.9 by Dave Thomas Metaprogramming Ruby by Paolo Perrotta Mailing lists: Ruby-Talk (subscribe through Ruby's website) Ruby-pt (Google Groups) IRC: #ruby and #ruby-pt on Freenode
?

Ruby — An introduction

  • 1.
    Ruby An introduction tothe Ruby programming language LPOO — MIEIC — FEUP May 2011
  • 2.
  • 3.
    Ruby Created by Yukihiru “Matz” Matsumoto, in Japan Perl + SmallTalk + Eiffel + Ada + Lisp = Ruby “I wanted a scripting language that was more powerful than Perl, and more object-oriented than Python.” — matz
  • 4.
    Ruby Interpretedlanguage, with many implementations: YARV , Rubinius , JRuby , etc Functional, object-oriented, imperative and reflective Dynamically typed, with automatic garbage collection, exception handling and built-in unit testing Optimized for programmer productivity and happiness
  • 5.
  • 6.
    Methods def hello_world() return "Hello world!" end hello_world()
  • 7.
    Methods def hello_world #no need for () "Hello world!" # implicit return end hello_world # no need for ()
  • 8.
    Classes class HelloWorld def say "Hello world done right!" end end hello_world_object = HelloWorld.new hello_world_object.say
  • 9.
    It's object-oriented class Lecture def initialize(name = "TBA", duration_in_minutes = 60) @name = name @duration = duration_in_minutes/60.0; end def description "Name: #{@name}nDuration: #{@duration} hours" end end lecture = Lecture.new("Ruby", 45) lecture.description
  • 10.
    It's really object-oriented //Java maximum = Math.max(1, 3) # Ruby maximum = [1, 3].max
  • 11.
    It's really object-oriented #Python positive = abs(num) # Ruby positive = num.abs
  • 12.
    It's really object-oriented //C length = strlen(name) # Ruby length = name.length
  • 13.
    It's really object-oriented InRuby, all functions and most operators are methods of an object In Python, for instance, some functions are procedural Classes themselves are instances of Class
  • 14.
    It's really object-oriented Rubyhas a permanent notion of the current object : self self controls how Ruby accesses instance variables All method calls are made on some object, called the receiver If no receiver is specified, self is used — it is implicit!
  • 15.
    It's really object-oriented #self is "main", an instance of Object class MyClass # self is MyClass def my_method # self will depend on the receiver for this method end end # self is "main" again, so my_object exists in the main scope my_object = MyClass.new # explicit receiver, so self will be my_object inside my_method my_object.my_method
  • 16.
    Arrays Store indexedcollections of objects accessible through an integer key Can contain objects with different classes simultaneously # array a = [1, "second", 3.14] a[2]
  • 17.
    Hashes Storeindexed collections of objects accessible through a key which can be any object Slightly less efficient but much more flexible # hash h = { "string" => [3,4,5], 2 => "everything can be a value!", [1,2] => "everything can be a key!" } h[[1,2]]
  • 18.
    Symbols A significantname, generally a static variable // java static final int NORTH = 1; // ... more code move(NORTH); # ruby move(:north) No need to predeclare these constants, they are unique return true if direction == :north
  • 19.
    Control Structures # if ifscore.between?(100, 199) puts "You rock!" elsif score < 50 puts "You suck!" else puts "Meh" end # while while score < 100 puts "You're almost there! Try again..." # ... end # more goodness with unless, until, case, for, etc # and awesome shortcuts like statement modifiers puts "You cheated!" if score >= 200
  • 20.
  • 21.
    Blocks A chuckof code enclosed between { } or do/end keywords Similar to the concept of anonymous methods Can take parameters Can be passed to methods as arguments (at the end, like an extra parameter) sum = 0 (1..5).each do |n| # same as [1,2,3,4,5] sum += n end sum # same thing with { } sum = 0 (1..5).each { |n| sum += n } sum
  • 22.
    Blocks as Objects Can be converted into objects Can be stored in variables, pass them around, invoke them whenever you want Great for implementing callbacks, dispatch tables, etc class BlockAsObject def store_block(&my_block) @stored_block = my_block # converted to Proc end def use_block(parameter) @stored_block.call(parameter) end end foo = BlockAsObject.new foo.store_block { |param| "The block was called with " + param } foo.use_block("delay")
  • 23.
    Blocks as Closures They can use local variables from the surrounding scope def powers_of_2_proc value = 1 lambda { value += value } end powers_of_2 = powers_of_2_proc powers_of_2.call # 2 powers_of_2.call # 4 powers_of_2.call # will return 8! So, powers_of_2_proc returns a Proc that references value When the block is called, value is out of scope The block is still able to access it (and will be for the remaining life of this block)
  • 24.
    Iterators Inmany languages, collections implement methods to generate external iterator objects // C++ for (std::vector<int>::iterator i=list.begin(); i!=list.end(); i++) { // code }
  • 25.
    Iterators Inmany languages, collections implement methods to generate external iterator objects // C# IEnumerator<int> i = list.GetEnumerator(); while (i.MoveNext()) { // code }
  • 26.
    Iterators Inmany languages, collections implement methods to generate external iterator objects // Java Iterator i = list.iterator(); while (i.hasNext()) { // code }
  • 27.
    Iterators When comingfrom other languages, many people iterate collections like this: # familiar? for i in 0..2 number = array[i][0] word = array[i][1] puts "#{word}: #{number}" end
  • 28.
    Iterators However,there's another approach: # the "ruby way", with a lot less coupling array.each do |word, number| puts "#{word}: #{number}" end The “Ruby way” is different: an iterator is internal to the collection... it's just a method that calls yield every time it generates a new value
  • 29.
    Iterators Ruby providesa lot of useful iterators: each, map , inject , etc But you can build your own def fib(max) i1, i2 = 1, 1 # parallel assignment while i1 <= max yield i1 i1, i2 = i2, i1+i2 end end result = "" fib(1337) { |n| result += "#{n} " } result
  • 30.
    Iterators Ruby's internal iteratorsaren't necessarily the best solution What if you need the iterator to be an object? What if you want to iterate multiple collections simultaneously?
  • 31.
    Enumerators When iteratorsaren't suitable, you can resort to enumerators To put it simply, an enumerator is an external iterator array = ["my", 1337, "array"] enumerator = array.to_enum # same as "enumerator = array.each" enumerator.next # returns "my" and moves to the next element enumerator.next
  • 32.
    Enumerators Most internaliterators are can be used as enumerators string = "le fu" enumerator = string.each_char enumerator.next # returns "l" and moves to the next char enumerator.next # returns "e" and moves to the next char
  • 33.
    Containers, containers, containers! Blocks and iterators are core concepts of Ruby With practice, you'll start building classes that iterate over their contents instead of using the conventional looping constructs It might seem complicated at first, but you'll start using these features naturally Easy to read and maintain
  • 34.
  • 35.
    Inheritance class SuperClass def hello_world "Hello world!" end end class SubClass < SuperClass end superclass = SuperClass.new subclass = SubClass.new "Hello world from the superclass: #{superclass.hello_world}n" + "Hello world from the subclass : #{subclass.hello_world}"
  • 36.
    Inheritance Single inheritance, unlikeC++ Top-level classes are subclasses of Object , which is a subclass of BasicObject Classes have built-in functionality because they inherited it!
  • 37.
    Modules Modules providea namespace, avoiding name collisions module MyModule # a module has it all MY_CONSTANT = "my constant" # constants def my_method # methods end class MyClass # and classes end end namespaced = MyModule::MyClass.new Modules have another wonderful use: mixins
  • 38.
    Mixins and Inheritance Some OO languages support multiple inheritance (C++, Lisp, Python, etc) This is very powerful, but can be troublesome because of inheritance ambiguity Ruby offers a great compromise: the simplicity of single inheritance and the power of multiple inheritance
  • 39.
    Mixins include isused to mix modules into classes or other modules module ToBeIncluded def foo "bar" end end class MyClass include ToBeIncluded end object = MyClass.new object.foo
  • 40.
    Mixins extend isused to add instance methods to a given object module ToBeIncluded def foo "bar" end end class MyClass end object = MyClass.new object.extend ToBeIncluded object.foo
  • 41.
    Mixins extend isalso used to mix instance methods into a class module ToBeIncluded def foo "bar" end end class MyClass extend ToBeIncluded end MyClass.foo
  • 42.
    Mixins module StringHelpers class Over9000Number < Number def stringify include StringHelpers if self.value > 9000 "Over 9000!" def initialize(value) else super(value + 9000) "Meh" end end end def status end "Current status: "+stringify end class Number end attr_reader :value number = Over9000Number.new(42) def initialize(value) number.status @value = value # => "Current status: Over 9000!" end end Inherit from one class, include functionality from multiple modules — mixins !
  • 43.
    Inheritance, Mixins andDesign Ruby allows you to right code once and inject it into multiple places When to use each? Inheritance You should be able to replace a parent object with a child object, honoring its contract A child object is a kind of the parent (an apple is a fruit) In the real world, strict hierarchies are restrictive... we need composition! Mixins For composition: A has a B, or A uses a B Exclusively using mixins can be messy — both should be combined
  • 44.
    Inheritance, Mixis andDesign Each of them serves its purpose, our job is to use the appropriately None of these make any sense: class Person < DataWrapper end class Banana include FruitProperties end Think before you type
  • 45.
  • 46.
    Reflection Ruby is very powerful when it comes to examining the aspects of a program within itself It can discover information about: What objects exist All class hierarchies Attributes and methods of all objects Information about methods “When you introspect, you think about your thoughts and feelings. This is interesting because you're using thought to analyze thought.” — Dave Thomas
  • 47.
    Reflection and Objects You can transverse all living objects: ObjectSpace.each_object(Float) { |x| puts x }
  • 48.
    Reflection and Objects You can look inside objects: [1,2,3].methods[0..4] [1,2,3].respond_to?(:to_s) [1,2,3].kind_of?(Hash)
  • 49.
    Reflection and Objects You can invoke any method by name using send : a = [1,2,3] a.send(a.private_methods.first.to_sym) # "initialize" a # now it's empty!
  • 50.
    Reflection and Objects Another way is to use the Method class: a = [1,2,3] constructor = a.method(:initialize) constructor.call a You get a Method object which you can store, pass around and call anytime!
  • 51.
    Reflection and Classes It's also possible to look inside classes: String.ancestors # all superclasses and mixed-in modules
  • 52.
    Reflection and Classes It's also possible to look inside classes: klass = String result = klass.to_s begin klass = klass.superclass result += " < " + klass.to_s end while klass.superclass result
  • 53.
    Reflection and Classes It's also possible to look inside classes: Fixnum.constants Fixnum.class_variables Fixnum.singleton_methods(false) Fixnum.instance_methods(false) # private/protected/public prefix
  • 54.
    Reflection and theProgram's Execution Ruby let's you look at the interpreter while it executes your code set_trace_func lambda { |event, file, line, id, binding, classname| printf "%8s %s:%s-2d %-15sn", event, file, line, classname, id } # all code is traced from now on And you can also get the current call stack by using calling caller on your methods
  • 55.
    Reflection and theProgram's Execution You can even get the current source file being executed with the __FILE__ special variable, leading to an interesting Quine : print File.read(__FILE__) A program that outputs itself!
  • 56.
  • 57.
    Metaprogramming Rubycode can modify aspects of its own structure at runtime and it makes it all easy You've seen it before on this presentation: remember include and extend? Ruby is effectively injecting their code into their receiver
  • 58.
    Singleton Methods Ruby lets you define methods specific to a particular object person = "person" # String object def person.say_hi "Hi!" end person.say_hi Magic? No. Ruby created an anonymous class (often called singleton class) based on String and added the method say_hi to it
  • 59.
    Singleton Class Thereare other ways of creating methods in an object's singleton class person = "person" class << person def say_hi "Hi there!" end end person.say_hi
  • 60.
    Inherited Visibility Thevisibility of an inherited method can be changed class SuperClass private def my_private_method "U can't touch this" end end class SubClass < SuperClass public :my_private_method end object = SubClass.new object.my_private_method # not so private anymore
  • 61.
    Inherited Visibility What's really happening is that Ruby is inserting a hidden proxy method in the subclass that invokes the original method with super class SubClass < SuperClass def my_private_method super end public :my_private_method end super calls can access the parent's method regardless of its visibility
  • 62.
    Defining Methods Ruby allows you to define methods at runtime using define_method class BabyInfo ["cry", "eat", "poop"].each do |baby_action| define_method(baby_action) do "Of course, babies #{baby_action}" end end end baby_info = BabyInfo.new baby_info.cry Methods can also be blocked/removed by calling undef_method and remove_method, respectively
  • 63.
    Class-level Macros Rubyhas a few class-level macros that generate code behind the scenes class Laptop attr_accessor :memory # injects a getter/setter for "memory" end If you've used Ruby on Rails, you've probably dealt with associations class Post < ActiveRecord::Base has_many :comments end
  • 64.
    Eval Similarly toother languages, eval evaluates the passed Ruby expression(s) course = "LPOO" eval "'Hello ' + course + '!'"
  • 65.
    Instance eval instance_eval allows you to evaluate Ruby expression(s) in the context of an instance string = "cool man cool" string.instance_eval "def shout; self.upcase; end" string.shout Remember: classes are instances of Class, so you can also use instance_eval with them: Fixnum.instance_eval "def ten; 10; end" Fixnum.ten
  • 66.
    Class eval As the name implies, class_eval can only be used with classes It evaluates the code as if you were in the context of the class definition String.class_eval do def shout self.upcase end end string = "cool man cool" string.shout Note: eval, instance_eval and class_eval can take blocks as arguments as shown above
  • 67.
    Eval evilness eval isslow eval is dangerous eval doesn't generally make sense, given all other metaprogramming facilities
  • 68.
    Callbacks Rubyprovides hook methods , called by the interpreter when a specific event occurs Among all available hook methods —also known as callbacks —are: Method-related hooks: method_added, method_missing, method_removed, etc Class/Module-related hooks: const_missing, extended, included, inherited, etc Object marshaling and coercion hooks
  • 69.
    Callbacks: method_missing Rubyallows you to act upon calls to undefined methods by using method missing class BabyInfo ["cry", "eat", "poop"].each do |baby_action| define_method(baby_action) do "Of course, babies #{baby_action}" end end def method_missing(name, *args, &block) "Nope, babies don't #{name}" end end baby_info = BabyInfo.new baby_info.surf
  • 70.
    Callbacks: inherited Rubyallows your classes to act when they're subclassed class SuperClass @children = [] # class variable def self.inherited(child) @children << child end def self.children @children end end
  • 71.
    Callback: method calls Ruby allows you to intercept calls to specific methods: class Object alias_method :old_to_s, :to_s def to_s result = self.old_to_s "Your modified to_s returned this (should be '"+result+"')" end end object = Object.new object.to_s This isn't a direct hook: you're copying the original method and inserting a hook by yourself
  • 72.
  • 73.
    Ruby Rubyis an incredibly powerful language Many successful projects/products use it at their core: Ruby on Rails, God, Redmine, etc You can add and remove code in a running process, redefine methods on the fly, change their scope You can even modify basic types like String, Float or even Class and Object After using it for a while, you'll notice the lack of flexibility on static languages like C++ or half-static languages like Java
  • 74.
    Ruby This presentation,for instance, is running on a Sinatra application And most of the shown code was executed in real time No? Then check this out: class Lecture def initialize(name = "TBA") @name = name end def finish "Finished at #{(Time.now.utc+3600).strftime('%H:%M:%S')}!" end end lecture = Lecture.new("Ruby") lecture.finish
  • 75.
    More Books: Programming Ruby 1.9 by Dave Thomas Metaprogramming Ruby by Paolo Perrotta Mailing lists: Ruby-Talk (subscribe through Ruby's website) Ruby-pt (Google Groups) IRC: #ruby and #ruby-pt on Freenode
  • 76.