In the last episode we got a Rails sample application with a model up and running and played a little bit around with it in the Rails console a.k.a. IRB.
Before we're digging into authentication I'll show you how do really make websites with Rails. To do that we have to encounter the missing two parts of the MVC pattern: Views and Controller.
Controllers
Controllers are the bread and butter of your web application. They shouldn't be very heavy weight, meaning much logic or code in it, but they are the glue between your data (models) and the views (a.k.a. templates).
Lets carry on and create a controller that manages our car model. It should be able to display the available cars, add new, edit existing ones and delete cars. This operations are often called CRUD operations (Create Read Update Delete) and can be found in many places all over the web development sphere. The HTTP verbs are basically representing CRUD operations (POST, GET, PUT, DELETE), CRUD is the fundament of SQL (INSERT, SELECT, UPDATE, DELETE) and so on and so on. Rails offers an easy way to create all this with one command and allows you to customize the generated stuff afterwards. This might be very handy but due the limited extent of this series I'll show you how to do it manually and leave it to you, to discover Rail's scaffolding (that is how it's called) mechanisms on your own.
|
Scaffolding |
|
Scaffolding is a cool mechanism to get a good impression of an idea, very quick. But it was misused so many times since it made it's way into Rails! Using scaffolds isn't an option for any "real" website. It's just about testing things fast. But the way scaffolds are handled in Rails was cleaned up a lot with the Rails 2.0 release and today scaffolds are a good starting point to explore Rails. I forego to use them as I would like to explain Rails' components to you step by step. Scaffolding would kill this approach as it generates a fully functional CRUD mechanism for a model at once. If you're interested in Rails' scaffolding mechanism anyway you can find a short introduction to scaffolds here and very bulked one here. Both of them cover things that you already know, like creating a Rails project and so on. |
Now lets just generate the controller:
script/generate controller cars
You can see that it's generating a couple of files for you. The one we should take a look at is:
/app/controllers/cars_controller.rb
This is the main controller file. Open it. It should look very empty like this.
You can also find a folder named
/app/views/cars
this is the folder where all the views that we need for the car controller belong into. Let's start with a very simple test. Create a new file within this folder, name it index.html.erb. It should look like this: /app/views/cars/index.html.erb. You might wonder about the strange file extension .html.erb but it's very straight forward: Since Rails 2.0 the first part (in this case .html) denotes the file type we would like create with this template and the second part (in this case .erb) means the templating engine (like Smarty in PHP) we use. In our example we would like to create a HTML file with the erb (embedded Ruby) templating engine. This templating language is quite easy: Write normal HTML or XHTML and spice it up with some Ruby. But for our first test we don't need anything else than pure HTML.Fill the created file with some, like I did here.
Now start the server with a
script/server start
at the console. When it's running head with your browser to
http://localhost:3000/cars/
And TADA! You should see your first rendered template. Awesome!
But why is that working if our controller has still no logic in it at all?! The answer lays in Rails' conventions. And at this point we hit two of them at the same time.
1. A controller consists of many so called actions. In our example we will have a list action to display the cars, a delete action to delete cars, an edit action to edit them and so on. The URL to those actions will be http://localhost:3000/cars/delete/123 or http://localhost:3000/cars/edit/123 where 123 is some ID of a car. So where is the convention? Look at the address bar of your browser. We just hit .../cars/ no action specified! And here is the convention: if you call a controller and doesn't specify an action, an action named index is called.
2. You might say: And where is that action defined? We will see how to get some logic into the action. But right now there actually is no action called index! But the next convention safes us: If you call the action X of a controller Y Rails will look into the Y_controller.rb first if it can find the action X in it. If it can't like in our case it just looks into /app/views/Y/ for a file called X.format.engine. In our case X is "index" and Y is "cars". Our format is HTML and the rendering engine is erb. So this is: /app/views/cars/index.html.erb YEAHA! So now you now how Rails found your template :)
Let's carry on.
What we want to do now is to create new cars. Therefore we need two things: A controller which handles the creating and a view which shows us the right HTML page to create a car. Lets begin with the view.
Have a look at this. The view mainly consists of pure HTML with a few lines that look unfamiliar. This is where the magic happens. Let me explain:
Line 3: <% form_for :car do |f| %>
Anything in between the <% and the matching %> is normal Rails code. So what we do here is calling a method in Rails which is named "form_for" this method is a so called: helper. Helpers are little methods that make our day a lot easier in views. You can write your own helpers but that's a different story! form_for is a built-in helper of Rails, maybe it's the most important one at all. What form for does is opening some kind of context in which we can built a form to submit data to our Rails application. In this case we're creating a form for the Car model that we've created earlier and we decide that the context of that form is called just f .
Line 5,6 or 7: <li><label for="brand_name">Brand:label>
<%= f.text_field :brand_name %>li>
This sample comes from line 5 but the other two lines look and work quite similar. First of all we have a normal HTML list element. In this li Tag we create a label for a form element. Just more standard HTML. But now we're running into even more funny brackets: <%= They are looking quite similar to to the <% brackets but the additional = has a significant impact: While anything between <% and %> is Ruby code that gets evaluated when the view is shown, anything between <%= and %> get evaluated as the view is shown, too but in addition to this the result of the evaluation will be inserted in the view at the place where the <%= ... %> tag is. So in this case we call the method text_field on the form context we've created in line 3. It should not be a very big surprise that this method created an input type="text" HTML field. And it does this for a specific field of the Car model. In line 5 it's all about the brand_name field of a car. And here you can see another cool thing: In the label tag at the beginning of the line I wrote:
as you can see the generated input box is automatically named in a reasonable manner.
Line 9: <%= submit_tag 'Create Car' %>
This line calls the submit_tag helper which creates a shiny input type="submit" button on our form. Be careful! This helper is not called at our form context f but as a stand alone helper! So unlike the text_field or check_box helpers from line 5,6 or 7 this is NOT f.submit_tag.
The parameter of this helper is the label on the submit button.
Line 10: <% end %>
So the context f, we've opened in line 3, must be closed before this view ends! This line does exactly this and nothing more.
Ok, that's the view. Simple. Put the file into your app as app/views/cars/new.html.erb.
If your script/server is still running, good! If not, just head over to your console, go to the root dir of your application and start script/server.
Now have a look at http://localhost:3000/cars/new
Wow! Our form! A little ugly but CSS to the rescue this is a defect we can easily fix. All we need is a simple style sheet like this one. We place it in our application into the public/stylesheets folder and name it application.css.
Now we need to link this style sheet into the rendered HTML page of our view. Hm, in a normal PHP or whatever web page it's very likely that you have some kind of header where you can link in style sheets or javascripts or anything like that. Rails knows a concept called: layout for this purpose.
A layout is a normal ERB view but it's like the outer shell of your application. In normal use cases the layout consists of a header, a footer and a special call that inserts the content, rendered by views like our new.htm.erb, in the middle of the header and the footer. The cool thing about layouts is: They can be an application wider default so that you don't need to worry about your header etc on any page you add to your application.
So let's create a layout that links our application.css into the page. For that reason create a file named app/views/layouts/application.html.erb and copy and paste this code to it.
Let's have a look at it:
Line 6: <%= stylesheet_link_tag "application" %>
This is the link in of our style sheet. The stylesheet_link_tag helper just takes the name of a css file in public/stylesheets and then creates a nice link to it. As you can see it's not even necessary to add .css to the name, the stylesheet_link_tag determines this by itself. Actually this helper is capable of a lot more! Merging multiple css files into one and cache them for example. But this is an advanced topic. This beginners guide will not cover it but if you're interested anyhow, have a look at the API.
Line 9: <%= yield %>
To explain what this technically means would go far beyond the intention of this tutorial as a beginners guide to Rails! But all you need to know now is, that yield is tha magical, little key word of Ruby that inserts your view's content into a layout! So this is the place where our new.html.erb's rendering is inserted.
Ok, so refresh the http://localhost:3000/cars/new page in your browser and the input boxes of the form should now be aligned under each other. Feel free to modify the view or the css or the layout to make this page fit your design desires :)
It seems that we've our first view in place, so let's create the controller to really be able to create a new car!
Copy this to you app/controllers/cars_controller.rb.
And now lets examine the code:
Line 2: def new
In the technically sense this is the start of a method called new in the class CarsController. In Rails any (public) method in a controller class is a so called action. An action most of the time consists of a controller method and a corresponding view. Our view is already done, and this is the beginning of the controller part of the action.
Line 3: return unless request.post?
This is an excellent demonstration of cool Ruby syntax! Instead of a little bit longish if clause like this:
if (request.post?) {
return
}
like you would have to write it in PHP e.g. you can write such short if clauses in one line! So instead of:
if (condition) {
do_action
}
you can write:
do_action if condition
But why is the order inverted? do_action first and then the condition?! Just read the line out loud and you will get it! It's more like "normal" english which means is more expressive and that's good ;)
But on line 3 there is no if. Yeah! But there is an unless! This is another cool Ruby idea.
Instead of:
do_action if !condition
we can do:
do_action unless condition
Why you should do so? Same reason as above: It's more expressive and easier to read.
So what does line 3 do? It returns form the method if the request was not send with the HTTP method POST. In other words: If someone just clicked on a link or entered cars/new in the address bar of his browser the request will be send with the HTTP request method GET and our controller action will exit at line 3! This means: The view (new.html.erb) gets rendered and you will see the normal form. Only if you have submitted the form, which means you send the request with POST as the HTTP request method, our controller will do any real stuff.
And don't get irritated by the ? at the end of post? ! It does not mean anything. It's just a convention in the Ruby world, that a method that returns a boolean value end in a ?. I think this is really expressive, too!
Line 4: return(redirect_to(:action => 'index')) if Car.create(params[:car])
Ok, this is where anything happens. Just one line! Really Railsish :) I will try to disentangle it.
First: Car.create(params[:car])
Anything that comes to our application in form of a HTTP parameter is accessible from the controller with the params method. You can think of this as a associative array of key/value pairs. In our case the form was created for the model Car and therefore it's possible to access only the information of params that is relevant to the new car we would like to create by params[:car]. Ok so Car.create(params[:car]) is a very cool stuff of Rails code! It creates a new car and takes anything we filled in in our form as an argument. This means: With this 24 characters we've created a new thing in the database from a form input! Try this with PHP but don't start to cry ;)
But the line isn't over: if Car.create(params[:car])
the Car.create method returns true if anything is cool and the new car could be inserted into the DB and false an error has occurred. So this is what we catch with the if clause.
The last part: return(redirect_to(:action => 'index'))
This is a little tricky. redirect_to does what it says ;) It redirects the user to a certain location. In our case this location is the index action of the cars controller (we only specify the action explicitly, but Rails assumes that the location stays the same beside the changes we specify. So in this case we're in the new action of the Cars controller and have specified the index action explicitly. This means we mean the index action of the Cars controller). So the user will not see the new page anymore but the index page of the cars controller. At the moment this is still a dummy page, but we will create a list of cars there in the next article. So it's a good idea to redirect there. BUT remember the if clause :) We do not redirect there in any case! Only if the Car.create statement returns true which means that everything went well.
The return statement around the redirect_to is necessary, because redirect_to alone would not abort the execution of the action! But it will raise serious erros if we redirect but not abort the action. As Rails would complain if it encounters more than one redirect_to call or a redirect_to and a render call etc. To avoid this, we just return from the action. That we return the return value of redirect_to is totally irrelevant! You can write this:
if Car.create(params[:car])
redirect_to(:action => 'index')
return
end
as well, it would have the same effect but is 3 rows too long ;)
Line 5: render :text => "Error during car creation!"
If we hit this line the return value of Car.create from line 4 was false and we've not returned from the action. Which means: An error occured! To present this to the user we take a very simple way: Instead of rendering the new.html.erb view we're rendering just a text! So the user would only see this text, instead of any real view. I admit that this isn't a very shiny solution but you will learn how to improve this, in the next episodes.
Right now our application should be capable of adding cars to the database! Try it!
Go to http://localhost:3000/cars/new
Enter some data and hit "Create Car". You should be redirected to the cars dummy index page. Now go to your console, head to the application's root dir and start script/console.
Now type:
Car.find(:all)
This gives you a list of all cars in your database in a little bit cryptic but somehow readable format. You should see the car you've just created on the webpage!
Great! So now you know how to write views and controllers, have learned a little about layouts and dug just a little more into the cool Ruby syntax.
In the next episode I'll show you how to show the list of all cars and how to edit existing cars. And with this step we're getting closer and closer to the start of our first game in Rails. Yeah!
So stay tuned. I promise that the next episode will not take as long, as this episode took! As always: Feel free to leave suggestions, questions or critic in the comments.
Yours,
Thorben












