Using a headless Rails for tasks and tests

Posted about 1 month back at The Pug Automatic

You might know that the Rails console gives you an app object to interact with:

<figure class="code">
>> app.items_path
# => "/items"
>> app.get "/items"
# => 200
>> app.response.body
# => "<!DOCTYPE html><html>My items!</html>"
>> app.response.success?
# => true
</figure>

You might also know that this is the same thing you’re using in Rails integration tests:

<figure class="code">
get "/items"
expect(response).to be_success
</figure>

In both cases you’re interacting with an ActionDispatch::Integration::Session.

Here are two more uses for it.

Rake tasks

If you have an app that receives non-GET webhooks, it’s a bit of a bother to curl those when you want to trigger them in development.

Instead, you can do it from a Rake task:

<figure class="code">
namespace :dev do
  desc "Fake webhook"
  task :webhook => :environment do
    session = ActionDispatch::Integration::Session.new(Rails.application)
    response = session.post "/my_webhook", { my: "data" }, { "X-Some-Header" => "some value" }
    puts "Done with response code #{response}"
  end
end
</figure>

I used this in a current project to fake incoming GitHub webhooks.

You could of course make your controller a thin wrapper around some object that does most of the work, and just call that object from tasks, but the HTTP part isn’t neglible with things like webhooks, and it can be useful to go through the whole thing sometimes during development.

Non-interactive sessions in feature tests

Your Capybara tests can alternate between multiple interactive sessions very easily, which is super handy for testing real time interactions, e.g. a chat.

But Capybara only wants you to test through the fingers of a user. If the user doesn’t click to submit a form, you can’t easily trigger a POST request.

So if you want to test something like an incoming POST webhook during an interactive user session, you can again use our friend ActionDispatch::Integration::Session:

<figure class="code">
it "reloads when a webhook comes in" do
  visit "/"

  expect_page_to_reload do
    the_heroku_webhook_is_triggered
  end
end

def expect_page_to_reload
  page.evaluate_script "window.notReloaded = true"
  yield
  sleep 0.01  # Sadly, we need this.
  expect(page.evaluate_script("window.notReloaded")).to be_falsy
end

def the_heroku_webhook_is_triggered
  session = ActionDispatch::Integration::Session.new(Rails.application)
  session.post("/heroku_webhook")
end
</figure>

This too is extracted from a current project.

Explain the trade-off

Posted about 1 month back at The Pug Automatic

This is a post in my series on things I’ve remarked on in code review.

Say there’s a commit with a message like

Bump number of workers to fix image upload problem

and a change like

<figure class="code">
- "number_of_workers": 4,
+ "number_of_workers": 8,
</figure>

Then I’m left wondering why it wasn’t at 8 to start with. Or why we don’t bump it all the way to 16, or 32. Surely more workers are better?

Usually there’s a trade-off at play. More workers use more memory, perhaps.

If you tweak a value I want to know that you’ve considered that trade-off.

Ideally, a code comment (rather than the commit message) will explain what the trade-off is. Then the next tweaker sees it, and the next code reviewer too.

Make your assumptions explode

Posted about 1 month back at The Pug Automatic

This is a post in my series on things I’ve remarked on in code review.

You constantly make simplifying assumptions in code. Like this one:

<figure class="code">
class Order < ActiveRecord::Base
  has_many :line_items

  def currency
    # All line items have the same currency.
    line_items.first.currency
  end
end
</figure>

The assumption in itself is a good thing: we shouldn’t design for multiple currencies if we don’t need to.

But it’s dangerous that the assumption is left unchecked. It may be true today, but will it be true tomorrow? Mixing up currencies can have pretty dire consequences.

There’s a simple solution. Make your assumptions explode!

<figure class="code">
def currency
  currencies = line_items.map(&:currency).uniq
  raise "Assumed all line items have the same currency" if currencies.length > 1
  currencies.first
end
</figure>

You still reap the benefits of the simplifying assumption but can also trust that it remains valid.

If you make the same type of assertion in multiple places, you can of course extract it, or find some gem. We do CurrencyVerification.assert_single_currency(records) in one project, for example.

Assertive programming is a thing, but I haven’t see it a lot in Ruby outside tests.

The Golden Rule for programmers

Posted about 1 month back at The Pug Automatic

The Golden Rule is the idea that one should treat others as one would like to be treated.

I try to follow this rule when it comes to solving problems in programming, and I wish all of us would do so.

We all search for solutions to issues we encounter. When we do, we’re obviously expecting someone else to have provided the answer. So help the next person out in the same way. I would feel dirty and selfish if I didn’t.

If you solve (or partially solve, or work around) a tricky issue that others may also run into, try to do one or more of these:

  • answer a Stack Overflow question you found
  • add a new Stack Overflow question and answer it yourself
  • comment on a blog post or GitHub issue you found
  • report a bug
  • contribute a code or documentation fix
  • create a code snippet (e.g. on Gist) or a public repository
  • write a blog post

In my experience Twitter isn’t great as the only place to put a solution – search engine indexing can be iffy.

When you write it up, consider how you tried to find the answer. Where did you look? What terms did you search for? Make sure there’s something to find.

You’ll be surprised at the number of people you end up helping.

Font Awesome icons on file fields in WebKit

Posted about 1 month back at The Pug Automatic

Screenshot

I wanted to add a Font Awesome icon to an <input type="file"> field.

This is a private project that will only be used with WebKit browsers (e.g. Chrome or Mobile Safari), so I have not considered cross-browser compatibility or fallbacks.

You can position an element on top of the file field and pass clicks through as described e.g. on QuirksMode.org, but it requires JavaScript for some things and gets complicated.

Keeping in mind that I only cared about WebKit browsers, I simply did this:

<figure class="code">
input[type=file]::-webkit-file-upload-button {
  padding: 6px 10px;  /* Make it pretty. */
  /* …other prettification like background color… */

  /* Make room for icon. */
  padding-left: 35px;
}

input[type=file] {
  position: relative;
}

input[type=file]:before {
  font-family: "FontAwesome";
  /* http://fortawesome.github.io/Font-Awesome/icon/picture-o/ */
  content: "\f03e";
  color: #aaa;

  position: absolute;
  top: 0;
  left: 0;

  padding: 6px 10px;  /* NOTE: Same padding as on the upload button. */
  line-height: 21px;  /* Magic number :/ Modify this until it looks good. */
}
</figure>

So the file upload button gets padding to make room for the icon.

Then the icon is absolutely positioned within the file field (couldn’t get it to work within the upload button).

The content value is a \ followed by the Unicode code point as listed on each icon’s page or in the cheatsheet (after &#x).

font-awesome-sass

If you use font-awesome-sass in a Ruby/Rails project, you can even use its variables:

<figure class="code">
input[type=file] {
  // …

  &:before {
    // …
    content: $fa-var-picture-o;
  }
}
</figure>

Just make sure to import the lib with @import "font-awesome"; and not //= require font-awesome in your application.scss. Otherwise the variables will not be available.

Archiving a (Rails) site as static files on Nginx

Posted about 1 month back at The Pug Automatic

I had an old Rails 2 app (a blog) that still got visits, but no updates. It’s effectively been read-only for years.

Since I’m consolidating servers, I wanted to get rid of the machine it was hosted on, and moving the Rails app elsewhere wouldn’t be trivial.

So I replaced it with a static copy of the site. Just flat files.

(I also made a database dump just in case I want to make it dynamic again in the future.)

This is how I did it.

Archive the site

I installed wget via Homebrew since I didn’t have it on my Mac:

brew install wget

If you don’t have it already on e.g. Ubuntu, try

sudo apt-get install wget

Then I told wget to archive the site:

wget --convert-links --mirror mysite.com

It will end up in a ./mysite.com directory.

Upload the site

rsync -azv mysite.com myserver:apps

Substituting whatever server and path you prefer. I keep sites in subdirectories of ~/apps.

You could also call wget on the server, of course, and skip the upload step. I wanted a local copy and to verify the download with my local tools.

Configure Nginx

In the Nginx configuration for the site, I had to do some special things:

<figure class="code">
server {
  # …

  location / {
    try_files $request_uri $uri $uri/ =404;
    default_type text/html;
  }

  location /stylesheets {
    try_files $request_uri $uri $uri/ =404;
    default_type text/css;
  }

  location /javascripts {
    try_files $request_uri $uri $uri/ =404;
    default_type text/javascript;
  }

  location /images/uploads {
    try_files $request_uri $uri $uri/ =404;
    default_type image/jpg;
  }
}
</figure>

I needed try_files $request_uri so that requesting e.g. index.html?page=2 or stylesheets/all.css?1393152599 would look for a file by that exact name, query string and all.

And I needed the default_type declarations to handle HTML files archived without an extension, as well as e.g. stylesheets ending with a query string.

I only had JPG uploads, but you could use a regexp for more complex needs.

Hope this helps!

Cheatsheet: login/log in, setup/set up etc

Posted about 1 month back at The Pug Automatic

Unsure whether it’s “log in” or “login”, “set up” or “setup”, and so on? Have a cheatsheet.

“check out” vs. “checkout”

In a sentence: “I check out on the checkout page.”

“Check out” is what you do.

A “checkout” (or “check-out”) is a thing: the act of checking out. It’s also used in phrases like “a checkout page”.

In grammatical terms, “check out” is a phrasal verb construction and “checkout” is a noun.

In a phrase like “a checkout page”, the word “checkout” acts as a noun adjunct.

“log in” vs. “login”

In a sentence: “I log in on the login page, with my user login and password.”

“Log in” is what you do.

A “login” (or “log-in”) is a thing: the act of logging in, or the credentials you use to do so. It’s also used in phrases like “a login page”.

“log out” vs. “logout”

In a sentence: “I will log out on the logout page.”

“Log out” is what you do.

A “logout” (or “log-out”) is a thing: the act of logging out. It’s also used in phrases like “a logout page”.

“set up” vs. “setup”

In a sentence: “I will set up my account on the setup page.”

“Set up” is what you do.

A “setup” is a thing: the act of setting something up. It’s also used in phrases like “a setup page”.

I believe “set-up” with a hyphen is not commonly used in an IT sense. (Corroboration.)

“sign up” vs. “signup”

In a sentence: “I will sign up on the signup page.”

“Sign up” is what you do.

A “signup” (or “sign-up”) is a thing: the act of signing up. It’s also used in phrases like “a signup page”.

Why should you care?

Language is determined by use and ever in flux – there’s no right or wrong as such, but the above is what I believe most dictionaries and nitpickers would propose.

Not making these distinctions looks just as bad as any other typo to those of us who are sensitive to the difference, and it is unnecessarily misleading – I would expect a setupController method to return the controller for setting things up, not to perform the action of setting up a controller.

I’ve seen a lot of people get it “wrong” – in my team’s code review as well as in major public projects like Ember.js.

By making these distinctions, those who notice will be happier, and those who don’t won’t care either way.

Custom 404 error page with Rails 4

Posted about 1 month back at The Pug Automatic

This is what I did to get a custom 404 error page on Rails 4, without replacing the default 500 and 422 error pages.

There are other solutions where you just use the router as the exceptions app, but then you have to handle those other errors as well.

It’s very much based on this Gist by Turadg Aleahmad, but with some cleanup and fixes.

Code changes

Remove the default public/404.html to avoid any collisions.

Modify these files like so:

<figure class="code"><figcaption>config/application.rb</figcaption>
# …

module NameOfMyApp
  class Application < Rails::Application
    # …

    require Rails.root.join("lib/custom_public_exceptions")
    config.exceptions_app = CustomPublicExceptions.new(Rails.public_path)
  end
end
</figure> <figure class="code"><figcaption>config/routes.rb</figcaption>
Rails.application.routes.draw do
  match "/404" => "errors#error404", via: [ :get, :post, :patch, :delete ]

  # …
end
</figure>

Add these files:

<figure class="code"><figcaption>lib/custom_public_exceptions.rb</figcaption>
class CustomPublicExceptions < ActionDispatch::PublicExceptions
  def call(env)
    status = env["PATH_INFO"][1..-1]

    if status == "404"
      Rails.application.routes.call(env)
    else
      super
    end
  end
end
</figure> <figure class="code"><figcaption>app/controllers/errors_controller.rb</figcaption>
class ErrorsController < ApplicationController
  def error404
    render status: :not_found
  end
end
</figure> <figure class="code"><figcaption>app/views/errors/error404.erb</figcaption>
<p>Sorry! No such page!</p>
</figure>

Verify in development

To see the page in development, just visit /404.

If you see the default Rails 404 page, you probably forgot to remove public/404.html.

If you want to make sure it actually works, change config/environments/development.rb to say

<figure class="code">
# Do not commit!
config.consider_all_requests_local = false
</figure>

instead of true.

Just don’t keep that value, since you’ll get less helpful errors in development, and you’ll also disable the /rails/info/properties page with debug info.

Tests

I haven’t been able to figure out a way to do production-style error handling in a single test, so I settled for this:

<figure class="code"><figcaption>spec/features/errors_spec.rb</figcaption>
require "rails_helper"

describe "404 page" do
  it "is customized" do
    # Haven't been able to get the "show instead of exceptions" thing working in tests, but this at least makes sure the page can render correctly.
    visit "/404"
    expect(page.status_code).to eq 404
    expect(page).to have_content("Sorry!")
  end
end
</figure>

UNION with Active Record

Posted about 1 month back at The Pug Automatic

I wanted to do a UNION query in Active Record, combining the results of two subqueries.

This is how I did it in Rails 4.1.5 with Postgres:

<figure class="code">
items = category.items
query1 = items.some_scope
query2 = items.other_scope

# Get a real category_id instead of "$1" in the generated SQL.
sql = Item.connection.unprepared_statement {
  "((#{query1.to_sql}) UNION (#{query2.to_sql})) AS items"
}

Item.from(sql).order("we_can_add_an_order_if_we_like ASC")
</figure>

The generated query will be something like

<figure class="code">
SELECT items.*
  FROM ((SELECT ) UNION (SELECT )) AS items
  ORDER BY we_can_add_an_order_if_we_like ASC;
</figure>

This is of course not guaranteed to be fast just because it’s raw SQL.

Consider UNION vs. UNION ALL, benchmark as needed, and use multiple queries or Ruby code if that’s faster or more readable in your situation.

Maintenance Friday 29th at 10pm EST

Posted about 1 month back at entp hoth blog - Home

We will have a routine maintenance for Lighthouse this Friday 29th at 10pm EST. It should last about 30 mins, hopefully less.

Go enjoy your friday night!

Cheers.

Episode #492 - August 27, 2014

Posted about 1 month back at Ruby5

In today's episode we cover the new Rails 4.2beta, JSON API Resources, Country Select 2.0, Harpoon, Poodr course learning, and Feature Focus all while riding in our Roles Royce.

Listen to this episode on Ruby5

Sponsored by Top Ruby Jobs

If you're looking for a top Ruby job or for top Ruby talent, then you should check out Top Ruby Jobs. Top Ruby Jobs is a website dedicated to the best jobs available in the Ruby community.
Top Ruby Jobs

Rails 4.2beta1 Released

The Rails core team has just released Rails 4.2beta1 on August 20th. This includes a lot of new features such as the ActiveJob interface, ActionMailer#deliver_later method, Adequate Record and the Rails Web console.
Rails 4.2beta1 Released

Royce

The Royce gem, by Martin Nash is a new library to add roles on your ActiveRecord models.
Royce

Introducing JSON API Resources

JSON API Resources is a framework by Dan and Larry Gebhardt for developing a server that complies with the JSON API Specification. JSON API Resources requires little more than a definition of your resources including their attributes and relationships to make them compliant with the near 1.0 JSON API release.
Introducing JSON API Resources

Country Select Gem 2.0

Adam Anderson reached out to let us know about the country_select gem just hitting 2.0. A lot of libraries depend on this library, like simple_form and formtastic. This new release includes a number of breaking changes to increase i18n compatibility. You’ll want to stick with version 1 until you’re ready to do an upgrade, there’s some differences around how countries are stored.
Country Select Gem 2.0

Deploying Static Sites to S3 with Harpoon

Harpoon is a simple static site deployment tool by Ryan Quinn. It has a lot of the same features you're used to with Capistrano.
Deploying Static Sites to S3 with Harpoon

I Spent 3 Days with Sandi Metz, here’s what I learned

Jack Hoy attended one of Sandi Metz 3 day long POODR Ruby courses, and he wrote up a great blog post about some of the core principles he took away from the course.
I Spent 3 Days with Sandi Metz, here’s what I learned

Code School Groupon Feature Focus

On Thursday last week Code School released the 5th episode in the Feature Focus series. In the 5th episode we implement geolocation features seen on Groupon.com and then go on location at Groupon HQ in Chicago to get feedback on our implementation.
Code School Groupon Feature Focus

Sponsored by Ruby5

Ruby5 is released Tuesday and Friday mornings. To stay informed about and active with this podcast, we encourage you to do one of the following:

Thank You for Listening to Ruby5

The Case for Buying Technical Books

Posted about 1 month back at Jay Fields Thoughts

In the past few months I've seen more than a few articles encouraging programmers to write books. Each article provides at least a bit of good advice, and proceeds to conclude with the same idea:
You should write a book to build your brand.
I find this conclusion accurate and extremely disappointing. If the overwhelming reason to write a book is brand building, then the pool of potential authors is restricted to people who would benefit from brand building (and people who don't value their time).
How Did We Get Here?
The Internet, obviously. Practically everyone knows how to download any movie, song, or book at no cost. Opinions on "illegal downloading" range from opposition to pride. I'm not particularly interested in discussing those opinions; however, I believe it's worth observing the impact of the combination of ability and desire to acquire content without compensating the creator.
“Books aren't written - they're rewritten...” -- Michael Crichton
If you've never written a book, you may not be aware of colossal effort it takes to write a mediocre book. When it's all said and done, it can take well over an hour of effort per page. Great books, such as Java Concurrency in Practice, require an even greater level of attention to detail, and cost even more time to create. Brian Goetz estimates that it took them approximately 2,400 hours to create JCiP. If we also knew their royalty structure and the number of copies sold, we'd be able to calculate the hourly rate for writing a high quality book.
It turns out, one of the recent articles encouraging writing gives you royalty numbers and a hint on how many copies a quality book might sell.
Royalties for print should start at 18% of net revenues to the publisher. (Expect that figure to be around $10-20, so you're only making a few dollars on each sale.)
...
Selling 10 thousand copies of a print tech book these days is a solid success and should be compensated accordingly. -- Obie Fernandez
Let's assume JCiP was more than a solid success and sold 20K copies (doubling Obie's "solid success" benchmark). Assuming they negotiated royalties well, that would mean making $40,000 - thus the hourly rate for writing JCiP would be under $17 per hour.
Clearly I've made a few assumptions, but I believe all of them are based on sound logic. As long as you work 8 hours a day, 5 days a week, for 50 weeks and write a modern classic, you'll make around $34,000 per year. Anecdotal evidence among my author friends who've yet to write a modern classic is worse: the hourly rate is less than minimum wage.
The royalty structures combined with lessening sales create an environment where writing a book for (royalty) profit isn't a reasonable use of your time. As a result, the majority of today's authors are either consultants or unknown programmers. Established, non-consultant programmers gain little from the brand building aspect of writing a book, and likely make far more than $34,000 a year at their full-time jobs - why would they take on a poorly paying second job?
Around 2005 it became fairly easy to download, for free, practically any book. It might be coincidence that 10 of 13 of these Must-Read books were written prior to 2005. Despite the possibility, I don't believe it's a coincidence. Rather, I believe that at one time it paid to create a best selling technical book, and people with various backgrounds took up the challenge.
Nice Assumption Filled History Lesson, What's Your Point?
My point is fairly simple. If you're, like I am, tired of having to choose between books written decades ago and books written by those with at least a slightly ulterior motive, buy some books. Does your company have a book buying policy? If you aren't spending your entire book budget, why not? It costs you nothing to buy a book and give it to a teammate, and every royalty penny reminds an author that someone cares about all of those hours writing and rewriting.
Even if your company doesn't have a book budget, ask yourself if you'd rather your next book about Java be written by a consultant you've never heard of or Java's language architect. The average technical book costs little compared to life's other expenses, and buying a technical book is investing in your profession twice. You stand to gain knowledge both from today's book purchase and a potential future book written by the same author - a future book that may never be written given the current financial incentives.
If you're a CTO, Director or Manager, why aren't you constantly buying books for the developers you work with? They could probably use your advice on which books will best guide their careers.
Makes Sense, What Should I Buy?
There are several good books now available on leanpub - where the authors are paid significantly higher royalties. If you want to support authors you should always start there. From there I would own at least a copy of Chad's (previously referenced) Must-Read books. I'd also buy Chad's Passionate Programmer. Finally, you can't go wrong working your way through this list: Clojure Bookshelf.

Baseimage-docker 0.9.13 released

Posted about 1 month back at Phusion Corporate Blog

Baseimage-docker is a special Docker image that is configured for correct use within Docker containers. It is Ubuntu, plus modifications for Docker-friendliness. You can use it as a base for your own Docker images. Learn more at the Github repository and the website, which explain in detail what the problems are with the stock Ubuntu base image, and why you should use baseimage-docker.

Changes in this release

  • Fixed my_init not properly exiting with a non-zero exit status when Ctrl-C is pressed.
  • The GID of the docker_env group has been changed from 1000 to 8377, in order to avoid GID conflicts with any groups that you might want to introduce inside the container.
  • The syslog-ng socket is now deleted before starting the syslog-ng daemon, to avoid the daemon from failing to start due to garbage on the filesystem. Thanks to Kingdon Barrett. Closes GH-129.
  • Typo fixes by Arkadi Shishlov.

Using baseimage-docker

Please learn more at the README.

The post Baseimage-docker 0.9.13 released appeared first on Phusion Corporate Blog.

Baseimage-docker 0.9.13 released

Posted about 1 month back at Phusion Corporate Blog

Baseimage-docker is a special Docker image that is configured for correct use within Docker containers. It is Ubuntu, plus modifications for Docker-friendliness. You can use it as a base for your own Docker images. Learn more at the Github repository and the website, which explain in detail what the problems are with the stock Ubuntu base image, and why you should use baseimage-docker.

Changes in this release

  • Fixed my_init not properly exiting with a non-zero exit status when Ctrl-C is pressed.
  • The GID of the docker_env group has been changed from 1000 to 8377, in order to avoid GID conflicts with any groups that you might want to introduce inside the container.
  • The syslog-ng socket is now deleted before starting the syslog-ng daemon, to avoid the daemon from failing to start due to garbage on the filesystem. Thanks to Kingdon Barrett. Closes GH-129.
  • Typo fixes by Arkadi Shishlov.

Using baseimage-docker

Please learn more at the README.

The post Baseimage-docker 0.9.13 released appeared first on Phusion Corporate Blog.

Phusion Passenger 4.0.49 released

Posted about 1 month back at Phusion Corporate Blog


Phusion Passenger is a fast and robust web server and application server for Ruby, Python, Node.js and Meteor. Passenger takes a lot of complexity out of deploying web apps, and adds powerful enterprise-grade features that are useful in production. High-profile companies such as Apple, New York Times, AirBnB, Juniper, American Express, etc are already using it, as well as over 350.000 websites.

Phusion Passenger is under constant maintenance and development. Version 4.0.49 is a bugfix release.

Phusion Passenger also has an Enterprise version which comes with a wide array of additional features. By buying Phusion Passenger Enterprise you will directly sponsor the development of the open source version.

Recent changes

  • Upgraded the preferred Nginx version to 1.6.1.
  • Fixed a crash that may be triggered by the passenger_max_requests feature.
  • Introduced the spawn_failed hook, which is called when an application process fails to spawn. You could use this hook to setup an error notification system. Closes GH-1252.
  • Fonts, RSS and XML are now gzip-compressed by default in Phusion Passenger Standalone. Thanks to Jacob Elder. Closes GH-1254.
  • Fixed some user and group information lookup issues. Closes GH-1253.
  • Fixed some request handling crashes. Closes GH-1250.
  • Fixed some compilation problems on Gentoo. Closes GH-1261.
  • Fixed some compilation problems on Solaris. Closes GH-1260.

Installing or upgrading to 4.0.49

OS X OS X Debian Debian Ubuntu Ubuntu
Heroku Heroku Ruby gem Ruby gem Tarball Tarball

Final

Fork us on Github!Phusion Passenger’s core is open source. Please fork or watch us on Github. :)

<iframe src="http://ghbtns.com/github-btn.html?user=phusion&amp;repo=passenger&amp;type=watch&amp;size=large&amp;count=true" allowtransparency="true" frameborder="0" scrolling="0" width="170" height="30"></iframe><iframe src="http://ghbtns.com/github-btn.html?user=phusion&amp;repo=passenger&amp;type=fork&amp;size=large&amp;count=true" allowtransparency="true" frameborder="0" scrolling="0" width="170" height="30"></iframe><iframe src="http://ghbtns.com/github-btn.html?user=phusion&amp;type=follow&amp;size=large&amp;count=true" allowtransparency="true" frameborder="0" scrolling="0" width="190" height="30"></iframe>

If you would like to stay up to date with Phusion news, please fill in your name and email address below and sign up for our newsletter. We won’t spam you, we promise.