While it certainly makes no attempt to constitute a complete guide to the Ruby language, this tutorial will introduce you to some of the basics of Ruby. We'll power through a crash-course in object oriented programming, covering the more common features of the language along the way, and leaving the more obscure aspects of Ruby for a dedicated reference guide [1]. I'll also point out some of the advantages that Ruby has over other languages when it comes to developing applications for the Web.
Some Rails developers suggest that it's possible to learn and use Rails without learning the Ruby basics first, but as far as I'm concerned, it's extremely beneficial to know even a little Ruby before diving into the guts of Rails. In fact, if you take the time to learn the Ruby basics first, you'll automatically become a better Rails programmer.
That's what this tutorial is all about. In fact, this information is excerpted from my new book, Build Your Own Ruby On Rails Web Applications [2], which is now available through sitepoint.com. The two chapters presented here get straight into the finer points of Ruby and Rails. If you need installation and other setup instructions, download the sample PDF, which contains chapters 1 to 4 [3].
Ruby is a Scripting Language
In general, programming languages fall into one of two categories: they're either compiled languages or scripting languages. Let's explore what each of those terms means, and understand the differences between them.
Compiled Languages
The language in which you write an application is not actually something that your computer understands. Your code needs to be translated into bits and bytes that can be executed by your computer. This process of translation is called compilation, and any language that requires compilation is referred to as a compiled language. Examples of compiled languages include C, C#, and Java.
For a compiled language, the actual compilation is the final step in the development process. You invoke a compiler -- the software program that translates your final hand-written, human-readable code into machine-readable code -- and the compiler creates an executable file. This final product is then able to execute independently of the original source code.
Thus, if you make changes to your code, and you want those changes to be incorporated into the application, you must stop the running application, recompile it, then start the application again.
Scripting Languages
On the other hand, a scripting language such as Ruby, PHP, or Python, relies upon an application's source code all of the time. Scripting languages don't have a compiler or a compilation phase per se; instead, they use an interpreter -- a program that runs on the web server -- to translate hand-written code into machine-executable code on the fly. The link between the running application and your hand-crafted code is never severed, because that scripting code is translated every time it is invoked -- in other words, for every web page that your application renders.
As you might have gathered from the name, the use of an interpreter rather than a compiler is the major difference between a scripting language and a compiled language.
The Great Performance Debate
If you've come from a compiled-language background, you might be concerned by all this talk of translating code on the fly -- how does it affect the application's performance?
These concerns are valid -- translating code on the web server every time it's needed is certainly more expensive, performance-wise, than executing pre-compiled code, as it requires more effort on the part of your machine's processor. The good news is that there are ways to speed up scripted languages, including techniques such as code caching and persistent interpreters. However, both topics are beyond the scope of this book.
There's also an upside to scripted languages in terms of performance -- namely, your performance while developing an application.
Imagine that you've just compiled a shiny new Java application, and launched it for the first time ... and then you notice a typo on the welcome screen. To fix it, you have to stop your application, go back to the source code, fix the typo, wait for the code to recompile, and restart your application to confirm that it is fixed. And if you find another typo, you'll need to repeat that process again. Lather, rinse, repeat.
In a scripting language, you can fix the typo and just reload the page in your browser -- no restart, no recompile, no nothing. It's as simple as that.
Ruby is an Object Oriented Language
Ruby, from its very beginnings, was built as a programming language that adheres to the principles of object oriented programming (OOP). Before getting into Ruby specifics, I'd like to introduce you to some fundamental concepts of OOP. Now I know that theory can seem a bit dry to those who are itching to start coding, but we'll cover a lot of ground in this short section, so don't skip it. This discussion will hold you in good stead -- trust me.
OOP is a programming paradigm that first surfaced in the 1960s, but didn't gain traction until the 1980s with C++. The core idea behind it is that programs should be composed of individual entities, or objects, each of which has the ability to communicate with other objects around it. Additionally, each object may have the facility to store data internally, as depicted in Figure 3.1.
Figure 3.1. Communication between objects
Objects in an OOP application are often modeled on real-world objects, so even non-programmers can usually recognize the basic role that an object plays.
And, just like the real world, OOP defines objects with similar characteristics as belonging to the same class. A class is a construct for defining properties for objects that are alike, and equipping them with functionality. For example, a class named Car might define the attributes color and mileage for its objects, and assign them functionality -- actions such as "open the trunk," "start the engine," and "change gears." These different actions are known as methods, although you'll often see Rails enthusiasts refer to the methods of a controller as "actions" -- you can safely consider the two terms to be interchangeable.
Understanding the relationship between a class and its objects is integral to understanding how OOP works. For instance, one object can invoke functionality on another object, and can do so without affecting other objects of the same class. So, if one car object was instructed to open its trunk (think of KITT, the talking car from the classic 80s television show "Knight Rider," if it helps with the metaphor), then its trunk would open, but the trunk of other cars would remain closed. Similarly, if our high-tech talking car were instructed to change color to red, then it would do so, but other cars would not. ("Knight Rider [4]" was a popular series in the 1980s that featured modern-day cowboy Michael Knight (played by David Hasselhoff) and his opinionated, talking, black Pontiac Firebird named KITT. Having seen the show is not critical to understanding object oriented programming?just knowing that the car could talk will suffice!)
When we create a new object in OOP, we base it on an existing class. The process of creating new objects from a class is called instantiation. Figure 3.2 illustrates the concept.
Figure 3.2. Classes and objects
As I mentioned, objects can communicate with each other and invoke functionality (methods) on other objects. Invoking an object's methods can be thought of as asking the object a question, and getting an answer in return.
Consider the example of our famous talking car again. Let's say we ask the talking car object to report its current mileage. This question is not ambiguous -- the answer that the object gives is called a return value, and is shown in Figure 3.3.
Figure 3.3. Asking a simple question
In some cases, the question and answer analogy doesn't quite fit. In these situations, we might rephrase the analogy to consider the question to be an instruction, and the answer a status report indicating whether or not the instruction was executed successfully. This might look something like the diagram in Figure 3.4.
Figure 3.4. Sending instructions
Sometimes we need a bit more flexibility with our instructions. For example, if we wanted to tell our car to change gear, we need to tell it not only to change gear, but also which gear to change to. The process of asking these kinds of questions is referred to as passing an argument to the method.
An argument is an input value that's provided to a method. An argument can be used in two ways:
- to influence how a method operates
- to influence which object a method operates on
An example is shown in Figure 3.5, where the method is "change gear," and the number of the gear to which the car must change (two) is the argument.
Figure 3.5. Passing arguments
A more general view of all of these different types of communication between objects is this: invoking an object's methods is accomplished by sending messages to it. As one might expect, the object sending the message is called the sender, and the object receiving the message is called the receiver.
Armed with this basic knowledge about object oriented programming, let's look at some Ruby specifics.
Reading and Writing Ruby Code
Learning the syntax of a new language has the potential to induce the occasional yawn. So, to make things more interesting, I'll present it to you in a practical way that lets you play along at home: we'll use the interactive Ruby shell.
The Interactive Ruby Shell (irb)
You can fire up the interactive Ruby shell by entering irb into a terminal window.
Not the Standard DOS Box!
Windows users, don't forget to use the Open Ruby Console option from the Instant Rails control panel, to make sure the environment you're using contains the right settings.
irb allows you to issue Ruby commands interactively, one line at a time. This is great for playing with the language, and it's also great for debugging, as we'll see in Chapter 11, Debugging, Testing, and Benchmarking.
A couple of points about the irb output you'll see in this chapter:
- Lines beginning with the Ruby shell prompt (irb>) are typed in by you (and me).
- Lines beginning with => show the return value of the command that has been entered.
We'll start with a really brief example:
irb> 1
=> 1
In this example, I've simply thrown the number 1 at the Ruby shell, and got back what appears to be the very same number.
Looks can be deceiving, though -- it's actually not the very same number. What we were handed back is in fact a fully-featured Ruby object.
Remember our discussion about object oriented programming in the previous section? Well, in Ruby, absolutely everything is treated as an object with which we can interact -- each object belongs to a certain class, therefore each object is able to store data and functionality in the form of methods.
To find the class to which our number belongs, we call the number's class method:
irb> 1.class
=> Fixnum
We touched on senders and receivers earlier. In this example, we've sent the class message to the 1 object, so the 1 object is the receiver (there's no sender, as we're sending the message from the interactive command line rather than from another object). The value that's returned by the method we've invoked is Fixnum, which is the Ruby class that represents integer values.
Since everything in Ruby (including a class) is an object, we can actually send the very same message to the Fixnum class. The result is different, as we'd expect:
irb> Fixnum.class
=> Class
This time, the return value is Class, which is somewhat reassuring -- we did invoke it on a class name, after all.
Note that the method class is all lowercase, yet the return value Class begins with a capital letter. A method in Ruby is always written in lowercase, whereas the first letter of a class is always capitalized.
Interacting with Ruby Objects
Getting used to thinking in terms of objects can take some time. Let's look at a few different types of objects, and see how we can interact with them.
Literal Objects
Literal objects are character strings or numbers that appear directly in the code, as did the number 1 that was returned in the previous section. We've seen numbers in action; next, let's look at a string literal.
A string literal is an object that contains a string of characters, such as a name, an address, or an especially witty phrase. In the same way that we created the 1 literal object in the previous example, we can easily create a new string literal object, then send it a message. A string literal is created by enclosing the characters that make up the string in single or double quotes, like this:
irb> "The quick brown fox"
=> "The quick brown fox"
First, we'll confirm that our string literal indeed belongs to class String:
irb> "The quick brown fox".class
=> String
This String object has a wealth of embedded functionality. For example, we can ascertain the number of characters that our string literal comprises by sending it the length message:
irb> "The quick brown fox".length
=> 19
Easy stuff, eh?
Variables and Constants
Every application needs a way to store information. Enter: variables and constants. As their names imply, these two data containers have their own unique roles to play.
A constant is an object that's assigned a value once, and once only (usually when the application starts up). Constants are therefore used to store information that doesn't need to change within a running application. As an example, a constant might be used to store the version number for an application. Constants in Ruby are always written using uppercase letters, as shown below:
irb> CONSTANT = "The quick brown fox in a constant"
=> "The quick brown fox in a constant"
irb> APP_VERSION = 5.04
=> 5.04
Variables, in contrast, are objects that are able to change at any time. They can even be reset to nothing, which frees up the memory space that they previously occupied. Variables in Ruby always start with a lowercase character:
irb> variable = "The quick brown fox in a variable"
=> "The quick brown fox in a variable"
There's one more special (and, one might say, evil) thing about a variable -- its scope. The scope of a variable is the part of the program to which a variable is visible. If you try to access a variable from outside its scope (i.e. from a part of an application to which that variable is not visible), you generally won't be able to.
The notable exception to the rules defining a variable's scope are global variables. As the name implies, a global variable is accessible from any part of the program. While this might sound convenient at first, usage of global variables is discouraged -- the fact that they can be written to and read from any part of the program introduces security concerns.
Let's return to the string literal example we saw earlier. Assigning a String to a variable allows us to invoke on that variable the same methods we invoked on the string literal earlier:
irb> fox = "The quick brown fox"
=> "The quick brown fox"
irb> fox.class
=> String
irb> fox.length
=> 19
Punctuation in Ruby
The use of punctuation in Ruby code differs greatly from other languages such as Perl and PHP, so it can seem confusing at first if you're used to programming in those languages. However, once you have a few basics under your belt, punctuation in Ruby begins to feel quite intuitive and can greatly enhance the readability of your code.
Dot Notation
One of the most common punctuation characters in Ruby is the period (.). As we've seen, Ruby uses the period to separate the receiver from the message that's being sent to it, in the form Object.receiver.
If you need to comment a line, either for documentation purposes or to temporarily take a line of code out of the program flow, use a hash mark (#). Comments may start at the beginning of a line, or they may appear further along, after some Ruby code:
irb> # This is a comment. It doesn't actually do anything.
irb> 1 # So is this, but this one comes after a statement.
=> 1
irb> fox = "The quick brown fox" # Assign to a variable
=> "The quick brown fox"
irb> fox.class # Display a variable's class
=> String
irb> fox.length # Display a variable's length
=> 19
Chaining Statements Together
Ruby doesn't require us to use any character to separate commands, unless we want to chain multiple statements together on a single line. In this case, a semicolon (;) is used as the separator. However, if you put every statement on its own line (as we've been doing until now), the semicolon is completely optional.
If you chain multiple statements together in the interactive shell, only the output of the last command that was executed will be displayed to the screen:
irb> fox.class; fox.length; fox.upcase
=> "THE QUICK BROWN FOX"
Use of Parentheses
If you've ever delved into the source code of one of the many JavaScript libraries out there, you might have run screaming from your computer when you saw all the parentheses that are involved in the passing of arguments to methods [5].
In Ruby, the use of parentheses for method calls is optional in cases in which no arguments are passed to the method. The following statements are therefore equal:
irb> fox.class()
=> String
irb> fox.class
=> String
It's common practice to include parentheses for method calls with multiple arguments, such as the insert method of the String class:
irb> "jumps over the lazy dog".insert(0, 'The quick brown fox ')
=> "The quick brown fox jumps over the lazy dog"
This call inserts the second argument passed to the insert object ("The quick brown fox ") at position 0 of the receiving String object ("jumps over the lazy dog"). Position 0 refers to the very beginning of the string.
Method Notation
Until now, we've looked at cases where Ruby uses less punctuation than its competitors. In fact, Ruby makes heavy use of expressive punctuation when it comes to the naming of methods.
A regular method name, as we've seen, is a simple, alphanumeric string of characters. If a method has a potentially destructive nature (for example, it directly modifies the receiving object rather than changing a copy of it), it's commonly suffixed with an exclamation mark (!).
The following example uses the upcase method to illustrate this point:
irb> fox.upcase
=> "THE QUICK BROWN FOX"
irb> fox
=> "The quick brown fox"
irb> fox.upcase!
=> "THE QUICK BROWN FOX"
irb> fox
=> "THE QUICK BROWN FOX"
Here, the contents of the fox variable have been modified by the upcase! method.
Punctuation is also used in the names of methods that return boolean values. A boolean value is a value that's either true or false; these values are commonly used as return values for methods that ask yes/no questions. Such methods end in a question mark, which nicely reflects the fact that they have yes/no answers:
irb> fox.empty?
=> false
irb> fox.is_a? String
=> true
These naming conventions make it easy to recognize methods that are destructive, and those that return boolean values, making your Ruby code more readable.
Object Oriented Programming in Ruby
Let's build on the theory that we covered at the start of this chapter as we take a look at Ruby's implementation of OOP.
As we already know, the structure of an application based on OOP principles is focused on interaction with objects. These objects are often representations of real-world objects, like a Car. Interaction with an object occurs when we send it a message or ask it a question. If we really did have a Car object called kitt (we don't -- yet), starting the car might be as simple as:
irb> kitt.start
This short line of Ruby code sends the message start to the object kitt. Using OOP terminology, we would say that this code statement calls the start method of thekitt object.
As I mentioned before, in contrast to other object oriented programming languages such as Python and PHP, in Ruby, everything is an object. Especially when compared with PHP, Ruby's OOP doesn't feel like a "tacked-on" afterthought -- it was clearly intended to be a core feature of the language from the beginning, which makes using the OOP features in Ruby a real pleasure.
As we saw in the previous section, even the simplest of elements in Ruby (like literal strings and numbers) are objects to which you can send messages.
Classes and Objects
As in any other OOP language, in Ruby, each object belongs to a certain class (for example, PontiacFirebird might be an object of class Car). As we saw in the discussion at the beginning of this chapter, a class can group objects of a certain kind, and equip those objects with common functionality. This functionality comes in the form of methods, and in the object's ability to store information. For example, a PontiacFirebird object might need to store its mileage, as might any other object of the class Car.
In Ruby, the instantiation of a new object that's based on an existing class is accomplished by sending that class the new message. The result is a new object of that class. The following few lines of code show an extremely basic class definition into Ruby -- the third line is where we create an instance of the class that we just defined.
irb> class Car
irb> end
=> nil
irb> kitt = Car.new
=> #
Another basic principle in OOP is encapsulation. According to this principle, objects should be treated as independent entities, each taking care of its own internal data and functionality. If we need to access an object's information -- for instance, its internal variables -- we make use of the object's interface, which is the subset of the object's methods that are made available for other objects to call.
Ruby provides objects with functionality at two levels -- the object level, and class level -- and it adheres to the principle of encapsulation while it's at it! Let's dig deeper.
Object-level Functionality
At the object level, data storage is handled by instance variables (a name that's derived from the instantiation process mentioned above). Think of instance variables as storage containers that are attached to the object, but to which other objects do not have direct access.
To store or retrieve data from these variables, another object must call an accessor method on the object. An accessor method has the ability to set (and get) the value of the object's instance variables.
Let's look at how instance variables and accessor methods relate to each other, and how they're implemented in Ruby.
Instance Variables
Instance variables are bound to an object, and contain values for that object only.
Revisiting our cars example, the mileage values for a number of different Car objects are likely to differ, as each car will have a different mileage. Therefore, mileage is held in an instance variable.
An instance variable can be recognized by its prefix: a single "at" sign (@). And what's more, instance variables don't even need to be declared! There's only one problem: we don't have any way to retrieve or change them once they do exist. This is where instance methods come into play.
Instance Methods
Data storage and retrieval is not the only capability that can be bound to a specific object -- functionality, too, can be bound to objects. We achieve this binding through the use of instance methods, which are specific to an object. Invoking an instance method (in other words, sending a message that contains the method name to an object) will invoke that functionality on the receiving object only.
Instance methods are defined using the def keyword, and end with the end keyword. Enter the following example into a new Ruby shell:
$ irb
irb> class Car
irb> def open_trunk
irb> # code to open trunk goes here
irb> end
irb> end
=> nil
irb> kitt = Car.new
=> #
What you've done is define a class called Car, which has an instance method with the name open_trunk. A Car object instantiated from this class will (possibly using some fancy robotics connected to our Ruby program) open its trunk when its open_trunk method is called. (Ignore that nil return value for the moment; we'll look at nil values in the next section.)
Indenting your Code
While the indentation of code is a key element of the syntax of languages such as Python, in Ruby, indentation is purely cosmetic -- it aids readability, but does not affect the code in any way. In fact, while we're experimenting with the Ruby shell, you needn't be too worried about indenting any of the code. However, when we're saving files that will be edited later, you'll want the readability benefits that come from indenting nested lines.
The Ruby community has agreed upon two spaces as being optimum for indenting blocks of code such as class or method definitions. We'll adhere to this indentation scheme throughout this book.
With our class in place, we can make use of this method:
irb> kitt.open_trunk
=> nil
Since we don't want the trunks of all cars to open at once, we've made this functionality available as an instance method.
I know, I know: we still haven't modified any data. We use accessor methods for this task.
Accessor Methods
An accessor method is a special type of instance method, and is used to read or write to an instance variable. There are two types: readers (sometimes called "getters") and writers (or "setters").
A reader method will look inside the object, fetch the value of an instance variable, and hand this value back to us. A writer method, on the other hand, will look inside the object, find an instance variable, and assign the variable the value that it was passed.
Let's add some methods for getting and setting the @mileage attribute of our Car objects. Once again, exit from the Ruby shell so that we can create an entirely new Carclass definition. Our class definition is getting a bit longer now, so enter each line carefully. If you make a typing mistake, exit the shell and start over.
$ irb
irb> class Car
irb> def set_mileage(x)
irb> @mileage = x
irb> end
irb> def get_mileage
irb> @mileage
irb> end
irb> end
=> nil
irb> kitt = Car.new
=> #
Now, we can finally modify and retrieve the mileage of our Car objects!
irb> kitt.set_mileage(5667)
=> 5667
irb> kitt.get_mileage
=> 5667
This is still a bit awkward. Wouldn't it be nice if we could give our accessor methods exactly the same names as the attributes that they read from or write to? Luckily, Ruby contains shorthand notation for this very task. We can rewrite our class definition as follows:
$ irb
irb> class Car
irb> def mileage=(x)
irb> @mileage = x
irb> end
irb> def mileage
irb> @mileage
irb> end
irb> end
=> nil
irb> kitt = Car.new
=> #
With these accessor methods in place, we can read to and write from our instance variable as if it were available from outside the object.
irb> kitt.mileage = 6032
=> 6032
irb> kitt.mileage
=> 6032
These accessor methods form part of the object's interface.
Class-level Functionality
At the class level, class variables handle data storage. They're commonly used to store state information, or as a means of configuring default values for new objects. Class variables are typically set in the body of a class, and can be recognized by their prefix: a double "at" sign (@@).
First, enter the following class definition into a new Ruby shell.
$ irb
irb> class Car
irb> @@number_of_cars = 0
irb> def initialize
irb> @@number_of_cars = @@number_of_cars + 1
irb> end
irb> end
=> nil
The class definition for the class Car above has an internal counter for the total number of Car objects that have been created. Using the special instance methodinitialize, which is invoked automatically every time an object is instantiated, this counter is incremented for each new Car object.
By the way, we have actually already used a class method. Do you like how I snuck it in there? The new method is an example of a class method that ships with Ruby and is available to all classes -- whether they're defined by you, or form part of the Ruby Standard Library. (The Ruby Standard Library is a large collection of classes that's included with every Ruby installation. The classes facilitate a wide range of common functionality, such as accessing web sites, date calculations, file operations, and more.)
Custom class methods are commonly used to create objects with special properties (such as a default color for our Car objects), or to gather statistics about the class's usage.
Extending the earlier example, we could use a class method called count to return the value of the @@number_of_cars class variable. Remember that this is a variable that's incremented for every new Car object that's created. Class methods are defined identically to instance methods: using the def and end keywords. The only difference is that class method names are prefixed with self. Enter this code into a new Ruby shell:
$ irb
irb> class Car
irb> @@number_of_cars = 0
irb> def self.count
irb> @@number_of_cars
irb> end
irb> def initialize
irb> @@number_of_cars+=1
irb> end
irb> end
=> nil
The following code instantiates some new Car objects, then makes use of our new class method:
irb> kitt = Car.new # Michael Knight's talking car
=> #<0xba8c>
irb> herbie = Car.new # The famous VolksWagen love bug!
=> #<0x8cd20>
irb> batmobile = Car.new # Batman's sleek automobile
=> #<0x872e4>
irb> Car.count
=> 3
The method tells us that three instances of the Car class have been created. Note that we can't call a class method on an object (Ruby actually does provide a way to invoke some class methods on an object, using the :: operator, but we won't worry about that for now. We'll see the :: operator in use in Chapter 4, Rails Revealed.):
irb> kitt.count
NoMethodError: undefined method 'count' for #
As implied by the name, the count class method is available only to the Car class, not to any objects instantiated from that class.
I sneakily introduced something else in there. Did you spot it? In many languages, including PHP and Java, the ++ and -- operators are used to increment a variable by one. Ruby doesn't support this notation; instead, when working with Ruby, we need to use the += operator. Therefore, the shorthand notation for incrementing our counter in the class definition is:
irb> @@number_of_cars+=1
This code is identical to the following:
irb> @@number_of_cars = @@number of cars + 1
Both of these lines can be read as "my_variable becomes equal to my_variable plus one."
Inheritance
If your application deals with more than the flat hierarchy we've explored so far, you might want to construct a scenario whereby some classes inherit from other classes. Continuing with the car analogy, let's suppose that we had a green limousine named Larry (this assigning of names to cars might feel a bit strange, but it's important for this example, so bear with me). In Ruby, the larry object would probably descend from a StretchLimo class, which could in turn descend from the class Car. Let's implement that, to see how it works:
$ irb
irb> class Car
irb> @@wheels = 4
irb> end
=> nil
irb> class StretchLimo < class="Apple-converted-space">
irb> @@wheels = 6
irb> def turn_on_television
irb> # Invoke code for switching on on-board TV here
irb> end
irb> end
=> nil
Now, if we were to instantiate an object of class StretchLimo, we'd end up with a different kind of car. Instead of the regular four wheels that standard Car objects have, this one would have six wheels (stored in the class variable @@wheels). It would also have extra functionality, made possible by an extra method --turn_on_television -- which would be available to be called by other objects.
However, if we were to instantiate a regular Car object, the car would have only four wheels, and there would be no instance method for turning on an on-board television. Think of inheritance as a way for the functionality of a class to become more specialized the further we move down the inheritance path.
Don't worry if you're struggling to wrap your head around all the aspects of OOP -- you'll automatically become accustomed to them as you work through this book. You might find it useful to come back to this section, though, especially if you need a reminder about a certain term later on.
Return Values
It's always great to receive feedback. Remember our talk about passing arguments to methods? Well, regardless of whether or not a method accepts arguments, invoking a method in Ruby always results in feedback -- it comes in the form of a return value, which is returned either explicitly or implicitly.
To return a value explicitly, use the return statement in the body of a method:
irb> def toot_horn
irb> return "toooot!"
irb> end
=> nil
Calling the toot_horn method in this case would produce the following:
irb> toot_horn
=> "toooot!"
However, if no return statement is used, the result of the last statement that was executed is used as the return value. This behavior is quite unique to Ruby:
irb> def toot_loud_horn
irb> "toooot!".upcase
irb> end
=> nil
Calling the toot_loud_horn method in this case would produce:
irb> toot_loud_horn
=> "TOOOOT!"
Standard Output
When you need to show output to the users of your application, use the print and puts ("put string") statements. Both methods will display the arguments passed to them as Strings; puts also inserts a carriage return at the end of its output. Therefore, in a Ruby program the following lines:
print "The quick "
print "brown fox"
would produce this output:
The quick brown fox
However, using puts like so:
puts "jumps over"
puts "the lazy dog"
would produce this output:
jumps over
the lazy dog
At this stage, you might be wondering why all of the trial-and-error code snippets that we've typed into the Ruby shell actually produced output, given that we haven't been making use of the print or puts methods. The reason is that irb automatically writes the return value of the last statement it executes to the screen before displaying the irb prompt. This means that using a print or puts from within the Ruby shell might in fact produce two lines of output -- the output that you specify should be displayed, and the return value of the last command that was executed, as in the following example:
irb> puts "The quick brown fox"
"The quick brown fox"
=> nil
Here, nil is actually the return value of the puts statement. Looking back at previous examples, you will have encountered nil as the return value for class and method definitions, and you'll have received a hexadecimal address, such as <#Car:0x89da0>, as the return value for object definitions. This hexadecimal value showed the location in memory that the object we instantiated occupied, but luckily we won't need to bother with such geeky details any further.
Having met the print and puts statements, you should be aware that a Rails application actually has a completely different approach to displaying output, called templates. We'll look at templates in Chapter 4, Rails Revealed.
Ruby Core Classes
We've already talked briefly about the String and Fixnum classes in the previous sections, but Ruby has a lot more under its hood. Let's explore!
Arrays
We use Ruby's Arrays to store collections of objects. Each individual object that's stored in an Array has a unique numeric key, which we can use to reference it. As with many languages, the first element in an Array is stored at position 0 (zero).
To create a new Array, simply instantiate a new object of class Array (using the Array.new construct). You can also use a shortcut approach, which is to enclose the objects you want to place inside the Array in square brackets.
For example, an Array containing the mileage at which a car is due for its regular service might look something like this:
irb> service_mileage = [5000, 15000, 30000, 60000, 100000]
=> [5000, 15000, 30000, 60000, 100000]
To retrieve individual elements from an Array, we specify the numeric key in square brackets.
irb> service_mileage[0]
=> 5000
irb> service_mileage[2]
=> 30000
Ruby has another shortcut, which allows us to create an Array from a list of Strings: the %w( ) syntax. Using this shortcut saves us from having to type a lot of double-quote characters:
irb> available_colors = %w( red green blue black )
=> ["red", "green", "blue", "black"]
irb> available_colors[0]
=> "red"
irb> available_colors[3]
=> "black"
In addition to facilitating simple element retrieval, Arrays come with an extensive set of class methods and instance methods that ease data management tasks tremendously.
empty? returns true if the receiving Array doesn't contain any elements:
irb> available_colors.empty?
=> false
size returns the number of elements in an Array:
irb> available_colors.size
=> 4
first and last return an Array's first and last elements, respectively:
irb> available_colors.first
=> "red"
irb> available_colors.last
=> "black"
delete removes the named element from the Array and returns it:
irb> available_colors.delete "red"
=> "red"
irb> available_colors
=> ["green", "blue", "black"]
The complete list of class methods and instance methods provided by the Array class is available via the Ruby reference documentation, which you can access by entering the ri command into the terminal window (for your operating system, not the Ruby shell), followed by the class name you'd like to look up:
$ ri Array
Oh, and ri stands for ruby interactive, in case you're wondering. Don't confuse it with irb.
Hashes
A Hash is another kind of data storage container. Hashes are similar, conceptually, to dictionaries: they map one object (the key -- for example, a word) to another object (the value -- for example, a word's definition) in a one-to-one relationship.
New Hashes can be created either by instantiating a new object of class Hash (using the Hash.new construct) or by using the curly brace shortcut shown below. When we define a Hash, we must specify each entry using the key => value syntax.
For example, the following Hash maps car names to a color:
irb> car_colors = {
irb> 'kitt' => 'black',
irb> 'herbie' => 'white',
irb> 'batmobile' => 'black',
irb> 'larry' => 'green'
irb> }
=> {"kitt"=>"black", "herbie"=>"white", "batmobile"=>"black",
"larry"=>"green"}
To query this newly built Hash, we pass the key of the entry we want to look up in square brackets, like so:
irb> car_colors['kitt']
=> "black"
All sorts of useful functionality is built into Hashes, including the following methods:
empty? returns true if the receiving Hash doesn't contain any elements:
irb> car_colors.empty?
=> false
size returns the number of elements in a Hash:
irb> car_colors.size
=> 4
keys returns all keys of a Hash as an Array:
irb> car_colors.keys
=> ["kitt", "herbie", "larry", "batmobile"]
values returns all values of a Hash as an Array, although care should be taken with regards to the order of the elements (keys in a Hash are ordered for optimal storage and retrieval; this order does not necessarily reflect the order in which they were entered):
irb> car_colors.values
=> ["black", "white", "green", "black"]
There are lots more -- for the complete list of class methods and instance methods provided by the Hash class, consult the Ruby reference documentation.
Strings
The typical Ruby String object -- yep, that very object we've been using in the past few sections -- holds and manipulates sequences of characters. Most of the time, newString objects are created using string literals that are enclosed in single or double quotes. The literal can then be stored in a variable for later use:
irb> a_phrase = "The quick brown fox"
=> "The quick brown fox"
irb> a_phrase.class
=> String
If the string literal includes the quote character used to enclose the string itself, it must be escaped with a backslash character (\):
irb> 'I\'m a quick brown fox'
=> "I'm a quick brown fox"
irb> "Arnie said, \"I'm back!\""
=> "Arnie said, \"I'm back!\""
An easier way to specify string literals that contain quotes is to use the %Q shortcut, like this:
irb> %Q(Arnie said, "I'm back!")
=> "Arnie said, \"I'm back!\""
String objects also support the substitution of Ruby code into a string literal via the Ruby expression #{}:
irb> "The current time is: #{Time.now}"
=> "The current time is: Wed Aug 02 21:15:19 CEST 2006"
The String class also has rich embedded functionality for modifying String objects. Here are some of the most useful methods:
gsub substitutes a given pattern within a String:
irb> "The quick brown fox".gsub('fox', 'dog')
=> "The quick brown dog"
include? returns true if a String contains another specific String:
irb> "The quick brown fox".include?('fox')
=> true
length returns the length of a String in characters:
irb> "The quick brown fox".length
=> 19
slice returns a portion of a String:
irb> "The quick brown fox".slice(0, 3)
=> "The"
The complete method reference is available using the ri command-line tool:
$ ri String
Numerics
Since there are so many different types of numbers, Ruby has a separate class for each, the popular Float, Fixnum, and Bignum classes among them. In fact, they're all subclasses of Numeric, which provides the basic functionality.
Just like Strings, numbers are usually created from literals:
irb> 123.class
=> Fixnum
irb> 12.5.class
=> Float
Each of the specific Numeric subclasses comes with features that are relevant to the type of number it's designed to deal with. However, the following functionality is shared between all Numeric subclasses:
integer? returns true if the object is a whole integer:
irb> 123.integer?
=> true
irb> 12.5.integer?
=> false
round rounds a number to the nearest integer:
irb> 12.3.round
=> 12
irb> 38.8.round
=> 39
zero? returns true if the number is equal to zero:
irb> 0.zero?
=> true
irb> 8.zero?
=> false
Additionally, there are ways to convert numbers between the Numeric subclasses. to_f converts a value to a Float, and to_i converts a value to an Integer:
irb> 12.to_f
=> 12.0
irb> 11.3.to_i
=> 11
Symbols
In Ruby, a Symbol is a simple textual identifier. Like a String, a Symbol is created using literals; the difference is that a Symbol is prefixed with a colon:
irb> :fox
=> :fox
irb> :fox.class
=> Symbol
The main benefit of using a Symbol instead of a String is that a Symbol contains less functionality. This can be an advantage in certain situations. For example, thecar_colors Hash that we looked at earlier could be rewritten as follows:
car_colors =
:kitt => 'black',
:herbie => 'white',
:larry => 'green',
:batmobile => 'black'
}
Objects of class String can be converted to Symbols, and vice-versa:
irb> "fox".to_sym
=> :fox
irb> :fox.to_s
=> "fox"
We'll use Symbols frequently as we deal with Rails functionality in successive chapters of this book.
nil
I promised earlier that I'd explain nil values -- now's the time!
All programming languages have a value that they can use when they actually mean nothing. Some use undef; others use NULL. Ruby uses nil. A nil value, like everything in Ruby, is also an object. It therefore has its own class: NilClass.
Basically, if a method doesn't return anything, it is, in fact, returning the value nil. And if you assign nil to a variable, you effectively make it empty. nil shows up in a couple of additional places, but we'll cross those bridges when we come to them.
Running Ruby Files
For the simple Ruby basics that we've experimented with so far, the interactive Ruby shell (irb) has been our tool of choice. I'm sure you'll agree that experimenting in a shell-like environment, where we can see immediate results, is a great way to learn the language.
However, we're going to be talking about control structures next, and for tasks of such complexity, you'll want to work in a text editor. This environment will allow you to run a chunk of code many times without having to retype it.
In general, Ruby scripts are simple text files containing Ruby code and have a .rb extension. These files are passed to the Ruby interpreter, which executes your code, like this:
$ ruby myscript.rb
To work with the examples that follow, I'd recommend that you open a new text file in your favorite text editor (which might be one of those I recommended back in Chapter 2, Getting Started) and type the code out as you go -- this really is the best way to learn. However, I acknowledge that some people aren't interested in typing everything out, and just want to cut to the chase. These more impatient readers can download the code archive for this book, which contains all of these examples. You can execute this code in the Ruby interpreter straight away.
As demonstrated above, to run the files from the command line, you simply need to type ruby, followed by the filename.
Control Structures
Ruby has a rich set of features for controlling the flow of your application. Conditionals are key words that are used to decide whether or not certain statements are executed based on the evaluation of one or more conditions; loops are constructs that execute statements more than once; blocks are a means of encapsulating functionality (for example, to be executed in a loop).
To demonstrate these control structures, let's utilize some of the Car classes that we defined earlier. Type out the following class definition and save the file (or load it from the code archive); we'll build on it in this section as we explore some control structures.
Example 3.1. 01-car-classes.rb
class Car
@@wheels = 4 # class variable
@@number_of_cars = 0 # class variable
def initialize
@@number_of_cars = @@number_of_cars + 1
end
def self.count
@@number_of_cars
end
def mileage=(x) # mileage writer
@mileage = x
end
def mileage # mileage reader
@mileage
end
end
class StretchLimo < class="Apple-converted-space">
@@wheels = 6 # class variable
@@televisions = 1 # class variable
def turn_on_television
# Invoke code for switching on on-board TV here
end
end
class PontiacFirebird < class="Apple-converted-space">
end
class VolksWagen < class="Apple-converted-space">
end
Conditionals
There are two basic conditional constructs in Ruby: if and unless. Each of these constructs can be used to execute a group of statements on the basis of a given condition.
The if Construct
An if construct wraps statements that are to be executed only if a certain condition is met. The keyword end defines the end of the if construct. The statements contained between the condition and the end keyword are executed only if the condition is met.
Example 3.2. 02-if-construct.rb (excerpt)
if Car.count.zero?
puts "No cars have been produced yet."
end
You can provide a second condition by adding an else block: when the condition is met, the first block is executed; otherwise, the else block is executed. This kind of control flow will probably be familiar to you. Here it is in action:
Example 3.3. 03-if-else-construct.rb (excerpt)
if Car.count.zero?
puts "No cars have been produced yet."
else
puts "New cars can still be produced."
end
The most complicated example involves an alternative condition. If the first condition is not met, then a second condition is evaluated. If neither conditions are met, theelse block is executed:
Example 3.4. 04-if-elsif-else.rb (excerpt)
if Car.count.zero?
puts "No cars have been produced yet."
elsif Car.count >= 10
puts "Production capacity has been reached."
else
puts "New cars can still be produced."
end
If the count method returned 5, the code above would produce the following output:
New cars can still be produced.
An alternative to the traditional if condition is the if statement modifier. A statement modifier does just that -- it modifies the statement of which it is part. The ifstatement modifier works exactly like a regular if condition, but it sits at the end of the line that's affected, rather than before a block of code:
Example 3.5. 05-if-statement-modifier.rb (excerpt)
puts "No cars have been produced yet." if Car.count.zero?
This version of the if condition is often used when the code that's to be executed conditionally comprises just a single line. Having the ability to create conditions like this results in code that's a lot more like English than other programming languages with more rigid structures.
The unless Construct
The unless condition is a negative version of the if condition. It's useful for situations in which you want to execute a group of statements when a certain condition is not met.
Let's create a few instances to work with (Aficionados of comics will notice that I've created the BatMobile as a Pontiac Firebird -- in fact, the caped crusader's choice of transport has varied over the years, taking in many of the automobile industry's less common innovations, and including everything from a 1966 Lincoln Futura to an amphibious tank. But we'll stick with a Pontiac for this example.):
Example 3.6. 06-unless-construct.rb (excerpt)
kitt = PontiacFirebird.new
kitt.mileage = 5667
herbie = VolksWagen.new
herbie.mileage = 33014
batmobile = PontiacFirebird.new
batmobile.mileage = 4623
larry = StretchLimo.new
larry.mileage = 20140
Now if we wanted to find out how many Knight Rider fans KITT could take for a joy-ride, we could check which class the kitt object was. As with the if expression, theend keyword defines the end of the statement.
Example 3.7. 06-unless-construct.rb (excerpt)
unless kitt.is_a?(StretchLimo)
puts "This car is only licensed to seat two people."
end
Like the if condition, the unless condition may have an optional else block of statements, which is executed when the condition is met:
Example 3.8. 07-unless-else.rb (excerpt)
unless kitt.is_a?(StretchLimo)
puts "This car only has room for two people."
else
puts "This car is licensed to carry up to 10 passengers."
end
Since KITT is definitely not a stretch limousine, this code would return:
This car only has room for two people.
Unlike if conditions, unless conditions do not support a second condition. However, like the if condition, the unless condition is also available as a statement modifier. The following code shows an example of this. Here, the message will not display if KITT's mileage is less than 25000:
Example 3.9. 08-unless-statement-modifier.rb (excerpt)
puts "Service due!" unless kitt.mileage <>
Loops
Ruby provides the while and for constructs for looping through code (i.e. executing a group of statements a specified number of times, or until a certain condition is met). Also, a number of instance methods are available for looping over the elements of an Array or Hash; we'll cover these in the next section.
while and until Loops
A while loop executes the statements it encloses repeatedly, as long as the specified condition is met.
Example 3.10. 09-while-loop.rb (excerpt)
while Car.count < class="Apple-converted-space">
Car.new
puts "A new car instance was created."
end
This simple while loop executes the Car.new statement repeatedly, as long as the total number of cars is below ten. It exits the loop when the number reaches ten.
Like the relationship between if and unless, the while loop also has a complement: the until construct. If we use until, the code within the loop is executed until the condition is met. We could rewrite the loop above using until like so:
Example 3.11. 10-until-loop.rb (excerpt)
until Car.count == 10
Car.new
puts "A new car instance was created."
end
The Difference Between = and ==
It's important to note the difference between the assignment operator (a single equal sign) and the equation operator (a double equal sign) when using them within a condition.
If you're comparing two values, use the equation operator:
if Car.count == 10
...
end
If you're assigning a value to a variable, use the assignment operator:
my_new_car = Car.new
If you confuse the two, you might modify a value that you were hoping only to inspect, with potentially disastrous consequences!
for Loops
for loops allow us to iterate over the elements of a collection -- such as an Array -- and execute a group of statements once for each element. Here's an example:
Example 3.12. 11-for-loop.rb (excerpt)
for car in [ kitt, herbie, batmobile, larry ]
puts car.mileage
end
The code above would produce the following output:
5667
33014
4623
20140
This simple for loop iterates over an Array of Car objects and outputs the mileage for each car. For each iteration, the car variable is set to the current element of theArray. The first iteration has car set to the equivalent of lincoln_towncar, the second iteration has it set to chrysler_voyager, and so forth.
In practice, the traditional while and for loops covered here are little used. Instead, most people tend to use the instance methods provided by the Array and Hashclasses, which we'll cover next.
Blocks
Blocks are probably the single most attractive feature of Ruby. However, they're also one of those things that take a while to "click" for Ruby newcomers. Before we dig deeper into creating blocks, let's take a look at some of the core features of Ruby that use blocks.
We looked at some loop constructs in the previous section, and this was a useful way to explore the tools that are available to us. However, you'll probably never actually come across many of these constructs in your work with other Ruby scripts, simply because it's almost always much easier to use a block to perform the same task. A block, in conjunction with the each method that is provided by the Array and Hash classes, is a very powerful way to loop through your data.
Let me illustrate this point with an example. Consider the for loop we used a moment ago. We could rewrite that code to use the each method, which is an instance method of the Array class, like so:
Example 3.13. 12-simple-block.rb (excerpt)
[ kitt, herbie, batmobile, larry ].each do |car_name|
puts car_name.mileage
end
Let's analyze this: the block comprises the code between the do and end keywords. A block is able to receive parameters, which are placed between vertical bars (|) at the beginning of the block. Multiple parameters are separated by commas. Therefore, this code performs an identical operation to the for loop we saw before, but in a much more succinct manner.
Let's take another example. To loop through the elements of a Hash, we use the each method, and pass two parameters to the block -- the key (car_name) and thevalue (color) -- like this:
Example 3.14. 13-block-with-params.rb (excerpt)
car_colors = {
'kitt' => 'black',
'herbie' => 'white',
'batmobile' => 'black',
'larry' => 'green'
}
car_colors.each do |car_name, color|
puts "#{car_name} is #{color}"
end
This code produces the following output:
kitt is black
herbie is white
batmobile is black
larry is green
The Integer class also sports a number of methods that use blocks. The times method of an Integer object, for example, executes a block exactly n times, where n is the value of the object.
Example 3.15. 14-block-integer.rb (excerpt)
10.times { Car.new }
puts "#{Car.count} cars have been produced."
The code above produces this output:
10 cars have been produced.
One final point to note here is the alternate block syntax of curly braces. Instead of the do...end keywords that we used in previous examples, curly braces are the preferred syntax for blocks that are very short, as in the previous example.
Here's another method of the Integer class -- in the spirit of times, the upto method counts from the value of the object up to the argument passed to the method.
Example 3.16. 15-block-upto.rb
5.upto(7) { |i| puts i }
This code produces the output shown here:
5
6
7
In Ruby parlance, the object i is a parameter of the block. Parameters for blocks are enclosed in vertical bars, and are usually available only from within the block. If we have more than one parameter, we separate them using commas, like so: |parameter1, parameter2|. In the example above, we would no longer have access to ionce the block had finished executing.
As we work through this book, we'll explore many more uses of blocks in combination with the Rails core classes.
Summary
Wow, we covered a lot in this chapter! First, we swept through a stack of object oriented programming theory -- probably the equivalent of an introductory computer science course! This gave us a good grounding for exploring the basics of the Ruby programming language, and the Interactive Ruby Shell (irb) was a fun way to do this exploration.
We also investigated many of the Ruby core classes, such as String, Array, and Hash, from within the Ruby shell. We then moved from the shell to create and save proper Ruby files, and using these files, we experimented with control structures such as conditionals, loops, and blocks.
In the next chapter, we'll look at the major cornerstones that make up the Rails framework -- the integrated testing facilities -- as well as the roles that the development, testing, and production environments play.
Chapter 4, Rails Revealed
As you might have gathered from Chapter 1, Introducing Ruby on Rails, quite a bit of thought has been put into the code base that makes up the Rails framework. Over time, many of the internals have been rewritten, which has improved their speed and efficiency, and allowed the implementation of additional features, but the original architecture remains largely unchanged. This chapter will shed some light on the inner workings of Rails.
Three Environments
Rails encourages the use of a different environment for each of the stages in an application's lifecycle -- development, testing, and production. If you've been developing Web applications for a while, this is probably how you operate anyway; Rails just formalizes these environments.
Development
In the development environment, changes to an application's source code are immediately visible; all we need to do is reload the corresponding page in a web browser. Speed is not a critical factor in this environment; instead, the focus is on providing the developer with as much insight as possible into the components involved in displaying each page. When an error occurs in the development environment, the developer is able to tell at a glance which line of code is responsible for the error, and how that particular line was invoked. This capability is provided by the stack trace (a comprehensive list of all the method calls leading up to the error), which is displayed when an unexpected error occurs.
Test
In testing, we usually refresh the database with a baseline of dummy data each time a test is repeated -- this ensures that the results of the tests are consistent, and that behavior is reproducible. Unit and functional testing procedures are fully automated in Rails.
When we test a Rails application, we don't view it using a traditional web browser. Instead, tests are invoked from the command line, and can be run as background processes. The testing environment provides a dedicated environment in which these processes can operate.
Production
By the time your application finally goes live, it should be well tested, so that all (or at least most) of the bugs have been eliminated. As a result, updates to the code base should be infrequent, which means that the production environments can be optimized to focus on performance. Tasks such as writing extensive logs for debugging purposes should be unnecessary at this stage. Besides, if an error does occur, you don't want to scare your visitors away with a cryptic stack trace -- that's best kept for the development environment.
As the requirements of each of the three environments are quite different, Rails stores the data for each environment in entirely separate databases. So at any given time, you might have:
- live data with which real users are interacting in the production environment
- a partial copy of this live data that you're using to debug an error or develop new features in the development environment
- a set of testing data that's constantly being reloaded into the testing environment
Let's look at how we can configure our database for each of these environments.
Database Configuration
Configuring the database for a Rails application is frighteningly easy -- all of the critical information is contained in just one file. We'll take a look at it now, then create some databases for our application to use.
The Database Configuration File
The separation of environments is reflected in the Rails database configuration file database.yml. We saw a sample of this file back in Chapter 1, Introducing Ruby on Rails, and in fact we created our very own configuration file in Chapter 2, Getting Started, when we used the rails command. Go take a look! It lives in the config subdirectory of our Shovell application.
With the comments removed, the file should look like this (Depending on your MySQL configuration, you may need to use 127.0.0.1 as your host value, instead oflocalhost.):
Example 4.1. 01-database.yml
development:
adapter: mysql
database: shovell_development
username: root
password:
host: localhost
test:
adapter: mysql
database: shovell_test
username: root
password:
host: localhost
production:
adapter: mysql
database: shovell_production
username: root
password:
host: localhost
This file lists the minimum amount of information we need in order to connect to the database server for each of our environments (development, test, and production). With the default setup of MySQL that we installed in Chapter 2, Getting Started, we can be confident to proceed with our development using the root user and an empty password for the time being -- all of our development should take place on a local machine, so we needn't be concerned about someone accessing our super-secret Shovell application.
The parameter database sets the name of the database that's to be used in each environment. As the configuration file suggests, Rails is able to support multiple databases in parallel. Note that we're actually talking about different databases here, not just different tables -- each database can host an arbitrary number of different tables in parallel. Figure 4.1 shows a graphical representation of this architecture.
Figure 4.1. The database architecture of a Rails application
However, there's one vital aspect missing from our current configuration: the databases referenced in our configuration file don't exist yet! Let's create them now.
We can create these databases using one of the many graphical front ends that are available for MySQL, or we can just jump into the command line. Because the commands are fairly simple, let's create the databases from the command line for now; we'll look at graphical database clients later in this chapter.
Creating the Databases
To launch the MySQL command line interface, type mysql -u root at the command prompt. (On a Mac, the command is called mysql5 instead of mysql -- Mac users like to be different.)
$ mysql -u root
mysql>
The command to create a new database is simple enough: create database newdatabasename.
We'll use it to create three databases -- one for each of our environments -- as shown in Figure 4.2.
Example 4.2. 02-create-databases.sql
CREATE DATABASE shovell_development;
CREATE DATABASE shovell_test;
CREATE DATABASE shovell_production;
Database Security and the root User
If you have any experience with databases, you might be feeling a little uncomfortable that we're developing our application using the root user, without even setting a password. The reasoning behind this advice is as follows:
1. The default configuration of MySQL is such that connections to the database server can only be made from the same machine. This means that nobody -- whether they're sitting next to you, or working from the other side of the world -- will be able to wreak havoc in your Rails development environment.
2. The MySQL command line and permissions system are complex and powerful tools, and database security is a topic that's definitely beyond the scope of this book.
Of course, this is not a configuration that I would recommend for your production environment, but we'll get into that in Chapter 12, Deployment and Production Use. If you're interested in securing the database in your development environment, the MySQL manual contains some post-installation instructions [6] that should serve you well.
Figure 4.2. Creating a database for each environment
Now that our databases exist, we can use them to store data for our application!
development is the Default Database
By default, all Rails applications use the development environment unless specified otherwise. So any Rails commands that you execute from the command line will, for the time being, only affect the data in the development database. In Chapter 12, Deployment and Production Use, we'll learn how to switch to the production environment.
The Model-view-controller Architecture
The model-view-controller (MVC) architecture that we first encountered in Chapter 1, Introducing Ruby on Rails is not unique to Rails. In fact, it pre-dates both Rails and the Ruby language by many years. However, Rails really takes the idea of separating an application's data, user interface, and control logic to a whole new level.
Let's take a look at the concepts behind building an application using the MVC architecture. Once we have the theory in place, we'll see how it translates to our Rails code.
MVC in Theory
MVC is a pattern for the architecture of a software application. It separates an application into the following three components:
- models, for handling data and business logic
- controllers, for handling the user interface and application logic
- views, for handling graphical user interface objects and presentation logic
This separation results in user requests being processed as follows:
- The browser, on the client, sends a request for a page to the controller on the server.
- The controller retrieves the data it needs from the model in order to respond to the request.
- The controller renders the page and sends it to the view.
- The view sends the page back to the client for the browser to display.
This process is illustrated in Figure 4.3.
Figure 4.3. Processing a page request in an MVC architecture
Separating a software application into these three distinct components is a good idea for a number of reasons, including the following:
- It improves scalability (the ability for an application to grow): if your application begins experiencing performance issues because database access is slow, for example, you can upgrade the hardware running the database without other components being affected.
- It makes maintenance easier: because the components have a low dependency on each other, making changes to one (to fix bugs or change functionality) does not affect another.
- It promotes reuse: a model may be reused by multiple views, and vice versa.
- It makes the application distributable: a distinct separation of code between components means that each of them could potentially reside on a separate machine, if necessary.
If you haven't quite got your head around the concept of MVC yet, don't worry. For now, the important thing is to remember that your Rails application is separated into three distinct components. Jump back to Figure 4.3 if you need to refer to it later on.
MVC the Rails Way
Rails implements the concept that models, views, and controllers should be kept quite separate by storing the code for each of these elements as separate files, in separate directories.
Figure 4.4. The app subdirectory
This is where the Rails directory structure that we created back in Chapter 2, Getting Started comes into play. The time has come for us to poke around a bit within that structure. If you take a look inside the app directory, which is depicted in Figure 4.4, you'll see some folders whose names might be starting to sound familiar.
As you can see, each component of the model-view-controller architecture has its place within the app subdirectory -- the models, views, and controllerssubdirectories, respectively. (We'll talk about that helpers directory in Chapter 6, Helpers, Forms, and Layouts.)
This separation continues within the code that comprises the framework itself. The classes that form the core functionality of Rails reside within the following modules:
ActiveRecord-ActiveRecordis the module for handling business logic and database communication. It plays the role of model in our MVC architecture. While it might seem odd thatActiveRecorddoesn't have the word "model" in its name, there is a reason for this: Active Record is also the name of a famous design pattern -- one that this component implements in order to perform its role in the MVC world. Besides, if it had been called ActionModel, it would have sounded more like an overpaid Hollywood star than a software component...ActionController-ActionControlleris the component that handles browser requests and facilitates communication between the model and the view. Your controllers will inherit from this class. It forms part of theActionPacklibrary, a collection of Rails components that we'll explore in depth in Chapter 5, Models, Views, and Controllers.ActionView-ActionViewis the component that handles the presentation of pages returned to the client. Views inherit from this class, which is also part of theActionPacklibrary.
Let's take a closer look at each of these components in turn.
ActiveRecord (the Model)
ActiveRecord is designed to handle all of an application's tasks that relate to the database, including:
- establishing a connection to the database server
- retrieving data from a table
- storing new data in the database
It also has a few other neat tricks up its sleeve. Let's look at some of them now.
Database Abstraction
ActiveRecord ships with a large number of database adapters to connect to a variety of popular database server packages, such as MySQL, PostgreSQL, Oracle, and Microsoft SQL Server.
The ActiveRecord module is based on the concept of database abstraction. As we mentioned in Chapter 1, Introducing Ruby on Rails, database abstraction is a way of coding an application so that it isn't dependent upon any one database. Code that's specific to a particular database server is hidden safely in ActiveRecord, and invoked as needed. The result is that a Rails application is not bound to any specific database server software. Should you need to change the underlying database server at a later time, no changes to your application code should be required.
Examples of code that differs greatly between vendors, and which ActiveRecord abstracts, include:
- the process of logging into the database server
- date calculations
- handling of boolean (true/false) data
Before I can show you the magic of ActiveRecord in action, though, we need to do a little housekeeping.
Database Tables
We've already created a database for each of our environments (development, testing, production), but there aren't any tables in those databases yet. Tables are the containers within a database that store our data in a structured manner, and they're made up of rows and columns. The rows map to individual objects, and the columns map to the attributes of those objects. The collection of all the tables in a database, and the relationships between those tables, is called the database schema. An example of a table is shown in Figure 4.5.
Figure 4.5. The structure of a typical database table, including rows and columns
In Rails, the naming of Ruby classes and database tables follows an intuitive pattern: if we have a table called stories that consists of five rows, then that table will store the data for five Story objects. The nice thing about the mapping between classes and tables is that it's not something that you need to write code to achieve -- it just happens, because ActiveRecord infers the name of the table from the name of the class. Note that the name of our class in Ruby is a singular noun (Story), but the name of the table is plural (stories).
This relationship makes sense if you think about it: when we refer to a Story object in Ruby, we're dealing with a single story. But the MySQL table holds a multitude of stories, so its name should be plural. While it's possible to override these conventions (as is sometimes necessary when dealing with legacy databases), it's much easier to adhere to them.
The close relationship between tables and objects extends even further: if our stories table were to have a link column, as our example in Figure 4.5 does, then the data in this column would automatically be mapped to the link attribute in a Story object. And adding a new column to a table would cause an attribute of the same name to become available in all of that table's corresponding objects.
So, let's create some tables to hold the stories we create.
For the time being, we'll create a table using the old-fashioned approach of entering SQL into the MySQL command line. You could type out the following SQL commands, although I acknowledge that typing out SQL isn't much fun. Instead, I'd encourage you to download the following script from the code archive, and copy and paste it straight into your MySQL console.
Example 4.3. 03-create-stories-table.sql
USE shovell_development;
CREATE TABLE `stories` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(255) default NULL,
`link` varchar(255) default NULL,
PRIMARY KEY (`id`)
);
You needn't worry about remembering these SQL commands to use in your own projects; instead, take heart in knowing that in Chapter 5, Models, Views, and Controllers, we'll look at something called migrations, which are special Ruby classes that we can write to create database tables for our application without using any SQL at all.
Using the Rails Console
Now that we have our stories table in place, let's exit the MySQL console and open up a Rails console. A Rails console is just like the interactive Ruby console (irb) that we used in Chapter 3, Introducing Ruby, but with one key difference: in a Rails console, you have access to all of the environment variables and classes that are available to your application while it is running. These are not available from within a standard irb console.
To enter a Rails console, change to your shovell folder, and enter the command ruby script/console, as shown below. The >> prompt is ready to accept your commands:
$ cd shovell
$ ruby script/console
Loading development environment.
>>
Saving an Object
To start using ActiveRecord, simply define a class that inherits from the ActiveRecord::Base class. (We touched on the :: operator very briefly in Chapter 3, Introducing Ruby, where we used it to refer to constants. It can also be used to refer to classes that exist within a module, which is what we're doing here.) Flip back to the section on object oriented programming (OOP) in Chapter 3, Introducing Ruby if you need a refresher on inheritance.
Consider the following code snippet:
class Story < class="Apple-converted-space">
end
These two lines of code define a seemingly empty class called Story. However, this class is far from empty, as we'll soon see.
From the Rails console, let's create this Story class, and an instance of the class called story, by entering these commands:
>> class Story < class="Apple-converted-space">
=> nil
>> story = Story.new
=> #
@new_record=true>
>> story.class
=> Story
As you can see, the syntax for creating a new ActiveRecord object is identical to the syntax we used to create other Ruby objects in Chapter 3, Introducing Ruby. At this point, we've created a new Story object. However, this object exists in memory only -- we haven't stored it in our database yet.
We can confirm the fact that it hasn't been saved yet by checking the value of the new_record attribute, using the object's accessor method:
>> story.new_record?
=> true
Because the object has not been saved yet, it will be lost when we exit the Rails console. To save it to the database, we need to invoke the object's save method:
>> story.save
=> true
Now that we've saved our object (a return value of true indicates that the save method was successful) our story is no longer a new record. It's even been assigned a unique ID, as shown below:
>> story.new_record?
=> false
>> story.id
=> 1
Defining Relationships Between Objects
As well as the basic functionality that we've just seen, ActiveRecord makes the process of defining relationships (or associations) between objects as easy as possible. Of course, it's possible with some database servers to define such relationships entirely within the database schema. However, in order to put ActiveRecord through its paces, let's look at the way it defines these relationships within Rails.
Object relationships can be defined in a variety of ways; the main difference between these relationships is the number of records that are specified in the relationship. The primary types of database associations are:
- one-to-one associations
- one-to-many associations
- many-to-many associations
Let's look at some examples of each of these associations. Feel free to type them into the Rails console if you like, for practice. Remember that your class definitions won't be saved, though -- I'll show you how to define associations in a file later.
Suppose our application has the following associations:
An Author can have one Weblog:
class Author < class="Apple-converted-space">
has_one :weblog
end
An Author can submit many Stories:
class Author < class="Apple-converted-space">
has_many :stories
end
A Story belongs to an Author:
class Story < class="Apple-converted-space">
belongs_to :author
end
A Story has, and belongs to, many different Topics:
class Story < class="Apple-converted-space">
has_and_belongs_to_many :topics
end
class Topic < class="Apple-converted-space">
has_and_belongs_to_many :stories
end
You're no doubt growing tired of typing class definitions into a console, only to have them disappear the moment you exit the console. For this reason, we won't go any further with the associations between our objects -- we'll delve into the ActiveRecord module in more detail in Chapter 5, Models, Views, and Controllers.
The ActionPack Module
ActionPack is the name of the library that contains the view and controller parts of the MVC architecture. Unlike the ActiveRecord module, these modules are a little more intuitively named: ActionController and ActionView.
Exploring application logic and presentation logic on the command line doesn't make a whole lot of sense (views and controllers are designed to interact with a web browser, after all!). Instead, I'll just give you a brief overview of the ActionPack components, and we'll cover the hands-on stuff in Chapter 5, Models, Views, and Controllers.
ActionController (the Controller)
The controller handles the application logic of your program, acting as a glue between the application's data, the presentation layer, and the web browser. In this role, a controller performs a number of tasks, including:
- deciding how to handle a particular request (for example, whether to render a full page or just one part of it)
- retrieving data from the model to be passed to the view
- gathering information from a browser request, and using it to create or update data in the model
When we introduced the MVC diagram in Figure 4.3 earlier in this chapter, it might not have occurred to you that a Rails application can consist of a number of different controllers. Well, it can! Each controller is responsible for a specific part of the application.
For our Shovell application, we'll create:
- one controller for displaying story links, which we'll name
StoryController - another controller for handling user authentication, called
AccountController
Both controllers will inherit from the ActionController::Base class, but they'll have different functionality, implemented as instance methods. (There will actually be an intermediate class between this class and the ActionController::Base class; we'll cover the creation of the StoryController class in more detail in Chapter 5, Models, Views, and Controllers. However, this doesn't change the fact that ActionController::Base is the base class from which every controller inherits. Here's a sample class definition for the StoryController class:
class StoryController < class="Apple-converted-space">
def index
end
def show
end
end
This simple class definition sets up our StoryController with two empty methods -- the index method, and the show method -- both of which we'll expand upon in later chapters.
Naming Classes and Files
You'll have noticed by now that the names of classes and files follow different conventions:
- Class names are written in CamelCase (each word beginning with a capital letter, with no spaces between words).
- Filenames are written in lowercase, with underscores separating each word.
This is important! If this convention is not followed, Rails will have a hard time locating your files. Luckily, you won't need to name your files manually very often, if ever, as you'll see when we look at generated code in Chapter 5, Models, Views, and Controllers.
Each controller resides in its own Ruby file (with a .rb extension), which lives within the app/controllers directory. The StoryController class that we just defined, for example, would live in the file app/controllers/story_controller.rb.
ActionView (the View)
As we discussed earlier, one of the principles of MVC is that a view should contain presentation logic only. This means that the code in a view should only perform actions that relate to displaying pages in the application -- none of the code in a view should perform any complicated application logic, nor should it store or retrieve any data from the database. In Rails, everything that is sent to the web browser is handled by a view.
Predictably, views are stored in the app/views folder of our application.
A view need not actually contain any Ruby code at all -- it may be that one of your views is a simple HTML file. However, it's more likely that your views will contain a combination of HTML and Ruby code, making the page more dynamic. The Ruby code is embedded in HTML using embedded Ruby (ERb) syntax.
ERb is similar to PHP or JSP, in that it allows server-side code to be scattered throughout an HTML file by wrapping that code in special tags. For example, in PHP you might do something like this:
The equivalent in ERb would be the following:
<%= 'Hello World from Ruby!' %>
There are two forms of the ERb tag pair: one that includes the equal sign, and one that does not:
<%= ... %>
This tag pair is for regular output. The output of a Ruby expression between these tags will be displayed in the browser.
<% ... %>
This tag pair is for code that is not intended to be displayed, such as calculations, loops, or variable assignments.
An example of each is shown below:
<%= 'This line is displayed in the browser' %>
<% 'This line executes silently, without displaying any output' %>
You can place any Ruby code -- be it simple or complex -- between these tags.
Creating an instance of a view is a little different to that of a model or a controller. While ActionView::Base (the parent class for all views) is one of the base classes for views in Rails, the instantiation of a view is handled completely by the ActionView module. The only thing a Rails developer needs to modify is the template, which is the file that contains the presentation code for the view. As you might have guessed, these templates are stored in the app/views folder.
As with most things in Rails, a strict convention applies to the naming and storage of template files:
- A template has a one-to-one mapping to the action (method) of a controller. The name of the template file matches the name of the action to which it maps.
- The folder that stores the template is named after the controller.
- The extension of the template file varies on the basis of the template's type. By default there are three types of template in Rails:
rhtml- This is the extension for standard HTML templates that are sprinkled with ERb tags.rxml- This extension is used for templates that output XML (for example, to generate RSS feeds for your application).rjs- This extension is used for templates that return JavaScript instructions. This type of template might be used, for example, to modify an existing page (via Ajax) to update the contents of atag.This convention may sound complicated, but it's actually quite intuitive. For example, consider the
StoryControllerclass that we defined earlier. Invoking the read method for this controller would, by default, attempt to display theActionViewtemplate that lived in theapp/views/storydirectory. Assuming the page was a standard HTML page (containing some ERb code), the name of this template would beread.rhtml.Rails also comes with special templates such as layouts and partials. Layouts are templates that control the global layout of an application, such as structures that remain unchanged between pages (the primary navigation menu, for instance). Partials are special subtemplates (the result of a template being split into separate files, such as a secondary navigation menu or a form) that can be used multiple times within the application. We'll cover both layouts and partials in Chapter 7, Ajax and Web 2.0.
Communication between controllers and views occurs via instance variables that are populated from within the controller's action. Let's expand upon our sample
StoryControllerclass to illustrate this point (there's no need to type any of this out just yet):class StoryController < class="Apple-converted-space">
def index
@variable = 'Value being passed to a view'
end
endAs you can see, the instance variable
@variableis being assigned a string value within the controller's action. Through the magic ofActionView, this variable can now be referenced directly from the corresponding view, as shown in the code below:The instance variable @variable contains: <%= @variable %>
This approach allows more complex computations to be performed outside the view (remember, it should only contain presentational logic), leaving the view to display just the end result of the computation.
Rails also provides access to special containers, such as the params and session hashes. These contain information including the current page request and the user's session. We'll make use of these hashes in the chapters that follow.
Code Generation
Rather than having us create all of our application code from scratch, Rails gives us the facility to generate an application's basic structure with considerable ease. In the same way that we created our application's entire directory structure, we can create new models, controllers, and views using a single command.
To generate code in Rails, we use the generate script, which lives in the script folder. Give it a try now: type ruby generate without any command line parameters. Rails displays an overview of the available parameters for the command, and lists the generators from which we can choose, as Figure 4.6 illustrates.
Figure 4.6. Sample output fromscript/generateRails can generate code of varying complexity. At its simplest, creating a new controller causes a template file to be placed in the appropriate subdirectory of your application. The template itself consists of a mainly empty class definition, similar to the
StoryandAuthorclasses that we looked at earlier in this chapter.However, code generation can also be a very powerful tool for automating complex, repetitive tasks; for instance, you might generate a foundation for handling user authentication. We'll launch straight into generating code in Chapter 5, Models, Views, and Controllers, when we begin generating our models and controllers.
Another example is the generation of a basic web-based interface to a model, referred to as scaffolding. We'll also look at scaffolding in Chapter 5, Models, Views, and Controllers, as we make a start on building our views.
ActionMailerWhile not strictly part of the Web, email is a big part of our online experience, and Rails's integrated support for email is worth a mention. Web applications frequently make use of email for tasks like sending sign-up confirmations to new users and resetting a user's password.
ActionMaileris the Rails component that makes it easy to incorporate the sending and receiving of email into your application.ActionMaileris structured in a similar way to ActionPack in that it consists of controllers and actions with templates.While the creation of emails, and the processing of incoming email, are complex tasks,
ActionMailerhides these complexities and handles the tasks for you. This means that creating an outgoing email is simply a matter of supplying the subject, body, and recipients of the email using templates and a little Ruby code. Likewise,ActionMailerprocesses incoming email for you, providing you with a Ruby object that encapsulates the entire message in a way that's easy to access.Adding email functionality to a web application is beyond the scope of this book, but you can read more about
ActionMaileron the Ruby on Rails wiki.Testing and Debugging
Testing
A number of different types of testing are supported by Rails, including automated and integration testing.
Automated Testing
The concept of automated testing isn't new to the world traditional software development, but it's fairly uncommon in web application development. While most Java-based web applications make use of comprehensive testing facilities, a large number of PHP and Perl web applications go live after only some manual tests have been performed (and sometimes without any testing at all!). Although performing automated tests may be an option, developers may decide not to use them for reasons ranging from the complexity of the task to time constraints.
We touched on this briefly in Chapter 1, Introducing Ruby on Rails, but it's worth stressing again: the fact that comprehensive automated testing is built into Rails, and is dead easy to implement, means there's no longer a question about whether or not you should test your apps: just do it!
The
generatecommand that we introduced a moment ago can automatically create testing templates that you can use with your controllers, views, and models. (Note that Rails just assists you in doing your job, it's not replacing you -- yet!)The extent to which you want to implement automated testing is up to you. It may suit your needs to wait until something breaks, then write a test that proves the problem exists. Once you've fixed the problem so that the test no longer fails, you'll never get a bug report for that particular problem again.
If, on the other hand, you'd like to embrace automated testing completely, you can write tests to ensure that a specific HTML tag exists at a precise position within a page's hierarchy. (The hierarchy referred to here is the Document Object Model (DOM), a W3C standard for describing the hierarchy of an (X)HTML page.) Yes, automated tests can be that precise.
Integration Testing
Rails's testing capabilities also include integration testing.
Integration testing refers to the testing of several web site components in succession -- typically, the order of the components resembles the path that a user would follow when using the application. You could, for example, construct an integration test that reconstructs the actions of a user clicking on a link, registering for a user account, confirming the registration email you send, and visiting a page that's restricted to registered users.
We'll look at both automated testing and integration testing in more detail in later chapters.
Debugging
When you're fixing problems, the first step is to identify the source of the problem. Like many languages, Rails assists this process by providing the developer (that's you!) with a full stack trace of the code. As we saw earlier, a stack trace is a list of all of the methods that were called up to the point at which an exception was raised. The list includes not only the name of each method, but also the classes to which those methods belong, and the names of the files in which they reside.
Using the information contained in the stack trace, you can go back to your code to determine the problem. There are a few different ways to approach this, depending on the nature of the problem itself:
- If you have a rough idea of what the problem might be, and are able to isolate it to your application's model (either a particular class or aspect of your data), your best bet is to use the Rails console that we looked at earlier in this chapter. Type console from the script directory to launch the console. Once inside, you can load the particular model that you're interested in, and poke at it to reproduce and fix the problem.
- If the problem leans more towards something related to the user's browser or session, you can add a breakpoint statement around the spot at which the problem occurs. With this in place, you can reload the browser and step through your application's code using the breakpointer command line tool to explore variable content or to execute Ruby statements manually.
We'll be covering all the gory details of debugging in Chapter 11, Debugging, Testing, and Benchmarking.
A GUI Tool for MySQL
The MySQL command line that we've been using in this chapter is one way to maintain your database structure and the data that it contains. But working in a command line client can definitely be overwhelming, complex, and tedious -- especially when you're just taking your first steps with databases and don't know your way around!
A GUI tool available for use with MySQL that's worth a mention is the MySQL Query Browser. Published by MySQL AB (the makers of MySQL), the MySQL Query Browser is a free, cross-platform tool that is currently available for Windows, Linux, and Mac OS X. The MySQL Query Browser is available for download fromhttp://dev.mysql.com/downloads/query-browser/ [7]. Unfortunately there is no binary install package for OS X 10.3 or earlier; CocoaMySQL is a good alternative.
Installing MySQL Query Browser is a straightforward process on most platforms:
- Windows - A binary installer exists for Windows -- launch the installer and select the default options for each step in the wizard.
- Mac OS X - A binary installer exists for the Mac as well. Mount the disk image and drag the application icon to your Applications folder.
- Linux - A package exists for most distributions of Linux; install the application using your distribution's package manager.
Figure 4.7. The MySQL Query Browser connection screenThe MySQL Query Browser can be used to perform queries against your database. You can also use it to alter your database structure by creating and modifying databases and tables. Figure 4.7 shows the connection screen.
The connection details to use are identical to the ones we used earlier, when we configured Rails to connect to our database in the config/database.yml file. Assuming that you haven't changed your MySQL configuration since then, enter localhost into the Hostname field and root for the Username. Now hit Connect.
Once you're connected, you should be greeted by a window similar to the one shown in Figure 4.8.
Figure 4.8. MySQL Query Browser's Query windowAt the top of the main window is a text field in which you can type database queries. The pull-out tab on the right-hand side lists the databases to which you can connect. There's even a function and syntax reference.
The MySQL Query Browser displays the table schema of the stories table exactly as we've created it, with the added benefit that we can alter it with just a mouse click. By clicking the + button at the bottom of the column list, you can add a column; clicking the - button removes the currently selected column. Pretty simple, isn't it?
Exploring every feature of the query browser is definitely beyond the scope of this book, but there is one more thing I'd like to show you: the Table Editor, which is shown in Figure 4.9.
To launch the Table Editor, first expand the shovell_development database by clicking on the triangle next to the database's name. This will list all of the tables in the database. Currently, there should only be one: the
storiestable that we created earlier. Now right-click (Control-click on a Mac) on the table that you want to edit, and select Edit Table from the menu that appears.
Figure 4.9. The MySQL Table EditorThe Table Editor allows you to edit existing tables and add new tables, all using a nice GUI interface. I'm sure you'll agree this is a much friendlier experience than having to battle with the cryptic command line tool.
That's it for the MySQL Query Browser -- we'll revisit it briefly in Chapter 5, Models, Views, and Controllers, but feel free to close the application now.
Summary
In this chapter, we peeled back some of the layers that comprise the Ruby on Rails framework. By now you should have a good understanding of which parts of Rails perform particular roles in the context of an MVC architecture. It should also be reasonably clear how a request that's made by a web browser is processed by a Rails application.
We looked at the different environments that Rails provides to address the different stages in the lifecycle of an application, and we created databases to support these environments. We also provided Rails with the necessary details to connect to our database.
We also had our first contact with real code, as we looked at the ActiveRecord models, ActionController controllers, and ActionView templates for our clone of digg.com[8], Shovell. We explored the topics of code generation, testing, and debugging, and we took a brief look at a GUI client that makes interacting with our MySQL database more convenient.
In the next chapter, we'll build on all of this knowledge as we use the code generation tools to create actual models, controllers, and views for our Shovell application. It's going to be a big one!
That's it for this excerpt of Build Your Own Ruby On Rails Web Applications [9] -- but don't forget that the downloadable PDF contains three more chapters [10] than are included here. See the complete Table of Contents [11] for the full details of what's covered in this book.
[1] http://www.ruby-doc.org/stdlib/
[2] http://www.sitepoint.com/books/rails1/
[3] http://www.sitepoint.com/popup/popup.php?zone=2&popupid=92
[4] http://en.wikipedia.org/wiki/Knight_rider/
[5] http://www.sitepoint.com/article/javascript-library/
[6] http://dev.mysql.com/doc/refman/5.0/en/post-installation.html
[7] http://dev.mysql.com/downloads/query-browser/
[8] http://digg.com/
[9] http://www.sitepoint.com/books/rails1/
[10] http://www.sitepoint.com/popup/popup.php?zone=2&popupid=92
[11] http://www.sitepoint.com/books/rails1/toc.php
No comments:
Post a Comment