“What’s this idiot talking about? Drupal is a content management system; Rails is a framework. Drupal is PHP; Rails is Ruby. The Whitehouse website is built in Drupal!”

I hear you. I get it. I’ve built university websites in Drupal. It’s powerful. It provides a ton of features with the click of a button. It gets you much of what you need without having to touch a line of code.

The problems arise when it comes time to build something that Drupal (or one of its community modules) does not provide. That’s when Drupal can get in the way. Any app or website that does anything interesting is going to change over time. There’s a point where the platform gets in the way and the costs of using it begin to outweigh the benefits. Sadly, this occurs when it’s too late to scrap everything and switch platforms. I have never felt like Ruby on Rails was in my way.

In this post, I show how some of the features that make Drupal a great sell can be implemented in Rails.

User Registration and Authentication

Drupal provides this right out of the box. Visitors can make an account and login and logout. Rails doesn’t provide this. Luckily, there are multiple libraries - called “gems” in Ruby - that can be used to add this functionality to your app. Devise is one of them. Here’s how to use it.

Add the devise gem to your Gemfile.

gem 'devise'

Install it, and be sure to follow the few additional instructions that display after the installation.

$ bundle install
$ rails g devise:install

Generate a User model.

$ rails g devise User
$ rails db:migrate

You can find links to sign up and sign in at /users/sign_in. You can also restrict anonymous visitors from accessing parts of your app.

class ApplicationController < ActionController::Base
  before_action :authenticate_user!
end

Content Types and Fields

Drupal provides the ability to create content types with custom fields. It also makes it possible to list, add, edit, view, and delete content. This must require a lot of code in Rails, right? Wrong. Rails provides generators for creating models, controllers, views, and more. Suppose we have a Post content type in Drupal with a title, body. To create something similar in Rails, we can use the scaffold generator.

$ rails g scaffold Post title:string body:text
$ rails db:migrate

The above code creates a Post model (app/models/post.rb), database migration, routes, a controller with actions for adding, editing, viewing, indexing, and deleting posts, and more. If you visit /posts, you’ll see links to everything you need. Adding other field types like select menus, enums, and radio buttons requires a little bit more work, but not as much as you might expect.

User Authorization

Drupal provides support for permissions. Rails doesn’t. However, the Pundit gem makes it pretty easy.

Add pundit to your Gemfile.

gem 'pundit'

Install it.

$ bundle install
$ rails g pundit:install

Include Pundit in your application controller.

class ApplicationController < ActionController::Base
  include Pundit
end

Suppose we have a Post model and we only want logged-in users to be able to view posts. Let’s authorize the post in the controller’s show method.

class PostsController < ApplicationController
  def show
    @post = authorize Post.find(params[:id])
  end
end

Calling authorize will instantiate a post policy with the current user and the post record. It will raise an exception if the show? method returns a false-y value. By simply returning the user, non-logged-in visitors will raise an exception and logged-in users will be able to view the post.

# /app/policies/post_policy.rb
class PostPolicy < ApplicationPolicy
  def show?
    user
  end
end

Views

Drupal’s Views UI helps create blocks and pages that display certain types of content based on certain criteria. In Drupal, you can create a block that displays the titles of the 5 most recent posts linked to their URLs in minutes. Well, you can do it in Rails, too. Let’s add one to our homepage.

# app/controllers/welcome_controller.rb
class WelcomeController < ApplicationController
  def index
    @posts = Post.last(5)
  end
end
# app/views/welcome/index.html.erb
<% @posts.each do |post| %>
  <%= link_to post.title, post %><br>
<% end %>

You can see that Rails doesn’t require a ton of code to do some of the things that Drupal can do. Plus, you have control over the code. When you want to do something interesting, you don’t have to create a custom module, describe it to Drupal, enable it, and wrestle with multiple “hooks” to determine where to put your code. Rails provides a clean directory structure and a wide array of classes and helper functions you can leverage to build your app… without getting in your way!

I hope you’ll consider Ruby on Rails for your next project. I did years ago, and I haven’t regretted it.

Thinking about it? Reach out to me on Twitter at @elegantbrew if you have any questions.