I’ve been developing a new application from the ground up built on Rails 4. I wanted to publicize my thoughts so far and try to encourage the weary to stay on the edge lest they face their own deprecation. Even though we’re extremely close to an official Rails 4 release there are countless gems out there that haven’t even begun to upgrade themselves and countless projects undoubtedly have yet to even discuss their upgrade path to Rails 4. While the upgrade from Rails 3 to 4 isn’t likely to be as difficult as it was from Rails 2 to 3, there will certainly be a handful of changes required. You’re either ahead of the game or you’re playing catchup. Don’t get caught without an upgrade plan while the rest of the community migrates to Rails 4. So enough preaching. Lets get into it.
So far, I’m fairly happy with how few habits I have to change when working with Rails 4. The most notable change you’ll discover is strong_parameters. There’s a gem you can use already to get your application using strong_parameters while you’re still on Rails 3 and I highly recommend you start there if you’re doing an upgrade. If you’re not familiar with strong_parameters, it basically gets rid of attr_accessible and moves the logic into the controller. Initially, this feels a bit strange because we’re used to thinking of the model as sort of self encapsulating in a way. It does what it needs to do to manage the data. The problem with attr_accessible is that it never really allowed for much flexibility and was a fairly primitive and rigid approach to mass assignment protection. strong_parameters moves that logic into the controller where, once you’re used to it, it feels much much more powerful, customizable, and arguably better organized. It allows you to embed business logic around your mass assignment protection for instance. One example is managing a record as a user versus an admin. An admin might be allowed to update a bunch of attributes that the user isn’t allowed to update. Before strong_parameters, we’d end up having to grant all the records both the user and admin could update in attr_accessible and then customize the controller’s update action to manually set each parameter the user would be allowed to change. This ends up making a big mess of code when you compare it to how strong_parameters handles it. strong_parameters lets you specify different sets of attributes allowed for the user and the admin and it does it in a way that eliminates the need for attr_accessible, cleans up the update actions, and keeps the mass assignment protection logic much dryer and much more useful. Here’s how we might do it without strong_parameters if we want to allow a user to edit name and description but only the admin to edit contact:
|
1 2 3 4 5 6 7 8 9 10 |
// post.rb class Post < ActiveRecord::Base validates :name, presence: true validates :description, presence: true validates :contact, presence: true attr_accessible :name, :description, :contact end |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// posts_controller.rb class PostsController < ApplicationController respond_to :html def create if current_user.is_admin? @rental = Rental.create(params) else @rental = Rental.new @rental.name = params[:name] @rental.description = params[:description] @rental.save end end end |
Normally I’d make a separate admin controller for this but for the sake of the example lets assume this is ok. Obviously this will get out of hand if you really want to be safe with your attr_accessible because with each new attribute you end up having to manually assign it in the controller. Here’s how you’d do the same thing with strong_parameters:
|
1 2 3 4 5 6 7 8 |
// post.rb class Post < ActiveRecord::Base validates :name, presence: true validates :description, presence: true validates :contact, presence: true end |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// posts_controller.rb class PostsController < ApplicationController respond_to :html def create @rental = Rental.create(post_params) end private def post_params if current_user.is_admin? params.require(:post).permit(:name, :description, :contact) else params.require(:post).permit(:name, :description) end end end |
Now you can see pretty quickly how much more powerful this is. It allows us much finer grained control over mass assignment protection and lets up keep our code clean while making it more secure. After you get used to it, you’re going to love it.
The second big gotcha you’ll discover is turbolinks. Its a gem that’s enabled by default and as soon as you start crafting some Javascript for your application you’ll discover it. Basically your Javascript won’t work correctly in a lot of cases because of turbolinks. For those not familiar with turbolinks, it a way to speed up page loads by doing it through turbolinks Javascript instead of doing an actual browser page load. Turbolinks takes a typical anchor tag and fetches the page. Instead of rendering the entire page it does some trickery with the DOM and replaces the BODY contents, page title, and modifies the displayed URL so it looks correct. What this does in effect is it prevents your browser from having to go fetch the CSS and JS all over again for each page load. This can save a ton of time especially when you’re loading bulky JS frameworks or fluffed up CSS like Twitter Bootstrap for instance. The unexpected side effect of course is that your JS and CSS isn’t loaded when you’re expecting it to. Its a trivial to fix your Javascript once you know what’s happening though. Overall, I like turbolinks and it’ll generally reduce the amount of data you have to transmit but its a strange default addition to Rails in my opinion. Its almost like defaulting will_paginate as a gem. Sure its useful but does it need to be included by default? Either way though, you’ll learn to love it.
Here’s how you might do something before turbolinks and discover it no longer works:
|
1 2 3 4 |
$ -> $('.table').dataTable() |
Basically this is saying to initialize a datatable once the page is loaded. Turbolinks isn’t going to even look at this when you visit the page via a turbolink though. Its just going to replace the body, page title, and url. Here’s how you’d do it if you’re using turbolinks:
|
1 2 3 4 5 6 7 8 |
$ -> load_datatable = -> $('.table').dataTable() $(document).ready(load_datatable) $(document).on('page:load', load_datatable) |
What this is doing is it defines the load_datatable function and calls it like you’d normally do when the DOM is ready if you manually entered the URL or did a manual page refresh. The last line is what you need to learn though. It tells turbolinks to call that function when the page is loaded with turbolinks. So it replaces the body, the title, and the url and then it will execute the load_datatable method and everything will work how it used to work.
There’s a few future deprecation notices that you might come across as well but for the most part its fairly painless. However, there’s a ton of gems out there that aren’t compatable for one reason or another. The handful that didn’t work that I’ve investigated were basically written poorly to begin with or made heavy use of attr_accessible which will undoubtedly make upgrading those more difficult. However, there are a bunch of gems already working on Rails 4 support and its only a matter of time before you’re going to need to upgrade yours as well. I’m running everything off the latest from github and its working pretty good. I haven’t had any gems’ repo ruin my bundle yet either. Rails 4 is probably the most stable new Rails version that I’ve seen before and I’ve seen every major release including version 1. The early days everything was a mess and barely worked right. Rails 2 was the first really stable release there was and upgrading to Rails 3 was a nightmare usually. Despite the history, however, this latest release is going to be the easiest upgrade there has ever been in the history of major Rails releases. Its simple, its stable, and it still has that new car smell. So what are you waiting for? Upgrade to Rails 4 already.
