Climbing up the ladder

UPGRADING TO RAILS 4.0, 4.1, AND 4.2 (ANTIPATTERN: STAYING ON VERY OLD VERSIONS OF RAILS)

As with all Rails upgrades, first pay attention to the deprecation warnings in your Rails development output (that is, the log), for the current version you are running. That’s because in your Rails 3 app gave you deprecation notifications of any of the syntaxes and methods which were to be dropped Rails 4.

Rails 4.0

seven nested wooded dolls russian doll nesting

Russian Doll Caching

Like a Russian doll with a smaller Russian doll inside that Russian doll with a smaller Russian doll.

Today I’ll give you a brief introduction to Russian doll caching, but I will be expand on this in a full post in the future. Before talking about view caching— where I store a part of the rendered version of the page in a memory store (like Redis or Memcached) for easily fetching again so that we don’t have to re-render (see Cache or Bust)— I need to tell you about a little method that belongs to belongs_to (sorry feeling punny today). On the belongs_to there is a flag called touch. Remember belongs_to is typically on the “child” record and you specify belong_to on the same table that contains the foreign key to the parent record. If you specify touch: true, you are telling Active record to reset the updated_at timestamp of the parent record any time the child record is changed (that is, cause to become the current time). That is, even if the parent record isn’t otherwise being updated, it will have its updated_at timestamp set.

As I discussed in the post Cache or Bust, the Rails cache key incorporates the updated at timestamp of the record– that’s how it works. When the updated_at timestamp changes, it busts the cache, thus forcing the associated cached pages to re-render.

Remember, a “cache hit” is when we have the thing we’re looking for in the cache already and we don’t have to re-render it. A “cache miss” is when we don’t and instead we re-render it, and “busting” the cache is when the mechanism of the cache—as we’re doing here—makes it so that the next time the view is rendered it will not use the cached version and instead render a new version. (And, presumably, cache the new version.)

Example

Take for example a layout where we show a Course page and a list of Lectures (much like the one you are taking now).

Course showing four lectures

The Course page, which encompasses the whole page, might have some information on it: the name of the course, the instructor’s name, perhaps a summary of what students can expect to learn.

On this page we will display list of lectures. Each lecture will have a title, a date, and prepared readings for that lecture.

As this Rails app will allow the teacher to put together her course, I’m going demonstrate how to cache the page using Russian-doll caching. Keep in mind that in a simple app, you might not need caching. This type of view caching is a great strategy for high-traffic websites with data that changes on inconsistent intervals. (That is you can’t predict when it will change.)

class Course < ActiveRecord::Base
has_many :lectures
end
class Lecture < ActiveRecord::Base
  belongs_to :course, touch: true
end

The trick here is that

<!-- app/views/courses/show.html.erb -->
<% cache @team do%>
  <h1>Course: <%= @course.name %></h1>
  <h2>Instructor: <%= @course.insturctor %></h1>
  <%= render @course.lectures %>
<% end %>

<!-- app/views/lectures/_lecture.html.erb -->
<% cache lecture do %>
  <div class='lecture'>
    <%= lecture.name %>
    <p>To prepare: <%= lecture.homework %></p>
  </div>
<% end %>

Notice that we have two partials. Each has a cache block surrounding their content. The Course partial takes the @course object as it parameters. This gets converted to something like:

views/course/1-20200620231122

Here, the cache key is created for us by ActiveRecord. The “1” you see after views/course/ is the id of the @course, and the rest of the numbers object’s updated_at timestamp.

Both Courses and Lectures cache themselves. In our example, if a Lecture is changed, it will update the timestamp on the lecture (thus busting, or invalidating, the cache for that lecture), and also, by means of the touch: true on the belongs_to association, also updating the updated_at timestamp on the lecture’s parent (Course). This is key, because it will tell Rails that the next time the page is rendered, Course will need to be re-rendered.

What happens next? Rails will re-render the course, but it will only re-render the course page as a whole. As it goes to render each individual Lecture partial, there will be one Lecture (the updated one) that will need to be re-rendered. The others will be cache hits and will not cause a re-rendering.

Caching dramatically speeds up your Rails app. I will discuss caching in more detail in my next series.

Strong Parameters

ActionPack introduces something called strong parameters. This is designed to prevent you from inadvertently allowing a controller action to update parameters it shouldn’t. You still need to provide authorization and access control on your controllers. Strong parameters makes sure the controller can’t update fields it isn’t supposed to.

Turbolinks

Turbolinks is a quick-and-dirty way to avoid writing a lot of front-end Javascript. If you are someone who doesn’t like Javascript or doesn’t want to learn it and you don’t plan to move in the direction of a single-page app, Turbolinks is for you. Basically Turbolinks operates behind the scenes to move between pages for the user. Turbolinks will orchestrate your request and push the content from the server into the DOM. This prevents a “full page reload,” but still has the overhead of having the browser change the content in the DOM. For most dashboard-style and page-style applications (that is, applications with dashboards and discreet pages) this is typically all you need.

One way you know Turbolinks is working is that you will see an ever-so-small blue loading bar at the top of you browser window when you navigate between pages. This is easy to miss because it typically happens very fast. In short, the experience for the user “feels” much faster and “is” only slightly faster.

Why does it feel faster? In normal operation, when a user navigates from one page to another, the browser will momentarily freeze the current page, load the new page, then momentarily present a white screen to the user and then render out the new page’s content. With Turbolinks, it’s almost the very same, except that the loading operation happens without the white screen between the pages and with less of a momentary “freeze” effect. The result is that the blue bar at the top of the screen lets the user know something is loading, but then the page nicely “snaps” into its new content. It just feels faster than the normal way.

Rails 4.1

Spring

Spring is an application preloader than makes your application boot changes as you make changes in development. It’s super fast, but in some of these early versions of Rails it can be slightly difficult to maneuver, especially when debugging things that might hang or crash. I recommend waiting until later versions of Rails (where it works better and faster) to adopt it.

Config Secrets

This is the Rails version where config/secrets.yml is first introduced. However, as we will see later, it does not support environment-specific secrets. For this reason, many Rails apps do not adopt it until later when Rails supports environment-specific secrets.

Top Secret

Active Record

Several old (pre Rails-3) APIs were removed, including scope and default_scope? You should have fixed these already via your deprecation warnings.

Rails 4.2

ActiveJob

Mailers are now asynchronous by default. That means they’ll enqueue jobs in the background to send your mail out later on a queue.

Active Record

Some optimizations makes some find and find_by queries up to 2x faster. Under the hood, this is called “adequate record.” You can find out more at Aaron Patterson’s blog post here.

Funky record

Using render "abc/somefile"

Previously, calling render "foo/bar" in a controller action was equivalent to render file: "foo/bar". In Rails 4.2, this has been changed to mean render template: "foo/bar" instead. If you need to render a file, please change your code to use the explicit form (render file: "foo/bar") instead. Remember, rendering a template means it will render the layout along with your template. Rendering a file will just render the file without the layout.

Developing from your iPhone? — The Default Host is Now 0.0.0.0

This means request from the machine itself (to, for example, localhost or 127.0.0.1) will hit the Rails server. Both http://127.0.0.1:3000 and http://localhost:3000 will continue to work as before on your own machine. However, if you are testing from another device on your network (for example, an iPhone), you need to take a few more steps.

From the Rails docs:

with this change you will no longer be able to access the Rails server from a different machine, for example, if your development environment is in a virtual machine and you would like to access it from the host machine. In such cases, please start the server with rails server -b 0.0.0.0 to restore the old behavior. If you do this, be sure to configure your firewall properly such that only trusted machines on your network can access your development server.

REMOVALS in Rails 4.2

  • Deprecated *_path helpers in mailers. Always use *_url helpers instead.
  • Deprecated deliver / deliver! in favor of deliver_now / deliver_now!.
  • Deprecated reset_#{attribute} in favor of restore_#{attribute}

I hope you’ve enjoyed this brief tour of Rails 4.0, 4.1, and 4.2. As always, first fix your deprecation warnings on the prior version of Rails. Then switch the Gem in your Gemfile to the next Rails version (I recommend that you go one version at a time, i.e., 3.2 to 4.0, then 4.0 to 4.1, then 4.1 to 4.2). Run bundle install, then try to boot your app. Most likely, you will see many of your deprecations or broken parts on booting your app. Fix them, then move onto your specs. Finally, when you are able to boot your app and your specs pass, go through your workflows in your Rails app and test your functionality manually.

Good luck upgrading from Rails 3 to Rails 4.0, Rails 4.1, and then Rails 4.2. Since these versions are very old, it is recommended that if you are on these Rails versions that you upgrade as soon as possible. (As of Rails 6.0 in 2019, everything below Rails 5 is will no longer receive security updates).