Wednesday, June 26, 2013

Silence is NOT golden...

Here's a sneaky little routing gotcha.  Let's say you're TDD'ing a new feature and your test requires a 'new' action. Following best practice, you start by saying
resources :accounts, only: [:new]
instead of
resources :accounts
Before we get to the gotcha, why would I say 'best practice' is to avoid the complete definition? Well... remember one of the core principles of TDD:
Write ONLY enough code to get the test to pass.
This means that adding only a single action is all I need do to get the test to pass, so that's all I add (even though I actually write more code in that case). This idea certainly isn't new to me; for example, check out the Thoughtbot TDD class if you're interested in learning more: great resource!

On a broader scale, assuming you've got this whole feature written, you still want to avoid adding any unused actions. If you use every RESTful action except for :delete, then you don't want to allow the delete action to exist, as it offers one more opportunity for the bad guys to attack your site.

Back to the gotcha. Let's say that you code the above example, but - oops - you make a typo and actually code:
resources :accounts, only: [:newt]
Guess what - this error is silently ignored. No route is generated and you don't get an error message. In fact, the only: and except: options just ignore anything that isn't on the list of RESTful actions (index, create, new, update, edit, show, destroy) and give no warning.

It's easy to overlook this as we're used to having Rails give us helpful error messages if we code something incorrectly. Not so in this case.

Sunday, June 23, 2013

"Can't find generator help."

Here is a tricky little case of  mis-direction. I've gotten used to github, heroku, and a host of other command-line tools that allow you to add the keyword 'help' to the command to get some basic usage info. Not so, Rails.  If you type the following inside a Rails application (I'm on Rails 3.2.13 or 3.2.12):
rails generate help
you get the decidedly unhelpful message:
Could not find generator help.
Rails thinks you're looking to generate a "help" something or other.

However, if you type the same command outside a Rails app, you get what you'd expect:
  rails new APP_PATH [options]

  -r, [--ruby=PATH]              # Path to the Ruby binary of your choice
                                 # Default: /home/jseidel/.rvm/rubies/ruby-1.9.3-p194/bin/ruby
  -b, [--builder=BUILDER]        # Path to a application builder (can be a filesystem path or URL)
  -m, [--template=TEMPLATE]      # Path to an application template (can be a filesystem path or URL)
      [--skip-gemfile]           # Don't create a Gemfile
      [--skip-bundle]            # Don't run bundle install
  -G, [--skip-git]               # Skip Git ignores and keeps
  -O, [--skip-active-record]     # Skip Active Record files
  -S, [--skip-sprockets]         # Skip Sprockets files
  -d, [--database=DATABASE]      # Preconfigure for selected database (options: mysql/oracle/postgresql/sqlite3/frontbase/ibm_db/sqlserver/jdbcmysql/jdbcsqlite3/jdbcpostgresql/jdbc)
                                 # Default: sqlite3
  -j, [--javascript=JAVASCRIPT]  # Preconfigure for selected JavaScript library
                                 # Default: jquery
  -J, [--skip-javascript]        # Skip JavaScript files
      [--dev]                    # Setup the application with Gemfile pointing to your Rails checkout
      [--edge]                   # Setup the application with Gemfile pointing to Rails repository
  -T, [--skip-test-unit]         # Skip Test::Unit files
      [--old-style-hash]         # Force using old style hash (:foo => 'bar') on Ruby >= 1.9

Runtime options:
  -f, [--force]    # Overwrite files that already exist
  -p, [--pretend]  # Run but do not make any changes
  -q, [--quiet]    # Suppress status output
  -s, [--skip]     # Skip files that already exist

Rails options:
  -h, [--help]     # Show this help message and quit
  -v, [--version]  # Show Rails version number and quit

    The 'rails new' command creates a new Rails application with a default
    directory structure and configuration at the path you specify.

    You can specify extra command-line arguments to be used every time
    'rails new' runs in the .railsrc configuration file in your home directory.

    Note that the arguments specified in the .railsrc file don't affect the
    defaults values shown above in this help message.

    rails new ~/Code/Ruby/weblog

    This generates a skeletal Rails installation in ~/Code/Ruby/weblog.
    See the README in the newly created application to get going.

Thursday, June 20, 2013

wkhtmltopdf and all those gems...

Looking back on my wkhtmltopdf / Heroku experience, there are several take-aways for me.

First off, I had contacted Heroku technical support for assistance, and got back a quick reply:

Unfortunately we don't support installing additional libraries or binaries to our stacks. The best workaround is to vendor these into your project. You'll need to use 64-bit Linux versions to make them work on Heroku; compiling statically can also help ensure that any dependencies needed are included. Similarly, for gems that depend on external libraries, we recommend compiling the gem statically and vendoring it into your project (which is what it seems you have already tried to do).

We realize this is not a trivial task and can be very difficult to get working, and we hope to provide an easier way to do this in the future. Unfortunately we do not have an ETA on when this improved functionality will be available.

If you do wish to try to vendor, or if you have no success with vendoring, your binary, library, or gem, you can use Heroku as your build environment. One of our engineers created a build server that allows you to upload source code, run the compilation step, and then download the resulting binary. You can find this project on Github.
Kinda makes sense to me: they have to protect their stack. I'm not surprised, just a wee bit disappointed that they don't make something as widespread and generally useful as wkhtmltopdf available as a standard binary. Oh well; they do seem to be looking into a better solution, and they did provide some good information. The link to that project looks like it might be useful in the future.

Once I got my app with PDF working, I submitted a documentation update to WickedPDF, which was promptly merged (thanks @unixmonkey). Hopefully that'll provide some assistance to other folks.

One of the problems I had with the various gems that include binary versions of wkhtmltopdf was that they hadn't been updated recently... sometimes in over a year.  So I figured that I should be a good citizen and update them with the latest version (0.11.0 rc1 at the time I'm writing this).  To that end, I just submitted a pull request for the wkhtmltopdf-heroku gem. It purports to automagically support WidkedPDF, PDFKit, and wisepdf, depending on which one is loaded (I've not tested it myself).

However, when I started looking at the wkhtmltopdf-binary "family" of gems, it was pretty involved: steerio/wkhtmltopdf-binary (the one I originally investigated), is forked from unixmonkey/wkhtmltopdf_binary_gem which in turn is forked from michaelcollas/wkhtmltopdf_binary_gem, which is itself forked from zakird/wkhtmltopdf_binary_gem and who know how many forks there are on side legs...

That's not something I'm inclined to sign up for! And given the simplicity of adding the single binary in my app (now that I know how to do it), I'm inclined to keep it that way and avoid any of those gems entirely. In fact, I'm beginning to come to the conclusion that it may be better in the long run to install things manually for what I'll call "minor" gem functionality [Note to self: come up with better terminology for this situation] such as this one: I know more of what's going on and have more control over the final outcome.

It also reminds me that, while we work hard to DRY up our code, the proliferation of similar gems on the web is anything but DRY.

Friday, June 14, 2013

WickedPDF, wkhtmltopdf, and Heroku...a tricky combination

At least for me as it took a bit of twiddling to get it running, first in development and then on the Heroku stack. But with a lot of testing and some help from unixmonkey and my friends at Thoughtbot, I finally got it working. Here's how.

First off, I'm running Rails 3.2.13, WickedPDF 0.9.6, and wkhtmltopdf 0.11.0 rc1, running development on a Linux (Kubuntu 10.04) 64-bit machine. NB: The machine you're developing on and deploying to can be different but then you'll have to have two (or more) different copies of wkhtmltopdf and properly configure WickedPDF to handle that difference. I cover that later in this post.

Install wkhtmltopdf

The download site for wkhtmltopdf has pretty much everything you need for whatever machine architecture you are working with. Download the matching flavor for your development and production machines. If you have only a single machine architecture, then just call your one binary 'wkhtmltopdf'.

If you're working with multiple architectures, label each one appropriately, either with the architecture suffix (e.g., -amd64) or simply '-dev' and '-prod'.

Place the one or more binaries in the directory "/bin", right off your root directory (e.g., "#{Rails.root}/bin") and mark them as executable (chmod +x on linux/mac). Note that for Windows development, you'll have a wkhtmltopdf.exe file for development and probably plain 'ol wkhtmltopdf for production.

Install WickedPDF

Installation is straight-forward; just follow the instructions here. However, there are some critical changes you must make for generating PDFs successfully. The section titled "Styles" gives you the key:
You must define absolute paths to CSS files, images, and javascripts
I'm highlighting this because I missed it the first time around and it cost me dearly.  This section goes on to suggest that you use the helpers that are provided. They are:
Every external file that you use must be referenced with an absolute address, so the normal Rails helpers do not work in many cases [Technical Note: the wicked_pdf helpers simply embed the actual CSS or Javascript in the page]. To make life easier for me, I did the following:
  • Create a special pdf.html.haml layout for all PDF pages which uses the wicked_pdf helpers
  • Use the jQuery CDN for including that set of libraries; the Rails-provided helpers do work here because I'm providing an absolute URL; nothing relative to the Rails root of my application 
= javascript_include_tag ""
= javascript_include_tag ""

Now here's the strange part [I believe this has to do with the Rails asset pipeline; I'll update this post once I nail that down]. I also had to add all my CSS and JS files to my application.rb file for inclusion in the asset pipeline, thusly:
config.assets.precompile += ['blueprint/screen.css', 'jquery.timepicker.css', ...]
Only with this incantation did everything work properly.

Configure WickedPDF for Development & Production

If you're developing on Linux 64-bit, then configuration is easy as you'll use the same binary for both development and production. Update your WickedPDF initializer file ("config/initializers/wicked_pdf.rb") as follows:

WickedPdf.config = { :exe_path => "#{Rails.root}/bin/wkhtmltopdf" }
If you're developing on something other than Linux 64-bit, you'll have to test for the environment and set your exe_path appropriately.. something like this:
WickedPdf.config do |config|  
  if Rails.env == 'production' then
    config.exe_path = Rails.root.to_s + "/bin/wkhtmltopdf"
  else  ### Following allows for development on my MacBook or Linux box
    if /darwin/ =~ RUBY_PLATFORM then
      config.exe_path = '/usr/local/bin/wkhtmltopdf' 
    elsif /linux/ =~ RUBY_PLATFORM then
      config.exe_path = '/usr/bin/wkhtmltopdf' 
      raise "UnableToLocateWkhtmltopdf"

For windows, you'll have to check out the RUBY_PLATFORM values you see and modify appropriately; my windows machine reports "i386-mingw32'.

 If you need different versions of the same architecture (say for final testing a new version of wkhtmltopdf before rolling into production), then just label it with the version number and update your WickedPDF configuration file.

Monday, June 10, 2013

Chrome / PDF Problem...

I've recently been working to add PDF generation to a client site and got WickedPDF working. I ran into a problem which I finally resolved (RTFM very carefully)... got some great help from the Wicked folks along the way. In any event, I was finally generating my PDFs fine with Firefox, but Chrome kept failing with the message:
Sorry, we were unable to find the document at the original source. 
Verify that the document still exists.
You can also try to download the original document by clicking here.
All that had to be done was to click on the 'here' link provided, but that's less than an optimal user experience... Not Good, Google! There are quite a few posts out there with the same problem. I finally figured out that this was caused by the Google Docs PDF/PowerPoint Viewer (I was running version 3.10). Easy solution: in Chrome, go to:
Customize | Tools | Extensions
and disable that extension. Now PDFs are properly displayed.