Skip to content

Latest commit

 

History

History
35 lines (18 loc) · 7.4 KB

Lesson-8-Some-Rails-Concepts.md

File metadata and controls

35 lines (18 loc) · 7.4 KB

It can be useful to understand how Rails works under the covers. This part of the lesson explains a few things.

The Flow for a Web Request

The Rails framework is built with an approach called Model/View/Controller (MVC).

The model describes the data objects that are being managed, and may provide additional methods to communicate with those objects. When you create the model, you also create the database schema for objects of that type. In Rails, you communicate with the database using SQL, but the SQL is actually invoked using a wrapper called Active Record. Active Record is an ORM (object relational mapper), so you can do SQL operations using object oriented programming.

The views are how the application information is shown to the user. We are using a templating language called ERB for our views. This allows the views to be populated dynamically on the Rails side before they are sent to the user's browser. The dynamic population has access to server side data, so it can show the current state of the objects in the database, as well as providing other state information, such as whether a user is logged in.

The controller fields the request from the user and performs the tasks needed to satisfy the request. This often involves communicating with the model to get database objects, and it always involves sending data back to the user, often by rendering a view.

The Rails server is continually listening for web requests. When one arrives, the request may require that database access or other somewhat time consuming operations are done. But the server has to remain responsive to other requests. Ruby is multithreaded, so the request is handled on a worker thread, while the server continues to listen for more requests on the main thread. The server has a pool of threads, and one is dispatched with the information from the request.

With each request, the browser sends a verb (GET, PUT, PATCH, POST, or DELETE) to a URL, along with some other information such as headers, cookies, and, for PUT, PATCH, and POST, a request body. The URL and the verb determine the route of the request in Rails. The config/routes.rb file contains the information necessary to route the request, or to return a 404 if no route matches. Each route specifies a controller and a method to be invoked. In Rails, each controller is a class. The worker thread does a Controller.new to generate an instance of the matching controller class, and then it calls the corresponding method on the instance that is created. As you’ve seen, the controller performs various actions such as reads or writes to a database, and then returns one of two things: a page (using a render call) or a redirect. Only one of these can occur within a controller method, or it’s a code error. Once that is done, the controller method exits, so the worker thread goes back into the thread pool, waiting idle for the next task.

Convention over Configuration

Rails is an “opinionated” framework. That means it sets a lot of stuff up automatically. Do bin/rails routes to see your routes. You will see that each has a URL, a verb, a controller, and a method. A route may also have an alias, a variable name by which it may be referenced in the code. The customers_path gives the name of a route, for example. But a lot of this is not specified in routes.rb. Rails assumes that for a URL starting with “customers”, there will be a CustomersController, and that if a POST comes in, that there will be a corresponding “create” method in that controller. The developer can override all of this by changing the routes.rb, and that’s often necessary, for example to add additional routes and controller methods.

Similarly, within the CustomersController, Rails assumes that one is accessing the Customer Active Record model. And Rails assumes that there are corresponding index, show, new, and edit views, with those names. If you look at CustomersController.rb, you’ll see that some of the methods are empty. That means all the defaults are being used. All of this naming derives from the URI.

Saving State

The controller instance is not used after the worker thread completes. That object goes to garbage collection and all instance variables are discarded. Subsequent operations are handled by new instances of the controller class. So, suppose you need to save some state associated with the user. You have a number of choices. One bad idea is to put it in a class variable for the controller or some other kind of persistent server memory. That’s bad because all users accessing the application would have access to the same state data. Of course, you can store state data in a database. But, sometimes you want to store state data just for the user’s current logon session. You can do that as well, using the Rails session hash, which is associated with the session. The default session store in Rails is in a cookie, which is a key-value pair that is set by the server and sent from the client with every subsequent request. But there is a limit to the size of a cookie, about 4k or something, so one may need to use a database as well. Unfortunately we don’t use the session in this class, but you may want to check out the Rails documentation on this feature.

PUT, PATCH, DELETE?

As we’ve built Rails applications so far, they are sending ordinary HTML, with little client side JavaScript. So, how can we do these other operations? Links in HTML only do GET, and forms in HTML only do POST. Rails provides a workaround to support the additional operations. It does a POST from the form and includes a hidden field in the form with the name “_method”. The value of this hidden field might be patch or put or delete. A normal POST is sent back to the server, with this information in the request body. The Rails router then routes the request as if it were a PATCH or PUT or DELETE. You can see this hidden field by going to the customer edit view in your browser and examining the form using developer tools. You’ll also see another hidden field with the name “authenticity_token”. The value is a long string of gobbledygook. This token is used to prevent a security attack called cross site request forgery. Our application doesn’t have any security, but it would need security if it were to be deployed to the internet.

Some students ask about PATCH vs. PUT. Best practice is to use PATCH to replace some of the attributes of an entry, and PUT to replace the whole thing. By default, Rails maps them both to the update method of the controller, but you can change this behavior, if your application were to need it, by changing routes.rb to send each of these operations to a different controller method.

What about Internet Deployment?

We don’t get to that in this class, unfortunately, but you may elect to do this for your final project. You should not do this until you have protected write operations for your database with user logon, because otherwise users could make arbitrary changes to your database. You also have to convert your database to use SQL with an internet version of Postgres, because the Sqlite database you have been using is not supported for internet deployment Here are some instructions: https://learn.codethedream.org/resources/promoting-to-the-internet/ . These instructions recommend the use of ElephantSQL, but unfortunately that offering is coming to an end. You'd need to use the SQL provided by Render.com. For Rails, you typically use the Devise gem to implement the logon.