When you're writing your own programs from beginning to end, it's easy to see flow control. The program starts here, there's a loop there, method calls are here, it's all visible. But in a Rails application, things are not so simple. With a framework of any kind, you relinquish control of such things as "flow" in favor of a faster or simpler way to do complex tasks. In the case of Ruby on Rails, the flow control is all handled behind the scenes, and all you're left with is (more or less) a collection of models, view and controllers.
At the core of any web application is HTTP. HTTP is the network protocol your web browser uses to talk to a web server. This is where terms like "request," "GET" and "POST" come from, they're the basic vocabulary of this protocol. However, since Rails is an abstraction of this, we won't spend much time talking about it.
When you open a web page, click on a link or submit a form in a web browser, the browser will connect to a web server via TCP/IP. The browser then sends the server a "request," think of it like a mail-in form that the browser fills out asking for information on a certain page. The server ultimately sends the web browser a "response." Ruby on Rails is not the web server though, the web server can be anything from Webrick (what usually happens when you start a Rails server from the command line) to Apache HTTPD (the web server that powers most of the web). The web server is just a facilitator, it takes the request and hands it to your Rails application, which generates the response and passes is back to the server, which in turn sends it back to the client. So the flow so far is:
Client -> Server -> [Rails] -> Server -> Client
But "Rails" is what we're really interested in, let's dig deeper there.
One of the first thing a Rails application does with a request is to send it through the router. Every request has a URL, this is what appears in the address bar of a web browser. The router is what determines what is to be done with that URL, if the URL makes sense and if the URL contains any parameters. The router is configured in config/routes.rb.
First, know that the ultimate goal of the router is to match a URL with a controller and action (more on these later). And since most Rails applications are RESTful, and things in RESTful applications are represented using resources, you'll see lines like resources :posts in typical Rails applications. This matches URLs like /posts/7/edit with the Posts controller, the edit action on the Post with the ID of 7. The router just decides where requests go. So our [Rails] block can be expanded a bit.
Router -> [Rails]
Now that the router has decided which controller to send the request to, and to which action on that controller, it sends it on. A Controller is a group of related actions all bundled together in a class. For instance, in a blog, all of the code to view, create, update and delete blog posts is bundled together in a controller called "Post." The actions are just normal methods of this class. Controllers are located in app/controllers.
So let's say the web browser sent a request for /posts/42. The router decides this refers to the Post controller, the show method and the ID of the post to show is 42, so it calls the show method with this parameter. The show method is not responsible for using the model to retrieve the data and using the view to create the output. So our expanded [Rails] block is now:
Router -> Controller#action
The model is both the simplest to understand and most difficult to implement. The Model is responsible for interacting with the database. The simplest way to explain it is the model is a simple set of method calls that return plain Ruby objects that handle all interactions (reads and writes) from the database. So following the blog example, the API the controller will use to retrieve data using the model will look something like Post.find(params[:id]). The params is what the router parsed from the URL, Post is the model. This makes SQL queries, or does whatever is needed to retrieve the blog post. Models are located in app/models.
It's important to note that not all actions need to use a model. Interacting with the model is only required when data needs to be loaded from the database or saved to the database. As such, we'll put a question mark after it in our little flowchart.
Router -> Controller#action -> Model?
Finally, it's time to start generating some HTML. HTML is not handled by the controller itself, nor is it handled by the model. The point of using an MVC framework is to compartmentalize everything. Database operations stay in the mode, HTML generation stays in the view, and the controller (called by the router) calls them both.
HTML is normally generated using embedded Ruby. If you're familiar with PHP, that is to say an HTML file with PHP code embedded in it, then embedded Ruby will be very familiar. These views are located in app/views, and a controller will call one of them to generate the output and send it back to the web server. Any data retrieved by the controller using the model will generally be stored in an instance variable which, thanks to some Ruby magic, will be available as instance variables from within the view. Also, embedded Ruby doesn't need to generate HTML, it can generate any type of text. You'll see this when generating XML for RSS, JSON, etc.
This output is sent back to the web server, which sends it back to the web browser, which completes the process.
The Complete Picture
And that's it, here is the complete life of a request to a Ruby on Rails web application.
- Web Browser - The browser makes the request, usually on behalf of the user when they click on a link.
- Web Server - The web server takes the request and sends it to the Rails application.
- Router - The router, the first part of the Rails application that sees the request, parses the request and determines which controller/action pair it should call.
- Controller - The controller is called. The controller's job is to retrieve data using the model and send it to a view.
- Model - If any data needs to be retrieved, the model is used to get data from the database.
- View - The data is sent to a view, where HTML output is generated.
- Web Server - The generated HTML is sent back to the server, Rails is now finished with the request.
- Web Browser - The server sends the data back to the web browser, and the results are displayed.