~/dev

Musings on Ruby, Rails, and Web Technologies

To Engine or Not to Engine?

<tl-dr>

Rails purist wants to use Rails Engines only for the separation of development concerns to support Follow-The-Sun development. Accepting criticism, constructive or otherwise, on this idea.

</tl-dr>

Is breaking up a Rails application into engines based only on development coordination a Rails anti-pattern? Or does splitting an application along logical lines for development make sense in an environment with disparate teams? WhitePages, like many other Ruby and Rails shops, has been struggling with the current hyper-competitive marketplace for talented engineers. WhitePages maintains very high standards for it’s engineering teams (with the exception of the author) and has been forced to widen its search for talent not only nationwide, but globally. Our team has been fortunate enough to find an extremely talented Ruby and Rails engineer in Hungary. Now comes the real challenge: How do we integrate and coordinate development? As many engineering teams have experienced, follow-the-sun-development introduces several challenges for coordination of development efforts. As an Agile team, the lack of overlapping work hours and limited communication exacerbates these issues. Can Rails Engines provide us a mechanism to break down our project into parts that can be easier managed by co-located teams?

With a nine hour time differential, our daily overlap is extremely limited. We needed a way to allow the remote team to work independently without cross dependencies, eliminating as much as possible blocking events to one team caused by another. These events generally mean at the least, a several man-hour loss of productivity as changes are rolled back or corrections made to the offending code or systems. The answer seems to lie in separating development concerns between the two teams as much as possible. Enter Rails Engines.

Conventionally, Rails Engines solve a code redundancy problem. A common Rails mantra is Don’t Repeat Yourself (DRY). When a piece of functionality is used across several applications, Engines solve the problem of repeated code by making the redundant code portable and easily shared among applications. Functionality like user authentication, role management, and application administration are commonly implemented as Rails Engines. What we wanted to do was make use of Engines, not solely for DRY purposes, but to divide a single application into smaller, easily digestible sections to be implemented in parallel, independent of each other. Let’s illustrate with an example:

WhitePages Names (names.whitepages.com) and WhitePages Phones (phones.whitepages.com) allow for index-like browsing of WhitePages data. Both pieces of functionality share the same application layout and branding, navigation, advertising, etc, but are clearly distinguishable in terms of functionality. This functionality is unique to the WhitePages app and not repeated on any other WhitePages managed properties. By implementing both of these features as full Rails Engines rather than implementing them within the WhitePages application itself, we have sectioned off two well defined pieces of development that can be worked on independent from one another, and in relative isolation. The only moving part should be the application that they both share. If implemented correctly, there shouldn’t be any dependencies on each other or the application itself, drastically limiting the chances of a team effecting the productivity of another. Quality control and performance management is clearer, and bugs introduced into production can be rolled back easily with a change to the Gemfile of the host application, without having to roll back the code of the host application or any other engines included in that application. This is a huge win for taking some of the bite out of the potential complications of globalized development.

As somewhat of a Rails purist myself, I was initially resistant to this idea. In my mind, Engines were purely a DRY containment structure wrapped in an elegant deployment mechanism. The model described above does not fit that description. The benefits to the development life cycle however, have won me over. I’m curious what the community thinks about this paradigm. A Rails anti-pattern? Or a valid use of a utilitarian technology? Sound off in the comments or hit me up on twitter @_saarinen …

Mounting a Rails 4 App in a Subdirectory Using NGINX

Here at WhitePages, I’ve been working on a Rails 4.0.rc1 application to serve third party integration functionality. I needed this service to be available to all domains and sub-domains serviced by WhitePages ( of which there are several ), and I needed the application to be available on a same-origin basis for integration reasons. These constraints necessitated deploying my new application mounted to a unique subdirectory that could be detected by the load balancer on any domain, and routed to our NGINX instance. The question then becomes: “How can I mount my Rails app on a subdirectory?” Let’s take this step by step:

1: Letting Rails know it is mounted on a subdirectory

The first task to mark off the list is to tell Rails that the routes that it needs to generate and answer to will include a subdirectory. Rails, thankfully, provides just such a configuration variable. In keeping with Rails 4 conventions, I placed this config change in an intializer in config/initializers:

config/initializers/mount_location.rb
1
2
3
# Mount this application to a unique subdirectory
# You can either use Rails.application.config or <AppName>::Application.config
<AppName>::Application.config.relative_url_root = '/my_directory'
2: Getting Rails to respond on a subdirectory

Now that rails knows that it is mounted on a subdirectory, we need to configure rack to pass requests on that subdirectory to our Rails application. We do that by modifying the config.ru file in our application root:

config.ru
1
2
3
4
5
6
7
# This file is used by Rack-based servers to start the application.

require ::File.expand_path('../config/environment',  __FILE__)

map <AppName>::Application.config.relative_url_root || "/" do
  run Rails.application
end

Here we check to see if our application has been configured with a relative_url_root. If so, we mount our rails app at that location.

Starting up our Rails server, our application should now be responding on the subdirectory we mounted it on. Success!! Well…not quite. All of our static assets are currently responding correctly because they are being served from our Rails 4 application in development mode. When running in production mode, all my static assets are failing to load? What is going on? It looks like NGINX is not resolving paths to my static assets correctly. Time to make some NGINX configuration changes to make sure it looks for static assets in the right location when we are running in production mode…

3: Telling NGINX where my static assets are

OK, heres the problem, the following entry comes from the nginx site config file:

nginx.conf
1
2
3
4
5
6
7
root <path_to_my_app>/public;

location @proxy_to_app {
  proxy_pass http://<%= @service_name %>_workers;
}

try_files $uri @proxy_to_app;

When a request comes to our application on our subdirectory path, for example http://test.com/our_path/assets/images/test.png, NGINX will look in our public directory for a file mathing the path ‘/our_path/assets/images/test.png’. The file doesn’t exist there, it exists at ‘/assets/images/test.png’. How can we tell NGINX to drop our subdirectory from the path it is trying to locate our static assets from? The answer lies in the alias directive. Using the alias directive, we can use a location matcher that matches our subdirectory and NGINX will drop the matched element of our location from the static asset search path. Let’s look at the modified code:

nginx.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
root <path_to_my_app>/public;

location @proxy_to_app {
  proxy_pass http://<%= @service_name %>_workers;
}

location /our_path/ {
  alias <path_to_my_app>/public/;

  try_files $uri @proxy_to_app;
}

try_files $uri @proxy_to_app;

Success at last! Now NGINX is correctly searching our public directory for our apps static files, and our Rails 4 application is correctly responding to the routes containing the subdirectory and generating paths with our configured subdirectory.

Resourceful Routes in the Real World

When designing a resourceful object hierarchy in Rails, often a single model has meaning in multiple different contexts. Take for example an application managing rental properties. This application has two main models, Properties and Tenants. These models have obvious relationships with each other. Relating a set of tenants to a specific property, for example. These models also have value independent of this relationship. At WhitePages.com we are often modeling entities that exist in many different contexts, somewhat like a graph. The question then becomes, how can I model a relationship such as a has_many/belongs_to using resourceful routes while still allowing direct root access to a model, or potentially access through several different model connections?. Can I access the same controller resource through different routes? The answer is yes. Lets take a look at a basic example to see how we can make this happen.

Let’s start with the following models:

Property and Tenant Models
1
2
3
4
5
6
7
8
class Property < ActiveRecord::Base
  has_many :tenants
  accepts_nested_attributes_for :tenants
end

class Tenant < ActiveRecord::Base
  belongs_to :property
end

The default scaffolding generator gives us the following routes with both resources at root:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Prefix Verb   URI Pattern                    Controller#Action
      tenants GET    /tenants(.:format)             tenants#index
              POST   /tenants(.:format)             tenants#create
   new_tenant GET    /tenants/new(.:format)         tenants#new
  edit_tenant GET    /tenants/:id/edit(.:format)    tenants#edit
       tenant GET    /tenants/:id(.:format)         tenants#show
              PATCH  /tenants/:id(.:format)         tenants#update
              PUT    /tenants/:id(.:format)         tenants#update
              DELETE /tenants/:id(.:format)         tenants#destroy
   properties GET    /properties(.:format)          properties#index
              POST   /properties(.:format)          properties#create
 new_property GET    /properties/new(.:format)      properties#new
edit_property GET    /properties/:id/edit(.:format) properties#edit
     property GET    /properties/:id(.:format)      properties#show
              PATCH  /properties/:id(.:format)      properties#update
              PUT    /properties/:id(.:format)      properties#update
              DELETE /properties/:id(.:format)      properties#destroy

These routes are workable, but do not correctly illustrate our designed model. Modeling the routes to match our belongs_to/has_many relationship, we would generate the following nested routes in config/routes.rb:

routes.rb
1
2
3
4
5
TestApp::Application.routes.draw do
  resources :properties do
    resources :tenants
  end
end

Nesting our resources like this, we now have the following routes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Prefix Verb   URI Pattern                                         Controller#Action
    property_tenants GET    /properties/:property_id/tenants(.:format)          tenants#index
                     POST   /properties/:property_id/tenants(.:format)          tenants#create
 new_property_tenant GET    /properties/:property_id/tenants/new(.:format)      tenants#new
edit_property_tenant GET    /properties/:property_id/tenants/:id/edit(.:format) tenants#edit
     property_tenant GET    /properties/:property_id/tenants/:id(.:format)      tenants#show
                     PATCH  /properties/:property_id/tenants/:id(.:format)      tenants#update
                     PUT    /properties/:property_id/tenants/:id(.:format)      tenants#update
                     DELETE /properties/:property_id/tenants/:id(.:format)      tenants#destroy
          properties GET    /properties(.:format)                               properties#index
                     POST   /properties(.:format)                               properties#create
        new_property GET    /properties/new(.:format)                           properties#new
       edit_property GET    /properties/:id/edit(.:format)                      properties#edit
            property GET    /properties/:id(.:format)                           properties#show
                     PATCH  /properties/:id(.:format)                           properties#update
                     PUT    /properties/:id(.:format)                           properties#update
                     DELETE /properties/:id(.:format)                           properties#destroy

Much better! This routing structure allows us to access our list of properties at /properties, a specific property at /properties/:id, a list of a properties Tenants at /properties/:property_id/tenants, and a specific tenant at /properties/:property_id/tenants/:id. This now models the relationship we’ve created between properties and Tenants. The only problem now is that our tenant controller does not know how to use the :property_id parameter correctly to set our scope. We need to make a few modifications to make use of the provided property_id. The majority of our changes are in ‘index’ and ‘new’.

tenants_controller.rb
1
2
3
4
5
6
7
8
9
class TenantsController < ApplicationController
  def index
    @tenants = Tenant.find_all_by_property_id(params[:property_id])
  end

  def new
    @tenant = Tenant.new({property_id: params[:property_id]})
  end
end

You can see from the code above that we are now using the property_id parameter provided by our route to inform ActiveRecord of the scope of our search as well as initializing new models. Hooray! But this isn’t the goal we are looking for. What we want is the ability to see Tenants in both the context of a property, but also, to view Tenants without any context. This will allow us to view all our Tenants without regard to what Property they are assigned to, and provide a Tenant details path without having to find through the Property relationship. Lets start with adding the rout to our routes file:

routes.rb
1
2
3
4
5
6
7
TestApp::Application.routes.draw do
  resources :tenants

  resources :properties do
    resources :tenants
  end
end

This gives us the following routes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Prefix Verb   URI Pattern                                         Controller#Action
             tenants GET    /tenants(.:format)                                  tenants#index
                     POST   /tenants(.:format)                                  tenants#create
          new_tenant GET    /tenants/new(.:format)                              tenants#new
         edit_tenant GET    /tenants/:id/edit(.:format)                         tenants#edit
              tenant GET    /tenants/:id(.:format)                              tenants#show
                     PATCH  /tenants/:id(.:format)                              tenants#update
                     PUT    /tenants/:id(.:format)                              tenants#update
                     DELETE /tenants/:id(.:format)                              tenants#destroy
    property_tenants GET    /properties/:property_id/tenants(.:format)          tenants#index
                     POST   /properties/:property_id/tenants(.:format)          tenants#create
 new_property_tenant GET    /properties/:property_id/tenants/new(.:format)      tenants#new
edit_property_tenant GET    /properties/:property_id/tenants/:id/edit(.:format) tenants#edit
     property_tenant GET    /properties/:property_id/tenants/:id(.:format)      tenants#show
                     PATCH  /properties/:property_id/tenants/:id(.:format)      tenants#update
                     PUT    /properties/:property_id/tenants/:id(.:format)      tenants#update
                     DELETE /properties/:property_id/tenants/:id(.:format)      tenants#destroy
          properties GET    /properties(.:format)                               properties#index
                     POST   /properties(.:format)                               properties#create
        new_property GET    /properties/new(.:format)                           properties#new
       edit_property GET    /properties/:id/edit(.:format)                      properties#edit
            property GET    /properties/:id(.:format)                           properties#show
                     PATCH  /properties/:id(.:format)                           properties#update
                     PUT    /properties/:id(.:format)                           properties#update
                     DELETE /properties/:id(.:format)                           properties#destroy

Now we have both root access to our Tenants as well as routes through our properties relationship! Success! Well, not yet. If we use any of the root routes to Tenants, our controller is going to throw an error, as property_id is not being sent along. Let’s fix that right now:

tenants_controller.rb
1
2
3
4
5
6
7
8
9
class TenantsController < ApplicationController
  def index
    @tenants = params[:property_id].nil? ? Tenant.all : Tenant.find_all_by_property_id(params[:property_id])
  end

  def new
    @tenant = params[:property_id].nil? ? Tenant.new : Tenant.new({property_id: params[:property_id]})
  end
end

Now we can declare success. We now have the ability to access all our Tenants through a root route, as well as through their defined relationship through Properties using a nested route.

Thoughts on RailsConf 2013

I left Portland this year with mixed feelings after the four days of RailsConf 2013. Listening to DHH’s opening keynote outlining the focus of the technology decisions made for the Rails codebase, (specifically, a focus on document based UI rather than a richer, thick client UI) I was prepared to look at the Rails 4 featureset, and decisions made for this latest version, from that world view. Through the rest of the conference, talks and technologies presented often were in direct conflict with this direction. Talks focusing on utilization of the new web socket ‘Live Streaming’ framework for Rails 4, a focus on “Rails API” architectures using thick clients and JSON transport mediums, and a recurring focus on JavaScript MVC libraries such as ember.js had me questioning whether the community is seeing and embracing the same “Rails is document based” vision as DHH. I am in agreement with DHH’s vision of not diluting the Rails stack to attempt to “be everything to everyone”, but enjoyed haring from those who are pushing their visions of where the stack should go.

Several talks (such as one entitled ‘Cache = Cash’) demonstrated one alignment of the Rails community: the quest for performance. Many of the new technologies for Rails 4, including the new (and contentious) TurboLinks framework, have been introduced to increase end user performance. I am still on the fence regarding the usefulness of the TurboLinks framework, or even whether the implementation is fully baked enough for production web sites. For example: when developing for an ad-supported application, dealing with third party JavaScript is a constant headache but a necessary evil. Interrupting the standard request/response model of the web page (which TurboLinks does), and the browser events associated with these actions may have far reaching impacts on the viability of code that we have no organizational control over. Opting out of the TurboLinks functionality seems to be grossly manual and error prone, requiring all generated links to be specifically marked up. There may be ways around this but further investigation is needed. Fodder for another Blog post perhaps.

All in all, my biggest takeaway from RailsConf 2013. was motivation to contribute to the open source projects built to the benefit of a great many engineers and organization. The inspiring work shown at the conference, and the people behind them, are truly the engine powering the growth we have seen in the Rails ecosystem.

Countdown to RubyConf?