Using D3 JS with React JS

In this blog, we will see how to plot a simple line chart using ReactJS and D3JS.

If you are not familiar with ReactJS then please take a look at official ReactJS webpage. You can also look at our Learn ReactJS in steps video series.

What is D3.js

D3.js is a Javascript library used to create interactive, dynamic visualizations.

Let’s take a step by step look at how we can integrate ReactJS with D3JS to plot some interactive visualizations.

Step 1 - Get ReactJS example working

We will be using JSFiddle example from ReactJS Docs to begin with. Fork the JSFiddle example and you should be good to go.

Step 2 - Add D3.js as an external resource

We will be using D3.js from Cloudflare CDN. Add D3.js as an external resource as shown in the image given below and type the following URL as an external resource.

https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.12/d3.js

Add D3 js as an external resource

Step 3 - Build ReactJS components to create visualizations with D3.js

Now let’s try to draw a Line Chart using D3.js.

Let’s create a Line component that renders line path for the data points provided.

const Line = React.createClass({

  propTypes: {
    path:         React.PropTypes.string.isRequired,
    stroke:       React.PropTypes.string,
    fill:         React.PropTypes.string,
    strokeWidth:  React.PropTypes.number
  },

  getDefaultProps() {
    return {
      stroke:       'blue',
      fill:         'none',
      strokeWidth:  3
    };
  },

  render() {
    let { path, stroke, fill, strokeWidth } = this.props;
    return (
      <path
        d={path}
        fill={fill}
        stroke={stroke}
        strokeWidth={strokeWidth}
        />
    );
  }

});

Here in above code, Line component renders an SVG path. Path data d is generated using D3 path functions.

Let’s create another component DataSeries that will render Line component for each series of data provided. This generates path based on xScale and yScale generated for plotting a line chart.

const DataSeries = React.createClass({

  propTypes: {
    colors:             React.PropTypes.func,
    data:               React.PropTypes.object,
    interpolationType:  React.PropTypes.string,
    xScale:             React.PropTypes.func,
    yScale:             React.PropTypes.func
  },

  getDefaultProps() {
    return {
      data:               [],
      interpolationType:  'cardinal',
      colors:             d3.scale.category10()
    };
  },

  render() {
    let { data, colors, xScale, yScale, interpolationType } = this.props;

    let line = d3.svg.line()
      .interpolate(interpolationType)
      .x((d) => { return xScale(d.x); })
      .y((d) => { return yScale(d.y); });

    let lines = data.points.map((series, id) => {
      return (
        <Line
          path={line(series)}
          stroke={colors(id)}
          key={id}
          />
      );
    });

    return (
      <g>
        <g>{lines}</g>
      </g>
    );
  }

});

Here in above code d3.svg.line creates a new line generator which expects input as a two-element array of numbers.

Now we will create LineChart component that will calculate xScale, yScale based on data and will render DataSeries by passing xScale, yScale, data (input x,y values), width, height for the chart.

const LineChart = React.createClass({

  propTypes: {
    width:  React.PropTypes.number,
    height: React.PropTypes.number,
    data:   React.PropTypes.object.isRequired
  },

  getDefaultProps(){
    return {
      width:  600,
      height: 300
    }
  },

  render() {
    let { width, height, data } = this.props;

    let xScale = d3.scale.ordinal()
                   .domain(data.xValues)
                   .rangePoints([0, width]);

    let yScale = d3.scale.linear()
                   .range([height, 10])
                   .domain([data.yMin, data.yMax]);

    return (
      <svg width={width} height={height}>
          <DataSeries
            xScale={xScale}
            yScale={yScale}
            data={data}
            width={width}
            height={height}
            />
      </svg>
    );
  }

});

Here d3.scale.ordinal constructs an ordinal scale that can have discrete domain while d3.scale.linear constructs a linear quantitative scale.

You can learn more about D3 Quantitative scales here.

Now we need to call LineDataSeries component with the data.

let data = {
  points: [
    [ { x: 0, y: 20 }, { x: 1, y: 30 }, { x: 2, y: 10 }, { x: 3, y: 5 },
      { x: 4, y: 8 }, { x: 5, y: 15 }, { x: 6, y: 10 } ]
    ,
    [ { x: 0, y: 8 }, { x: 1, y: 5 }, { x: 2, y: 20 }, { x: 3, y: 12 },
      { x: 4, y: 4 }, { x: 5, y: 6 }, { x: 6, y: 2 } ]
    ,
    [ { x: 0, y: 0 }, { x: 1, y: 5 }, { x: 2, y: 8 }, { x: 3, y: 2 },
      { x: 4, y: 6 }, { x: 5, y: 4 }, { x: 6, y: 2 } ]
    ],
  xValues: [0,1,2,3,4,5,6],
  yMin: 0,
  yMax: 30
};

ReactDOM.render(
  <LineChart
    data={data}
    width={600}
    height={300}
    />,
  document.getElementById('container')
);

An element with id container is replaced with content rendered by LineChart.

If we take a look at the output now, we see how the Line Chart gets plotted.

ReactJS + D3.js Line Chart example

To build complex visualizations in a modularized fashion, we can use one of the open source libraries mentioned below based on their advantages and disadvantages.

ReactJS + D3.js Open Source Projects

Here are two popular open source ReactJS + D3.JS projects.

react-d3

Pros

  • Supports Bar chart, Line chart, Area chart, Pie chart, Candlestick chart, Scattered chart and Treemap.
  • Legend support.
  • Tooltips support.

Cons

  • No support for Animations. You can implement animations using D3 Transitions.
  • Only stacked Bar chart support.
react-d3-components

Pros

  • Custom scales support.
  • Supports Bar chart (Stacked, Grouped), Line chart, Area chart, Pie chart, Scattered chart.
  • Tooltips support.

Cons

  • No Legend support.
  • No support for Animations.

Summary

Below is final working example of JSFiddle built in the post.

Caching result sets and collection in Rails 5

This blog is part of our Rails 5 series.

Often while developing a Rails application you may look to have one of these caching techniques to boost the performance. Along with these, Rails 5 now provides a way of caching a collection of records, thanks to the introduction of the following method:

ActiveRecord::Relation#cache_key

What is collection caching?

Consider the following example where we are fetching a collection of all users belonging to city of Miami.

@users = User.where(city: 'miami')

Here @users is a collection of records and is an object of class ActiveRecord::Relation.

Whether the result of the above query would be same depends on following conditions.

  • The query statement doesn’t change. If we change city name from “Miami” to “Boston” then result might change.
  • No record is deleted. The count of records in the collection should be same.
  • No record is added. The count of records in the collection should be same.

Rails community implemented caching for a collection of records . Method cache_key was added to ActiveRecord::Relation which takes into account many factors including query statement, updated_at column value and the count of the records in collection.

Understanding ActiveRecord::Relation#cache_key

We have object @users of class ActiveRecord::Relation. Now let’s execute cache_key method on it.

 @users.cache_key
 => "users/query-67ed32b36805c4b1ec1948b4eef8d58f-3-20160116111659084027"

Let’s try to understand each piece of the output.

users represents what kind of records we are holding. In this example we have collection of records of class User. Hence users is to illustrate that we are holding users records.

query- is hardcoded value and it will be same in all cases.

67ed32b36805c4b1ec1948b4eef8d58f is a digest of the query statement that will be executed. In our example it is MD5( "SELECT "users".* FROM "users" WHERE "users"."city" = 'Miami'")

3 is the size of collection.

20160116111659084027 is timestamp of the most recently updated record in the collection. By default, the timestamp column considered is updated_at and hence the value will be the most recent updated_at value in the collection.

Using ActiveRecord::Relation#cache_key

Let’s see how to use cache_key to actually cache data.

In our Rails application, if we want to cache records of users belonging to “Miami” then we can take following approach.

users = User.where(city: 'Miami')

if users.cache_key == current_cache_key
 # do not hit db again and return the same cached page
else
 # get data from database
end
What if your table doesn’t have updated_at column?

Previously we mentioned that cache_key method uses updated_at column. cache_key also provides an option of passing custom column as a parameter and then the highest value of that column among the records in the collection will be considered.

For example if your business logic considers a column named last_bought_at in products table as a factor to decide caching, then you can use the following code.

 products = Product.where(category: 'cars')
 products.cache_key(:last_bought_at)
 => "products/query-211ae6b96ec456b8d7a24ad5fa2f8ad4-4-20160118080134697603"

Edge cases to watch out for

Before you start using cache_key there are some edge cases to watch out for.

Consider you have an application where there are 5 entries in users table with city Miami.

Using limit puts incorrect size in cache key if collection is not loaded.

If you want to fetch three users belonging to city “Miami” then you would execute following query.

 users = User.where(city: 'Miami').limit(3)
 users.cache_key
 => "users/query-67ed32b36805c4b1ec1948b4eef8d58f-3-20160116144936949365"

Here users contains only three records and hence the cache_key has 3 for size of collection.

Now let’s try to execute same query without fetching the records first.

 User.where(name: 'Sam').limit(3).cache_key
 => "users/query-8dc512b1408302d7a51cf1177e478463-5-20160116144936949365"

You can see that the count in the cache is 5 this time even though we have set a limit to 3. This is because the implementation of ActiveRecord::Base#collection_cache_key executes query without limit to fetch the size of the collection.

Cache key doesn’t change when an existing record from a collection is replaced

I want 3 users in the descending order of ids.

 users1 = User.where(city: 'Miami').order('id desc').limit(3)
 users1.cache_key
 => "users/query-57ee9977bb0b04c84711702600aaa24b-3-20160116144936949365"

Above statement will give us users with ids [5, 4, 3].

Now let’s remove the user with id = 3.

 User.find(3).destroy

 users2 = User.where(first_name: 'Sam').order('id desc').limit(3)
 users2.cache_key
 => "users/query-57ee9977bb0b04c84711702600aaa24b-3-20160116144936949365"

Note that cache_key both users1 and users2 is exactly same. This is because none of the parameters that affect the cache key is changed i.e., neither the number of records, nor the query statement, nor the timestamp of the latest record.

There is a discussion undergoing about adding ids of the collection records as part of the cache key. This might help solve the problems discussed above.

Using group query gives incorrect size in the cache key

Just like limit case discussed above cache_key behaves differently when data is loaded and when data is not loaded in memory.

Let’s say that we have two users with first_name “Sam”.

First let’s see a case where collection is not loaded in memory.

 User.select(:first_name).group(:first_name).cache_key
 => "users/query-92270644d1ec90f5962523ed8dd7a795-1-20160118080134697603"

In the above case, the size is 1 in cache_key. For the system mentioned above, the sizes that you will get shall either be 1 or 5. That is, it is size of an arbitrary group.

Now let’s see when collection is first loaded.

 users = User.select(:first_name).group(:first_name)
 users.cache_key
 => "users/query-92270644d1ec90f5962523ed8dd7a795-2-20160118080134697603"

In the above case, the size is 2 in cache_key. You can see that the count in the cache key here is different compared to that where the collection was unloaded even though the query output in both the cases will be exactly same.

In case where the collection is loaded, the size that you get is equal to the total number of groups. So irrespective of what the records in each group are, we may have possibility of having the same cache key value.

Caching in development environment in Rails 5

This blog is part of our Rails 5 series.

In Rails 4 if I’m doing work related to caching then first I need to turn caching “on” by opening file config/environments/development.rb and changing following line.

config.action_controller.perform_caching = false

After changing the value from false to true, I need to restart the server.

This means that if I am testing caching behavior locally then every time I turn caching “on” or “off” I need to restart the server.

New command to create development cache in Rails 5

Rails 5 has introduced a new command to create development cache and help us test how caching behaves in development mode. Here is the issue and here is the pull request.

$ rails dev:cache
Development mode is now being cached.

Execution of the above command creates file caching-dev.txt in tmp directory.

How does it work?

In Rails 5 when a brand new Rails app is created then config/environments/development.rb file will have the following snippet of code.

if Rails.root.join('tmp/caching-dev.txt').exist?
  config.action_controller.perform_caching = true
  config.static_cache_control = "public, max-age=172800"
  config.cache_store = :mem_cache_store
else
  config.action_controller.perform_caching = false
  config.cache_store = :null_store
end

In the above code we are checking if the file tmp/caching-dev.txt is present and then use :mem_cache_store to enable caching only if the file is found.

Also, here is a snippet from the dev cache source code.

def dev_cache
  if File.exist? 'tmp/caching-dev.txt'
    File.delete 'tmp/caching-dev.txt'
    puts 'Development mode is no longer being cached.'
  else
    FileUtils.touch 'tmp/caching-dev.txt'
    puts 'Development mode is now being cached.'
  end

  FileUtils.touch 'tmp/restart.txt'
end

What is the advantage

The advantage is that we do not need to restart the server manually if we want to turn caching “on” or “off”. It is internally taken care by the dev_cache method that is executed when rails dev:cache is executed. You can see in the source code that tmp/restart.txt is being touched.

Please note that this feature is not supported by unicorn, thin and webrick. My guess is that DHH wants this feature because his team uses pow and pow restarts when tmp/restart.txt is touched. He also created an issue for spring to watch tmp/restart.txt long time back.

Disabling development cache

Execute the same command that was used to enable caching. If caching was previously enabled then it will be turned “off” now.

$ rails dev:cache
Development mode is no longer being cached.

Configure PostgreSQL to allow remote connection

By default PostgreSQL is configured to be bound to “localhost”.

$ netstat -nlt
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 0.0.0.0:443             0.0.0.0:*               LISTEN
tcp        0      0 127.0.0.1:11211         0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN
tcp        0      0 127.0.0.1:5432          0.0.0.0:*               LISTEN
tcp        0      0 127.0.0.1:3737          0.0.0.0:*               LISTEN
tcp6       0      0 :::22                   :::*                    LISTEN

As we can see above port 5432 is bound to 127.0.0.1. It means any attempt to connect to the postgresql server from outside the machine will be refused. We can try hitting the port 5432 by using telnet.

$ telnet 107.170.11.79 5432
Trying 107.170.11.79...
telnet: connect to address 107.170.11.79: Connection refused
telnet: Unable to connect to remote host

Configuring postgresql.conf

In order to fix this issue we need to find postgresql.conf. In different systems it is located at different place. I usually search for it.

$ find \ -name "postgresql.conf"
/var/lib/pgsql/9.4/data/postgresql.conf

Open postgresql.conf file and replace line

listen_addresses = 'localhost'

with

listen_addresses = '*'

Now restart postgresql server.

$ netstat -nlt
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 127.0.0.1:11211         0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:5432            0.0.0.0:*               LISTEN
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN
tcp        0      0 127.0.0.1:2812          0.0.0.0:*               LISTEN
tcp6       0      0 ::1:11211               :::*                    LISTEN
tcp6       0      0 :::22                   :::*                    LISTEN
tcp6       0      0 :::5432                 :::*                    LISTEN
tcp6       0      0 ::1:25                  :::*                    LISTEN

Here we can see that “Local Address” for port 5432 has changed to 0.0.0.0.

Configuring pg_hba.conf

Let’s try to connect to remote postgresql server using “psql”.

$ psql -h 107.170.158.89 -U postgres
psql: could not connect to server: Connection refused
	Is the server running on host "107.170.158.89" and accepting
	TCP/IP connections on port 5432?

In order to fix it, open pg_hba.conf and add following entry at the very end.

host    all             all              0.0.0.0/0                       md5
host    all             all              ::/0                            md5

The second entry is for IPv6 network.

Do not get confused by “md5” option mentioned above. All it means is that a password needs to be provided. If you want client to allow collection without providing any password then change “md5” to “trust” and that will allow connection unconditionally.

Restart postgresql server.

$ psql -h 107.170.158.89 -U postgres
Password for user postgres:
psql (9.4.1, server 9.4.5)
Type "help" for help.

postgres=# \l

You should be able to see list of databases.

Now we are able to connect to postgresql server remotely.

Please note that in the real world you should be using extra layer of security by using “iptables”.

Rails 5 brings consistency by wrapping all rake commands using rails

This blog is part of our Rails 5 series.

In Rails 4 some commands start with rails and some commands start with rake. This could be quite confusing for people new to Rails. Let’s see an example.

Our task is to write a database migration and then to run that migration.

rails g migration create_users

Above command creates a migration. Now we need to run that migration.

rake db:migrate

As you can see first command starts with rails and second command starts with rake.

In order to consolidate them we can either use rails for everything or we can use rake for everything.

Choosing rails command over rake command

Some favor using rake over rails. But an important feature missing in Rake is the ability to pass arguments.

rails console development

In order to execute the above command using rake we will have to pass console and development as arguments. We can pass these values using environment variables. That would mean adding additional code in Rake task to fetch right values and then only we will be able to invoke command rails console development.

Rails 5 enables executing rake commands with rails

Rails core team decided to have consistency by enabling rails command to support everything that rake does.

For example in Rails 5 commands like db:migrate, setup, test etc which are part of rake command in Rails 4 are now being supported by rails command. However you can still choose to use rake to run those commands similar to how they were run in Rails 4. This is because Rails community has introduced Rake Proxy instead of completely moving the command options from rake to rails.

What happens internally is that when rails db:migrate command is executed, Rails checks if db:migrate is something that rails natively supports or not. In this case db:migrate is not natively supported by rails, so Rails delegates the execution to Rake via Rake Proxy.

If you want to see all the commands that is supported by rails in Rails 5 then you can get a long list of options by executing rails --help.

More improvements in pipeline

In Rails 4, the routes are usually searched like this.

$ rake routes | grep pattern

There is an effort underway to have a Rails command which might work as shown below.

$ rails routes -g pattern

There is also an effort to enable lookup for controller like this.

$ rails routes -c some_controller

Each form gets its own CSRF token in Rails 5

This blog is part of our Rails 5 series.

We have written an extensive blog on what CSRF is and what steps Rails 4 takes to prevent CSRF. We encourage you to read that blog to fully understand rest of this article.

Nested form can get around CSRF protection offered by Rails 4

A typical form generated in Rails 4 might look like this.

<form method= "post" action="/money_transfer">
  <input type="hidden" name="authenticity_token" value="token_value">
</form>

Using code-injection, a Hacker can add another form tag above the form tag generated by Rails using JavaScript. Now the markup looks like this.

<form method="post" action="http://www.fraud.com/fraud">
<form method= "post" action="/money_transfer">
  <input type="hidden" name="authenticity_token" value="token_value">
</form>
</form>

HTML specification does not allow nested forms.

Since nested forms are not allowed browser will accept the top most level form. In this case that happens to be the form created by the hacker. When this form is submitted then “authenticity_token” is also submitted and Rails will do its check and will say everything is looking good and thus hacker will be able to hack the site.

Rails 5 fixes the issue by generating a custom token for a form

In Rails 5, CSRF token can be added for each form. Each CSRF token will be valid only for the method/action of the form it was included in.

You can add following line to your controller to add authenticity token specific to method and action in each form tag of the controller.

class UsersController < ApplicationController
  self.per_form_csrf_tokens = true
end

Adding that code to each controller feels burdensome. In that case you can enable this behavior for all controllers in the application by adding following line to an initializer.

# config/application.rb
Rails.configuration.action_controller.per_form_csrf_tokens = true

This will add authenticity token specific to method and action in each form tag of the application. After adding that token the generated form might look like as shown below.

<form method= "post" action="/money_transfer">
  <input type="hidden" name="authenticity_token" value="money_transfer_post_action_token">
</form>

Authenticity token included here will be specific to action money_transfer and method post. Attacker can still grab authenticity_token here, but attack will be limited to money_transfer post action.

Rendering views outside of controllers in Rails 5

This blog is part of our Rails 5 series.

Rails request-response cycle is very easy to understand. A request hits the app, a route is matched to a controller action from routes.rb, and finally controller action processes the request and renders HTML or JSON based on the type of the request.

But sometimes we want to render our HTML or JSON response outside of this request-response cycle.

For example let’s say user is allowed to download PDF version of a report on web. This can be done using request-response cycle. We also need to send a weekly report to managers and the email should have the report as an attachment. Now we need to generate the same PDF but since emails are sent using background job the request-response cycle is missing.

Rails 5 has this feature baked in.

Let’s say we have a OrdersController and we want to render individual order outside of controller.

Fire up rails console and execute following command.

OrdersController.render :show, assigns: { order: Order.last }

This will render app/views/orders/show.html.erb with @order set to Order.last. Instance variables can be set using assignsin the same way we use them in controller actions. Those instance variables will be passed to the view that is going to be rendered.

Rendering partials is also possible.

OrdersController.render :_form, locals: { order: Order.last }

This will render app/views/orders/_form.html.erb and will pass order as local variable.

Say I want to render all orders, but in JSON format.

OrdersController.render json: Order.all
# => "[{"id":1, "name":"The Well-Grounded Rubyist", "author":"David A. Black"},
       {"id":2, "name":"Remote: Office not required", "author":"David & Jason"}]

Even rendering simple text is possible.

>> BooksController.render plain: 'this is awesome!'
  Rendered text template (0.0ms)
# => "this is awesome!"

Similar to text, we can also use render file and render template.

Request environment

A typical web request carries it’s own environment with it. We usually handle this environment using request.env in controllers. Certain gems like devise depends on env hash for information such as warden token.

So when we are rendering outside of controller, we need to make sure that the rendering happens with correct environment.

Rails provides a default rack environment for this purpose. The default options used can be accessed through renderer.defaults.

>> OrdersController.renderer.defaults
=> {:http_host=>"example.org", :https=>false, :method=>"get", :script_name=>"", :input=>""}

Internally, Rails will build a new Rack environment based on these options.

Customizing the environment

We can customize environment using method renderer. Let’s say that we need “method as post” and we want “https to be true” for our background job processing.

renderer = ApplicationController.renderer.new(method: 'post', https: true)
# => #<ActionController::Renderer:0x007fdf34453f10 @controller=ApplicationController, @defaults={:http_host=>"example.org", :https=>false, :method=>"get", :script_name=>"", :input=>""}, @env={"HTTP_HOST"=>"example.org", "HTTPS"=>"on", "REQUEST_METHOD"=>"POST", "SCRIPT_NAME"=>"", "rack.input"=>""}>

Now that we have our custom renderer we can use it to generate view.

renderer.render template: 'show', locals: { order: Order.last }

Overall this is a nice feature which enables reuse of existing code.

Not using https might be breaking file uploads

Recently we came across following error when mobile app was trying to upload images using backend API.

==> nginx-error.log <==

2015/12/16 07:34:13 [error] 1440#0: *1021901
sendfile() failed (32: Broken pipe) while sending request to upstream,
client: xxx.xx.xxx.xx, server: app-name.local,
request: "POST /api_endpoint HTTP/1.1",
upstream: "http://unix:/data/apps/app-name/shared/tmp/sockets/unicorn.sock:/api_endpoint",
host: "appname.com"

==> nginx-error.log <==

xxx.xx.xxx.xx - - [16/Dec/2015:07:34:13 - 0500]
"POST /api_endpoint HTTP/1.1" 502 1051 "-"
"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Mobile/13B143 (366691552)"

The backend API was developed using Ruby on Rails and was powered using NGINX HTTP server along with unicorn application server.

Notice the the http response received was 502 which is documented in RFC as shown below.

10.5.3 502 Bad Gateway The server, while acting as a gateway or proxy, received an invalid response from the upstream server it accessed in attempting to fulfill the request.

Debugging the problem

From the first look it seemed that the client_max_body_size parameter for nginx was too less, which might cause the request to fail if the uploaded filesize is greater than the allowed limit.

nginx.conf
http {
  ...
  client_max_body_size 250M;
  ...
}

But that was not the case with our NGINX configuration. The NGINX was configured to accept file size upto 250 megabytes, while the file being uploaded was around 6 megabytes.

In our Rails code we are forcing all traffic to use HTTPS by having production.rb to have following setting.

config.force_ssl = true

What this setting does is, it forces HTTPS by redirecting HTTP requests to their HTTPS counterparts. So any request accessing http://domain.com/path will be redirected to https://domain.com/path

Forcing all traffic to HTTPS still does not explain the vague error sendfile() failed (32: Broken pipe) while sending request to upstream and why the HTTP 502 error which means Bad Gateway

Here is how our nginx.conf is configured.

server {
  ...
  listen 80;
  listen 443 ssl;
  ...
}

NGINX is configured to accept both HTTP and HTTPS requests. This is a pretty standard setting so that application can support both HTTP and HTTPS. What it means is that nginx will accept both HTTP and HTTPS request. It would not force HTTP request to be HTTPS. It would forward HTTP request to rails backend api.

Here is what happens when you upload a file

When we upload a file then NGINX takes that request and passes that request to Rails. NGINX expects a response from Rails only when NGINX is done sending the whole request.

Let’s see what happens when a user visits login page using HTTP. NGINX takes the request and passes the request to Rails. When NGINX is done handing over the request to Rails then NGINX expects a response from Rails. However in this case rather than sending 200 Rails sends a redirect over HTTPS. So now NGINX takes up the request again over HTTPS and then hands over request to Rails. Rails processes the request and returns 200. Everything works out.

Now let’s see what happens when a file is uploaded over HTTP.

User uploads a file over HTTP. NGINX takes up the request and starts sending the file to Rails. More details about the file upload process can be see at RFC. The data is sent to server in chunks. When first chunk is sent to server then server notices that data is being sent over HTTP and it immediately sends a response that data should be sent over HTTPS.

In the meantime on the client side , client is still pushing rest of the data to the server. Client expects a response from the server only when it is done pushing all the data. However in this case server sends a response when client is not expecting it.

NGINX at this time is all confused and thinks something has gone wrong with the gateway and returns HTTP/1.1 502 Bad Gateway and aborts the upload operation. And that how we get the 502 error.

So next time you see an error like this make sure that you are using https to upload the file if server is enforcing https.

Year in review 2015

Year 2015 was an exciting year for BigBinary !

We added more clients. We increased our team size. We wrote more blogs, spoke at more conferences, and made even more videos !

Here is the breakdown.

We love conferences

We presented at 10 conferences across 7 countries, on topics from Rails to ReactJS.

We are all in ReactJS

We at BigBinary adopted ReactJS pretty early. Early in the year we published series of videos titled Learn ReactJS in steps which takes a “Hello World” app into a full TODO application using ReactJS in incremental steps.

Vipul and Prathamesh are currently authoring a book on ReactJS. Check it out at ReactJS by Example- Building Modern Web Applications with React Book

We also started an ios app using React Native. It’s coming along pretty good and soon we should see it in app store.

We authored many blogs

We love sharing our experiences via blogs on various topics like ReactJS, Rails, React Native and Robot framework etc. Here are some of our blogs from 2015.

Video Summary

Open Source

Apart from our team members contributing to various OpenSource projects, we also support some projects from our team. This year, we added and helped build following projects-

  • Wheel : Wheel is our Rails template for new Ruby on Rails projects, with sane defaults and setups for different environments, and common functionalities like image uploaders, debugging, etc.
  • Mail Interceptor : Interception, Forwarding emails in Ruby on Rails application
  • Handy : A collection handy tools and Rails tasks for your Project.
  • Learn ReactJS in Steps : Collection of examples from Learn ReactJS in step video series.
  • DoceboRuby : Ruby wrapper for Decobo LMS API
  • Doctsplit Chef : Check cookbox for docsplit ruby gem
  • Fixtures Dumper : Dump your Rails data to fixtures easily to a database to populate data.

Community Engagement

Along with speaking at various conferences, we also helped organize, our Fun edition of Pune’s regional RubyConf, DeccanRubyConf, and supported other Indian Conferences, including RubyConfIndia, GardenCity RubyConf.

We also help Pune’s local ruby meetup, which had a splendid engagement this year.

Overall we are super excited about what we accomplished in year 2015. We are looking forward to an exciting year of 2016!

Test runner in Rails 5

This blog is part of our Rails 5 series.

If you run bin/rails -h in a Rails 5 app, you will see a new command for running tests.

$ bin/rails -h
Usage: rails COMMAND [ARGS]

The most common rails commands are:
 generate    Generate new code (short-cut alias: "g")
 console     Start the Rails console (short-cut alias: "c")
 server      Start the Rails server (short-cut alias: "s")
 test        Run tests (short-cut alias: "t")

Before Rails 5, we had to use bin/rake test to run tests. But in Rails 5, we can use bin/rails test. It is not just replacement of old rake task but it is backed by a test runner based on maxitest.

Let’s see what bin/rails test can do.

Running a single test

Now it is possible to run a single test out of the box using the line number of the test.

$ bin/rails test test/models/user_test.rb:27

Rails will intelligently run the test from user_test.rb having line number 27. Note that the line number 27 does not need to be first line of the test. Below is an example.

22 def test_valid_user
23    hash = { email: 'bob@exmaple.com',
24             first_name: 'John',
25             last_name: 'Smith' }
26
27    user = User.new hash
28
29    assert user.valid?
30  end

In the above case test_valid_user can be run as long as line number provide is between 22 and 30.

Running multiple tests

You can also pass multiple test paths and Rails will run all of those tests.

$ bin/rails test test/models/user_test.rb:27 test/models/post_test.rb:42

It is also possible to run all tests from a directory.

$ bin/rails test test/controllers test/integration

Improved failure messages

When a test fails, Rails displays a command which can be used to just run the failed test.

$ bin/rails t
Run options: --seed 51858

# Running:

.F

Failure:
PostsControllerTest#test_should_get_new:
Expected response to be a <success>, but was a <302> redirect to <http://test.host/posts>


bin/rails test test/controllers/posts_controller_test.rb:15

I can simply copy bin/rails test test/controllers/posts_controller_test.rb:15 and rerun the failing test.

Failing fast

By default when a test fails then rails reports about the test failures and then moves on to the next test. If you want to stop running the test when a test fails then use option -f.

$ bin/rails t -f
Run options: -f --seed 59599

# Running:

..F

Failure:
PostsControllerTest#test_should_get_new:
Expected response to be a <success>, but was a <302> redirect to <http://test.host/posts>


bin/rails test test/controllers/posts_controller_test.rb:15

Interrupted. Exiting...


Finished in 0.179125s, 16.7481 runs/s, 22.3308 assertions/s.

3 runs, 4 assertions, 1 failures, 0 errors, 0 skips

Defer test output until the end of the full test run

By default when a test fails then rails prints F and then details about the failure like what assertion failed and how to re run the test etc.

If you want to have a clean output of . and F and would like all the test failures report to come at the every end then use option -d.

$ bin/rails t -d
Run options: -d --seed 29906

# Running:

..F...F

Finished in 0.201320s, 34.7704 runs/s, 49.6721 assertions/s.

  1) Failure:
PostsControllerTest#test_should_create_post [/Users/prathamesh/Projects/fun/rails-5-test-runner-app/test/controllers/posts_controller_test.rb:19]:
"Post.count" didn't change by 1.
Expected: 3
  Actual: 2


  2) Failure:
PostsControllerTest#test_should_get_new [/Users/prathamesh/Projects/fun/rails-5-test-runner-app/test/controllers/posts_controller_test.rb:15]:
Expected response to be a <success>, but was a <302> redirect to <http://test.host/posts>

7 runs, 10 assertions, 2 failures, 0 errors, 0 skips

Failed tests:

bin/rails test test/controllers/posts_controller_test.rb:19
bin/rails test test/controllers/posts_controller_test.rb:15

Verbose output

It is also possible to see the verbose output using -v switch. It shows time required to run each test. This would help in detecting slow running tests.

$ bin/rails t -v
Run options: -v --seed 30118

# Running:

PostsControllerTest#test_should_destroy_post = 0.07 s = .
PostsControllerTest#test_should_update_post = 0.01 s = .
PostsControllerTest#test_should_show_post = 0.10 s = .
PostsControllerTest#test_should_create_post = 0.00 s = F

Failure:
PostsControllerTest#test_should_create_post:
"Post.count" didn't change by 1.
Expected: 3
  Actual: 2

bin/rails test test/controllers/posts_controller_test.rb:19

PostsControllerTest#test_should_get_new = 0.02 s = .
PostsControllerTest#test_should_get_index = 0.01 s = .
PostsControllerTest#test_should_get_edit = 0.00 s = .

Finished in 0.210071s, 33.3220 runs/s, 47.6028 assertions/s.

7 runs, 10 assertions, 1 failures, 0 errors, 0 skips

Better backtrace output

By default when an error is encountered while running the test then the output does not contain full stacktrace. This makes debugging little bit difficult.

Error:
PostsControllerTest#test_should_create_post:
NameError: undefined local variable or method `boom' for #<PostsController:0x007f86bc62b728>
    app/controllers/posts_controller.rb:29:in `create'
    test/controllers/posts_controller_test.rb:20:in `block (2 levels) in <class:PostsControllerTest>'
    test/controllers/posts_controller_test.rb:19:in `block in <class:PostsControllerTest

Now we can use -b switch, which will display complete backtrace of error message.

$ bin/rails t -b

Error:
PostsControllerTest#test_should_create_post:
NameError: undefined local variable or method `boom' for #<PostsController:0x007fc53c4eb868>
    /rails-5-test-runner-app/app/controllers/posts_controller.rb:29:in `create'
    /sources/rails/actionpack/lib/action_controller/metal/basic_implicit_render.rb:4:in `send_action'
    /sources/rails/actionpack/lib/abstract_controller/base.rb:183:in `process_action'
    /sources/rails/actionpack/lib/action_controller/metal/rendering.rb:30:in `process_action'
    /sources/rails/actionpack/lib/abstract_controller/callbacks.rb:20:in `block in process_action'
    /sources/rails/activesupport/lib/active_support/callbacks.rb:126:in `call'
.....
    /sources/rails/activesupport/lib/active_support/testing/assertions.rb:71:in `assert_difference'
    /rails-5-test-runner-app/test/controllers/posts_controller_test.rb:19:in `block in <class:PostsControllerTest>'

Switch -s to provide your own seed

Now we can also provide our own seed using -s switch.

$ bin/rails t --s 42000

Switch -n to run matching tests

Switch -n will run tests matching the given string or regular expression pattern.

$ bin/rails t -n "/create/"
Run options: -n /create/ --seed 24558

# Running:

E

Error:
PostsControllerTest#test_should_create_post:
NameError: undefined local variable or method `boom' for #<PostsController:0x007faa39c2df90>
    app/controllers/posts_controller.rb:29:in `create'
    test/controllers/posts_controller_test.rb:20:in `block (2 levels) in <class:PostsControllerTest>'
    test/controllers/posts_controller_test.rb:19:in `block in <class:PostsControllerTest>'


bin/rails test test/controllers/posts_controller_test.rb:18

Finished in 0.073857s, 13.5396 runs/s, 0.0000 assertions/s.

1 runs, 0 assertions, 0 failures, 1 errors, 0 skips

Colored output

Now by default we will get colored output. No need to add additional gem to colored output.

colored test output

With all these awesome features, testing Rails 5 apps has definitely become a better experience. Rails has shipped all these features within the framework itself so you don’t have to use multiple gems and libraries to achieve all of these things.