This post is about the Model-View-Controller design pattern that supports the entire Rails framework. It is made for the beginners, for those curious people that went through one or two basic Rails workshops, but never learned how the parts of the framework communicate to each other. Usually on RailsGirls events you have an opportunity to create your own application but depending on the coach and your interest, you may not get it fully. Keep on reading, this might help you to understand better where is your controller action, model or the view that is rendered …huh? Let’s start!
Design patterns in software engineering are highly recommended snippets of code that are based on the good practices, this is a simplified definition and for now you don’t need to know more. MVC, or Model-View-Controller design pattern is just one of them. This one is so awesome because it allows us to separate the logic of the domain, presentation and the flow, or its cases, by putting them into 3 places. You might find this as complicated to understand but let’s see how does it work on the example.
Assuming you used the tutorial for building your first Rails application available on the official pages of the RailsGirls you have the directory structure shown on the image below.
Everything that belongs to the controller layer is placed in the controllers folder. Besides the initial one,
application_controller.rb, we have our
ideas_controller.rb. Inside this file you will notice the basic CRUD methods: we have index, show, new, edit, create, update and destroy. Those actions allow us to have a different flow of actions depending on what the user wants. The good practice is to keep those and reuse them as you need. However, sometimes you might need to expand it with your own methods which is also possible. We will see an example later on.
Another layer contain models, so we have the models folder with one model named
ideas.rb. This file is responsible for the logic of the domain, or at least it is a good practice to use it having this in mind.
The last one, the one that is the most important for the user is of course the views layer located in
views/ideas folder. Generated folder contains index, edit, new, show and form files. Notice that the last one,
_form.html.erb starts with an underscore, this means that this file is a partial. It is a part of the view that can be reused in different main views. Index, new, edit and show are on the other hand the main views.
Ok. But what does that mean for you? Well, next step is to understand how the things work. Let me try to simplify it. Whenever user types an url, that url leads to something. How to find that something? Well, let’s first find our routes. On the Terminal (or Command Prompt for Windows users) type:
The output might be something like:
How to read this? You have Prefix, Verb, URI pattern and Controller#Action.
Prefix is the way the specific url is named in the application. If you actually use in the code
ideas_url you will get the full path or url as it is shown in the URI pattern for the first line:
/ideas(.:format). Format specifies the request that is done, for now, remember it is html what the browser wants. As you continue, you will find out there are more formats such as js, json, xml… Another thing to notice is the part that starts with semi column “:id”, this means that the url will have an id of the record.
More, whenever a browser calls for the
http://localhost:3000/ideas (assuming you are in the development mode), application will call for the ideas#index, or if we read it: ideas_controller # index action. The column that remained is the Verb. I won’t go in deep explaining what verbs are, you can find more information about it on Open Sourcery.
Imagine controller is a big boss that knows how the things should work. It manages the rest but does not really care how. Controller action that was called specifies how the request should be handled. The big boss, controller, has two great friends that are helping him out, model and the view. However, model and view don’t really like each other because of different interest so controller has to mediate between them. Also, controller does not bother himself talking to the database, nor database wants that. In order to get some data from the database and show it to the user, controller action first calls the model. Here is the index action:
# GET /ideas # GET /ideas.json def index @ideas = Idea.all end
The line with the
Idea.all is the call to the model, it retrieves all the records for Idea. So retrieving was delegated to the model and model and database with the help of ActiveRecord came up with some records and those are returned to the controller. You might notice that there is no definition for the method all, that is correct, that part is the ActiveRecord thing. He knows more about that.
Further, controller is so nice and caring to store those in the object variable, that variable that starts with @, which makes it available for the view. So controller action calls the view named the same as itself –
index.html.erb and provides the view with the collection of ideas that were retrieved from the model.
The responsibility of the view then is to show those records to our user. In order to improve the view with new labels, paragraphs or other contents, in this case we should change the
Next time, when you wonder what action is called, first check the rake routes in order to find the controller action, then see how is it defined and the good practice is to named the view with the same name. If you respect this convention (based on the MVC design pattern) you can make your life easier. As I already mentioned, sometimes you do need slightly different action and in that case you can extend your controller with something new. Still, be careful not to increase the complexity of the controller with no reason.
Imagine we want to make a random generator page for our ideas. We are bored and we want a great idea, that is the purpose, how to approach this problem? (Notice, there are many approaches and here will be presented one that follows the process above)
First, we can make a url that goes to what we want. We have to add another route to the application and we will do that by adding a route to the
resources :ideas do get :random_generator, on: :collection end
We specified the /ideas/random_generator route, on collection means that no id will be sent and the url will start with ideas/… Now, if we run
rake routes we get the following:
We have our url, that goes to the ideas controller # random_generator action, so let’s create one, add the following to
def random_generator end
This method itself only requires the
random_generator.html.erb view so let’s create one. Inside
app/views/index create and save a new file with the name:
random_generator.html.erb. If we really want a random idea form the database we have to make controller actually talk to the model so we add call to the random_generator action and now it looks like:
def random_generator @idea = Idea.get_one_random end
This time, ActiveRecord has no idea what is
get_one_random so we have to specify it. Go to the
app/models/idea.rb and add definition:
def self.get_one_random r = Random.new total_count = self.count find(:first, offset: r.rand(total_count)) if total_count > 0 end
This method allows us the retrieve the idea from the specific position based on the generated number if there are some records. Notice that we call
self., this means we specified it is a class method. The method should be called like
The record that is returned to the view is stored to
@idea. Now controller can send this to the view and view can show it however he likes, for example, knowing that our Idea has the following attributes: name, description and picture we can have the following code in the
<h1> Random generator!!</h1> <p> The super awesome random idea you wanted to see is: <p> <p> <strong>Name:</strong> <%= @idea.name %> </p> <p> <strong>Description:</strong> <%= @idea.description %> </p> <p>You better use it wisely!!</p> <%= link_to 'Edit', edit_idea_path(@idea) %> | <%= link_to 'Back', ideas_path %>
And here it is! A working example for our application. We successful extended the basic behavior respecting the MVC design pattern. Now challenge yourself and create your example! Try to list 10 random records, or list the last 20, or come up with something more original! Good luck!