I think that each of the features (and more) add to the level of dynamism of the language.
In this article I’ll explore some of the characteristics that make Ruby an incredibly dynamic language. I’ll name some of these features first and then go briefly into what they are and how they work.
Dynamic Typing:
This characteristic is easy, just meaning that there doesn’t exist a type for variables at compile time, just at runtime, and it can be changed during the variable lifecycle as this irb session shows:
irb(main):001:0> a="dsd"
=> "dsd"
irb(main):002:0> a.class
=> String
irb(main):003:0> a=5
=> 5
irb(main):004:0> a.class
=> Fixnum
irb(main):008:0> local_variables
=> [:a, :_]
Duck Typing
This is another common characteristic of dynamic languages, it’s meaning is that regardless the type of a variable if it responds to a particular message, then it can receive that message and act accordingly:
For example, the message “fly” can be sended to a Airplane, or a Bird. In a language like Java, you might need to define an Interface like “FlyCapable” or similar that defines the fly method, in Ruby (and other languages with Duck Typing) you can just do:
- class Bird
- def fly
- puts "I am a flying bird"
- end
- end
- class Airplane
- def fly
- puts "I need a motor, but also fly"
- end
- end
- def make_entity_fly(entity)
- entity.fly
- end
- bird = Bird.new
- plane = Airplane.new
- make_entity_fly bird
- make_entity_fly plane
and when you run it, you get
I am a flying bird
I need a motor, but also fly
Extending existent classes at runtime.
In this point i am not refering to extend in the sense of inheritance but instead actually extending the functionality of a class that already exists, for example adding a new method to the String class. There are a few ways to do this, you can reopen the class, or you can use the class_eval method to do it.
let’s see both, let’s suppose we want to add a method to the String class we can just do:
- class String
- def my_method
- self + " and some extra "
- end
- end
- string = "Original String"
- puts string.my_method
- #And this is still a normal String with the common String methods of course
- puts string.capitalize
Using class_eval:
- String.class_eval do
- define_method :my_method do
- self + " and some extra "
- end
- end
- string = "Original String"
- puts string.my_method
- #And this is still a normal String with the common String methods of course
- puts string.capitalize
Modules
Modules is one of the nicest things in Ruby, easy to understand and a very powerful feature. Modules allow you to isolate behaviour in one place and use this behaviour (methods) from classes that include the module, as if the methods where defined in the class itself, allowing its instances to use them. An example to illustrate this better.
- module StartAndStop
- def start
- @state = "started"
- end
- def stop
- @state = "stoped"
- end
- end
- class Car
- include StartAndStop
- def state
- puts @state
- end
- end
- car = Car.new
- car.start
- car.state
- car.stop
- car.state
And the output
ruby Ruby-1.rb
started
stoped
Ruby is Object oriented no class oriented
I didn’t realize this until i started working with Ruby, But Java is really more Class oriented than object oriented. In Java one object’s features are forever bound to it’s class (and superclass) definitions. In Ruby by contrast, Objects can have a life of their own independent from their class, So in Ruby classes work as a template for creating objects, but from that moment the object lives on its own:
- class Person
- def talk
- puts "hola"
- end
- end
- p1 = Person.new
- p2 = Person.new
- def p1.scream
- puts "HOLA!!"
- end
- p2.instance_eval do
- undef :talk
- end
- p1.talk
- p1.scream
- p2.talk
And the output
ruby Ruby-1.rb
hola
HOLA!!
Ruby-1.rb:20:in `
Extending the singleton class of an object.
In Ruby every object, apart from the classes it extends explicitly, has associated a class for it’s own. This classes are known as Eigenclasses or Anonymous classes. We already see a little of how to extend this classes with behaviour when we saw the definition of the scream method directly in the object. However if you want to open a Eigenclass of an object and keep it open to add something to them you can do it like:
- a = String.new
- class << a
- def append_salute
- self << "Hola"
- end
- end
- a.append_salute
- puts a
Extending using modules dynamically
We already saw that you can add behaviour to a class with the inclusion of modules. And you can also include modules directly in an object (actually to it’s Eigenclass) You can do this two ways:
1)
- module DummyModule
- def method_dummy
- puts "inside dummy method"
- end
- end
- a=String.new
- a.extend DummyModule
- a.method_dummy
2)
- module DummyModule
- def method_dummy
- puts "inside dummy method"
- end
- end
- a=String.new
- class << a
- include DummyModule
- end
- a.method_dummy
Using eval and similars
We already saw about class_eval and instance_eval before, the other method to know about is the eval method. It basically allow you to execute arbitrary Strings of code as Ruby code:
- string_of_code = "class ClaseDummy
- def method_dummy
- puts 'dummy'
- end
- end"
- eval(string_of_code)
- a=ClaseDummy.new
- a.method_dummy
Creating a new type in runtime.
In Ruby, a class is an object of type Class, normally identified by a name as a Constant. So it is easy to create a new type that doesn’t exist:
- Person = Class.new do
- def talk
- puts "hola"
- end
- end
- p=Person.new
- p.talk
And the output
ruby Ruby-1.rb
hola
Dynamic Dispatch
Dynamic dispatch means allowing to send messages (call methods) to objects, determined at runtime. You don’t know the method you want to call until runtime:
- class Person
- def talk
- puts "hola :)"
- end
- end
- p=Person.new
- metodo = "talk"
- p.send metodo
And the output:
ruby Ruby-1.rb
hola :)
Defining methods dynamically
This means creating methods in Runtime that don’t yet exist at compile time. There are a couple of ways to do this. One is:
- class Person
- def talk
- puts "hola :)"
- end
- end
- new_method="scream"
- Person.class_eval do
- define_method new_method do
- puts "HOLA!"
- end
- end
- p=Person.new
- p.scream
- puts Person.instance_methods false
And the output
ruby Ruby-1.rb
HOLA!
talk
scream
Ghost Methods
Ghost methods are methods that not really exist but you can still call. How cool is that?. They leverage a special method in every Ruby object named missing_method that gets called when calling a method on an object that doesn’t exist in all its lookup path.
- class Person
- def method_missing (name,*args,&block)
- if name == :talk
- puts "hola "+args[0]
- else
- super
- end
- end
- end
- p=Person.new
- p.talk "carlo"
- p.scream
And the output
ruby Ruby-1.rb
hola carlo
Ruby-1.rb:6:in `method_missing': undefined method `scream' for #
from Ruby-1.rb:13:in `
This was just a small introduction, all these techniques and many more can be combined and extended to achieve really powerfull results.
A couple of good books on Ruby and the subjects of dynamics and metaprogramming are:
2 comments:
Nice teaser, thanks! You could elaborate class_eval and its friends more, maybe in a separate post...
Some of my articles in this topic:
Ruby's class system
Ruby blocks and closures
Thanks for your comment, I'll take a look at your articles.
I'll try to elaborate in new articles some of the points, I was thinking particularly in Ghost Methods, but also the Eval family.
Post a Comment