Feeds:
Posts
Comments

Overview

Providing the option of OpenID for account creation and sign-in helps keeps things easy on your users. It may even lower the barrier to people signing up for (and continuing to use) your service.

Authlogic implements user authentication beautifully, and it is readily extensible. By way of illustrating how to extend Authlogic, its author, Ben Johnson, released Authlogic OpenID. That makes supporting OpenID authentication as clean, easy, and flexible as Authlogic makes user authentication.

The difficulty, up to this point, has been testing OpenID. Some time ago, I was looking for ways to test OpenID, and I happened upon the Ruby OpenID Test Server (ROTS), by Román González. Googling various combination of ROTS and OpenID or Rails, one would think the experiment had failed, because of the lack of mention and its author’s only two blog posts on the topic [1] [2]. But I contacted Román via Git, and he offered very helpful assistance and pointed me to the rots_consumer, which is an example Rails (and Merb) application that uses ROTS to test OpenID. Today, I followed Román’s instructions and now have operational OpenID scenarios in my Cucumber suite (I was inspired to do so today because I wanted to add another OpenID related feature, and felt dirty not being able to do BDD).

This post covers how to get OpenID working in Cucumber (I’m running Cucumber 0.6.1, Cucumber-Rails 0.2.3, and Rails 2.3.5).

Details

To quote its ReadMe, “ROTS is a minimal implementation of an OpenID server.” When running OpenID tests against ROTS, the ROTS server must be running locally. It seems pretty lightweight, so isn’t a big deal, but if you don’t want to run against it all the time, tag your Cucumber scenarios that involve OpenID (or an entire file) with @openid and include “–tags ~@openid” as one of the std_opts in config/cucumber.yml. Then, when you want to test OpenID, run
$ cucumber --tags @openid features

Setting up your application to work with ROTS is pretty easy. First get ROTS. Román’s version is here. I ran into a problem in his current version with an application that requests dob from the OpenID server, so I wrote a patch. If you request dob as part of your OpenID request, check to see whether Román’s repository has fixed that issue, or use my fork. Either way, clone ROTS to your local system.

Once you have ROTS locally, build, install, and run the gem:
$ cd rots
$ gem build rots.gemspec
$ sudo gem install rots-0.2.2.gem
$ rots

I changed my default Screen settings to include a rots window and automatically launch rots. That way, I always have rots running when I open my work environment:
screen -t rots /bin/bash -c 'rots'

As an aside, I’m running Ubuntu 9.10, which comes with an elegant version of Screen called Byobu. Byobu makes it easy to set all the Screen preferences, such as changing ctrl-a to ctrl-j (not only is this better for Emacs, but J is the rest position for the right index finger, so is very fast).

Now for your Rails app.

Create features/support/openid.rb with the following contents:

require "rots"

World(Rots::TestHelper)

And features/step_definitions/openid_steps.rb with the following:

Then /^I (?:should )?receive a response from the OpenID Server$/ do
  server_request_uri = response.headers['Location']
  hash = openid_request(server_request_uri)
  visit(hash[:url], :get, hash[:query_params])
end

At this point, you can write your Cucumber scenarios. The important step to remember is, after any successful call to the OpenID server, you must have the line

    And I receive a response from the OpenID Server

or

    Then I should receive a response from the OpenID Server

Here are the OpenID steps I implemented for one application:
http://gist.github.com/334789

The AR-DBcopy gem, by Michael Siebert, made this very easy. The ReadMe doesn’t cover use of the gem, so I’ll cover that here.

Overview:

AR-DBcopy uses ActiveRecord to achieve database agnosticism. Both the source and target databases are accessible a single instance of Rails (in my case, both databases were on the same machine, but if you configure database.yml to connect to a different host, then they need not be). The source contains the data; the target starts out empty, but with the correct schema (generated by running rake db:migrate).

Steps: (Be sure that nobody will be interacting with the web application during this process.)

  1. Set up everything you need to in order for your Rails app to work with Postgres (install Postgres server, pg gem, create a Postgres user, change your database.yml file (keep the MySQL settings handy), and run rake db:create and rake db:migrate). That leaves you with a MySQL database with your data and an empty Postgres database with the correct schema.
  2. Modify database.yml so that it matches the example. This is easy, simply copy your old MySQL settings to “source:” and your new Postgres settings to “target:”

    Original database.yml:

    production:
      adapter: mysql
      encoding: utf8
      database: old_dbname
      username: old_dbuser
      password: old_pass
      socket: /var/run/mysqld/mysqld.sock
    

    Modified database.yml:

    source:
      adapter: mysql
      encoding: utf8
      database: old_dbname
      username: old_dbuser
      password: old_pass
      socket: /var/run/mysqld/mysqld.sock
    
    target:
      adapter: postgresql
      encoding: unicode
      database: new_dbname
      username: new_dbuser
      password: new_pass
    
    production:
      adapter: postgresql
      encoding: unicode
      database: new_dbname
      username: new_dbuser
      password: new_pass
    
  3. Install the ar-dbcopy gem:
    $ sudo gem install ar_dbcopy
  4. From the Rails root directory, run the gem:
    $ ar_dbcopy config/database.yml
  5. After this finishes (for me, it took on the order of 5 minutes for 100K records), you will need to reset the PostgreSQL id sequences by performing
    select setval(‘users_id_seq’, (select max(id) + 1 from users));
    for every table in the database that has a corresponding _id_seq

Was getting this error in a Postgres backed Rails app I was building. After much googling, I found someone mention something about having messed up their migration. My migrations were fine, but I dropped, created, and migrated the test database anyway, and the problem disappeared.

Here’s the error I was getting, in case it helps Google direct someone here:

ActiveRecord::StatementInvalid in 'CardsController GET index with user logged in assigns all cards as @cards'
PGError: ERROR:  relation "cards" does not exist
LINE 4:              WHERE a.attrelid = '"cards"'::regclass
                                        ^
:             SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull
              FROM pg_attribute a LEFT JOIN pg_attrdef d
                ON a.attrelid = d.adrelid AND a.attnum = d.adnum
             WHERE a.attrelid = '"cards"'::regclass
               AND a.attnum > 0 AND NOT a.attisdropped
             ORDER BY a.attnum

Clean and Dry Controllers

At the last Nashville Ruby on Rails Meetup, four of us each took 5 minutes to present some code, or some approach we use. I presented the way I write controllers. It received a positive response, so even though I’m not sure it’s anything out of the ordinary, I’m posting it here.

Some time last fall, I read Ben Johnson’s post about why he is discontinuing his take on resource controller magic. At that time, I was using Resources Controller, and was just gearing up to fix the bugs that had been introduced by its latest update.

In the article, Ben basically argues that this type of plugin reduces clarity of intent and, while they seem to keep things dry, they actually end up confusing things. At first, I balked at the idea, because what I had in mind with a regular controller was the kind of thing that Rails scaffolding spits out, which is neither dry nor elegant. But then I went about refactoring, the results of which you can see below.

The key points are that all permissions enforcement happens within before_filters, and all resource creation happens within three private methods: find_resources, find_resource, and new_resource (names inspired by Resources Controller). This makes it easy to see at a glance what kind of permissions are needed for which methods. It also makes it easy to set up permission defaults so that if you need to add an odd method, it isn’t missed by the permissions check.

require 'lib/modules/controller/paginated_search'

class OrganizationsController < ApplicationController
  include PaginatedSearch::OrganizationSearch

  around_filter :catch_exceptions

  before_filter :require_user
  before_filter :require_org_admin
  before_filter :find_resources, :only => [:index]
  before_filter :new_resource, :only => [:new, :create]
  before_filter :find_resource, :except => [:index, :new, :create]

  def new
    render(:edit)
  end

  def create
    if @organization.save : redirect_to(organizations_url)
    else render(:edit)
    end
  end

  def update
    if @organization.update_attributes(params[:organization]) : redirect_to(organizations_url)
    else render(:edit)
    end
  end

  def destroy
    @organization.destroy
    redirect_to(organizations_url)
  end

  private

  def find_resources
    @organizations = paginated_find_organizations(current_user.managed_organizations)
  end

  def find_resource
    @organization = current_user.managed_organizations.find(params[:id])
  end

  def new_resource
    @organization = Organization.new(params[:organization]) do |organization|
      organization.account_manager = current_user
      organization.expires_on = Date.today + 1.year if organization.expires_on.nil?
    end
  end

  def require_org_admin
    not_authorized_redirect() unless current_user.org_admin?
  end

  def catch_exceptions
    yield
  rescue ActiveRecord::RecordNotFound
    flash[:notice] = I18n.translate('flash.missing_requested_data')
    redirect_to(organizations_url)
  end
end

Rails issue: when responding with json, if I try to “render(:update)”, I end up with “LocalJumpError (no block given)”. Changing the .json.erb file and the render call from “update” to some other name (such as update_prime) solves the problem. This has happened to me twice now. I use ResourcesController, if that has anything to do with it.
Note that render(:create) and render(:edit) work just fine.

(For some reason, I found it difficult to find the solution to this online. I googled for it in the past, but always without success. Hopefully, this will help anyone else who wants to exclude / omit / ignore directories from an rgrep search in Emacs.)

rgrep performs grep recursively on a directory and all the directories contained therein. The nice thing about rgrep in Emacs–versus, say, ack–is that:

  1. it prompts you for the starting directory (and ido still works!)
  2. it prompts for the file name pattern to search (but defaults to the current extension, so you can just hit <return>)
  3. the line isn’t filled up with the raw grep or ack command

The Emacs grep commands already intelligently omit certain directories, but I am spending a lot of time in Rails projects, and rgrep-ing from the project root would dive into the “log” and “vendor” directories. Rails development and testing logs can become very large (which slows down rgrep) and often include a match for the term I am searching (so the search results would consist mainly of log entries). The vendor directory can contain a very large number of files (especially when Rails is frozen into the project), and I usually don’t want search results from 3rd party plugins or the Rails source.

The solution is the grep-find-ignored-directories option:
M-x customize-option
<return>
grep-find-ignored-directories

Add “log” and “vendor” as omitted directories, and then “save for future sessions”. That saves the settings to custom.el in your .emacs.d directory. If you keep your .emacs.d directory under source control (as you can do for free at Github if you make it a public repository), make sure you don’t exclude custom.el.

Note that if you ever do want to search the vendor directory, you can simply set the vendor directory as the “Base directory” in the rgrep prompt.

Unfortunately, this doesn’t appear to be taken into account by find-grep-dired, which is how I perform global search and replace. If anyone knows how to do this for find-grep-dired, please leave a comment. Thanks!

Slide presentation containing some useful links. Some slides are merely placeholders for me to talk around, so not very interesting.