Ruby — An introduction
-
Upload
goncalo-silva -
Category
Technology
-
view
1.854 -
download
0
description
Transcript of Ruby — An introduction
RubyRuby
An introduction to the Ruby programming languageAn introduction to the Ruby programming language
LPOO — MIEIC — FEUPLPOO — MIEIC — FEUP
May 2011May 2011
@goncalossilva@goncalossilva
RubyRuby
Created by Yukihiru “Matz” Matsumoto, in JapanCreated by Yukihiru “Matz” Matsumoto, in Japan
Perl + SmallTalk + Eiffel + Ada + Lisp = RubyPerl + SmallTalk + Eiffel + Ada + Lisp = Ruby
“I wanted a scripting language that was more powerful than Perl, and more object-oriented than Python.” — matz“I wanted a scripting language that was more powerful than Perl, and more object-oriented than Python.” — matz
RubyRuby
Interpreted language, with many implementations: Interpreted language, with many implementations: YARVYARV , , RubiniusRubinius , , JRubyJRuby , etc, etc
Functional, object-oriented, imperative and reflectiveFunctional, object-oriented, imperative and reflective
Dynamically typed, with automatic garbage collection, exception handling andDynamically typed, with automatic garbage collection, exception handling andbuilt-in unit testingbuilt-in unit testing
Optimized for programmer productivity and happinessOptimized for programmer productivity and happiness
BasicsBasics
MethodsMethods
defdef hello_world hello_world()() returnreturn "Hello world!""Hello world!"endend
hello_worldhello_world()()
MethodsMethods
defdef hello_world hello_world # no need for ()# no need for () "Hello world!""Hello world!" # implicit return# implicit returnendend
hello_world hello_world # no need for ()# no need for ()
ClassesClasses
classclass HelloWorld HelloWorld defdef say say "Hello world done right!""Hello world done right!" endendendend
hello_world_object hello_world_object == HelloWorld HelloWorld..newnewhello_world_objecthello_world_object..saysay
It's object-orientedIt's object-oriented
classclass Lecture Lecture defdef initialize initialize((name name == "TBA""TBA",, duration_in_minutes duration_in_minutes == 6060)) @name@name == name name @duration@duration == duration_in_minutes duration_in_minutes//60.060.0;; endend
defdef description description "Name: #{@name}\nDuration: #{@duration} hours""Name: #{@name}\nDuration: #{@duration} hours" endendendend
lecture lecture == Lecture Lecture..newnew(("Ruby""Ruby",, 4545))lecturelecture..descriptiondescription
It's It's reallyreally object-oriented object-oriented
// Java// Javamaximum maximum == Math Math..maxmax((11,, 33))
# Ruby# Rubymaximum maximum == [[11,, 33].].maxmax
It's It's reallyreally object-oriented object-oriented
# Python# Pythonpositive positive == absabs((numnum))
# Ruby# Rubypositive positive == num num..absabs
It's It's reallyreally object-oriented object-oriented
// C// Clength length == strlenstrlen((namename))
# Ruby# Rubylength length == name name..lengthlength
It's It's reallyreally object-oriented object-oriented
In Ruby, all functions and most operators are methods of an objectIn Ruby, all functions and most operators are methods of an object
In Python, for instance, some functions are proceduralIn Python, for instance, some functions are procedural
Classes themselves are instances of Classes themselves are instances of ClassClass
It's It's reallyreally object-oriented object-oriented
Ruby has a permanent notion of the Ruby has a permanent notion of the current objectcurrent object : : selfself
selfself controls how Ruby accesses instance variables controls how Ruby accesses instance variables
All method calls are made on some object, called the All method calls are made on some object, called the receiverreceiver
If no If no receiverreceiver is specified, is specified, selfself is used — it is implicit! is used — it is implicit!
It's It's reallyreally object-oriented object-oriented
# self is "main", an instance of Object# self is "main", an instance of Objectclassclass MyClass MyClass # self is MyClass# self is MyClass
defdef my_method my_method # self will depend on the receiver for this method# self will depend on the receiver for this method endendendend
# self is "main" again, so my_object exists in the main scope# self is "main" again, so my_object exists in the main scopemy_object my_object == MyClass MyClass..newnew
# explicit receiver, so self will be my_object inside my_method# explicit receiver, so self will be my_object inside my_methodmy_objectmy_object..my_methodmy_method
ArraysArrays
Store indexed collections of objects accessible through an integer keyStore indexed collections of objects accessible through an integer key
Can contain objects with different classes simultaneouslyCan contain objects with different classes simultaneously
# array# arraya a == [[11,, "second""second",, 3.143.14]]aa[[22]]
HashesHashes
Store indexed collections of objects accessible through a key which can be anyStore indexed collections of objects accessible through a key which can be anyobjectobject
Slightly less efficient but much more flexibleSlightly less efficient but much more flexible
# hash# hashh h == {{ "string""string" =>=> [[33,,44,,55],], 22 =>=> "everything can be a value!""everything can be a value!",, [[11,,22]] =>=> "everything can be a key!""everything can be a key!"}}hh[[[[11,,22]]]]
SymbolsSymbols
A significant name, generally a static variableA significant name, generally a static variable
// java// javastaticstatic finalfinal intint NORTH NORTH == 11;;// ... more code// ... more codemovemove((NORTHNORTH););
# ruby# rubymovemove(:(:northnorth))
No need to predeclare these constants, they are uniqueNo need to predeclare these constants, they are unique
returnreturn truetrue ifif direction direction ==== ::northnorth
Control StructuresControl Structures
# if# ififif score score..between?between?((100100,, 199199)) puts puts "You rock!""You rock!"elsifelsif score score < 50< 50 puts puts "You suck!""You suck!"elseelse puts puts "Meh""Meh"endend
# while# whilewhilewhile score score < 100< 100 puts puts "You're almost there! Try again...""You're almost there! Try again..." # ...# ...endend
# more goodness with unless, until, case, for, etc# more goodness with unless, until, case, for, etc
# and awesome shortcuts like statement modifiers# and awesome shortcuts like statement modifiersputs puts "You cheated!""You cheated!" ifif score score >=>= 200200
Blocks and IteratorsBlocks and Iterators
BlocksBlocks
A chuck of code enclosed between A chuck of code enclosed between { }{ } or or dodo//endend keywords keywords
Similar to the concept of anonymous methodsSimilar to the concept of anonymous methods
Can take parametersCan take parameters
Can be passed to methods as arguments (at the end, like an extra parameter)Can be passed to methods as arguments (at the end, like an extra parameter)
sum sum == 00((11....55).).each each dodo ||nn|| # same as [1,2,3,4,5]# same as [1,2,3,4,5] sum sum +=+= n nendendsumsum
# same thing with { }# same thing with { }sum sum == 00((11....55).).each each {{ ||nn|| sum sum +=+= n n }}sumsum
Blocks as ObjectsBlocks as Objects
Can be converted into objectsCan be converted into objects
Can be stored in variables, pass them around, invoke them whenever you wantCan be stored in variables, pass them around, invoke them whenever you want
Great for implementing callbacks, dispatch tables, etcGreat for implementing callbacks, dispatch tables, etc
classclass BlockAsObject BlockAsObject defdef store_block store_block(&(&my_blockmy_block)) @stored_block@stored_block == my_block my_block # converted to Proc# converted to Proc endend
defdef use_block use_block((parameterparameter)) @stored_block@stored_block..callcall((parameterparameter)) endendendend
foo foo == BlockAsObject BlockAsObject..newnewfoofoo..store_block store_block {{ ||paramparam|| "The block was called with ""The block was called with " ++ param param }}foofoo..use_blockuse_block(("delay""delay"))
Blocks as ClosuresBlocks as Closures
They can use local variables from the surrounding scopeThey can use local variables from the surrounding scope
defdef powers_of_2_proc powers_of_2_proc value value == 11 lambda lambda {{ value value +=+= value value }}endend
powers_of_2 powers_of_2 == powers_of_2_proc powers_of_2_proc
powers_of_2powers_of_2..call call # 2# 2powers_of_2powers_of_2..call call # 4# 4powers_of_2powers_of_2..call call # will return 8!# will return 8!
So, So, powers_of_2_procpowers_of_2_proc returns a returns a ProcProc that references that references valuevalue
When the block is called, When the block is called, valuevalue is out of scope is out of scope
The block is still able to access it (and will be for the remaining life of this block)The block is still able to access it (and will be for the remaining life of this block)
IteratorsIterators
In many languages, collections implement methods to generate external iteratorIn many languages, collections implement methods to generate external iteratorobjectsobjects
// C++// C++forfor ((stdstd::::vectorvector<<intint>::>::iteratoriterator ii==listlist..beginbegin();(); i i!=!=listlist..endend();(); i i++)++) {{ // code// code}}
IteratorsIterators
In many languages, collections implement methods to generate external iteratorIn many languages, collections implement methods to generate external iteratorobjectsobjects
// C#// C#IEnumerator<int>IEnumerator<int> i i == list list..GetEnumeratorGetEnumerator();();whilewhile ((ii..MoveNextMoveNext())()) {{ // code// code}}
IteratorsIterators
In many languages, collections implement methods to generate external iteratorIn many languages, collections implement methods to generate external iteratorobjectsobjects
// Java// JavaIteratorIterator i i == list list..iteratoriterator();();whilewhile ((ii..hasNexthasNext())()) {{ // code// code}}
IteratorsIterators
When coming from other languages, many people iterate collections like this:When coming from other languages, many people iterate collections like this:
# familiar?# familiar?forfor i i inin 00....22 number number == array array[[ii][][00]] word word == array array[[ii][][11]]
puts puts "#{word}: #{number}""#{word}: #{number}"endend
IteratorsIterators
However, there's another approach:However, there's another approach:
# the "ruby way", with a lot less coupling# the "ruby way", with a lot less couplingarrayarray..each each dodo ||wordword,, number number|| puts puts "#{word}: #{number}""#{word}: #{number}"endend
The “Ruby way” is different: an iterator is internal to the collection... it's just aThe “Ruby way” is different: an iterator is internal to the collection... it's just amethod that calls method that calls yieldyield every time it generates a new value every time it generates a new value
IteratorsIterators
Ruby provides a lot of useful iterators: Ruby provides a lot of useful iterators: eacheach, , mapmap , , injectinject , etc, etc
But you can build your ownBut you can build your own
defdef fib fib((maxmax)) i1 i1,, i2 i2 == 11,, 11 # parallel assignment# parallel assignment whilewhile i1 i1 <= max<= max yieldyield i1 i1 i1 i1,, i2 i2 == i2 i2,, i1 i1++i2i2 endendendend
result result == """"fibfib((13371337)) {{ ||nn|| result result +=+= "#{n} ""#{n} " }}resultresult
IteratorsIterators
Ruby's internal iterators aren't necessarily the best solutionRuby's internal iterators aren't necessarily the best solution
What if you need the iterator to be an object?What if you need the iterator to be an object?
What if you want to iterate multiple collections simultaneously?What if you want to iterate multiple collections simultaneously?
EnumeratorsEnumerators
When iterators aren't suitable, you can resort to enumeratorsWhen iterators aren't suitable, you can resort to enumerators
To put it simply, an enumerator is an external iteratorTo put it simply, an enumerator is an external iterator
array array == [["my""my",, 13371337,, "array""array"]]enumerator enumerator == array array..to_enum to_enum # same as "enumerator = array.each"# same as "enumerator = array.each"
enumeratorenumerator..nextnext # returns "my" and moves to the next element# returns "my" and moves to the next elementenumeratorenumerator..nextnext
EnumeratorsEnumerators
Most internal iterators are can be used as enumeratorsMost internal iterators are can be used as enumerators
string string == "le fu""le fu"enumerator enumerator == string string..each_chareach_char
enumeratorenumerator..nextnext # returns "l" and moves to the next char# returns "l" and moves to the next charenumeratorenumerator..nextnext # returns "e" and moves to the next char# returns "e" and moves to the next char
Containers, containers, containers!Containers, containers, containers!
Blocks and iterators are core concepts of RubyBlocks and iterators are core concepts of Ruby
With practice, you'll start building classes that iterate over their contents insteadWith practice, you'll start building classes that iterate over their contents insteadof using the conventional looping constructsof using the conventional looping constructs
It might seem complicated at first, but you'll start using these features naturallyIt might seem complicated at first, but you'll start using these features naturally
Easy to read and maintainEasy to read and maintain
SharingSharingFunctionalityFunctionality
InheritanceInheritance
classclass SuperClass SuperClass defdef hello_world hello_world "Hello world!""Hello world!" endendendend
classclass SubClass SubClass < SuperClass< SuperClassendend
superclass superclass == SuperClass SuperClass..newnewsubclass subclass == SubClass SubClass..newnew
"Hello world from the superclass: #{superclass.hello_world}\n""Hello world from the superclass: #{superclass.hello_world}\n" ++"Hello world from the subclass : #{subclass.hello_world}""Hello world from the subclass : #{subclass.hello_world}"
InheritanceInheritance
Single inheritance, unlike C++Single inheritance, unlike C++
Top-level classes are subclasses of Top-level classes are subclasses of ObjectObject , which is a subclass of , which is a subclass of BasicObjectBasicObject
Classes have built-in functionality because they inherited it!Classes have built-in functionality because they inherited it!
ModulesModules
Modules provide a namespace, avoiding name collisionsModules provide a namespace, avoiding name collisions
modulemodule MyModule MyModule # a module has it all# a module has it all MY_CONSTANT MY_CONSTANT == "my constant""my constant" # constants# constants
defdef my_method my_method # methods# methods endend
classclass MyClass MyClass # and classes# and classes endendendend
namespaced namespaced == MyModule MyModule::::MyClassMyClass..newnew
Modules have another wonderful use: Modules have another wonderful use: mixinsmixins
Mixins and InheritanceMixins and Inheritance
Some OO languages support multiple inheritance (C++, Lisp, Python, etc)Some OO languages support multiple inheritance (C++, Lisp, Python, etc)
This is very powerful, but can be troublesome because of inheritance ambiguityThis is very powerful, but can be troublesome because of inheritance ambiguity
Ruby offers a great compromise: the simplicity of single inheritance and theRuby offers a great compromise: the simplicity of single inheritance and thepower of multiple inheritancepower of multiple inheritance
MixinsMixins
includeinclude is used to mix modules into classes or other modules is used to mix modules into classes or other modules
modulemodule ToBeIncluded ToBeIncluded defdef foo foo "bar""bar" endendendend
classclass MyClass MyClass includeinclude ToBeIncluded ToBeIncludedendend
object object == MyClass MyClass..newnewobjectobject..foofoo
MixinsMixins
extendextend is used to add instance methods to a given object is used to add instance methods to a given object
modulemodule ToBeIncluded ToBeIncluded defdef foo foo "bar""bar" endendendend
classclass MyClass MyClassendend
object object == MyClass MyClass..newnewobjectobject..extend ToBeIncludedextend ToBeIncludedobjectobject..foofoo
MixinsMixins
extendextend is also used to mix instance methods into a class is also used to mix instance methods into a class
modulemodule ToBeIncluded ToBeIncluded defdef foo foo "bar""bar" endendendend
classclass MyClass MyClass extend ToBeIncluded extend ToBeIncludedendend
MyClassMyClass..foofoo
modulemodule StringHelpers StringHelpers defdef stringify stringify ifif selfself..value value >> 90009000 "Over 9000!""Over 9000!" elseelse "Meh""Meh" endend endend endend
classclass Number Number attr_reader attr_reader ::valuevalue defdef initialize initialize((valuevalue)) @value@value == value value endend endend
classclass Over9000Number Over9000Number < Number< Number includeinclude StringHelpers StringHelpers defdef initialize initialize((valuevalue)) supersuper((value value ++ 90009000)) endend defdef status status "Current status: ""Current status: "++stringifystringify endend endend
number number == Over9000Number Over9000Number..newnew((4242)) number number..statusstatus # => "Current status: Over 9000!"# => "Current status: Over 9000!"
MixinsMixins
Inherit from one class, include functionality from multiple modules — Inherit from one class, include functionality from multiple modules — mixinsmixins !!
Inheritance, Mixins and DesignInheritance, Mixins and Design
Ruby allows you to right code once and inject it into multiple placesRuby allows you to right code once and inject it into multiple places
When to use each?When to use each?
InheritanceInheritance
You should be able to replace a parent object with a child object, honoringYou should be able to replace a parent object with a child object, honoringits contractits contract
A child object A child object is ais a kind of the parent (an apple is a fruit) kind of the parent (an apple is a fruit)
In the real world, strict hierarchies are restrictive... we need composition!In the real world, strict hierarchies are restrictive... we need composition!
MixinsMixins
For composition: A For composition: A has ahas a B, or A B, or A uses auses a B B
Exclusively using Exclusively using mixinsmixins can be messy — both should be combined can be messy — both should be combined
Inheritance, Mixis and DesignInheritance, Mixis and Design
Each of them serves its purpose, our job is to use the appropriatelyEach of them serves its purpose, our job is to use the appropriately
None of these make any sense:None of these make any sense:
classclass Person Person < DataWrapper< DataWrapperendend
classclass Banana Banana includeinclude FruitProperties FruitPropertiesendend
Think before you typeThink before you type
ReflectionReflection
ReflectionReflection
Ruby is very powerful when it comes to examining the aspects of a programRuby is very powerful when it comes to examining the aspects of a programwithin itselfwithin itself
It can discover information about:It can discover information about:
What objects existWhat objects exist
All class hierarchiesAll class hierarchies
Attributes and methods of all objectsAttributes and methods of all objects
Information about methodsInformation about methods
“When you introspect, you think about your thoughts and feelings. This is interesting because you're using thought to“When you introspect, you think about your thoughts and feelings. This is interesting because you're using thought toanalyze thought.” — Dave Thomasanalyze thought.” — Dave Thomas
Reflection and ObjectsReflection and Objects
You can transverse all living objects:You can transverse all living objects:
ObjectSpaceObjectSpace..each_objecteach_object((FloatFloat)) {{ ||xx|| puts x puts x }}
Reflection and ObjectsReflection and Objects
You can look inside objects:You can look inside objects:
[[11,,22,,33].].methodsmethods[[00....44]]
[[11,,22,,33].].respond_respond_to?to?(:(:to_sto_s))
[[11,,22,,33].].kind_kind_of?of?((HashHash))
Reflection and ObjectsReflection and Objects
You can invoke any method by name using You can invoke any method by name using sendsend ::
a a == [[11,,22,,33]]aa..sendsend((aa..private_methodsprivate_methods..firstfirst..to_symto_sym)) # "initialize"# "initialize"a a # now it's empty!# now it's empty!
Reflection and ObjectsReflection and Objects
Another way is to use the Another way is to use the MethodMethod class: class:
a a == [[11,,22,,33]]constructor constructor == a a..methodmethod(:(:initializeinitialize))constructorconstructor..callcallaa
You get a You get a MethodMethod object which you can store, pass around and call anytime! object which you can store, pass around and call anytime!
Reflection and ClassesReflection and Classes
It's also possible to look inside classes:It's also possible to look inside classes:
StringString..ancestors ancestors # all superclasses and mixed-in modules# all superclasses and mixed-in modules
Reflection and ClassesReflection and Classes
It's also possible to look inside classes:It's also possible to look inside classes:
klass klass == String Stringresult result == klass klass..to_sto_sbeginbegin klass klass == klass klass..superclasssuperclass result result +=+= " < "" < " ++ klass klass..to_sto_sendend whilewhile klass klass..superclasssuperclassresultresult
Reflection and ClassesReflection and Classes
It's also possible to look inside classes:It's also possible to look inside classes:
FixnumFixnum..constantsconstants
FixnumFixnum..class_variablesclass_variables
FixnumFixnum..singleton_methodssingleton_methods((falsefalse))
FixnumFixnum..instance_methodsinstance_methods((falsefalse)) # private/protected/public prefix# private/protected/public prefix
Reflection and the Program'sReflection and the Program'sExecutionExecution
Ruby let's you look at the interpreter while it executes your codeRuby let's you look at the interpreter while it executes your code
set_trace_func lambda set_trace_func lambda {{ ||eventevent,, file file,, line line,, id id,, binding binding,, classname classname|| printf printf "%8s %s:%s-2d %-15s\n""%8s %s:%s-2d %-15s\n",, event event,, file file,, line line,, classname classname,, id id}}
# all code is traced from now on# all code is traced from now on
And you can also get the current call stack by using calling And you can also get the current call stack by using calling callercaller on your on yourmethodsmethods
Reflection and the Program'sReflection and the Program'sExecutionExecution
You can even get the current source file being executed with the You can even get the current source file being executed with the __FILE____FILE__ special specialvariable, leading to an interesting variable, leading to an interesting QuineQuine ::
print Fileprint File..readread((__FILE____FILE__))
A program that outputs itself!A program that outputs itself!
MetaprogrammingMetaprogramming
MetaprogrammingMetaprogramming
Ruby code can modify aspects of its own structure at runtime and it makes it allRuby code can modify aspects of its own structure at runtime and it makes it alleasyeasy
You've seen it before on this presentation: remember You've seen it before on this presentation: remember includeinclude and and extendextend? Ruby is? Ruby iseffectively injecting their code into their receivereffectively injecting their code into their receiver
Singleton MethodsSingleton Methods
Ruby lets you define methods specific to a particular objectRuby lets you define methods specific to a particular object
person person == "person""person" # String object# String objectdefdef person person..say_hisay_hi "Hi!""Hi!"endendpersonperson..say_hisay_hi
Magic? No. Ruby created an anonymous class (often called singleton class) basedMagic? No. Ruby created an anonymous class (often called singleton class) basedon on StringString and added the method and added the method say_hisay_hi to it to it
Singleton ClassSingleton Class
There are other ways of creating methods in an object's singleton classThere are other ways of creating methods in an object's singleton class
person person == "person""person"classclass << person<< person defdef say_hi say_hi "Hi there!""Hi there!" endendendendpersonperson..say_hisay_hi
Inherited VisibilityInherited Visibility
The visibility of an inherited method can be changedThe visibility of an inherited method can be changed
classclass SuperClass SuperClass private private defdef my_private_method my_private_method "U can't touch this""U can't touch this" endendendend
classclass SubClass SubClass < SuperClass< SuperClass public public ::my_private_methodmy_private_methodendend
object object == SubClass SubClass..newnewobjectobject..my_private_method my_private_method # not so private anymore# not so private anymore
Inherited VisibilityInherited Visibility
What's really happening is that Ruby is inserting a hidden proxy method in theWhat's really happening is that Ruby is inserting a hidden proxy method in thesubclass that invokes the original method with subclass that invokes the original method with supersuper
classclass SubClass SubClass < SuperClass< SuperClass defdef my_private_method my_private_method supersuper endend
public public ::my_private_methodmy_private_methodendend
supersuper calls can access the parent's method regardless of its visibility calls can access the parent's method regardless of its visibility
Defining MethodsDefining Methods
Ruby allows you to define methods at runtime using Ruby allows you to define methods at runtime using define_methoddefine_method
classclass BabyInfo BabyInfo [["cry""cry",, "eat""eat",, "poop""poop"].].each each dodo ||baby_actionbaby_action|| define_method define_method((baby_actionbaby_action)) dodo "Of course, babies #{baby_action}""Of course, babies #{baby_action}" endend endendendend
baby_info baby_info == BabyInfo BabyInfo..newnewbaby_infobaby_info..crycry
Methods can also be blocked/removed by calling Methods can also be blocked/removed by calling undef_methodundef_method and and remove_methodremove_method,,respectivelyrespectively
Class-level MacrosClass-level Macros
Ruby has a few class-level macros that generate code behind the scenesRuby has a few class-level macros that generate code behind the scenes
classclass Laptop Laptop attr_accessor attr_accessor ::memory memory # injects a getter/setter for "memory"# injects a getter/setter for "memory"endend
If you've used Ruby on Rails, you've probably dealt with associationsIf you've used Ruby on Rails, you've probably dealt with associations
classclass Post Post < ActiveRecord::Base< ActiveRecord::Base has_many has_many ::commentscommentsendend
EvalEval
Similarly to other languages, Similarly to other languages, evaleval evaluates the passed Ruby expression(s) evaluates the passed Ruby expression(s)
course course == "LPOO""LPOO"eval eval "'Hello ' + course + '!'""'Hello ' + course + '!'"
Instance evalInstance eval
instance_evalinstance_eval allows you to evaluate Ruby expression(s) in the context of an allows you to evaluate Ruby expression(s) in the context of aninstanceinstance
string string == "cool man cool""cool man cool"stringstring..instance_eval instance_eval "def shout; self.upcase; end""def shout; self.upcase; end"stringstring..shoutshout
Remember: classes are instances of Remember: classes are instances of ClassClass, so you can also use , so you can also use instance_evalinstance_eval with withthem:them:
FixnumFixnum..instance_eval instance_eval "def ten; 10; end""def ten; 10; end"FixnumFixnum..tenten
Class evalClass eval
As the name implies, As the name implies, class_evalclass_eval can only be used with classes can only be used with classes
It evaluates the code as if you were in the context of the class definitionIt evaluates the code as if you were in the context of the class definition
StringString..class_eval class_eval dodo defdef shout shout selfself..upcaseupcase endendendendstring string == "cool man cool""cool man cool"stringstring..shoutshout
Note: Note: evaleval, , instance_evalinstance_eval and and class_evalclass_eval can take blocks as arguments as shown can take blocks as arguments as shownaboveabove
Eval evilnessEval evilness
evaleval is slow is slow
evaleval is dangerous is dangerous
evaleval doesn't generally make sense, given all other metaprogramming facilities doesn't generally make sense, given all other metaprogramming facilities
CallbacksCallbacks
Ruby provides Ruby provides hook methodshook methods , called by the interpreter when a specific event, called by the interpreter when a specific eventoccursoccurs
Among all available Among all available hook methodshook methods —also known as —also known as callbackscallbacks—are:—are:
Method-related hooks: Method-related hooks: method_addedmethod_added, , method_missingmethod_missing, , method_removedmethod_removed, etc, etc
Class/Module-related hooks: Class/Module-related hooks: const_missingconst_missing, , extendedextended, , includedincluded, , inheritedinherited, etc, etc
Object marshaling and coercion hooksObject marshaling and coercion hooks
Callbacks: Callbacks: method_missingmethod_missing
Ruby allows you to act upon calls to undefined methods by using Ruby allows you to act upon calls to undefined methods by using method missingmethod missing
classclass BabyInfo BabyInfo [["cry""cry",, "eat""eat",, "poop""poop"].].each each dodo ||baby_actionbaby_action|| define_method define_method((baby_actionbaby_action)) dodo "Of course, babies #{baby_action}""Of course, babies #{baby_action}" endend endend
defdef method_missing method_missing((namename,, **argsargs,, &&blockblock)) "Nope, babies don't #{name}""Nope, babies don't #{name}" endendendend
baby_info baby_info == BabyInfo BabyInfo..newnewbaby_infobaby_info..surfsurf
Callbacks: Callbacks: inheritedinherited
Ruby allows your classes to act when they're subclassedRuby allows your classes to act when they're subclassed
classclass SuperClass SuperClass @children@children == [][] # class variable# class variable
defdef selfself..inheritedinherited((childchild)) @children@children << child<< child endend
defdef selfself..childrenchildren @children@children endendendend
Callback: method callsCallback: method calls
Ruby allows you to intercept calls to specific methods:Ruby allows you to intercept calls to specific methods:
classclass Object Object alias_method alias_method ::old_to_sold_to_s,, ::to_sto_s defdef to_s to_s result result == selfself..old_to_sold_to_s "Your modified to_s returned this (should be '""Your modified to_s returned this (should be '"++resultresult++"')""')" endendendend
object object == Object Object..newnewobjectobject..to_sto_s
This isn't a direct hook: you're copying the original method and inserting a hookThis isn't a direct hook: you're copying the original method and inserting a hookby yourselfby yourself
Wrapping UpWrapping Up
RubyRuby
Ruby is an incredibly powerful languageRuby is an incredibly powerful language
Many successful projects/products use it at their core: Ruby on Rails, God,Many successful projects/products use it at their core: Ruby on Rails, God,Redmine, etcRedmine, etc
You can add and remove code in a running process, redefine methods on the fly,You can add and remove code in a running process, redefine methods on the fly,change their scopechange their scope
You can even modify basic types like You can even modify basic types like StringString, , FloatFloat or even or even ClassClass and and ObjectObject
After using it for a while, you'll notice the lack of flexibility on static languagesAfter using it for a while, you'll notice the lack of flexibility on static languageslike C++ or half-static languages like Javalike C++ or half-static languages like Java
RubyRuby
This presentation, for instance, is running on a Sinatra applicationThis presentation, for instance, is running on a Sinatra application
And most of the shown code was executed in real timeAnd most of the shown code was executed in real time
No? Then check this out:No? Then check this out:
classclass Lecture Lecture defdef initialize initialize((name name == "TBA""TBA")) @name@name == name name endend
defdef finish finish "Finished at #{(Time.now.utc+3600).strftime('%H:%M:%S')}!""Finished at #{(Time.now.utc+3600).strftime('%H:%M:%S')}!" endendendend
lecture lecture == Lecture Lecture..newnew(("Ruby""Ruby"))lecturelecture..finishfinish
MoreMore
Books:Books:
Programming Ruby 1.9 by Dave ThomasProgramming Ruby 1.9 by Dave Thomas
Metaprogramming Ruby by Paolo PerrottaMetaprogramming Ruby by Paolo Perrotta
Mailing lists:Mailing lists:
Ruby-Talk (subscribe through Ruby-Talk (subscribe through Ruby's websiteRuby's website))
Ruby-pt (Google Groups)Ruby-pt (Google Groups)
IRC:IRC:
#ruby and #ruby-pt on Freenode#ruby and #ruby-pt on Freenode
??