OmbuLabs Blog

The Lean Software Boutique

Articles about Rails

How to test a React app using capybara-webkit

I have been using the capybara-webkit gem for a while now since I first tried it out after making the switch from Capybara + Selenium.

Using capybara-webkit speeds up my Selenium tests due to its headless nature, and it's very straightforward. However, I had some trouble testing a Rails based React app.

In this post, I will explain how I worked around the issues that came up when trying to use capybara-webkit with React.

Read more »

How to use any gem in the Rails production console

How many times did you come across a great gem you wanted to try out in a production console, like benchmark-ips or awesome-print?

Be it for performance or for readability, sometimes it's nice to be able to try out something new quickly without going through a pull request + deployment process. This is possible by modifying the $LOAD_PATH Ruby global variable and requiring the gem manually.

Read more »

Present? vs Any? vs Exists?

When working on a Rails project, you may have seen present? calls on ActiveRecord relationships. This might feel natural, mostly because present? exists on all objects via ActiveSupport, so you expect the relationship to respond to it, but it's actually not a very good idea. If all we want to do is check if the scope returns any results from the database, there are better ways than using present?.

Read more »

Tips for Writing Fast Rails: Part 1

Rails is a powerful framework. You can write a lot of features in a short period of time. In the process you can easily write code that performs poorly.

At Ombu Labs we like to maintain Ruby on Rails applications. In the process of maintaining them, adding features and fixing bugs, we like to improve the code and its performance (because we are good boy scouts!)

Here are some tips based on our experience.

Prefer where instead of select

When you are performing a lot of calculations, you should load as little as possible into memory. Always prefer a SQL query vs. an object's method call.

Read more »

Tips for upgrading from Rails 3.2 to 4.0

There are already quite a few guides in the wild to help with the upgrade of Rails 3.2 to Rails 4.0. The official Rails guide for upgrading from Rails 3.2 to 4.0 is very thorough. With the recent release of Rails 5.0, apps currently in production running Rails 3.2 should probably be updated to any stable Rails 4 release as soon as possible.

There is even an e-book about upgrading from Rails 3 to 4, which serves as a useful guide to make this upgrade easier, and also helps understand the advantages & disadvantages of this new (soon to be old) version.

However, if you're using any non-standard gems, you're mostly on your own. Some gems stopped being maintained before Rails 4 was released, as was the case with CanCan, a well known authorization library. After many open pull requests were left unmerged, CanCanCan was released. It is a community driven effort to have a semi-official fork of CanCan. It serves as a drop-in replacement for people who want to use CanCan after upgrading to Rails 4.

Read more »

DRY your tests

I'm a big fan of having small classes. I'm not a big fan of having huge specs for a small class/object. Every time I see an opportunity to DRY my specs, I take it.

Today I wrote a spec to make sure that we gracefully ignore SPAMmy contact requests in the Ombu Labs contact page. It initially looked like this:

test "gracefully ignores spammy requests with valid attributes" do
  @valid_contact = contacts(:two)
  attributes = @valid_contact.attributes
                             .merge(email_confirmation: @valid_contact.email)

  assert_no_difference("Contact.count") do
    post :create, contact: attributes, format: 'js'
  end

  assert_response :success
end

The new behavior adds a simple SPAM trap field that bots will usually fall for. If a bot is submitting the email_confirmation field (which is hidden by a CSS class), then it is SPAM and it gracefully ignores the request.

Read more »

Use session variables to optimize your user flow

Sessions provide you a nice little data storage feature where the application does not need to get the information directly from the database. So you do not have to persist data in your database and can easily store info about the user on the fly. This is a nice way to enhance the user experience on your page.

Let's say that you want to show some users a new fancy sign up form and the rest the old form. If you store the version of the sign up form in a session variable, you don't need to persist this info in your database.

Read more »

Hunting Down a Slow Rails Request

Recently, we started using Skylight in production for one of our clients' Rails applications, in an attempt to try to improve the performance of some of the more critical API endpoints.

Skylight reports on:

  • Time taken per request
  • Breakdown of time taken per SQL query
  • Object allocations per request

I noticed an unusually large amount of allocated objects for one request:

Skylight report

This request would take anywhere from 400ms to 3000ms to respond, which is WAY too long.

Read more »

10 Steps to Evaluate a Rails Project

It will come a time when you will have to decide whether to maintain a Rails project or not.

If you want to seriously consider it, you should follow these 10 steps:

1. Setup the development environment

Git clone the repository and try to start the server. Is the README clear enough? Can you follow the steps in the file and easily get started?

A lot of projects will have a README that is out of date and/or instructions that don't work right off the bat.

Most of the projects will define guidelines like these:

  • Configure your config/database.yml
  • Configure your .env file
  • Setup the database rake db:create db:migrate db:seed
  • Start the server rails server

The best projects will have a one-liner that will setup the entire environment for you.

Read more »

Adding Csrf-Protection to your Rails-Backbone App

When integrating Backbone.js in your Rails App, you might face the problem of the inability to verify the CSRF-Token.

The CSRF Protection secures your app with a token. Rails makes sure that the person who is interacting with your app is someone who started a session in your site, not some random attacker from another site. So you should not turn it off, unless you know what you are doing.

For more information on this Topic, check out the Rails Security Guide.

This problem occurs as soon as you are trying to send form data, without the CSRF-Token provided by Rails.

Started POST "/products" for 127.0.0.1 at 2015-12-16 10:06:05 -0300
Processing by ProductsController#create as JSON
  Parameters: {"product"=>{"name"=>"foo"}}
WARNING: Can't verify CSRF token authenticity
...
Completed 302 Found in 7.6ms (ActiveRecord: 0.8ms)

After this request, Rails will terminate your session and you will have to login again.

This problem is caused by your Backbone.js application, which is sending the data directly to the backend without providing the CSRF-Token.

To solve this problem you need to add the token to your Backbone request. One of the simplest solutions I came across is the following by Anton Shuvalov.

// https://github.com/shuvalov-anton/backbone-rails-sync
Backbone._sync = Backbone.sync;
Backbone.sync = function(method, model, options) {
  if (!options.noCSRF) {
    var beforeSend = options.beforeSend;

    // Set X-CSRF-Token HTTP header
    options.beforeSend = function(xhr) {
      var token = $('meta[name="csrf-token"]').attr('content');
      if (token) { xhr.setRequestHeader('X-CSRF-Token', token); }
      if (beforeSend) { return beforeSend.apply(this, arguments); }
    };
  }
  return Backbone._sync(method, model, options);
};

It grabs the CSRF-Token provided in the meta tags of your Rails application and sets it for the request header field X-CSRF-Token.

After adding this to the Backbone code, it works as expected.

Started POST "/products" for 127.0.0.1 at 2015-12-16 10:08:29 -0300
Processing by ProductsController#create as JSON
  Parameters: {"product"=>{"name"=>"foo"}}
...
Completed 200 OK in 40.6ms (Views: 0.6ms | ActiveRecord: 10.3ms)
Read more »