This Cyber Monday Tuts+ courses will be reduced to just $3 (usually $15). Don't miss out.
Welcome to Ruby on Rails From Scratch Week 3! This week, we'll focus on specific things that you need to know about the framework. For example, we're going to talk about manipulating the structure of the project in two ways (rendering & redirecting). We'll also talk about how you add links in rails, and how to comment code. Let's get started already!
If you haven't read Parts 1 and 2, it is strongly advised that you review them before continuing on with this tutorial.
Adding Interactions Amongst Actions
Now that we've learned how to set instance variables to transfer information from the controller to the view, it is time to learn how to transmit data between different actions in a controller. For this tutorial, we're going to continue using the learn controller. We don't really need to keep what we did last week, but you can if you feel like it is a good resource.. We'll start off simple:
Perform Another Action in a different Action
For this example, the heading is more complex than the actual lesson. We are going to simply use what's in one action and do the same in another. Lets use the knowledge we gained last week about instance variables, and apply it now. First, let's create a new action called "another_action"; then another action called "text". We are going to then get the action from text and put it in "another_action". We are going to prove this, by referencing an instance variable in the view of "another_action" that was assigned in the "text" controller. Sorry, if that didn't make sense. If you didn't get what I just said, look at the following code, which will hopefully help you:
class LearnController < ApplicationController def another_action text end def text @text = 'This text came from another action' end end
We are then going to create a view called "another_action.html.erb", where we reference the variable text.
<html> <head> <title>Action Demo</title> </head> <body> <%= @text %> </body> </html>
As you can see, we referenced the action "text" simply by putting that inside of "another_action". By doing this, we also passed the definition of "text", which was the instance variable. To further demonstrate this principle, let's put this in several different situations, all of which work.
Situation 1- Setting An Instance Variable Equal to an Action
class LearnController < ApplicationController def another_action @text = text end def text misc_text = 'This text really traveled!' end end
Situation 1 Result
Situation 2 - Replace Variable
Now we're going to get even more complex:
<html> class LearnController < ApplicationController def another_action @text = "text you'll never see" @text = text end def text wanted_text = overwrite_view end def overwrite_view overwrite_text = 'This text overwrote the original text' end end
Situation 2 Result
This is the order of events that the following code did:
- Gave the instance variable "text" a value of "text you'll never see"
- Then it re-assigned it to the action "text"
- The variable "text", now has the value of the local variable wanted text.
- The wanted_text variable has a value of the action "overwrite_view"
- Overwrite_view has a local variable with a value of "This text overwrote the original text"
- This carries back to the instance variable of @text.
Situation 3 Carry a Variable
We'll keep this one simpler:
class LearnController < ApplicationController def another_action @text = "text you'll never see" text end def text @text = 'This text overwrote the original text' end end
Situation 3 Result
Situation 4 Returning Something Specific
class LearnController < ApplicationController def another_action @text = text end def text first = 'This text should not be passed to @text' second = 'This text would be put into the variable on default' return first end end
This example is particularly important, as you are now learning a little bit more syntax; the return command. This will tell the action what to return at the end.
Situation 4 Result
Everyone should already know how to create a normal HTML link. (Using <a href="">Text</a>) But there's also a way to do it with Rails. This method is not Ruby, but rather a part of the framework. That being said, you can only use it in embedded ruby HTML files, not pure rb files. So how do we do it? Here is the code:
<html> <head> <title>Rails Link Demo</title> </head> <body> <%= link_to('Another Action Link', :controller => 'learn', :action => 'another_action')%> </body> </html>
At first glance, you might wonder why you would ever change the way you write links, especially if the replacement is longer! Well one main reason is readability. You can easily read this and know exactly what it's going to link to. As for length, the :controller => part is not necessary if you're linking to an action within the same controller.
With this piece of rails code we are also seeing symbols. The ":controller =>" is a symbol. We'll talk more about what they do later, but for right now just recognize the colon.
Rendering in Rails
Earlier in this series, we talked a little bit about rails' sensible defaults. One of the defaults is to generate a view that has the same name as the action in the corresponding view. But what if we wanted to overwrite what rails automatically does? This is actually quite easy, and a very useful technique. When you're working on a real world project, you can imagine how 'clunky' your controller would get if you needed to create a separate view and action in the controller for each page. Rails has a solution though, which is called rendering. Rendering allows you to specify what view to use for each action. Keep in mind though, that like rails links, this is also rails exclusive code and not ruby, and meant only for the controller.
If the above has left you a little confused, let me show you how to do it. This will hopefully clarify it a little bit. For this first example, we're going to render the action to the view that it would normally render anyway. In other terms, this render code does nothing that wouldn't happen without it. All it is doing is showing some code that usually is just 'assumed'.
def index render :action => 'index' end
Rendering an Action to another View
The last example isn't really useful since the result is achieved automatically anyway. But what if you had a really advanced view for another action, and you wanted the same view for a different action. It would not only be tedious to create another identical view for each action you create, but it would also be nearly impossible to update! All of that can be fixed though by rendering every similar action to a specific action. Examine the following piece of code which I will explain afterwards, and notice the different parts.
class LearnController < ApplicationController def index render :action => 'text' end def text @text = '<u>Text</u>' end def other @text = '<b>Other</b>' render :action => 'text' end end
<html> <head> <title>Rails Rendering Demo</title> </head></p> <body> <p>Welcome to the <%= @text %> Page</p> </p> </body> </html></p>
Explanation of Code:
You might have noticed that I included an action called "index". This action will be viewed if you go up a directory to "learn". It works like an ordinary index file would. You don't have to create a specific action for index though. You can use the previously covered topic of inheriting another action as well.
Rendering to the same view
For this example, we only needed one view for all of the actions; and they all produced different pages. To me, this is pretty incredible for the amount of work we put in.
Note: This might seem obvious to some, but beware of double rendering. Double rendering will cause an error because you're trying to render an action to two different places. Can you be in two places at once? No, and neither can an action. If you want to see what it looks like, just render an action to two different views. The reason it happens as you're doing more complex projects is because the trail of actions can get pretty complex, and you might accidentally create this error.
Make sure you get the basic concept of rendering, as it will be a very vital technique that you will use often as a rails developer.
Redirecting An Action
Redirecting an Action is similar to rendering an action in the fact that it too is rails code and only used in the controller. Also, it effects the flow of the application. As you might guess from the name, redirecting allows you to... well redirect.
You might be surprised by how many times you use the redirect command. Here are some common uses:
- Creating alias pages
- Performing an action in the controller and then redirecting to a secondary page
- If there is an error, redirect to an error page
- If user is not logged in, send them to a login page
Enough chit chat, let's get to the code! For this code example, we're just going to modify the last example's controller and leave the view the same. As you'll see, redirecting is very similar in format to rendering:
class LearnController < ApplicationController def index redirect_to :action => 'text' end def text @text = '<u>Text</u>' redirect_to :action => 'other' end def other render :action => 'text' end end
Now go to the index action in your browser. You'll notice that you end up at the other action in your address bar. Let's run through this code:
- First, we redirect to the action text. Like linking in rails, you can specify a controller too if you're redirecting to another controller. You would just put :controller => 'learn', before the action.
- The browser now goes to the text action. Here it sets a value for @text. But the browser has no value for @text. This is because redirecting essentially forgets anything you tell it in that action.
- We are now redirected to other. Here we render out to the text view.
Render or Redirect?
While there are some obvious scenarios where you would chose either to render or to redirect; there can be some harder decisions. One important key though is that if you're submitting data into a database or anything that is changing the application, use a redirect. Otherwise, a refresh could resubmit the data.
Hope you guys learned a lot from this week's tutorial! We covered a lot of important concepts so if you are still confused by anything, re-read it. Next week we'll probably get into ruby syntax!
As always, feel free to leave questions in the comments. Also, please digg this article if it helped you!