Specs on ErrorsController in Rails

There are different approaches to respond to failed requests in Rails.
By default Rails handles exceptions serving static HTML documents stored in the public directory. In particular, 500 (server error) and 404 (not found) responses result in the application’s rendering 500.html and 404.html, respectively.

In our application usually we build custom 404 and 500 error pages served trough ErrorsController. Several guides illustrate how to move your error pages from static html to dynamic views, here some examples:

This article wants to be a simple reminder for execption pages testing. Remember to test expected error pages behaviour inside a inside a request spec.

The request spec provides a couple of nice things not available in a controller spec. First, it actually hits your application’s HTTP endpoints, as opposed to calling controller methods directly. Running this test insisde a request spec allow us to check the entire functionality specially the options that tells Rails to serve error pages from routes config.exceptions_app = self.routes.

For example if you want to test a simple ActiveRecord::RecordNotFound for a specific resource:

require 'rails_helper'

RSpec.describe 'Show Post', type: :request do
  ...
    
  context 'when post is not present' do
    get '/post/not_existing'
    it { expect(response).to have_http_status :not_found }

    it 'returns an error message' do
      expect(response).to have_content('This is not the page you are looking for.')
    end
  end

  ...
end

Rails update tips and tricks

In this post, I’m going to give you some advice on how to upgrade a Rails application. Rails 5 has been officially released recently, and I’m pretty sure that many developers are now trying to update their application to the latest version of the framework. Upgrading complex applications requires more time and a good test coverage can be really helpful in order to quickly discover broken features.

Running your test suite should also reveal unsopported gems and code incompatibilities with the upgraded version. Not every gem is ready to work with Rails 5 yet. If a gem does not work, you should check if there’s already a branch with Rails 5 support, or if the latest commit just works. Otherwise, you must resort to another solution or adapt the gem to Rails 5.

After creating a new branch for your app you should follow these simple two steps:

Upgrade using rails:update

⦿ ema:rails_app (rails_upgrade) $ rake rails:update
   identical  config/boot.rb
       exist  config
    conflict  config/routes.rb
Overwrite /Users/ema/dev/project/rails_app/config/routes.rb? (enter "h" for help) [Ynaqdh] a

In this step you should allow rake rails:update to overwrite your files (writing a in the console). Many of your configuration files files will be changed. You’ll need to check each changed file and restore your custom configuration, if you did change something in the past. This operation can be really painful if you don’t know what changes are included in the newer version. If you’re not sure what to discard and what to keep you should look at the Rails upgrade guide.

During this delicate operation I suggest you to use a GUI for git, my favorite one is GitX, to analize each changed file and stage only the relavant lines.

Re-initialize your app

I know it seems strange but if you want to include all the new features you should run rails new your_project in the parent folder of your app:

⦿ ema:rails_app (rails_upgrade) $ cd ..
⦿ ema:project $ rails new rails_app -T -d postgresql
       exist
Overwrite /Users/ema/dev/project/rails_app/README.md? (enter "h" for help) [Ynaqdh] a

Just like with the rails:update command, you should allow to overwrite all the files and then check again each one of them.
After running this command you’ll notice a lot differences compared to the running of the Rails update command.

For example if you upgrade from Rails 4.x to Rails 5 you’ll find the new class ApplicationRecord inside app/models/application_record.rb, you will find Action Cable code inside the files app/assets/javascripts/cable.js and app/channels/application_cable/connection.rb and some other files related to ActiveJob. All these changes are not included if you upgrade using only rails:update.

Gemfile

At this point your Gemfile should be clean, all your gems are gone, only Rails 5 gems are there. Now, with patience, you should add all your gems dependecies and if necessary upgrade them to the latest version.

Other files

Most of the modified files are part of the Rails configuration. Verify all these files and commit only the required changes.

Launch test suite

After incorporating all the changes, you should run your test suite. It’s normal that a major upgrade will break some tests. Fix them and then continue to test the application in the browser, in order to be sure that every part of the application still works as expected.

This workflow seems a bit complicated but remember that you’re working on a different branch so you can take all time you need to test and fix all the problems.

Managing uniqueness on self generated field

Recently I ran into a common problem for a Rails developer: how to manage uniqueness on a self generated field.
The problem was pretty simple, in my Rails app I’ve got a resource that contains two different attributes generated during resource creation. These attributes uuid, access_token are provided to the user in order to interact with an API. As tokens I’ve chosen to use UUID standard format.
SecureRandom.uuid, integrated with Ruby, aims to generate UUID but does not guarantee complete uniqueness so my goal was to avoid uniqueness collision at resource creation.

Here’s resource (Feed) migration:

class CreateFeeds < ActiveRecord::Migration
  def change
    create_table :feeds do |t|
      t.uuid :uuid, null: false
      t.uuid :access_token, null: false
      t.string :name, null: false
      t.string :time_zone, default: 'UTC', null: false

      t.timestamps null: false
    end

    add_index :feeds, :uuid, unique: true
    add_index :feeds, :access_token, unique: true
  end
end

My first approach to solve the problem was to manage uniqueness using Rails validations, loops and hooks.

class Feed < ActiveRecord::Base
  validates :uuid, presence: true
  validates :access_token, presence: true

  before_validation :generate_uuid, unless: :uuid?
  before_validation :generate_access_token, unless: :access_token?


  private

  def generate_uuid
    begin
      self.uuid = SecureRandom.uuid
    end while self.class.exists?(uuid: uuid)
  end

  def generate_access_token
    begin
      self.access_token = SecureRandom.uuid
    end while self.class.exists?(access_token: access_token)
  end
end

As you can see in the log below, this solution brings the disadvantage of multple query on the database to check the presence of the generated UUIDs.

2.3.0 :001 > Feed.create(name: 'Example')

(0.1ms)  BEGIN
Feed Exists (14.7ms)  SELECT  1 AS one FROM "feeds" WHERE "feeds"."uuid" = $1 LIMIT 1  [["uuid", "1be12f2a-23f0-49f9-bac8-456c09fcede6"]]
Feed Exists (5.7ms)  SELECT  1 AS one FROM "feeds" WHERE "feeds"."access_token" = $1 LIMIT 1  [["access_token", "9b773024-741f-4904-8718-a6b68eb8b389"]]
SQL (3.6ms)  INSERT INTO "feeds" ("name", "uuid", "access_token", "time_zone", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id"  [["name", "Example"], ["uuid", "1be12f2a-23f0-49f9-bac8-456c09fcede6"], ["access_token", "9b773024-741f-4904-8718-a6b68eb8b389"], ["time_zone", "UTC"], ["created_at", "2016-04-25 09:57:34.845915"], ["updated_at", "2016-04-25 09:57:34.845915"]]
(135.0ms)  COMMIT

After several attepts I’ve found out that the best solution was to let broke resource creation and manage ActiveRecord::RecordNotUnique.

class Feed < ActiveRecord::Base
  UNIQUE_FIELDS = %w[uuid access_token]

  before_save :assign_uuid, :assign_access_token
  around_save :save_managing_uniqueness

  private

  def save_managing_uniqueness
    self.class.connection.execute('SAVEPOINT before_record_create;')
    yield
    self.class.connection.execute('RELEASE SAVEPOINT before_record_create;')
  rescue ActiveRecord::RecordNotUnique => error
    @attempts = @attempts.to_i + 1
    if @attempts < 3
      duplicated_field = error.message[/Key ((w+))/, 1]
      send("assign_#{duplicated_field}", true) if UNIQUE_FIELDS.include?(duplicated_field)
      self.class.connection.execute('ROLLBACK TO SAVEPOINT before_record_create;')
      retry
    else
      raise error, 'Retries exhausted'
    end
  end

  def assign_uuid(force = false)
    self.uuid = SecureRandom.uuid if uuid.nil? || force
  end

  def assign_access_token(force = false)
    self.access_token = SecureRandom.uuid if access_token.nil? || force
  end
end

Be aware to change duplicated_field = error.message[/Key ((w+))/, 1] if you’re not working with Postgres.

I’m not fully convinced of using a regex to find what attribute raises duplication error, feel free to improve this part. The idea was to give a working example of rescue in a Rails transaction.

Json and Jsonb in Hanami framework

In the past week I’ve tried Hanami, formerly Lotus. Hanami is a web framework based on Ruby, it promises fast response time, security and simplicity.

After following the getting started guide I’ve started to reproduce a simple web API written in Rails. My goal was to understand Hanami pros and cons. At some point I came across a model with a jsonb attribute. Rails supports json and jsonb out of the box if you’re using Postgres 9.2 or greater.
Hanami data mapper supports the most common Ruby data type such as String, Integer, or DateTime, but if you need to store json and jsonb types you need to customize your setup using Postgres builtin type.

Here you can find some code that exaplains how to store this type of data:

# lib/ext/pg_jsonb.rb
require 'hanami/model/coercer'
require 'sequel'
require 'sequel/extensions/pg_json'

class PGJsonb < Hanami::Model::Coercer
  def self.dump value
    ::Sequel.pg_jsonb value
  end

  def self.load value
    ::Kernel.Hash(value) unless value.nil?
  end
end
# lib/ext/pg_json.rb
require 'hanami/model/coercer'
require 'sequel'
require 'sequel/extensions/pg_json'

class PGJson < Hanami::Model::Coercer
  def self.dump value
    ::Sequel.pg_json value
  end

  def self.load value
    ::Kernel.Hash(value) unless value.nil?
  end
end
# lib/bookshelf.rb
require_relative './ext/pg_jsonb'
require_relative './ext/pg_json'
...

Hanami::Model.configure do
  # ...
  mapping do
    # ...
    collection :user do
      attribute :id,   Integer
      attribute :options_in_jsonb, PGJsonb
      attribute :options_in_json, PGJson
    end
  end
end.load!

Let’s give Hanami a chance and try to embrace Hanami’s model separation (entity / repository).

Check your hashes

In this brief post I want to report a brief experiece regarding a Ruby 2.0+ feature.

A few weeks ago I wanted to enforce the name of the arguments accepted when initializing a simple PORO.

The object itself was pretty simple and its purpouse was to abstract the access and add some behavior to another one. Guess what, the encapsulated object was an Hash.

Thanks to the fact that I was on Ruby 2.0+ (specifically the last stable, i.e. 2.3.0) I immediately looked at keyword arguments.

This feature is already well known and there are actually many blog posts that describe how to use it. My favorite article is probably Justin’s post.

I’ll introduce some code I’ll explain later:

class Resource
  attr_reader :type, :id

  def initialize(resource_type:, resource_id:)
    @type = resource_type
    @id = resource_id
  end

  %i[name description].each do |method|
    define_method(method) { instance.send method }
  end

  def instance
    @instance ||= type.constantize.find(id)
  end

  def permitted?
    permitted_types.include? type
  end

  def not_permitted?
    !permitted?
  end

  def permitted_types
    self.class.permitted_types
  end

  def self.permitted_types
    %w[NewsItem Gate Course Document Gallery Video Survey Content]
  end
end

In this example I’ve created a simple PORO which can be used to interact with different ActiveRecord resources such as NewsItem, Gate, Course, Document Gallery, Video, Survey and Content.

I wrote the code with some specs and so I felt I had my back somehow guarded.

Despite that, when I put the object in use, I stumbled in a banal ArgumentError. The code was actually a simple initialization of my object: Resource.new home_block_params

I knew that home_block_params was an Hash and I also knew that it was composed by the expected key and value pairs, i.e. resource_type and resource_id.
You will probably have already realized what I clumsily missed: the nature of the Hash keys.
They were indeed strings while they had to be symbols!

I wrote some specs (just for fun ;)) to clear the things up:

describe '#initialize' do
  it 'raises an ArgumentError if no resource_type and resource_id arguments has been specified' do
    expect do
      described_class.new
    end.to raise_error(ArgumentError)
  end

  it 'raises an ArgumentError if resource_type and resource_id arguments has been specified as strings' do
    expect do
      described_class.new('resource_type' => 'News', 'resource_id' => 1)
    end.to raise_error(ArgumentError)
  end

  it 'does not raise any error if resource_type and resource_id arguments has been specified as strings' do
    expect do
      permitted_home_block_resource
    end.not_to raise_error
  end
end

The quick solution in my case was to rely on Hash#symbolize_keys. Unfortunately this is something given by Ruby On Rails.

Anyway the take home message here is to properly refresh your memory when dealing with some midly new language features and to always consider the nature of Hash objects keys!

Secure your Rails API

There are many approaches to locking down an API. In this post I will you some best practise aimed to secure your data and your service.

API key

Use API keys, not passwords to protect your API from unauthorized access. To authenticate request you can generate an unique token which can be passed through HTTP header. This token shoud be created upfront and made public only to authorized user.

class User < ActiveRecord::Base
  before_validation :generate_access_token, unless: :access_token?


  private

  def generate_access_token
    begin
      self.access_token = SecureRandom.hex
    end while self.class.exists?(access_token: access_token)
  end
end

Rails offers authenticate_with_http_token method, which automatically checks the Authorization request header for a token and passes it as an argument to the given block. Here’s an example used by controller.

module API
  class ResourcesController < ActionController::Base
    before_action :get_resource
    append_before_action :authenticate

    private


    def resource
      @resource ||= Resource.find_by id: params[:id]
    end

    def render_no_record_found
      render json: 'Not Found', status: 404
    end

    def get_resource
      resource || render_no_record_found
    end

    def authenticate_token
      authenticate_with_http_token do |token, options|
        resource.access_token == token
      end
    end

    def render_unauthorized
      self.headers['WWW-Authenticate'] = 'Token realm="Application"'
      render json: 'Invalid Token', status: 401
    end

    def authenticate
      authenticate_token || render_unauthorized
    end
  end
end

Identity token

Another solution to keep private your resource is to allow access to your data using a token instead of id. While id resource is easy to guess a 16 characters random token is hard to imagine. This token should be stored on resource itself and used by controller as parameter to get resource, here’s an example:

class Resource < ActiveRecord::Base
  before_validation :generate_identity_token, unless: :identity_token?


  private

  def generate_identity_token
    begin
      self.identity_token = SecureRandom.urlsafe_base64
    end while self.class.exists?(identity_token: identity_token)
  end
end

In ths case I’ve used SecureRandom with urlsafe_base64 to be sure that generate token can be used in a url. In your controller you can should change:

def resource
  @resource ||= Feed.find_by identity_token: params[:identity_token]
end

Rate limit

There are several ways to limit API usage. Depending on service you’ll need to protect the quality and reliability of your application. For example, rapidly creating content, polling aggressively instead of using webhooks, making API calls with a high concurrency, or repeatedly requesting data that is computationally expensive may result in abuse rate limiting.

If you’re not sure what I suggest you to try rack-attack. Rack::Attack is a rack middleware aimed to protect your web app from bad clients. It allows whitelisting, blacklisting, throttling, and tracking based on arbitrary properties of the request.

Rails + Postgres and JSON type, my first approach

I know that Postgres JSON data type has been released more than a year ago, but I just recently had the opportunity to use this feature on a Rails project.

I’ve been asked to develop an app that allow users to check into nearby event.

One of the requests was to track whether the user location was inaccurate or whether the user region differed from the event region. At the beginning I was not aware of exactly what information was required, so I decided to create a json column in my checkins table called report. Here’s my checkins table structure:

create_table "check_ins", force: :cascade do |t|
  t.integer  "user_id",                 null: false
  t.integer  "event_id",                null: false
  t.json     "report",     default: {}, null: false
  t.datetime "created_at",              null: false
  t.datetime "updated_at",              null: false
end

add_index "check_ins", ["event_id"], name: "index_check_ins_on_event_id", using: :btree
add_index "check_ins", ["user_id", "event_id"], name: "index_check_ins_on_user_id_and_event_id", using: :btree
add_index "check_ins", ["user_id"], name: "index_check_ins_on_user_id", using: :btree

The report column would contain a list of check-in status {:status_type => 'Message for status type', :other_status_type => "Message for other status type"} that are stored only when some constraints are met.

My decision to store report data as JSON turned out to be the right one when the client decided to include some new tracking information in the check-in record. With this dynamic attribute I was able to implement this new feature very quickly without problems.

JSON attributes type not only allow schemaless data structures but also can be queried with ease. For example:

anna = User.create name: 'Anna'
paul = User.create name: 'Paul'
john = User.create name: 'John'
party = Event.create name: 'Big party', starts_at: 2.days.from_now, ends_at: 3.days.from_now

CheckIn.create user: anna, event: party, report: {with_soda: 'Participant brings soda'}
CheckIn.create user: paul, event: party, report: {without_mask: 'Participant is not masquerade', bring_friends: false}
CheckIn.create user: john, event: party, report: {without_mask: 'Participant is not masquerade', with_soda: 'Participant brings soda', bring_friends: true}

CheckIn.where("(report ->> 'with_soda')::text IS NOT NULL")

[#<CheckIn id: 1, user_id: 1, event_id: 1, report: {"with_soda"=>"Participant brings soda"}, created_at: "2015-11-05 13:29:04", updated_at: "2015-11-05 13:29:04">, #<CheckIn id: 3, user_id: 3, event_id: 1, report: {"without_mask"=>"Participant is not masquerade", "with_soda"=>"Participant brings soda", "bring_friends"=>true}, created_at: "2015-11-05 13:29:05", updated_at: "2015-11-05 13:29:05">]

Or you can search for a specific value of a key:

CheckIn.where("report ->> 'bring_friends' = 'true'")

[#<CheckIn id: 3, user_id: 3, event_id: 1, report: {"without_mask"=>"Participant is not masquerade", "with_soda"=>"Participant brings soda", "bring_friends"=>true}, created_at: "2015-11-05 13:29:05", updated_at: "2015-11-05 13:29:05">]

It also possible to create a scope to filter JSON data:

class User < ActiveRecord::Base
  has_many :check_ins
  has_many :events, through: :check_ins

  scope :without_mask, -> { joins(:check_ins).where("(check_ins.report ->> 'without_mask')::text IS NOT NULL") }
end

As far as I can tell, it has been a positive experience using JSON type. I’ll definitely use this new feature again, Postgres with JSON type is now mature and offer a complete set of functions and operators.

Tune your factory

As a Rails developer you’ve surely seen factory like this:

FactoryGirl.define do
  factory :event do
    sequence(:id, 1000)
    sequence(:name) {|n| "Event #{n}" }
    description 'Lorem ipsum Elit proident Excepteur commodo aliqua ut tempor laborum sunt labore incididunt qui.'
    starts_at rand(3.days.ago..Time.now)
    stops_at rand(Time.now..3.days.from_now)
    lat rand(-90.0..90.0)
    long rand(-180.0..180.0)
    country_code 'IT'
    city 'Milan'
    address 'Via Venini 42'
    published false
  end
end

Faker is a gem that allow you to generate data. With Faker you can build a large amount of different kind of data with a simple DSL, saving you al lot of time. The same factroy rewritten using Faker would look like this.

FactoryGirl.define do
  factory :event do
    sequence(:id, 1000)
    sequence(:name) {|n| "Event #{n}" }
    description Faker::Lorem.paragraph
    starts_at Faker::Date.between(3.days.ago, Date.today)
    stops_at Faker::Date.forward(3)
    lat Faker::Address.latitude
    long Faker::Address.longitude
    country_code Faker::Address.country_code
    citi Faker::Address.city
    address Faker::Address.street_address
    published false
  end
end

To use Faker in your rails app simply add gem 'faker' in your gemfile.

Happy building!