Created
October 20, 2011 00:31
-
-
Save jfeldstein/1300087 to your computer and use it in GitHub Desktop.
Ruby is Just Like PHP, Except for a Few Things
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
###Ruby is Just Like PHP, Except for a Few Things### | |
After a few attempts and failures to learn Ruby, it finally clicked when I accepted that Ruby was just different than PHP. After that, learning was easy. | |
If you're trying to learn Ruby as well, I'll do my best to compare Ruby to PHP (and sometimes javascript) which I found confusing, unexpected or tricky. When I can, I’ll try to explain why these "peculiarities" exist. | |
- Syntax differences in Ruby | |
- Classes, Objects and Scope | |
##Ruby Syntax## | |
#EVERYTHING is an Object# | |
Like Javascript, everything in Ruby is an object, complete with methods and properties. | |
*Javascript* | |
“Hello”.length // returns 5 | |
*Ruby* | |
"Hello”.length // also 5 | |
Even Ruby’s concept of `null` (normally meaning that there isn’t any data here) is actually an ordinary object called `nil`. | |
Somewhat Strict Typing | |
In PHP, the string value ‘0’ can be a string, an integer or the boolean false. Ruby does not allow this type of dynamic typing. | |
PHP | |
5 + ‘0‘ // integer 5 | |
“Total: “ . ‘0’ // String “Total: 0” | |
if(‘0’) // Boolean false | |
In Ruby, a value is whatever it is, and you need to convert it yourself if you want to use it as something else. | |
Ruby | |
5 + ‘0’.to_i // converts string ‘0’ to integer 0, then ads | |
Ruby also has no concept of ‘loose evaluation’. All condition are type-strict. (“Triple equals” in PHP) | |
PHP | |
‘5’ == 5 // true | |
‘5’ === 5 // false | |
Ruby | |
‘5’ == 5 #false | |
‘5’ == ‘5’ # true | |
#‘5’ === 5 Oops... There is no triple-equals in Ruby | |
Dynamic Strings | |
Like PHP, Ruby lets you put variables inside strings as you define them. | |
Ruby | |
name = “Bob” | |
str = “Hello, #{name}.” # Hello, Bob | |
str = ‘Hello, #{name}.’ # Hello, #{name} | |
PHP | |
$name = “Bob”; | |
$str = “Hello, $bob.”; // Hello, Bob | |
$str = ‘Hello, $bob.’; // Hello, $name | |
Like PHP, this only works in double quoted strings. | |
But, unlike PHP, Ruby does more than simply substitute the value of a variable into the string. The #{...} is a full execution block. You can put function calls in there, and the returned content will be substituted. | |
Ruby | |
str = “The file contains: ‘#{read_from_file(‘/path/to/file.txt’)}’” | |
Suffix Conditionals | |
PHP | |
if( condition ) { | |
return false | |
} | |
Ruby | |
return false if condition | |
Ruby also has an ‘unless’ conditional. | |
return true unless condition | |
Can be handy. Can also be very confusing. Adhere to some basic sanity rules when using Ruby’s flexible and friendly conditional system. | |
Blocks | |
Ruby has an execution block structure that, to me, feels like Javascript’s vanilla functions. | |
jQuery | |
$(‘.special-buttons’).each(function() { | |
... passing in some instructions ... | |
} | |
Ruby | |
99.downto(1) do |num| | |
puts “#{num} bottles of beer on the wall.” | |
end | |
The `do` in this example is what tells the Ruby interpreter that we’re about to give it an executable block of code. | |
The `yield` keyword has some interesting properties when used in callback, or from inside methods which were given callbacks. I don’t quite understand it yet. | |
Implied Return | |
You can break execution and return a value at any time, using `return` as you normally would, but Ruby functions also assume that they should return the value of their last statement. | |
Ruby | |
def form_bottle_statement(num, contents) | |
return false unless contents == ‘beer’ | |
“#{num} bottles of #{contents}” | |
end | |
puts form_bottle_statement(3, ‘beer’) #prints “3 bottles of beer” | |
Symbols instead of Constants | |
Ruby has a syntax called “Symbols” which are like constants in PHP. A symbol is a colon followed by a lower-case string. Symbols don’t have corresponding string values, they just _are_. They’re also not defined in advance, but simply created as you type them. | |
I’ve mainly seen symbols used in string-indexed arrays or to indicate the keys of a hash. | |
PHP | |
$beers = array( | |
‘lager’ => ‘Sam Adams’, | |
‘ipa’ => ‘Blue Moon’ | |
); | |
$beers[‘ipa’]; // ‘Blue Moon’ | |
Ruby | |
beers = [ #ruby defines arrays with [...], instead of array(...) | |
:lager => ‘Sam Adams’, | |
:ipa => ‘Blue Moon’ | |
] | |
beers[:ipa] # ‘Blue Moon’ | |
Lots of Shortcuts Relating to Functions and Their Arguments | |
Technically, Ruby has almost identical syntax to PHP when it comes to defining and calling functions. | |
PHP | |
function test( $val = null ) { | |
var_dump($val) | |
} | |
test(‘value’); | |
Ruby | |
def test( val = nil ) { | |
puts val.inspect | |
} | |
test(‘value’) | |
But Ruby allows for some shortcuts that make your code simpler to write and cleaner to read. | |
1. Curly brackets and parenthesis are optional. | |
Ruby | |
def test val, otherval | |
“You specified #{val} and #{otherval}” | |
end | |
test ‘this’, ‘the other’ # “You specified this and the other” | |
Don’t forget your `end` keyword when writing this way. | |
2. Easily Pass Hashes as Arguments | |
It’s very common syntax for functions to accept an array or hash of parameters, rather than each requiring each parameter be a separate argument that must be passed in order. | |
It’s also very common that a function accepts only a hash or parameters as its one and only argument. | |
PHP | |
function performAction( $action, $params ) { | |
... | |
} | |
performAction(‘speak’, array( | |
‘speed’ => ‘fast’, | |
‘tone’ => ‘angry’ | |
)); | |
Ruby | |
def performAction action, params = {} | |
... | |
end | |
performAction ‘speak’, {:use_gestures => true, :speed => ‘fast’, :tone => happy} | |
Ruby, expecting this pattern, lets you omit the curly braces when calling any function whose last argument is a hash. It assumes that the last group of symbols you’re writing in whatever order, are actually key/value pairs meant for the hash argument. | |
This is especially nice with functions with only a single, hash-type argument. | |
Ruby | |
def speak params | |
performAction ‘speak’, params | |
end | |
speak :speed => ‘slow’, :enunciate => true | |
performAction ‘speak’, :enunciate => true, :speed => ‘slow’ # same thing | |
The first time I tried to learn Rails, not knowing how to interpret this ambiguous syntax was a major source of frustration for me. I wasn’t aware of many of the shortcuts Rails lets developers use while writing code, and wound up tangled in confusion. | |
Classes, Objects and Scope | |
Ruby has an incredibly powerful object/class system. | |
Classes, Subclasses and Modules are ways of organizing code into nice packages, and controlling the scope that’s available to each block of code. | |
Ruby Doesn’t Care About Type | |
When you pass an object into a function, there doesn’t seem to be any way to enforce that the object is of a certain type. I’m not even sure you can `get_class()` or `instanceOf` the way you can in PHP. | |
Instead, every object in Ruby (so... everything.) has a `respond_to?(method_name)` method which returns true if the object has a method by that name, and you use this to make sure that the object you have is something that knows how to behave in the way you’re asking it to. | |
Ruby | |
class Duck | |
def swim | |
... | |
end | |
end | |
class Fish | |
def swim | |
... | |
end | |
end | |
Now, you’re out in the ocean with a variable called `animal`. | |
Do you care what type of creature animal is, or just that it can swim? | |
Ruby | |
def drown | |
puts “dag, yo.” | |
die | |
end | |
drown unless animal.respond_to? ‘swim’ | |
Classes Can be Modified at Run Time | |
Ruby has a novel behavior that’s absent in PHP, but is very similar to Javascript’s `.prototype.` structure. | |
Javascript | |
var str = “Hello!” | |
String.prototype.shout_to_the_heavens = function() {...} | |
str.shout_to_the_heavens() // They heard your prayers. | |
Ruby | |
class String | |
def shout_to_the_heavens | |
puts “Your prayers are heard.” | |
end | |
end | |
Even though String is defined somewhere deep within Ruby, we can re-open the class declaration and add, or even overwrite, anything we want. | |
This can be done with any class. | |
All Data Members are ‘private’ Scoped | |
All instance variables are private, accessible only to the methods defined on the class itself. This means you need getters and setters for everything you want accessible outside the object. | |
Ruby | |
class Person | |
def age | |
@age | |
end | |
def age= age # with omitted parenthesis, we’re saying: def age= (age) { ... } | |
@age = age | |
end | |
end | |
Operations are Just Ordinary Methods | |
You can define operational methods, like adding or subtracting, for your own classes. | |
Ruby | |
class Person | |
def += companion | |
Person.new(“#{@name[0..2]}#{companion.name[-3..]}”) | |
end | |
end | |
# Adding two Persons now returns a new person with a composite name | |
jake_and_sally = jake + sally | |
jake_and_sally.name # Pretty sure this returns “Jally” | |
Classes can Implement and/or Include Any Other Class | |
Ruby | |
class Trex | |
implements Carnivore | |
... | |
end | |
Trex now needs its own definitions for all methods in Carnivore, such as `eat` and `rawr` | |
Ruby | |
class Trex | |
include Carnivore | |
... | |
end | |
Trex now has all the Carnivore methods defined, as if you’d re-typed the contents of Carnivore as part of Trex’s definition. | |
Modules Are Bundles of Classes | |
A module is just a scoping mechanism, like a class which can’t be instantiated on it’s own, but which bundles other subclasses together neatly. | |
Ruby | |
module XML | |
class Document | |
... | |
end | |
end | |
module PDF | |
class Document | |
... | |
end | |
end | |
paper = XML::Document.new | |
Modules can also be included from any scope, which brings their contents into the current scope. | |
Ruby | |
include XML | |
doc = Document.new # Document class is defined as XML::Document | |
Conclusion | |
I tried a few times to “learn Rails” with no success. In hindsight, I think this was because I was trying to learn Ruby and Ruby on Rails at the same time, without understanding where one stopped and the other began. | |
After learning Symfony, the “Rails for PHP,” I found Ruby and RoR much easier to pick up. I think learning MVC in a familiar environment (PHP) and then translating that knowledge onto a new language made things click. | |
My hope is that this guide will help you use your PHP and Javascript knowledge to make sense of Ruby, which is why this article is all about teaching Ruby as a language, and has nothing to do with Rails or MVC. | |
That being said, I’m looking for a good “Intro to MVC and Rails” guide that I can link to from here, that would pick up where this guide leaves off. | |
Cheers. | |
Questions or Corrections? @jfeldstein on twitter |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment