Year 5 with Rails

In year five with Rails I finally started contributing to high profile open source projects (ActiveAdmin, ActiveMerchant), albeit with mixed success. Understanding complex libraries written by others reacquainted me with debugging in RubyMine. In addition I published the first gems of my own using jeweler, taking my public GitHub contributions over 200 for the year. Working on OSS exposed me to Colloquy IRC and Travis CI. I contributed slightly more to StackOverflow and project related Google Groups.

At my employer our ‘mature’ Rails codebase has grown from 20 to 30KLOC, monitored soberly by Coveralls (87%), CodeClimate (2.8), Codeship CI, Rubocop, Gemnasium and Bliss. I remain committed to SublimeText, now with GitGutter and SublimeGit, and to RSpec, influenced heavily by a RSpec workshop at RailsConf 2014. New gems adopted this year included (tentatively) interactors, oink and thor. Documentation has been mildly improved using YARD.

Scaling Complex Rails Applications

After ten years of development Rails is a mature framework, and a recurring theme at conferences is how to build and maintain Rails applications that are increasingly complex. Rails achieved early success as an opinionated framework that emphasizes MVC and convention over configuration, but as developers have tackled ever more ambitious projects there has been divergence as to how to structure them.

What is generally agreed is that business logic should reside in models, away from controllers, views and API implementations. This has led to ‘fat’ models and discussion of strategies to manage them such as this blog post from CodeClimate titled ‘7 Patterns to Refactor Fat ActiveRecord Models‘. An important point often missed when first learning Rails is that app/models is not intended exclusively for classes persisted with ActiveRecord as it may include other domain objects. In addition to domain objects other popular patterns used to structure code include form objects, presenters, decorators and service objects. Presenters and decorators intermediate between controllers and views, whereas service objects intermediate between controllers and models.

Presenters and decorators are probably less controversial in the Rails community and have done much to replace view helpers. Service objects, however, have been disparaged by DHH in favor of concerns, but many developers with a traditional OOP background are unconvinced by his approach. One example project that uses service objects is GitHub itself. More recently Collective Idea have published an ‘interactor’ gem that elaborates this approach.

These strategies all result in more classes, and probably the first step in managing a codebase with many classes is to introduce namespaces. The Rails class loader automatically searches sub-folders matching namespaces so source files can be arranged similarly to the namespace hierarchy. Given most developers nowadays navigate code using global search or ‘jump anywhere’ features in their text editor the neater project directory layout is of limited benefit, but namespacing can reduce name conflicts with libraries or between different areas of an application.

The next step beyond namespacing, suggested by Stephan Hagemann is to divide a project into internal engines or gems, an approach labelled Component Based Rails Architecture. An example project is provided at https://github.com/shageman/the_next_big_thing Each component has its own test suite so the scope of regression testing of any feature change is more limited. If a component proves reusable it can be split out from the original project altogether.

Beyond a component based architecture the final step to scaling a complex application is to break it apart into separate services interacting with an internal API, an approach referred to as Service Oriented Architecture (SOA). I will defer comment on the benefits and challenges of such an approach to a later post.

See also

Rails Development Resources

Wyncode recently blogged a “top 10” list of Rails development resources, what would be on my list?

Getting started on OS X with MRI Ruby you typically need to install Xcode command line tools, homebrew and rvm (with JewelryBox) or rbenv.  MRI Ruby (aka. CRuby, aka. yarv) is the de facto reference implementation written in C, though JRuby has gained a following in larger enterprises.  Microsoft at one time supported development of Iron Ruby for .NET but it has failed to gain much following.

Emerging in part as a reaction against the complexity of Java EE, the Rails community has tended to favor lightweight text editors such as TextMate, and now Sublime Text, over more heavyweight IDEs such as Eclipse and Netbeans, though JetBrain’s Rubymine has had some success.  Vim continues to be popular with rubyists, with projects like Hermes.

If you don’t want to setup and maintain a local development environment you can now simply rent an online environment from Cloud9 or Nitrous.io.  Other essential online tools include shared code repositories like GitHub and Bitbucket and bug tracking/project management tools such as Pivotal Tracker, Assembla or CodeBaseHQ.

One of the strengths of Rails is it’s excellent community maintained documentation, both guides and apidocs.  Another great API resource is devdocs.io.

For online learning Code School and Rails for Zombies is a popular beginner resource, and Confreaks is great for catching up with conference presentations.  Ryan Bates stopped publishing RailsCasts a year ago but Avdi Grimm has stepped in with Ruby Tapas.

Finally, one of the most important ingredients of any programming ecosystem are the libraries: Ruby Toolbox is a great place for learning about available gems from Haml to Inherited Resource, from Nokogiri to Grape and ActiveAdmin.

I hope I’ve piqued your interest to look at any one of these.

Year four with Rails

Another year with Rails already.  The e-commerce project I’m leading has grown to 20kLOC with 1,400 tests maintained by an international team coordinated using Assembla.  The biggest addition to our toolbox has been Airbrake and its seemingly unrelenting production bug reports.  The other biggest challenge has been refactoring increasingly sophisticated factories to support the ever growing test suite.  Our infrastructure has changed significantly also in the face of continuing growth, but that’s another story.

A third year with Rails

This last year Ruby 2.0 has been released (Feb) and Rails 4.0 (Jun). I’ve embraced Sublime Text and git flow branching. Spork has been supplanted by Zeus. I’ve dabbled with OpenStruct, metaprogramming and AcitiveSupport::Concerns. New gems I’ve been learning include Enumerize, InheritedResources, Formtastic, Resque and Mechanize. I published a minor extension to the authorize-net gem and got hands on with CoffeeScript.

A second year with Rails

My second year with Rails has been increasingly team oriented, using online collaboration tools like GitHub, PivotalTracker, Atlassian BitBucket, CodeBaseHQ and Assembla.  Even though I was productive with Windows for a year on a one person project I’ve now joined my colleagues in using OS X with RVM.  My use of TDD has continued with adoption of Cucumber, RSpec, FactoryGirl and Spork, and for better or worse I am now using MySQL more than PostgreSQL.

The past year has seen the release of Ruby 1.9.3 (October) and Rails 3.1 (August) and 3.2 (January).  Ruby 1.8.7 has been scheduled for no further support after June 2013.

Testing ssl_requirement with pound

ssl_requirement is a Rails gem by DHH to declaratively redirect requests to SSL.  It is useful, for example, to ensure all access to a checkout page is secure.  SSL encryption, however, is usually handled by a webserver such as Nginx or Apache, and these are not typically used in Rails test mode.  It appears most developers simply disable SSL redirects during testing, but this allows certain defects such as correct handling of flash messages to go undetected.  To test SSL redirects I’ve started using a lightweight, secure reverse proxy called pound. The pound configuration file can be kept to a bare minimum to forward requests from port 8443 to 3000 and encrypt the response:

ListenHTTPS
  Address 127.0.0.1
  Port    8443
  Cert    "pound.pem"
  AddHeader "X_FORWARDED_PROTO: https"
  Service
    BackEnd
      Address 127.0.0.1
      Port    3000
    End
  End
End

To run pound:

pound -vf ./pound.conf

Apache, Unicorn & SSL

Having used Apache with Mongrel, Thin and Passenger, I’ve now moved on to Unicorn.  Setting up Apache on Ubuntu to proxy to Unicorn requires the following installation commands:

apt-get install apache2 -y
apt-get install libapache2-mod-proxy-html libxml2-dev -y
a2enmod headers
a2enmod proxy
a2enmod proxy_http
a2enmod proxy_balancer
a2enmod rewrite
a2enmod ssl
/etc/init.d/apache2 restart

With these modules in place a virtual host can be configured something like this:

<VirtualHost *:80>
  ServerName     myhost.example.com
  DocumentRoot   /opt/example/app/public

  RewriteEngine On
  # Redirect all non-static requests to unicorn
  RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
  RewriteRule ^/(.*)$ balancer://unicornservers%{REQUEST_URI} [P,QSA,L]

  <Proxy balancer://unicornservers>
    Allow from any
    BalancerMember http://127.0.0.1:8080
  </Proxy>
</VirtualHost>

<VirtualHost *:443>
  ServerName     myhost.example.com
  DocumentRoot   /opt/example/app/public

  RewriteEngine On
  # Redirect all non-static requests to unicorn
  RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
  RewriteRule ^/(.*)$ balancer://unicornservers%{REQUEST_URI} [P,QSA,L]
  RequestHeader set X-Forwarded-Proto "https"

  <Proxy balancer://unicornservers>
    Allow from any
    BalancerMember http://127.0.0.1:8080
  </Proxy>

  SSLEngine     on
  SSLCertificateFile /etc/apache2/ssl/ssl.crt
  SSLCertificateKeyFile /etc/apache2/ssl/ssl.key
</VirtualHost>

mod_headers and the RequestHeader directive are useful if the Rails app is using ssl_requirement.

Rails development with multiple MySQL instances

Sometimes supporting multiple clients or projects can result in a need for more than one version of MySQL on the same workstation. This can be a pain, especially with the Ruby native mysql2 gem that can fail with a cryptic ‘failed to allocate memory’ when used with the wrong client libraries.

To address this I’ve started using Bitnami’s RubyStack.  Bitnami’s stacks provide a graphical installer that allows you to quickly specify a non-standard installation location and ports for your developer infrastructure so you can easily install the stack alongside the project being supported.

Instead of using RVM for development I now prepend the RubyStack to my path before working on the supported project:

RUBYSTACK=~/.../rubystack-3.2.1-0
PATH=$RUBYSTACK/mysql/bin:$PATH
PATH=$RUBYSTACK/ruby/bin:$PATH

Be aware the Bitnami stacks are 32-bit. The stacks include graphical and command line tools to start and stop components, see the Bitnami wiki components for more details. Bitnami AIMs are also available on EC2, various deployment options are discussed in the RubyStack README.

One year with Rails

One year since I picked up ‘Agile Web Development with Rails’ and coded my first Rails model/controller/view: seems so long ago now.

Things I’ve mastered in the past year:

  • Test driven development using unit and functional tests.
  • Master-detail forms using Ajax to add and delete rows, fetching product details in JSON for codes entered into a line item.
  • Prototype and jQuery, including jQuery UI datepickers.
  • Loading and parsing XML data files using Nokogiri.
  • Building complex reports using nested views in PostgreSQL, maintained using rails_sql_views.
  • Creating formatted, multi-tab Excel spreadsheets using Spreadsheet gem.
  • Creating pixel-perfect PDF reports using JasperReports loaded using Java-Ruby bridge.
  • Hosting using Apache with Passenger, Mongrel, Thin and Unicorn.
  • Monitoring using ScoutApp.
  • Upgrading a production app from Rails 2.3.4 to Rails 3.0.9.
  • Switching from ERB to HAML.

Posts navigation

1 2 3
Scroll to top