• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Meta Programming in Ruby - Code Camp 2010
 

Meta Programming in Ruby - Code Camp 2010

on

  • 4,580 views

meta programming in ruby

meta programming in ruby
code camp 2010
steven soroka

Statistics

Views

Total Views
4,580
Views on SlideShare
4,545
Embed Views
35

Actions

Likes
17
Downloads
264
Comments
0

2 Embeds 35

http://www.slideshare.net 33
http://www.techgig.com 2

Accessibility

Categories

Upload Details

Uploaded via as Apple Keynote

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • <br />
  • <br />
  • <br />
  • <br />
  • And yes, the site is in Ruby on Rails. <br />
  • <br />
  • ruby-lang dot org describes it as.... <br />
  • what does author say? <br /> <br />
  • what does author say? <br /> <br />
  • 2. Scripting language, but don&#x2019;t call it php.4. smoothly elegant6. Ruby is also totally free. Not only free of charge, but also free to use, copy, modify, and distribute. <br />
  • 2. Scripting language, but don&#x2019;t call it php.4. smoothly elegant6. Ruby is also totally free. Not only free of charge, but also free to use, copy, modify, and distribute. <br />
  • 2. Scripting language, but don&#x2019;t call it php.4. smoothly elegant6. Ruby is also totally free. Not only free of charge, but also free to use, copy, modify, and distribute. <br />
  • 2. Scripting language, but don&#x2019;t call it php.4. smoothly elegant6. Ruby is also totally free. Not only free of charge, but also free to use, copy, modify, and distribute. <br />
  • 2. Scripting language, but don&#x2019;t call it php.4. smoothly elegant6. Ruby is also totally free. Not only free of charge, but also free to use, copy, modify, and distribute. <br />
  • 2. Scripting language, but don&#x2019;t call it php.4. smoothly elegant6. Ruby is also totally free. Not only free of charge, but also free to use, copy, modify, and distribute. <br />
  • simplest thing that could possibly work <br /> No libraries to import, no main declaration, no instantiation of string classes <br /> This, is a thing of beauty. <br />
  • Lets just get an idea of the syntax. <br /> Notice I don&#x2019;t have to declare the type of the car variable, it&#x2019;s automatically assigned at runtime. <br />
  • Lets just get an idea of the syntax. <br /> Notice I don&#x2019;t have to declare the type of the car variable, it&#x2019;s automatically assigned at runtime. <br />
  • Lets just get an idea of the syntax. <br /> Notice I don&#x2019;t have to declare the type of the car variable, it&#x2019;s automatically assigned at runtime. <br />
  • Lets just get an idea of the syntax. <br /> Notice I don&#x2019;t have to declare the type of the car variable, it&#x2019;s automatically assigned at runtime. <br />
  • Lets just get an idea of the syntax. <br /> Notice I don&#x2019;t have to declare the type of the car variable, it&#x2019;s automatically assigned at runtime. <br />
  • Lets just get an idea of the syntax. <br /> Notice I don&#x2019;t have to declare the type of the car variable, it&#x2019;s automatically assigned at runtime. <br />
  • Lets just get an idea of the syntax. <br /> Notice I don&#x2019;t have to declare the type of the car variable, it&#x2019;s automatically assigned at runtime. <br />
  • This is all fine and dandy, but does Ruby matter? I&#x2019;d argue that it&#x2019;s important. <br /> If you can judge that by the number of Ruby implementations, then I&#x2019;d say so. <br /> IronMonkey is Adobe VM <br /> HotRuby is Ruby interpreter implemented in JS <br /> IronRuby is Microsoft&#x2019;s work towards Ruby.NET <br /> <br />
  • This is all fine and dandy, but does Ruby matter? I&#x2019;d argue that it&#x2019;s important. <br /> If you can judge that by the number of Ruby implementations, then I&#x2019;d say so. <br /> IronMonkey is Adobe VM <br /> HotRuby is Ruby interpreter implemented in JS <br /> IronRuby is Microsoft&#x2019;s work towards Ruby.NET <br /> <br />
  • This is all fine and dandy, but does Ruby matter? I&#x2019;d argue that it&#x2019;s important. <br /> If you can judge that by the number of Ruby implementations, then I&#x2019;d say so. <br /> IronMonkey is Adobe VM <br /> HotRuby is Ruby interpreter implemented in JS <br /> IronRuby is Microsoft&#x2019;s work towards Ruby.NET <br /> <br />
  • This is all fine and dandy, but does Ruby matter? I&#x2019;d argue that it&#x2019;s important. <br /> If you can judge that by the number of Ruby implementations, then I&#x2019;d say so. <br /> IronMonkey is Adobe VM <br /> HotRuby is Ruby interpreter implemented in JS <br /> IronRuby is Microsoft&#x2019;s work towards Ruby.NET <br /> <br />
  • This is all fine and dandy, but does Ruby matter? I&#x2019;d argue that it&#x2019;s important. <br /> If you can judge that by the number of Ruby implementations, then I&#x2019;d say so. <br /> IronMonkey is Adobe VM <br /> HotRuby is Ruby interpreter implemented in JS <br /> IronRuby is Microsoft&#x2019;s work towards Ruby.NET <br /> <br />
  • This is all fine and dandy, but does Ruby matter? I&#x2019;d argue that it&#x2019;s important. <br /> If you can judge that by the number of Ruby implementations, then I&#x2019;d say so. <br /> IronMonkey is Adobe VM <br /> HotRuby is Ruby interpreter implemented in JS <br /> IronRuby is Microsoft&#x2019;s work towards Ruby.NET <br /> <br />
  • This is all fine and dandy, but does Ruby matter? I&#x2019;d argue that it&#x2019;s important. <br /> If you can judge that by the number of Ruby implementations, then I&#x2019;d say so. <br /> IronMonkey is Adobe VM <br /> HotRuby is Ruby interpreter implemented in JS <br /> IronRuby is Microsoft&#x2019;s work towards Ruby.NET <br /> <br />
  • This is all fine and dandy, but does Ruby matter? I&#x2019;d argue that it&#x2019;s important. <br /> If you can judge that by the number of Ruby implementations, then I&#x2019;d say so. <br /> IronMonkey is Adobe VM <br /> HotRuby is Ruby interpreter implemented in JS <br /> IronRuby is Microsoft&#x2019;s work towards Ruby.NET <br /> <br />
  • This is all fine and dandy, but does Ruby matter? I&#x2019;d argue that it&#x2019;s important. <br /> If you can judge that by the number of Ruby implementations, then I&#x2019;d say so. <br /> IronMonkey is Adobe VM <br /> HotRuby is Ruby interpreter implemented in JS <br /> IronRuby is Microsoft&#x2019;s work towards Ruby.NET <br /> <br />
  • This is all fine and dandy, but does Ruby matter? I&#x2019;d argue that it&#x2019;s important. <br /> If you can judge that by the number of Ruby implementations, then I&#x2019;d say so. <br /> IronMonkey is Adobe VM <br /> HotRuby is Ruby interpreter implemented in JS <br /> IronRuby is Microsoft&#x2019;s work towards Ruby.NET <br /> <br />
  • The Silverlight Dynamic Languages SDK includes IronRuby, which allows you to write Ruby that runs in the browser <br />
  • The Silverlight Dynamic Languages SDK includes IronRuby, which allows you to write Ruby that runs in the browser <br />
  • The Silverlight Dynamic Languages SDK includes IronRuby, which allows you to write Ruby that runs in the browser <br />
  • The Silverlight Dynamic Languages SDK includes IronRuby, which allows you to write Ruby that runs in the browser <br />
  • The Silverlight Dynamic Languages SDK includes IronRuby, which allows you to write Ruby that runs in the browser <br />
  • Here&#x2019;s an example of the GUI you can make with the WxRuby toolkit from Ruby <br />
  • Here&#x2019;s the exact same library and code run on a Mac. Notice both are native to their own OS. <br />
  • Let&#x2019;s get familiar with Ruby. <br /> We&#x2019;re going to cover a lot of topics really quickly, so keep up. :) <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • important @ <br /> less important @@ <br /> global $, if you litter your code with these; your peers will surprise you at how creative they can be when it comes to hiding your lifeless body. <br /> <br />
  • important @ <br /> less important @@ <br /> global $, if you litter your code with these; your peers will surprise you at how creative they can be when it comes to hiding your lifeless body. <br /> <br />
  • important @ <br /> less important @@ <br /> global $, if you litter your code with these; your peers will surprise you at how creative they can be when it comes to hiding your lifeless body. <br /> <br />
  • important @ <br /> less important @@ <br /> global $, if you litter your code with these; your peers will surprise you at how creative they can be when it comes to hiding your lifeless body. <br /> <br />
  • local variables <br />
  • <br />
  • <br />
  • This is what a symbol looks like. you can think of it as a string, and it is similar, but it&#x2019;s got a key difference which I&#x2019;ll get in to. <br /> In Ruby, symbols are mostly used as hash keys, column names, for referencing data types, or used as contant values, but they are not just cheap strings. <br /> <br />
  • This is what a symbol looks like. you can think of it as a string, and it is similar, but it&#x2019;s got a key difference which I&#x2019;ll get in to. <br /> In Ruby, symbols are mostly used as hash keys, column names, for referencing data types, or used as contant values, but they are not just cheap strings. <br /> <br />
  • This is what a symbol looks like. you can think of it as a string, and it is similar, but it&#x2019;s got a key difference which I&#x2019;ll get in to. <br /> In Ruby, symbols are mostly used as hash keys, column names, for referencing data types, or used as contant values, but they are not just cheap strings. <br /> <br />
  • This is what a symbol looks like. you can think of it as a string, and it is similar, but it&#x2019;s got a key difference which I&#x2019;ll get in to. <br /> In Ruby, symbols are mostly used as hash keys, column names, for referencing data types, or used as contant values, but they are not just cheap strings. <br /> <br />
  • <br />
  • <br />
  • The two name symbols here are literally the exact same object, where as the two name strings below are two separate objects that happen to have the same value. <br />
  • The two name symbols here are literally the exact same object, where as the two name strings below are two separate objects that happen to have the same value. <br />
  • The two name symbols here are literally the exact same object, where as the two name strings below are two separate objects that happen to have the same value. <br />
  • The two name symbols here are literally the exact same object, where as the two name strings below are two separate objects that happen to have the same value. <br />
  • curly braces represent a key-value hash, not a block of code ; the hash has keys and values <br /> ;; you can fetch the value for the keys <br />
  • curly braces represent a key-value hash, not a block of code ; the hash has keys and values <br /> ;; you can fetch the value for the keys <br />
  • curly braces represent a key-value hash, not a block of code ; the hash has keys and values <br /> ;; you can fetch the value for the keys <br />
  • curly braces represent a key-value hash, not a block of code ; the hash has keys and values <br /> ;; you can fetch the value for the keys <br />
  • curly braces represent a key-value hash, not a block of code ; the hash has keys and values <br /> ;; you can fetch the value for the keys <br />
  • curly braces represent a key-value hash, not a block of code ; the hash has keys and values <br /> ;; you can fetch the value for the keys <br />
  • <br />
  • {1}{2}, for example... <br />
  • {1}{2}, for example... <br />
  • <br />
  • <br />
  • <br />
  • means: you can change or extend any class. Good practices are up to you <br /> OCP is .. <br />
  • means: you can change or extend any class. Good practices are up to you <br /> OCP is .. <br />
  • means: you can change or extend any class. Good practices are up to you <br /> OCP is .. <br />
  • means: you can change or extend any class. Good practices are up to you <br /> OCP is .. <br />
  • lets reopen the Float class, and maybe our app doesn&#x2019;t want to round up ever. Our new CEO says rounding up is bad for business and we want to redefine round to call floor. <br /> Now 5.7.round always returns 5. This is clearly a double-edged sword. You want to open classes for extension, not for modification as we&#x2019;ve done here. This violates the OCP <br />
  • lets reopen the Float class, and maybe our app doesn&#x2019;t want to round up ever. Our new CEO says rounding up is bad for business and we want to redefine round to call floor. <br /> Now 5.7.round always returns 5. This is clearly a double-edged sword. You want to open classes for extension, not for modification as we&#x2019;ve done here. This violates the OCP <br />
  • lets reopen the Float class, and maybe our app doesn&#x2019;t want to round up ever. Our new CEO says rounding up is bad for business and we want to redefine round to call floor. <br /> Now 5.7.round always returns 5. This is clearly a double-edged sword. You want to open classes for extension, not for modification as we&#x2019;ve done here. This violates the OCP <br />
  • lets reopen the Float class, and maybe our app doesn&#x2019;t want to round up ever. Our new CEO says rounding up is bad for business and we want to redefine round to call floor. <br /> Now 5.7.round always returns 5. This is clearly a double-edged sword. You want to open classes for extension, not for modification as we&#x2019;ve done here. This violates the OCP <br />
  • lets reopen the Float class, and maybe our app doesn&#x2019;t want to round up ever. Our new CEO says rounding up is bad for business and we want to redefine round to call floor. <br /> Now 5.7.round always returns 5. This is clearly a double-edged sword. You want to open classes for extension, not for modification as we&#x2019;ve done here. This violates the OCP <br />
  • lets reopen the Float class, and maybe our app doesn&#x2019;t want to round up ever. Our new CEO says rounding up is bad for business and we want to redefine round to call floor. <br /> Now 5.7.round always returns 5. This is clearly a double-edged sword. You want to open classes for extension, not for modification as we&#x2019;ve done here. This violates the OCP <br />
  • lets reopen the Float class, and maybe our app doesn&#x2019;t want to round up ever. Our new CEO says rounding up is bad for business and we want to redefine round to call floor. <br /> Now 5.7.round always returns 5. This is clearly a double-edged sword. You want to open classes for extension, not for modification as we&#x2019;ve done here. This violates the OCP <br />
  • if we give the method a different name, it wont collide with the round function and break any existing code using it. <br />
  • if we give the method a different name, it wont collide with the round function and break any existing code using it. <br />
  • if we give the method a different name, it wont collide with the round function and break any existing code using it. <br />
  • if we give the method a different name, it wont collide with the round function and break any existing code using it. <br />
  • if we give the method a different name, it wont collide with the round function and break any existing code using it. <br />
  • if we give the method a different name, it wont collide with the round function and break any existing code using it. <br />
  • if we give the method a different name, it wont collide with the round function and break any existing code using it. <br />
  • <br />
  • <br />
  • strings are objects. <br />
  • integers are objects <br />
  • integers are objects <br />
  • integers are objects <br />
  • integers are objects <br />
  • integers are objects <br />
  • integers are objects <br />
  • integers are objects <br />
  • integers are objects <br />
  • floats are objects <br />
  • floats are objects <br />
  • floats are objects <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • shorthand <br />
  • Arrays are objects. You can actually do some REALLY cool things with methods that array gives you by default, things you&#x2019;d normally write iterators and custom code for, but before we touch on that, I want to cover a little bit more background on Ruby <br />
  • Like Array, You don&#x2019;t need to use Hash.new to create a new hash object in Ruby. <br /> Using the braces is enough to denote that a hash object should be created <br />
  • This shows a very common idiom in Ruby; using a hash to imitate Keyword Parameters <br />
  • In fact, you don&#x2019;t even need to provide the braces, and Ruby still figures out that you are declaring a hash. <br />
  • Regular Expressions are awesome. You should already be familiar with these as a programmer. If you&#x2019;re not, you&#x2019;re doing a lot of things the hard way. This is even more true in Ruby. Support for Regular expressions is built in, just like array and hash. <br />
  • Once you have an expression, you can use it with the match operator <br />
  • Once you have an expression, you can use it with the match operator <br />
  • Once you have an expression, you can use it with the match operator <br />
  • Handy with substitution <br />
  • Handy with substitution <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • {1}, because of this, {2}, and {3}. <br />
  • blocks with iteration <br />
  • blocks with iteration <br />
  • blocks with iteration <br />
  • blocks with iteration <br />
  • is the comparison operator <br />
  • is the comparison operator <br />
  • is the comparison operator <br />
  • is the comparison operator <br />
  • is the comparison operator <br />
  • A closure is a block of code that, when run, is executed within the scope it was defined (i.e. local variables are available to the block), even after the function has returned, and its local scope has been destroyed. the local variables remain in existence as part of the closure object. When nothing refers to the closure anymore, it&apos;s garbage collected, and the local variables go away. <br />
  • A closure is a block of code that, when run, is executed within the scope it was defined (i.e. local variables are available to the block), even after the function has returned, and its local scope has been destroyed. the local variables remain in existence as part of the closure object. When nothing refers to the closure anymore, it&apos;s garbage collected, and the local variables go away. <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • Now that we know a little bit about blocks, lets go back to array and look at the map() method <br /> map iterates the items in the array and calls the block you supply for each item, returning a new array with the results <br />
  • Now that we know a little bit about blocks, lets go back to array and look at the map() method <br /> map iterates the items in the array and calls the block you supply for each item, returning a new array with the results <br />
  • inject is used for reduction. You reduce the items of an array down to a single value. in this example I&#x2019;m using it for summation <br />
  • inject is used for reduction. You reduce the items of an array down to a single value. in this example I&#x2019;m using it for summation <br />
  • inject is used for reduction. You reduce the items of an array down to a single value. in this example I&#x2019;m using it for summation <br />
  • inject is used for reduction. You reduce the items of an array down to a single value. in this example I&#x2019;m using it for summation <br />
  • we can use inject to easily add support for summation to the array class. <br /> Now every array natively supports .sum. Rails gives you sum and .avg for free <br />
  • we can use inject to easily add support for summation to the array class. <br /> Now every array natively supports .sum. Rails gives you sum and .avg for free <br />
  • we can use inject to easily add support for summation to the array class. <br /> Now every array natively supports .sum. Rails gives you sum and .avg for free <br />
  • we can use inject to easily add support for summation to the array class. <br /> Now every array natively supports .sum. Rails gives you sum and .avg for free <br />
  • we can use inject to easily add support for summation to the array class. <br /> Now every array natively supports .sum. Rails gives you sum and .avg for free <br />
  • we can use inject to easily add support for summation to the array class. <br /> Now every array natively supports .sum. Rails gives you sum and .avg for free <br />
  • we can use inject to easily add support for summation to the array class. <br /> Now every array natively supports .sum. Rails gives you sum and .avg for free <br />
  • Here I use inject to find the greatest number divisible by two in the array. inject keeps track of the best candidate, and I use a ternary operator to determine if i is a better candidate than my existing best. <br />
  • Here I use inject to find the greatest number divisible by two in the array. inject keeps track of the best candidate, and I use a ternary operator to determine if i is a better candidate than my existing best. <br />
  • Here I use inject to find the greatest number divisible by two in the array. inject keeps track of the best candidate, and I use a ternary operator to determine if i is a better candidate than my existing best. <br />
  • Here I use inject to find the greatest number divisible by two in the array. inject keeps track of the best candidate, and I use a ternary operator to determine if i is a better candidate than my existing best. <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • I&#x2019;m sure all of you recognize the pattern of creating getters and setters for a class. I have a Car class here on the right with a getter and setter for a color attribute. On the left is the rest of the class with getters and setters for engine, make, and model. lot of repetition. Ruby embraces meta programming, and sets the bar for how we need to re-think what we might otherwise blindly accept as just &#x201C;the way our language works&#x201D;. In this case, the attr_accessor method. <br />
  • which shortens 33 lines of code down to three! But attr_accessor isn&#x2019;t magic Ruby compiler logic, it&#x2019;s just a method. We can take a look at what that method might look like. Don&#x2019;t worry if this goes over your head, I&#x2019;ll get in to each of the methods here after we see how they&#x2019;re used. <br />
  • which shortens 33 lines of code down to three! But attr_accessor isn&#x2019;t magic Ruby compiler logic, it&#x2019;s just a method. We can take a look at what that method might look like. Don&#x2019;t worry if this goes over your head, I&#x2019;ll get in to each of the methods here after we see how they&#x2019;re used. <br />
  • which shortens 33 lines of code down to three! But attr_accessor isn&#x2019;t magic Ruby compiler logic, it&#x2019;s just a method. We can take a look at what that method might look like. Don&#x2019;t worry if this goes over your head, I&#x2019;ll get in to each of the methods here after we see how they&#x2019;re used. <br />
  • which shortens 33 lines of code down to three! But attr_accessor isn&#x2019;t magic Ruby compiler logic, it&#x2019;s just a method. We can take a look at what that method might look like. Don&#x2019;t worry if this goes over your head, I&#x2019;ll get in to each of the methods here after we see how they&#x2019;re used. <br />
  • which shortens 33 lines of code down to three! But attr_accessor isn&#x2019;t magic Ruby compiler logic, it&#x2019;s just a method. We can take a look at what that method might look like. Don&#x2019;t worry if this goes over your head, I&#x2019;ll get in to each of the methods here after we see how they&#x2019;re used. <br />
  • which shortens 33 lines of code down to three! But attr_accessor isn&#x2019;t magic Ruby compiler logic, it&#x2019;s just a method. We can take a look at what that method might look like. Don&#x2019;t worry if this goes over your head, I&#x2019;ll get in to each of the methods here after we see how they&#x2019;re used. <br />
  • which shortens 33 lines of code down to three! But attr_accessor isn&#x2019;t magic Ruby compiler logic, it&#x2019;s just a method. We can take a look at what that method might look like. Don&#x2019;t worry if this goes over your head, I&#x2019;ll get in to each of the methods here after we see how they&#x2019;re used. <br />
  • which shortens 33 lines of code down to three! But attr_accessor isn&#x2019;t magic Ruby compiler logic, it&#x2019;s just a method. We can take a look at what that method might look like. Don&#x2019;t worry if this goes over your head, I&#x2019;ll get in to each of the methods here after we see how they&#x2019;re used. <br />
  • which shortens 33 lines of code down to three! But attr_accessor isn&#x2019;t magic Ruby compiler logic, it&#x2019;s just a method. We can take a look at what that method might look like. Don&#x2019;t worry if this goes over your head, I&#x2019;ll get in to each of the methods here after we see how they&#x2019;re used. <br />
  • which shortens 33 lines of code down to three! But attr_accessor isn&#x2019;t magic Ruby compiler logic, it&#x2019;s just a method. We can take a look at what that method might look like. Don&#x2019;t worry if this goes over your head, I&#x2019;ll get in to each of the methods here after we see how they&#x2019;re used. <br />
  • which shortens 33 lines of code down to three! But attr_accessor isn&#x2019;t magic Ruby compiler logic, it&#x2019;s just a method. We can take a look at what that method might look like. Don&#x2019;t worry if this goes over your head, I&#x2019;ll get in to each of the methods here after we see how they&#x2019;re used. <br />
  • which shortens 33 lines of code down to three! But attr_accessor isn&#x2019;t magic Ruby compiler logic, it&#x2019;s just a method. We can take a look at what that method might look like. Don&#x2019;t worry if this goes over your head, I&#x2019;ll get in to each of the methods here after we see how they&#x2019;re used. <br />
  • which shortens 33 lines of code down to three! But attr_accessor isn&#x2019;t magic Ruby compiler logic, it&#x2019;s just a method. We can take a look at what that method might look like. Don&#x2019;t worry if this goes over your head, I&#x2019;ll get in to each of the methods here after we see how they&#x2019;re used. <br />
  • which shortens 33 lines of code down to three! But attr_accessor isn&#x2019;t magic Ruby compiler logic, it&#x2019;s just a method. We can take a look at what that method might look like. Don&#x2019;t worry if this goes over your head, I&#x2019;ll get in to each of the methods here after we see how they&#x2019;re used. <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • introspection <br />
  • <br />
  • this gives you access to variables the class author might not have exposed. <br />
  • Every object responds to instance_variables. Here we ask the Gem class from rubygems what instance variables it has defined internally. After it responds, we can query it for the value stored in any of those instance variables as if we had direct access to them. <br />
  • every object responds to the .constants method. We can ask the Math module what constants it has defined. We can test them directly and see that they are in fact defined, and their values, but we can also request them by string, programatically. We can also set constants programmatically, as well as ask the class or module if they are defined, and even more weirdness with const_missing <br />
  • <br />
  • inspect sums up my look at introspection. Every object in ruby responds to inspect with a string that describes the data the object holds, which, among other things, is handy for debugging <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • You&#x2019;ve already seen define_method, it&#x2019;s what made attr_accessor work. <br />
  • <br />
  • but what happens when we want to implement div, span, anchors, unordered lists, etc? <br />
  • First lets define a constant to hold all our tag types <br />
  • There&#x2019;s a really handy syntax for writing arrays of strings with a single word each, it&#x2019;s %w() <br />
  • before we go on, I&#x2019;m going to switch the syntax I&#x2019;m using to define class methods. instead of self.method_name I&#x2019;m using this class &lt;&lt; self pattern which is going to help keep things clean. <br />
  • now instead of defining the strong method ourselves, lets change that to iterate the TAG_TYPES array and define each method. <br />
  • Now we have all these methods defined for free. If we later add support for list items, we don&#x2019;t have to add a method for it, we just add it to the TAG_TYPES array and the class automatically knows how to handle this. <br />
  • Another way you can do this is with method_missing. Don&#x2019;t even define the methods, just respond when they&#x2019;re called. <br />
  • In fact, why even have a list of supported tags at all? Now there&#x2019;s nothing to change to support new tags <br />
  • You get really cool code. What does this remind you of? It&#x2019;s starting to look more and more like we created something that can generate XML for us. And in fact, that&#x2019;s exactly the premise behind the Builder gem package authored by Jim Weirich. <br />
  • But there&#x2019;s a problem in using a class to generate xml that can use any tag name. Object already defines a ton of methods that if you try to call, will never hit method_missing, methods like new, id, send, freeze, class, and &#x201C;methods&#x201D;, and any class you define must descend from Object. Clearly, we need more magic. <br />
  • But there&#x2019;s a problem in using a class to generate xml that can use any tag name. Object already defines a ton of methods that if you try to call, will never hit method_missing, methods like new, id, send, freeze, class, and &#x201C;methods&#x201D;, and any class you define must descend from Object. Clearly, we need more magic. <br />
  • But there&#x2019;s a problem in using a class to generate xml that can use any tag name. Object already defines a ton of methods that if you try to call, will never hit method_missing, methods like new, id, send, freeze, class, and &#x201C;methods&#x201D;, and any class you define must descend from Object. Clearly, we need more magic. <br />
  • Meet undef_method. Care to take a guess what this does? <br />
  • Now, you might expect Html.methods to return an empty array now, *click* <br />
  • buuut, we undefined it. *pause* So there&#x2019;s no more dot-methods method in our class. <br /> Practically speaking, there&#x2019;s a couple methods you don&#x2019;t want to undefine, but lets just gloss over that. <br />
  • With Builder you create a new builder object, in this example it&#x2019;s called &#x2018;xml&#x2019;. the methods you call on it become your tags. Blocks automatically nest tags within other tags. <br />
  • And your output looks awesome. Life. is. good. <br />
  • No, I&#x2019;m not talking about restricting instantiation of a class to a single object. <br />
  • A common use for singleton methods would be for designing a GUI; A button is a button is a button, but you don&#x2019;t want each instance of a button to do the same thing. Singleton methods let us define or redefine instance methods on the fly for a single object. <br />
  • A common use for singleton methods would be for designing a GUI; A button is a button is a button, but you don&#x2019;t want each instance of a button to do the same thing. Singleton methods let us define or redefine instance methods on the fly for a single object. <br />
  • A common use for singleton methods would be for designing a GUI; A button is a button is a button, but you don&#x2019;t want each instance of a button to do the same thing. Singleton methods let us define or redefine instance methods on the fly for a single object. <br />
  • A common use for singleton methods would be for designing a GUI; A button is a button is a button, but you don&#x2019;t want each instance of a button to do the same thing. Singleton methods let us define or redefine instance methods on the fly for a single object. <br />
  • A common use for singleton methods would be for designing a GUI; A button is a button is a button, but you don&#x2019;t want each instance of a button to do the same thing. Singleton methods let us define or redefine instance methods on the fly for a single object. <br />
  • A common use for singleton methods would be for designing a GUI; A button is a button is a button, but you don&#x2019;t want each instance of a button to do the same thing. Singleton methods let us define or redefine instance methods on the fly for a single object. <br />
  • A common use for singleton methods would be for designing a GUI; A button is a button is a button, but you don&#x2019;t want each instance of a button to do the same thing. Singleton methods let us define or redefine instance methods on the fly for a single object. <br />
  • A common use for singleton methods would be for designing a GUI; A button is a button is a button, but you don&#x2019;t want each instance of a button to do the same thing. Singleton methods let us define or redefine instance methods on the fly for a single object. <br />
  • <br />
  • eval - evaluates code, typically <br /> class_eval - evaluates code in the context of the object&#x2019;s class, or metaclass <br /> instance_eval - evals code in the context of the object, <br /> module_eval - evals in context of a module <br />
  • One day a specific client said to me, &#x201C;These records should never get destroyed, ever. I want something in place to prevent them from being destroyed&#x201D;. So, mixing the use of module_eval and the concept of creating a DSL, I came up with this patch. I reopen the ActiveRecord::Associations::ClassMethods module, which is part of Rails, and I define a class method called refuse_to_destroy!. <br />
  • Now I have a DSL I can write in my models that tells it how to act. User.destroy now raises an exception <br />
  • it works by evaluating the code in the User class and adds a before_destory hook on the ActiveRecord model. <br />
  • I had this idiom in another language I came from, that essentially lets you temporarily change the scope. But when I came to Ruby, it just didn&#x2019;t exist. <br />
  • But it turns out you can achieve something similar with instance_eval, since it changes the scope while the block executes. Cool, but it doesn&#x2019;t read as nice as that &#x201C;with&#x201D; command in my old language. But this is ruby, and you can make anything work the way you want it to! <br />
  • We can add a with method to the Object class, which makes it available everywhere. We&#x2019;ll set it up to take an object and a block of code as parameters, then call instance_eval on the object to change the scope to that object, and pass the block along to instance_eval, which will execute it in the object&#x2019;s scope. <br />
  • Now I have the syntax and method I was familiar with from the old language, implemented in Ruby. The great thing about this is there really is not a lot of limits on what you can do. You could almost create your own programming language or script, and execute it as if it&#x2019;s ruby code using instance_eval <br />
  • There&#x2019;s already some really cool projects that take advantage of this. Cucumber actually executes english text as if it&#x2019;s code <br />
  • <br />
  • <br />
  • really common in Ruby <br />
  • railsy <br />
  • <br />
  • almost all operators in ruby... are methods! <br />
  • almost all operators in ruby... are methods! <br />
  • almost all operators in ruby... are methods! <br />
  • almost all operators in ruby... are methods! <br />
  • <br />
  • My favorite kind of proxy is by far the spy proxy, though there are many uses for an object proxy (adding an optional layer of logic to a class, such as access control, for example, without modifying the class). <br />
  • My favorite kind of proxy is by far the spy proxy, though there are many uses for an object proxy (adding an optional layer of logic to a class, such as access control, for example, without modifying the class). <br />
  • My favorite kind of proxy is by far the spy proxy, though there are many uses for an object proxy (adding an optional layer of logic to a class, such as access control, for example, without modifying the class). <br />
  • My favorite kind of proxy is by far the spy proxy, though there are many uses for an object proxy (adding an optional layer of logic to a class, such as access control, for example, without modifying the class). <br />
  • My favorite kind of proxy is by far the spy proxy, though there are many uses for an object proxy (adding an optional layer of logic to a class, such as access control, for example, without modifying the class). <br />
  • My favorite kind of proxy is by far the spy proxy, though there are many uses for an object proxy (adding an optional layer of logic to a class, such as access control, for example, without modifying the class). <br />
  • My favorite kind of proxy is by far the spy proxy, though there are many uses for an object proxy (adding an optional layer of logic to a class, such as access control, for example, without modifying the class). <br />
  • My favorite kind of proxy is by far the spy proxy, though there are many uses for an object proxy (adding an optional layer of logic to a class, such as access control, for example, without modifying the class). <br />
  • My favorite kind of proxy is by far the spy proxy, though there are many uses for an object proxy (adding an optional layer of logic to a class, such as access control, for example, without modifying the class). <br />
  • My favorite kind of proxy is by far the spy proxy, though there are many uses for an object proxy (adding an optional layer of logic to a class, such as access control, for example, without modifying the class). <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • duck typing, looks like a duck, quacks like a duck, we don&#x2019;t care. <br /> ppl objects are interacting with our bmw differently <br />
  • duck typing, looks like a duck, quacks like a duck, we don&#x2019;t care. <br /> ppl objects are interacting with our bmw differently <br />
  • duck typing, looks like a duck, quacks like a duck, we don&#x2019;t care. <br /> ppl objects are interacting with our bmw differently <br />
  • duck typing, looks like a duck, quacks like a duck, we don&#x2019;t care. <br /> ppl objects are interacting with our bmw differently <br />
  • duck typing, looks like a duck, quacks like a duck, we don&#x2019;t care. <br /> ppl objects are interacting with our bmw differently <br />
  • duck typing, looks like a duck, quacks like a duck, we don&#x2019;t care. <br /> ppl objects are interacting with our bmw differently <br />
  • duck typing, looks like a duck, quacks like a duck, we don&#x2019;t care. <br /> ppl objects are interacting with our bmw differently <br />
  • duck typing, looks like a duck, quacks like a duck, we don&#x2019;t care. <br /> ppl objects are interacting with our bmw differently <br />
  • lets say we have a method that does two things. the first it does very very slowly (perhaps, reading through 10 million database records to get the record count) <br /> the second it does much faster (finding a specific record). Now, in my experience with mysql, offset isn&#x2019;t really that fast either, but lets call it negligible. <br />
  • lets say we have a method that does two things. the first it does very very slowly (perhaps, reading through 10 million database records to get the record count) <br /> the second it does much faster (finding a specific record). Now, in my experience with mysql, offset isn&#x2019;t really that fast either, but lets call it negligible. <br />
  • lets say we have a method that does two things. the first it does very very slowly (perhaps, reading through 10 million database records to get the record count) <br /> the second it does much faster (finding a specific record). Now, in my experience with mysql, offset isn&#x2019;t really that fast either, but lets call it negligible. <br />
  • lets say we have a method that does two things. the first it does very very slowly (perhaps, reading through 10 million database records to get the record count) <br /> the second it does much faster (finding a specific record). Now, in my experience with mysql, offset isn&#x2019;t really that fast either, but lets call it negligible. <br />
  • lets say we have a method that does two things. the first it does very very slowly (perhaps, reading through 10 million database records to get the record count) <br /> the second it does much faster (finding a specific record). Now, in my experience with mysql, offset isn&#x2019;t really that fast either, but lets call it negligible. <br />
  • There are other ways to solve this problem, like making user count an instance variable and using the &#x201C;or equals&#x201D; meme (meam) you see here, which only evaluates the code on the right and sets user count if user count is nil or false. But lets ignore that, too. <br />
  • class_eval lets us evaluate the block of code as if we were running it inside the class, and not the current method. define_method takes a block, which it turns in to a closure--meaning it keeps a reference to the local variable user_count, so it doesn&#x2019;t drop out of scope and get freed <br />
  • class_eval lets us evaluate the block of code as if we were running it inside the class, and not the current method. define_method takes a block, which it turns in to a closure--meaning it keeps a reference to the local variable user_count, so it doesn&#x2019;t drop out of scope and get freed <br />
  • class_eval lets us evaluate the block of code as if we were running it inside the class, and not the current method. define_method takes a block, which it turns in to a closure--meaning it keeps a reference to the local variable user_count, so it doesn&#x2019;t drop out of scope and get freed <br />
  • class_eval lets us evaluate the block of code as if we were running it inside the class, and not the current method. define_method takes a block, which it turns in to a closure--meaning it keeps a reference to the local variable user_count, so it doesn&#x2019;t drop out of scope and get freed <br />
  • class_eval lets us evaluate the block of code as if we were running it inside the class, and not the current method. define_method takes a block, which it turns in to a closure--meaning it keeps a reference to the local variable user_count, so it doesn&#x2019;t drop out of scope and get freed <br />
  • class_eval lets us evaluate the block of code as if we were running it inside the class, and not the current method. define_method takes a block, which it turns in to a closure--meaning it keeps a reference to the local variable user_count, so it doesn&#x2019;t drop out of scope and get freed <br />
  • class_eval lets us evaluate the block of code as if we were running it inside the class, and not the current method. define_method takes a block, which it turns in to a closure--meaning it keeps a reference to the local variable user_count, so it doesn&#x2019;t drop out of scope and get freed <br />
  • class_eval lets us evaluate the block of code as if we were running it inside the class, and not the current method. define_method takes a block, which it turns in to a closure--meaning it keeps a reference to the local variable user_count, so it doesn&#x2019;t drop out of scope and get freed <br />
  • class_eval lets us evaluate the block of code as if we were running it inside the class, and not the current method. define_method takes a block, which it turns in to a closure--meaning it keeps a reference to the local variable user_count, so it doesn&#x2019;t drop out of scope and get freed <br />
  • class_eval lets us evaluate the block of code as if we were running it inside the class, and not the current method. define_method takes a block, which it turns in to a closure--meaning it keeps a reference to the local variable user_count, so it doesn&#x2019;t drop out of scope and get freed <br />
  • after the first time the method runs, it redefines itself to something like this. The method has both rewritten itself and accomplished a form of caching at the same time. <br /> self-optimization-code similar to this is used by Rails internally for routing. The first time the method is called it generates its own routing methods based on a more complete picture of what&#x2019;s required and cuts out a lot of dead cycles that would otherwise check routes that are never used. <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • If you&#x2019;re liking the Ruby vibe and you want to get in to it a little more, you&#x2019;ll want to make a note to visit some of these sites. <br />
  • Ruby screencasts <br />
  • <br />
  • <br />
  • <br />

Meta Programming in Ruby - Code Camp 2010 Meta Programming in Ruby - Code Camp 2010 Presentation Transcript

  • Meta Programming in Ruby Steven Soroka @ssoroka
  • self.inspect
  • self.inspect •@ssoroka on twitter
  • self.inspect •@ssoroka on twitter • github.com/ssoroka
  • self.inspect •@ssoroka on twitter • github.com/ssoroka • blog.stevensoroka.ca
  • self.inspect • Professionally writing software for 12 years • Ruby for 3+ years • Taught Ruby on Rails at RRC • My biggest site has over 1 million users
  • What is Ruby? A dynamic, open source programming language with a focus on simplicity and productivity. It has an elegant syntax that is natural to read and easy to write. http://www.ruby-lang.org
  • What is Ruby? - Yukihiro Matsumoto
  • What is Ruby? “Ruby is simple in appearance, but is very complex inside” - Yukihiro Matsumoto
  • What is Ruby? “Ruby is simple in appearance, but is very complex inside” “I wanted a scripting language that was more powerful than Perl, and more object-oriented than Python” - Yukihiro Matsumoto
  • What is Ruby?
  • What is Ruby? • Ruby is not Ruby on Rails
  • What is Ruby? • Ruby is not Ruby on Rails • Ruby is an interpreted language
  • What is Ruby? • Ruby is not Ruby on Rails • Ruby is an interpreted language • script language
  • What is Ruby? • Ruby is not Ruby on Rails • Ruby is an interpreted language • script language • terse
  • What is Ruby? • Ruby is not Ruby on Rails • Ruby is an interpreted language • script language • terse • completely Object Oriented
  • What is Ruby? • Ruby is not Ruby on Rails • Ruby is an interpreted language • script language • terse • completely Object Oriented • Free, really free.
  • puts “Hello World”
  • class Car
  • class Car def start
  • class Car def start puts "vroom vroom"
  • class Car def start puts "vroom vroom" end
  • class Car def start puts "vroom vroom" end end
  • class Car def start puts "vroom vroom" end end car = Car.new
  • class Car def start puts "vroom vroom" end end car = Car.new car.start # vroom vroom
  • Does Ruby matter?
  • Does Ruby matter? Rubinious MacRuby JRuby Ruby 1.8 IronRuby Cardinal HotRuby IronMonkey Ruby 1.9 Ruby Enterprise Edition
  • What Can I do with Ruby?
  • What Can I do with Ruby? • Cross-platform GUI apps (WxRuby, FXRuby, Ruby-GNOME2, Ruby Tk)
  • What Can I do with Ruby? • Cross-platform GUI apps (WxRuby, FXRuby, Ruby-GNOME2, Ruby Tk) • Mac Desktop apps with MacRuby and Cocoa
  • What Can I do with Ruby? • Cross-platform GUI apps (WxRuby, FXRuby, Ruby-GNOME2, Ruby Tk) • Mac Desktop apps with MacRuby and Cocoa • Web Apps (Ruby on Rails, Sinatra)
  • What Can I do with Ruby? • Cross-platform GUI apps (WxRuby, FXRuby, Ruby-GNOME2, Ruby Tk) • Mac Desktop apps with MacRuby and Cocoa • Web Apps (Ruby on Rails, Sinatra) • Replace Javascript?
  • What Can I do with Ruby? • Cross-platform GUI apps (WxRuby, FXRuby, Ruby-GNOME2, Ruby Tk) • Mac Desktop apps with MacRuby and Cocoa • Web Apps (Ruby on Rails, Sinatra) • Replace Javascript? • Soon Ruby.NET apps?
  • WxRuby GUI
  • WxRuby GUI
  • Introduction to Ruby New Concepts
  • Differences from C++
  • Differences from C++ • No type casting
  • Differences from C++ • No type casting • There’s only two container types: Array and Hash
  • Differences from C++ • No type casting • There’s only two container types: Array and Hash • There’s no type conversions, you’ll probably find that they aren’t necessary
  • Differences from C++ • No type casting • There’s only two container types: Array and Hash • There’s no type conversions, you’ll probably find that they aren’t necessary • Multithreading is built-in
  • Differences from C++
  • Differences from C++ • A unit testing lib comes standard with Ruby
  • Differences from C++ • A unit testing lib comes standard with Ruby • It’s self instead of this
  • Differences from C++ • A unit testing lib comes standard with Ruby • It’s self instead of this • Objects are strongly but dynamically typed. The runtime discovers at runtime if that method call actually works
  • Differences from Java
  • Differences from Java • There’s no static type checking.
  • Differences from Java • There’s no static type checking. • Variable names are just labels. They don’t have a type associated with them
  • Differences from Java • There’s no static type checking. • Variable names are just labels. They don’t have a type associated with them • There are no type declarations
  • Differences from Java • There’s no static type checking. • Variable names are just labels. They don’t have a type associated with them • There are no type declarations • foo = Foo.new(“hi”) instead of Foo foo = new Foo(“hi”)
  • Differences from Java • There’s no static type checking. • Variable names are just labels. They don’t have a type associated with them • There are no type declarations • foo = Foo.new(“hi”) instead of Foo foo = new Foo(“hi”)
  • Variables
  • Variables • Instance variables start with @
  • Variables • Instance variables start with @ • Class variables start with @@
  • Variables • Instance variables start with @ • Class variables start with @@ • Global variables start with $
  • Variables • Instance variables start with @ • Class variables start with @@ • Global variables start with $ • Any type of object can be stored in a given variable
  • a = 5.3 b = 15 a * b # 79.5
  • Truthiness • In Ruby, only false and nil are false • 0 is true • “” is true • [] is true
  • if 0 puts "0 is truthy" end # 0 is truthy
  • :recipient
  • deliver_report(:recipient => 'john@example.com') :recipient
  • Symbols
  • Symbols • Symbols are similar to strings, but are only allocated memory once, and are never freed.
  • Symbols • Symbols are similar to strings, but are only allocated memory once, and are never freed. • Great for keys, column names, and other strings that are repeated over and over again.
  • :name.object_id == :name.object_id
  • :name.object_id == :name.object_id # true
  • :name.object_id == :name.object_id # true “name”.object_id == “name”.object_id
  • :name.object_id == :name.object_id # true “name”.object_id == “name”.object_id # false
  • john = {
  • john = { :name => 'John Smith',
  • john = { :name => 'John Smith', :age => 42
  • john = { :name => 'John Smith', :age => 42 }
  • john = { :name => 'John Smith', :age => 42 } john[:name] # 'John Smith'
  • john = { :name => 'John Smith', :age => 42 } john[:name] # 'John Smith' john[:age] # 42
  • Interpolation • Double quoted strings are interpolated, single quoted strings are not. puts “five times three is #{5 * 3}” # five times three is 15 puts ‘five times three is #{5 * 3}’ # five times three is #{5 * 3}
  • Everything Has a Value
  • Everything Has a Value • Expressions and statements are the same thing in Ruby
  • Everything Has a Value • Expressions and statements are the same thing in Ruby • Everything returns a value, even if that value is nil
  • Implied Return Values
  • Implied Return Values if 3 > 2 5 end # returns 5
  • Implied Return Values
  • Implied Return Values if 2 > 21 5 end # returns nil
  • Implied Return Values
  • Implied Return Values pass = if 3.odd? ‘odd’ else ‘even’ end # pass == ‘odd’
  • Classes Are Open
  • Classes Are Open • Don’t violate Open/Close Principle
  • Classes Are Open • Don’t violate Open/Close Principle • OCP says classes should be...
  • Classes Are Open • Don’t violate Open/Close Principle • OCP says classes should be... • open for extension
  • Classes Are Open • Don’t violate Open/Close Principle • OCP says classes should be... • open for extension • closed for modification
  • Classes Are Open
  • Classes Are Open class Float
  • Classes Are Open class Float def round
  • Classes Are Open class Float def round floor
  • Classes Are Open class Float def round floor end
  • Classes Are Open class Float def round floor end end
  • Classes Are Open class Float def round floor end end 5.7.round
  • Classes Are Open class Float def round floor end end 5.7.round # 5
  • Classes Are Open
  • Classes Are Open class Float
  • Classes Are Open class Float def round_down
  • Classes Are Open class Float def round_down floor
  • Classes Are Open class Float def round_down floor end
  • Classes Are Open class Float def round_down floor end end
  • Classes Are Open class Float def round_down floor end end 5.7.round_down
  • Classes Are Open class Float def round_down floor end end 5.7.round_down # 5
  • Everything’s An Object
  • Everything’s An Object • properties are called attributes or instance variables
  • Everything’s An Object • properties are called attributes or instance variables • actions are called methods
  • String “I am a string” ‘I am a string’
  • Fixnum (Integers)
  • Fixnum (Integers) 42
  • Fixnum (Integers) 42 42.class # Fixnum
  • Fixnum (Integers) 42 42.class # Fixnum 42.methods
  • Fixnum (Integers) 42 42.class # Fixnum 42.methods # ["&", "*", "**", "+", "-", "/",
  • Fixnum (Integers) 42 42.class # Fixnum 42.methods # ["&", "*", "**", "+", "-", "/", "abs", "between?", "even?",
  • Fixnum (Integers) 42 42.class # Fixnum 42.methods # ["&", "*", "**", "+", "-", "/", "abs", "between?", "even?", "to_f", "zero?", "|", ...]
  • Fixnum (Integers) 42 42.class # Fixnum 42.methods # ["&", "*", "**", "+", "-", "/", "abs", "between?", "even?", "to_f", "zero?", "|", ...] 42.odd? # False
  • Fixnum (Integers) 42 42.class # Fixnum 42.methods # ["&", "*", "**", "+", "-", "/", "abs", "between?", "even?", "to_f", "zero?", "|", ...] 42.odd? # False 42.to_f # 42.0
  • Float
  • Float 42.0.class # Float
  • Float 42.0.class # Float 42.6.floor # 42
  • Float 42.0.class # Float 42.6.floor # 42 42.6.round # 43
  • Date (Rails)
  • Date (Rails) day = Date.today # Sat, 27 Feb 2010
  • Date (Rails) day = Date.today # Sat, 27 Feb 2010 day.tomorrow # Sun, 28 Feb 2010
  • Date (Rails) day = Date.today # Sat, 27 Feb 2010 day.tomorrow # Sun, 28 Feb 2010 day.year # 2010
  • Date (Rails) day = Date.today # Sat, 27 Feb 2010 day.tomorrow # Sun, 28 Feb 2010 day.year # 2010 day.leap? # false
  • Date (Rails) day = Date.today # Sat, 27 Feb 2010 day.tomorrow # Sun, 28 Feb 2010 day.year # 2010 day.leap? # false 42.days.ago # Sat, 16 Jan 2010
  • Array my_array = Array.new()
  • Array my_array = Array.new()
  • Array
  • Array my_array = []
  • Array
  • Array my_array = [1, 2, 3, 4, 5]
  • Hash email_settings = { :recipients => 'mom@mom.com', :subject => 'hi mom', :body => "I'm at CodeCamp!", :priority => :normal }
  • Hash email_settings = { :recipients => 'mom@mom.com', :subject => 'hi mom', :body => "I'm at CodeCamp!", :priority => :normal } send_email(email_settings)
  • Hash send_email({ :recipients => 'mom@mom.com', :subject => 'hi mom', :body => "I'm at Code Camp!", :priority => :normal })
  • Hash send_email( :recipients => 'mom@mom.com', :subject => 'hi mom', :body => "I'm at CodeCamp!", :priority => :normal )
  • Regular Expressions /[aeiou]/
  • Regular Expressions
  • Regular Expressions ‘scintillating!’ =~ /[aeiou]/
  • Regular Expressions ‘scintillating!’ =~ /[aeiou]/ #2
  • Regular Expressions ‘scintillating!’ =~ /[aeiou]/ #2
  • Regular Expressions
  • Regular Expressions ‘scintillating!’.gsub(/[aeiou]/, ‘*’)
  • Regular Expressions ‘scintillating!’.gsub(/[aeiou]/, ‘*’) # ‘sc*nt*ll*t*ng!’
  • Classes • Single inheritance only • but can include functionality from modules • can open a class at any time to add methods • ... are objects!
  • Methods • can end in ? or ! • “apples”.is_a?(String) • Are objects!
  • Operators are Methods
  • Operators are Methods • Most operators are methods
  • Operators are Methods • Most operators are methods • and can be redefined
  • Operators are Methods • Most operators are methods • and can be redefined class Fixnum def +(other) self - other end end
  • Operators are Methods • Most operators are methods • and can be redefined class Fixnum def +(other) self - other end end 5 + 3 # => 2
  • Blocks • In Ruby, a block is a namless function that can be stored as a callable object, called a Proc • Blocks can be stored in variables • Blocks can be passed to methods to customize their behavior
  • @users = User.find(:all)
  • @users = User.find(:all) @users.each { |user|
  • @users = User.find(:all) @users.each { |user| user.send_invoice!
  • @users = User.find(:all) @users.each { |user| user.send_invoice! }
  • arr = [5,3,1,5,3,2,6,8,1,2]
  • arr = [5,3,1,5,3,2,6,8,1,2] arr.sort{|a,b| a <=> b }
  • arr = [5,3,1,5,3,2,6,8,1,2] arr.sort{|a,b| a <=> b } # [1, 1, 2, 2, 3, 3, 5, 5, 6, 8]
  • arr = [5,3,1,5,3,2,6,8,1,2] arr.sort{|a,b| a <=> b } # [1, 1, 2, 2, 3, 3, 5, 5, 6, 8] arr.sort{|a,b| b <=> a }
  • arr = [5,3,1,5,3,2,6,8,1,2] arr.sort{|a,b| a <=> b } # [1, 1, 2, 2, 3, 3, 5, 5, 6, 8] arr.sort{|a,b| b <=> a } # [8, 6, 5, 5, 3, 3, 2, 2, 1, 1]
  • Block Objects are Closures
  • Block Objects are Closures • Block Objects (Procs) can be executed anywhere and can take their context (called binding) with them.
  • Block Objects are Closures • Block Objects (Procs) can be executed anywhere and can take their context (called binding) with them. • A closure is a block of code that, when run, is executed within the scope it was defined (i.e. local variables are available to the block), even after the function has returned, and its local scope has been destroyed.
  • user_total = Proc.new {
  • user_total = Proc.new { User.count
  • user_total = Proc.new { User.count }
  • user_total = Proc.new { User.count } user_total.call
  • user_total = Proc.new { User.count } user_total.call # 52
  • user_total = Proc.new { User.count } user_total.call # 52 # user is added
  • user_total = Proc.new { User.count } user_total.call # 52 # user is added user_total.call
  • user_total = Proc.new { User.count } user_total.call # 52 # user is added user_total.call # 53
  • Array#map
  • Array#map [1,2,3,4,5].map{|i| i * 2}
  • Array#map [1,2,3,4,5].map{|i| i * 2} # [2,4,6,8,10]
  • Array#inject
  • Array#inject [1,2,3,4,5].inject(0){|sum, i|
  • Array#inject [1,2,3,4,5].inject(0){|sum, i| i + sum
  • Array#inject [1,2,3,4,5].inject(0){|sum, i| i + sum }
  • Array#inject [1,2,3,4,5].inject(0){|sum, i| i + sum } # 15
  • Array#inject
  • Array#inject class Array
  • Array#inject class Array def sum
  • Array#inject class Array def sum self.inject(0){|sum, i| i + sum }
  • Array#inject class Array def sum self.inject(0){|sum, i| i + sum } end
  • Array#inject class Array def sum self.inject(0){|sum, i| i + sum } end end
  • Array#inject class Array def sum self.inject(0){|sum, i| i + sum } end end [1,2,3,4,5].sum
  • Array#inject class Array def sum self.inject(0){|sum, i| i + sum } end end [1,2,3,4,5].sum # 15
  • Array#inject
  • Array#inject [1,2,3,4,5].inject(0){|best, i|
  • Array#inject [1,2,3,4,5].inject(0){|best, i| i % 2 == 0 ? i : best
  • Array#inject [1,2,3,4,5].inject(0){|best, i| i % 2 == 0 ? i : best }
  • Array#inject [1,2,3,4,5].inject(0){|best, i| i % 2 == 0 ? i : best } #4
  • Array#select
  • Array#select [1,2,3,4,5].select{| i|
  • Array#select [1,2,3,4,5].select{| i| i % 2 == 0
  • Array#select [1,2,3,4,5].select{| i| i % 2 == 0 }
  • Array#select [1,2,3,4,5].select{| i| i % 2 == 0 } # [2, 4]
  • Array#reject
  • Array#reject [1,2,3,4,5].reject{| i|
  • Array#reject [1,2,3,4,5].reject{| i| i % 2 == 0
  • Array#reject [1,2,3,4,5].reject{| i| i % 2 == 0 }
  • Array#reject [1,2,3,4,5].reject{| i| i % 2 == 0 } # [1, 3, 5]
  • Extending Classes • Make a module • include it in to a class
  • Extending Classes • Make a module • include it in to a class module Awesomeness end class Mundane include Awesomeness end
  • Meta Programming Meta programming is writing code that writes code
  • class Car def color @color end def color=(color) @color = color end ...
  • class Car def color @color class Car end def color=(color) def color @color = color end @color def engine @engine end def engine=(engine) @engine = engine end end def make @make end def color=(color) def make=(make) @make = make end @color = color def model @model end end def model=(model) @model = model ... end end
  • class Car attr_accessor :color, :engine, :make, :model end
  • class Car attr_accessor :color, :engine, :make, :model end class Object
  • class Car attr_accessor :color, :engine, :make, :model end class Object def self.attr_accessor(*attributes)
  • class Car attr_accessor :color, :engine, :make, :model end class Object def self.attr_accessor(*attributes) attributes.each{|attribute|
  • class Car attr_accessor :color, :engine, :make, :model end class Object def self.attr_accessor(*attributes) attributes.each{|attribute| # define getter
  • class Car attr_accessor :color, :engine, :make, :model end class Object def self.attr_accessor(*attributes) attributes.each{|attribute| # define getter define_method(attribute) do
  • class Car attr_accessor :color, :engine, :make, :model end class Object def self.attr_accessor(*attributes) attributes.each{|attribute| # define getter define_method(attribute) do instance_variable_get("@#{attribute}")
  • class Car attr_accessor :color, :engine, :make, :model end class Object def self.attr_accessor(*attributes) attributes.each{|attribute| # define getter define_method(attribute) do instance_variable_get("@#{attribute}") end
  • class Car attr_accessor :color, :engine, :make, :model end class Object def self.attr_accessor(*attributes) attributes.each{|attribute| # define getter define_method(attribute) do instance_variable_get("@#{attribute}") end # define setter
  • class Car attr_accessor :color, :engine, :make, :model end class Object def self.attr_accessor(*attributes) attributes.each{|attribute| # define getter define_method(attribute) do instance_variable_get("@#{attribute}") end # define setter define_method("#{attribute}=") do |var|
  • class Car attr_accessor :color, :engine, :make, :model end class Object def self.attr_accessor(*attributes) attributes.each{|attribute| # define getter define_method(attribute) do instance_variable_get("@#{attribute}") end # define setter define_method("#{attribute}=") do |var| instance_variable_set("@#{attribute}", var)
  • class Car attr_accessor :color, :engine, :make, :model end class Object def self.attr_accessor(*attributes) attributes.each{|attribute| # define getter define_method(attribute) do instance_variable_get("@#{attribute}") end # define setter define_method("#{attribute}=") do |var| instance_variable_set("@#{attribute}", var) end
  • class Car attr_accessor :color, :engine, :make, :model end class Object def self.attr_accessor(*attributes) attributes.each{|attribute| # define getter define_method(attribute) do instance_variable_get("@#{attribute}") end # define setter define_method("#{attribute}=") do |var| instance_variable_set("@#{attribute}", var) end }
  • class Car attr_accessor :color, :engine, :make, :model end class Object def self.attr_accessor(*attributes) attributes.each{|attribute| # define getter define_method(attribute) do instance_variable_get("@#{attribute}") end # define setter define_method("#{attribute}=") do |var| instance_variable_set("@#{attribute}", var) end } end
  • Introspection self.inspect
  • .class
  • .class • Any object will respond to .class
  • .class • Any object will respond to .class • Car.new.class # Car
  • .class • Any object will respond to .class • Car.new.class # Car • Car.class # Class
  • .ancestors • class BMW < Car ... end • BMW.ancestors # [Car, Object, Kernel]
  • .methods • Every object responds to .methods
  • .instance_variable_set • object.instance_variable_set(“@quantity”, 51)
  • .instance_variable_get • object.instance_variable_get(“@quantity”) # 25
  • .instance_variables • Gem.instance_variables # [“@gem_home”, “@ruby”, “@gem_path”, ...] • Gem.instance_variable_get( “@gem_home”) # [“/Library/Ruby/Gems/1.8”, ...]
  • .constants • Math.constants # [“PI”, “E”] • Math::PI # 3.14159265358979 • Math::E # 2.71828182845905 • Math.const_get(“PI”) # 3.14159265358979 • Math.const_set(“GOOGOL”, 10 ** 100)
  • .respond_to? • Tells you if objects will respond to a certain method. • ‘string’.respond_to?(:size) # true
  • .inspect • Car.new.inspect # => "#<Car:0x123d9e8>" • fish = :tuna fish.inspect # => ":tuna" • 5.inspect # => "5"
  • Dynamically Calling Methods • car.respond_to?(:start_engine) • car.send(:start_engine)
  • Method Missing • You can define ghost methods that don’t actually exist, but pretend to when requested
  • class Elephant def initialize @called = [] end def method_missing(method, *args, &block) @called << method end def talk puts @called.inspect end end
  • class Elephant # ... def method_missing(method, *args, &block) @called << method end def talk # ... end
  • class Elephant # ... def method_missing(method, *args, &block) @called << method end def talk # ... end suzie = Elephant.new
  • class Elephant # ... def method_missing(method, *args, &block) @called << method end def talk # ... end suzie = Elephant.new suzie.brush
  • class Elephant # ... def method_missing(method, *args, &block) @called << method end def talk # ... end suzie = Elephant.new suzie.brush suzie.do_tricks
  • class Elephant # ... def method_missing(method, *args, &block) @called << method end def talk # ... end suzie = Elephant.new suzie.brush suzie.do_tricks suzie.ride
  • class Elephant # ... def method_missing(method, *args, &block) @called << method end def talk # ... end suzie = Elephant.new suzie.brush suzie.do_tricks suzie.ride suzie.talk # [‘brush’, ‘do_tricks’, ‘ride’]
  • define_method() class Html def self.tag(tag_name, content) "<#{tag_name}>#{content}</#{tag_name}>" end end
  • class Html def self.tag(tag_name, content) "<#{tag_name}>#{content}</#{tag_name}>" end end Html.tag(‘strong’, “I’m strong!”) # <strong>I’m strong!</strong>
  • class Html def self.tag(tag_name, content) "<#{tag_name}>#{content}</#{tag_name}>" end def self.strong(content) tag(‘strong’, content) end end Html.strong(“I’m strong!”) # <strong>I’m strong!</strong>
  • class Html TAG_TYPES = [‘strong’, ‘div’, ‘span’, ‘a’, ‘ul’] def self.tag(tag_name, content) "<#{tag_name}>#{content}</#{tag_name}>" end def self.strong(content) tag(‘strong’, content) end end Html.strong(“I’m strong!”) # <strong>I’m strong!</strong>
  • class Html TAG_TYPES = %w(strong div span a ul) def self.tag(tag_name, content) "<#{tag_name}>#{content}</#{tag_name}>" end def self.strong(content) tag(‘strong’, content) end end Html.strong(“I’m strong!”) # <strong>I’m strong!</strong>
  • class Html TAG_TYPES = %w(strong div span a ul) class << self def tag(tag_name, content) "<#{tag_name}>#{content}</ #{tag_name}>" end def strong(content) tag(‘strong’, content) end end end
  • class Html TAG_TYPES = %w(strong div span a ul) class << self def tag(tag_name, content) "<#{tag_name}>#{content}</#{tag_name}>" end TAG_TYPES.each{|tag_type| define_method(tag_type) do |content| tag(tag_type, content) end } end end
  • Html.strong(‘buffalo’) # <strong>buffalo</strong> Html.span(‘distances’) # <span>distances</span> Html.a(‘revelation’) # <a>revelation</a>
  • class Html TAG_TYPES = %w(strong div span a ul) class << self def tag(tag_name, content) "<#{tag_name}>#{content}</#{tag_name}>" end def method_missing(method, *args) if TAG_TYPES.include?(method) tag(method, *args) else super end end end end
  • class Html class << self def tag(tag_name, content) "<#{tag_name}>#{content}</#{tag_name}>" end def method_missing(method, *args) tag(method, *args) end end end
  • Html.li(‘list item 1’) # <li>list item 1</li> Html.think(‘test’) # <think>test</think> Html.name(‘Steven Soroka’) # <name>Steven Soroka</name>
  • Object.methods.size # 75 (or more)
  • Object.methods.size # 75 (or more) Object.methods
  • Object.methods.size # 75 (or more) Object.methods # ["inspect", "private_class_method", "const_missing", ...]
  • undef_method() class Html class << self methods.each{|method| undef_method(method) } end end
  • undef_method() Html.methods # [] ?
  • undef_method() NoMethodError: undefined method `methods' for #<Class:0x408bc>
  • XML Builder xml.instruct! xml.html { xml.head { xml.title("History") } xml.body { xml.h1("Header") xml.p("paragraph") } }
  • XML Builder <?xml version="1.0" encoding="UTF-8"? > <html> <head> <title>History</title> </head> <body> <h1>Header</h1> <p>paragraph</p> </body> </html>
  • Singleton Methods •methods defined on a single instance of a class. •unrelated to class singletons
  • big_button = Button.new
  • big_button = Button.new little_button = Button.new
  • big_button = Button.new little_button = Button.new def big_button.click
  • big_button = Button.new little_button = Button.new def big_button.click do_big_stuff
  • big_button = Button.new little_button = Button.new def big_button.click do_big_stuff end
  • big_button = Button.new little_button = Button.new def big_button.click do_big_stuff end def little_button.click
  • big_button = Button.new little_button = Button.new def big_button.click do_big_stuff end def little_button.click act_important
  • big_button = Button.new little_button = Button.new def big_button.click do_big_stuff end def little_button.click act_important end
  • Domain-Specific Language (DSL)
  • eval • eval • class_eval • instance_eval • module_eval
  • module ActiveRecord class RecordCannotBeDestroyed < ActiveRecordError end module Associations module ClassMethods def refuse_to_destroy! module_eval %Q{ before_destroy do |record| raise RecordCannotBeDestroyed, "You cannot destroy #{record.class.name} records." unless record.new_record? end } end end end end
  • class User < ActiveRecord::Base refuse_to_destroy! end # @user.destroy now raises an exception
  • def refuse_to_destroy! module_eval %Q{ before_destroy do |record| raise RecordCannotBeDestroyed, "You cannot destroy #{record.class.name} records." unless record.new_record? end } end
  • car.start car.reverse car.wheel.turn car.gas_pedal.press car.brake_pedal.press with car do start reverse wheel.turn gas_pedal.press brake_pedal.press end
  • car.instance_eval { start reverse wheel.turn gas_pedal.press brake_pedal.press }
  • class Object def with(obj, &block) obj.instance_eval &block end end
  • with car do start reverse wheel.turn gas_pedal.press brake_pedal.press end
  • Aliasing Methods • basically copies a method with a new name • alias chaining • aka: alias method chaining
  • ['apple', 'lobster', 'fish', 'balloons'].include?('fish') # true ['apple', 'lobster', 'fish', 'balloons'].include?(/(lobster|fish)/) # false
  • class Array def include_with_regexp?(matcher) if matcher.is_a?(Regexp) any?{|i| i =~ matcher } else include_without_regexp?(matcher) end end alias :include_without_regexp? :include? alias :include? :include_with_regexp? end
  • class Array def include_with_regexp?(matcher) if matcher.is_a?(Regexp) any?{|i| i =~ matcher } else include_without_regexp?(matcher) end end alias_method_chain :include?, :regexp end
  • ['apple', 'lobster', 'fish', 'balloons'].include?( /(lobster|fish)/) # true
  • Operator Overloading
  • Operator Overloading class Fixnum def +(other) self - other end end
  • Operator Overloading class Fixnum def +(other) self - other end end 5 + 3 # => 2
  • Operator Overloading
  • Operator Overloading class Fixnum def +(other) (self.to_s + other.to_s).to_i end end
  • Operator Overloading class Fixnum def +(other) (self.to_s + other.to_s).to_i end end 5 + 3 # => 53
  • More Examples • Object Proxies • Code that rewrites itself • Optimization • Caching
  • Object Proxy
  • Object Proxy class SpyProxy
  • Object Proxy class SpyProxy def initialize(target)
  • Object Proxy class SpyProxy def initialize(target) @target = target
  • Object Proxy class SpyProxy def initialize(target) @target = target end
  • Object Proxy class SpyProxy def initialize(target) @target = target end def method_missing(method, *args)
  • Object Proxy class SpyProxy def initialize(target) @target = target end def method_missing(method, *args) puts “#{method} called with args:
  • Object Proxy class SpyProxy def initialize(target) @target = target end def method_missing(method, *args) puts “#{method} called with args: #{args.inspect}”
  • Object Proxy class SpyProxy def initialize(target) @target = target end def method_missing(method, *args) puts “#{method} called with args: #{args.inspect}” @target.send(method, *args)
  • Object Proxy class SpyProxy def initialize(target) @target = target end def method_missing(method, *args) puts “#{method} called with args: #{args.inspect}” @target.send(method, *args) end
  • Object Proxy class SpyProxy def initialize(target) @target = target end def method_missing(method, *args) puts “#{method} called with args: #{args.inspect}” @target.send(method, *args) end end
  • Object Proxy
  • Object Proxy paul = Person.new
  • Object Proxy paul = Person.new steve = Person. new
  • Object Proxy paul = Person.new steve = Person. new bmw = BMW. new
  • Object Proxy paul = Person.new steve = Person. new bmw = BMW. new paul.start(bmw) # “clunk clunk”
  • Object Proxy paul = Person.new steve = Person. new bmw = BMW. new paul.start(bmw) # “clunk clunk” steve.start(bmw) # “vroom vroom”
  • Object Proxy paul = Person.new steve = Person. new bmw = BMW. new paul.start(bmw) # “clunk clunk” steve.start(bmw) # “vroom vroom” spy_bmw = SpyProxy.new(bmw)
  • Object Proxy
  • Object Proxy spy_bmw = SpyProxy.new(bmw)
  • Object Proxy spy_bmw = SpyProxy.new(bmw) steve.start(spy_bmw)
  • Object Proxy spy_bmw = SpyProxy.new(bmw) steve.start(spy_bmw) # press_clutch called with args: [1.0]
  • Object Proxy spy_bmw = SpyProxy.new(bmw) steve.start(spy_bmw) # press_clutch called with args: [1.0] # turn_key called with args: []
  • Object Proxy spy_bmw = SpyProxy.new(bmw) steve.start(spy_bmw) # press_clutch called with args: [1.0] # turn_key called with args: [] # “vroom vroom”
  • Object Proxy spy_bmw = SpyProxy.new(bmw) steve.start(spy_bmw) # press_clutch called with args: [1.0] # turn_key called with args: [] # “vroom vroom” paul.start(spy_bmw)
  • Object Proxy spy_bmw = SpyProxy.new(bmw) steve.start(spy_bmw) # press_clutch called with args: [1.0] # turn_key called with args: [] # “vroom vroom” paul.start(spy_bmw) # turn_key called with args: []
  • Object Proxy spy_bmw = SpyProxy.new(bmw) steve.start(spy_bmw) # press_clutch called with args: [1.0] # turn_key called with args: [] # “vroom vroom” paul.start(spy_bmw) # turn_key called with args: [] # “clunk clunk”
  • Self-Optimization
  • Self-Optimization def get_random_user_id
  • Self-Optimization def get_random_user_id user_count = sql("select count(*) from users")
  • Self-Optimization def get_random_user_id user_count = sql("select count(*) from users") sql("select id from users
  • Self-Optimization def get_random_user_id user_count = sql("select count(*) from users") sql("select id from users offset #{rand(user_count)} limit 1")
  • Self-Optimization def get_random_user_id user_count = sql("select count(*) from users") sql("select id from users offset #{rand(user_count)} limit 1") end
  • Self-Optimization
  • Self-Optimization def get_random_user_id @user_count ||= sql("select count(*) from users") sql("select id from users offset #{rand(@user_count)} limit 1") end
  • Self-Optimization
  • Self-Optimization def get_random_user_id
  • Self-Optimization def get_random_user_id class_eval {
  • Self-Optimization def get_random_user_id class_eval { user_count = sql("select count(*) from users")
  • Self-Optimization def get_random_user_id class_eval { user_count = sql("select count(*) from users") define_method(‘get_random_user_id’) do
  • Self-Optimization def get_random_user_id class_eval { user_count = sql("select count(*) from users") define_method(‘get_random_user_id’) do sql("select id from users
  • Self-Optimization def get_random_user_id class_eval { user_count = sql("select count(*) from users") define_method(‘get_random_user_id’) do sql("select id from users offset #{rand(user_count)} limit 1")
  • Self-Optimization def get_random_user_id class_eval { user_count = sql("select count(*) from users") define_method(‘get_random_user_id’) do sql("select id from users offset #{rand(user_count)} limit 1") end
  • Self-Optimization def get_random_user_id class_eval { user_count = sql("select count(*) from users") define_method(‘get_random_user_id’) do sql("select id from users offset #{rand(user_count)} limit 1") end }
  • Self-Optimization def get_random_user_id class_eval { user_count = sql("select count(*) from users") define_method(‘get_random_user_id’) do sql("select id from users offset #{rand(user_count)} limit 1") end } get_random_user_id
  • Self-Optimization def get_random_user_id class_eval { user_count = sql("select count(*) from users") define_method(‘get_random_user_id’) do sql("select id from users offset #{rand(user_count)} limit 1") end } get_random_user_id end
  • Self-Optimization
  • Self-Optimization def get_random_user_id sql("select id from users offset #{rand(1052023)} limit 1") end
  • Caching by rewriting methods?
  • Caching by rewriting methods? • You can rewrite a method as a form of caching
  • Caching by rewriting methods? • You can rewrite a method as a form of caching • Downside is cache invalidation becomes messy (doable, think alias)
  • Caching by rewriting methods? • You can rewrite a method as a form of caching • Downside is cache invalidation becomes messy (doable, think alias) • There’s easier ways
  • Caching by rewriting methods? • You can rewrite a method as a form of caching • Downside is cache invalidation becomes messy (doable, think alias) • There’s easier ways • @var ||= ...
  • Caching by rewriting methods? • You can rewrite a method as a form of caching • Downside is cache invalidation becomes messy (doable, think alias) • There’s easier ways • @var ||= ... • memoization (memoize in Rails)
  • Try Ruby in Your Browser http://TryRuby.org/ Download Ruby http://www.ruby-lang.org/en/downloads/
  • http://www.TeachMeToCode.com
  • Resources • http://www.linuxdevcenter.com/pub/a/linux/2001/11/29/ruby.html • http://www.ruby-lang.org/en/about • http://www.artima.com/intv/closures2.html • http://silverlight.net/learn/dynamic-languages/ • http://blog.headius.com/2008/04/promise-and-peril-for-alternative-ruby.html
  • Questions? @ssoroka - twitter
  • Merci @ssoroka - twitter