I was having troubles with autotest, and not really finding a good solution. But i noticed that inside rspec2 they were using watchr.

Watchr is a very generic gem that will allow you to watch a set of files and take action when something changes. That sounds great, but maybe a bit too general :)

So, it appears you need a script to run your rspec2 continuously using watchr. Luckily i did find some examples, which allowed my to brew my own (save in your rails-root as specs.watchr):

# adapted from http://github.com/rspec/rspec-rails/blob/master/specs.watchr

# Run me with:
#
#   $ watchr specs.watchr

# --------------------------------------------------
# Convenience Methods
# --------------------------------------------------
def all_spec_files
  Dir['spec/**/*_spec.rb']
end

def run_spec_matching(thing_to_match)
  matches = all_spec_files.grep(/#{thing_to_match}/i)
  if matches.empty?
    puts "Sorry, thanks for playing, but there were no matches for #{thing_to_match}"
  else
    run matches.join(' ')
  end
end

def run(files_to_run)
  puts("Running: #{files_to_run}")
  system("rspec -c #{files_to_run}")
  no_int_for_you
end

def run_all_specs
  run(all_spec_files.join(' '))
end

# --------------------------------------------------
# Watchr Rules
# --------------------------------------------------
watch('^spec/(.*)_spec\.rb')    { |m| run_spec_matching(m[1]) }
watch('^app/(.*)\.rb')          { |m| run_spec_matching(m[1]) }
watch('^app/(.*)\.haml')        { |m| run_spec_matching(m[1]) }
watch('^lib/(.*)\.rb')          { |m| run_spec_matching(m[1]) }
watch('^spec/spec_helper\.rb')  { run_all_specs }
watch('^spec/support/.*\.rb')   { run_all_specs }

# --------------------------------------------------
# Signal Handling
# --------------------------------------------------

def no_int_for_you
  @sent_an_int = nil
end

Signal.trap 'INT' do
  if @sent_an_int then
    puts "   A second INT?  Ok, I get the message.  Shutting down now."
    exit
  else
    puts "   Did you just send me an INT? Ugh.  I'll quit for real if you do it again."
    @sent_an_int = true
    Kernel.sleep 1.5
    run_all_specs
  end
end

It even seems to be quicker than autotest, but not sure if that is just wishful thinking.

If you want to get authlogic working in a fresh rails 3 project, it will take a bit more steps than devise. This has everything to do with vision: authlogic only claims to deliver you the backend, allowing itself to remain more stable and to easily replace other authentication libraries (even your home-brewn). That seems a great philosophy, but starting from scratch involves more steps.

First you need to setup a clean rails3 project, as i described before.

Install the gem

As simple as adding the following line to your Gemfile :

gem "authlogic"

and then run bundle install or bundle update.

Create the UserSession

rails g authlogic:session UserSession

This would be all, but in my installation it was not enough. Something inside rails3 broke the authlogic session.
But the fix, luckily, is pretty easy: you have to add the to_key function.

So your complete UserSession model will look as follows:

class UserSession < Authlogic::Session::Base

  def to_key
    new_record? ? nil : [ self.send(self.class.primary_key) ]
  end

end

Create the User

If you do not yet have a User model, and i am assuming you don’t, you need to

rails g model User

For now, this is an empty model. You will need to fill in your migration to create the model correctly.

class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.string    :login,               :null => false
      t.string    :email,               :null => false
      t.string    :crypted_password,    :null => false
      t.string    :password_salt,       :null => false
      t.string    :persistence_token,   :null => false
      #t.string    :single_access_token, :null => false                # optional, see Authlogic::Session::Params
      #t.string    :perishable_token,    :null => false                # optional, see Authlogic::Session::Perishability

      # magic fields (all optional, see Authlogic::Session::MagicColumns)
      t.integer   :login_count,         :null => false, :default => 0
      t.integer   :failed_login_count,  :null => false, :default => 0
      t.datetime  :last_request_at
      t.datetime  :current_login_at
      t.datetime  :last_login_at
      t.string    :current_login_ip
      t.string    :last_login_ip
      t.timestamps
    end

    add_index :users, ["login"], :name => "index_users_on_login", :unique => true
    add_index :users, ["email"], :name => "index_users_on_email", :unique => true
    add_index :users, ["persistence_token"], :name => "index_users_on_persistence_token", :unique => true 

  end

  def self.down
    drop_table :users
  end
end

Now we still have to add some code to the User model :

class User < ActiveRecord::Base
  acts_as_authentic
end

ApplicationController

We need to add some generic code to our applicationcontroller to persist the sessions, to check whether a user is required and do the correct redirects when needed.

class ApplicationController < ActionController::Base
  protect_from_forgery

  helper_method :current_user_session, :current_user

  private
    def current_user_session
      logger.debug "ApplicationController::current_user_session"
      return @current_user_session if defined?(@current_user_session)
      @current_user_session = UserSession.find
    end

    def current_user
      logger.debug "ApplicationController::current_user"
      return @current_user if defined?(@current_user)
      @current_user = current_user_session && current_user_session.user
    end

    def require_user
      logger.debug "ApplicationController::require_user"
      unless current_user
        store_location
        flash[:notice] = "You must be logged in to access this page"
        redirect_to new_user_session_url
        return false
      end
    end

    def require_no_user
      logger.debug "ApplicationController::require_no_user"
      if current_user
        store_location
        flash[:notice] = "You must be logged out to access this page"
        redirect_to account_url
        return false
      end
    end

    def store_location
      session[:return_to] = request.request_uri
    end

    def redirect_back_or_default(default)
      redirect_to(session[:return_to] || default)
      session[:return_to] = nil
    end
end

UserSessionsController

rails g controller UserSessions new

and fill the controller with the correct code:

class UserSessionsController < ApplicationController
  before_filter :require_no_user, :only => [:new, :create]
  before_filter :require_user, :only => :destroy

  def new
    @user_session = UserSession.new
  end

  def create
    @user_session = UserSession.new(params[:user_session])
    if @user_session.save
      flash[:notice] = "Login successful!"
      redirect_back_or_default users_url
    else
      render :action => :new
    end
  end

  def destroy
    current_user_session.destroy
    flash[:notice] = "Logout successful!"
    redirect_back_or_default new_user_session_url
  end
end

and of course that also needs a view, in new.html.haml

%h1 Login

-#= form_for @user_session, :url => user_session_path do |f|
-#= form_for @user_session, :url => {:controller => :user_sessions, :method => :post} do |f|
= form_for @user_session, :url => {:action => "create"} do |f|
  = f.error_messages
  %div
    = f.label :login
    = f.text_field :login
  %div
    = f.label :password
    = f.password_field :password
  %div
    = f.check_box :remember_me
    = f.label :remember_me
  %div
    = f.submit "Login"

We want to use the f.error_message, but that is now removed from Rails3 and we need to install a plugin instead:

rails plugin install git://github.com/rails/dynamic_form.git

We also need to define, inside config/routes.rb. You will see that the generator will have added the route get 'user_sessions/new', but that is not enough.

You will have to add:

  resources :user_sessions

  match 'login' => "user_sessions#new",      :as => :login
  match 'logout' => "user_sessions#destroy", :as => :logout

Restrict access

Suppose you now have some other controller, e.g. HomeController, then restricting acces is straightforward:

rails g controller home index
class HomeController < ApplicationController

  before_filter :require_user

  def index
  end

end

The method require_user, defined in ApplicationController, will check if there is a user logged on, and if not redirect to the login-page.

If we now delete the index.html inside your public folder, and we add the following at the bottom of config/routes.rb

root :to => 'home#index'

Now we can start our application, and it should redirect to our login-page.

But of course, since we have no users for now, we can’t login just yet. To allow testing, you fire up your console:

$ rails c
Loading development environment (Rails 3.0.0.rc)
ruby-1.9.2-p0 > User.create(:login => 'test', :email => 'test@tester.com', :password => 'test123', :password_confirmation => 'test123')
 => #<User id: 1, login: "test", email: "test@tester.com", crypted_password: "0129d9733b7912017e37a50263901488da90e127e6fd1ae6081...", password_salt: "ygufdflWkJQZGhbsGyia", persistence_token: "957788609b2067092dd3852b01613d53f96d0692ce5131174ca...", login_count: 0, failed_login_count: 0, last_request_at: nil, current_login_at: nil, last_login_at: nil, current_login_ip: nil, last_login_ip: nil, created_at: "2010-08-29 14:11:04", updated_at: "2010-08-29 14:11:04">
ruby-1.9.2-p0 >

Now you should be able to login using this user.

For completeness, you should add some links to your application-view to allow logging in and out:

      #user_nav
        - if current_user
          = "Signed in as #{current_user.email}. Not you?"
          = link_to "Sign out", logout_path
        - else
          = link_to "Sign in", new_user_session_path

This would get your rails3 project started.

The next steps would be to add some user management, or allowing users to sign up themselves, and maybe add some roles to limit certain users access if needed.

Now, to contrast this with devise: i now have a bunch of code in my application that actually is not specific to my code, but is also completely not tested. I will provide example rspec tests for this later.

Actually, the problem with this scenario as outlined, is it is one big bang. You should start little, declaring things you need to be able to do, and work in little steps to get there. But this scenario is intended as a draft to see how you could get there. Now, in your real application, you should start with the tests, and then from this example you now how you can implement it.

There used to be a greasemonkey script fast_tagger which allowed an etsy seller to add all tags at once when creating or editing an item. Since april of this year this script was broken.

My girlfriend has had a shop for a couple of years and missed the functionality from etsyhacks dearly. Apparently Ian, the person behind etsyhacks, is now working for etsy himself and isn’t maintaining the scripts anymore.

So, without further ado: you can download the fast_tagger_2 script to install. It should operate in the same way as before.

Installation instructions:

  • install firefox and greasemonkey if you haven’t done so already.
  • once Greasemonkey is installed and running, follow this link.
  • The “fast tagger 2″ script will be downloaded, and Greasemonkey will ask if you want to install it. Assuming you trust me, click “Install”.

Operation

When you create a new listing and go to the tags page, or when you edit the tags of an existing listing, you will see that a new box is now visible. Inside this box, you can enter tags, comma separated and …

  • click “add tags” to add the tags to the listing
  • click “replace tags” to remove any existing tags and replace them with your current ones.
  • or you can click “delete tags” to remove any existing tags.

[UPDATE] I have been in touch with Ian, and he assured me he is going to continue supporting etsyhacks, only that he currently was swamped in work. I leave this script for whoever it could help in the meantime.

When i try to run pgadmin3 on my ubuntu 10.04 box i now get the following error:

$ pgadmin3
pgadmin3: relocation error: pgadmin3: symbol _ZN21wxMemoryFSHandlerBase19AddFileWithMimeTypeERK8wxStringPKvjS2_, version WXU_2.8 not defined in file libwx_baseu-2.8.so.0 with link time reference

I assume it has something to do with one of the latest updates. I run all the updates always when they are proposed, but i am not really sure this always is a good idea.

When googling the error i found a lot of links, and ultimately, also the fix:

apt-get source pgadmin3
sudo apt-get install debhelper libpq-dev libwxgtk2.8-dev libxml2-dev libxslt1-dev autotools-dev devscripts
cd pgadmin3-1.10.2
dpkg-buildpackage
sudo dpkg -i ../pgadmin3_1.10.2-1_i386.deb

If you want to run longrunning jobs in the background, one very easy solution is using delayed_job. One other very interesting alternative is resque, but it seemed harder to setup (it uses redis), and delayed_job seemed to be just right for my needs.

In short (from the redis documentation) :

Choose Resque if:

  • You need multiple queues
  • You don’t care / dislike numeric priorities
  • You don’t need to persist every Ruby object ever
  • You have potentially huge queues
  • You want to see what’s going on
  • You expect a lot of failure / chaos
  • You can setup Redis
  • You’re not running short on RAM

Choose DelayedJob if:

  • You like numeric priorities
  • You’re not doing a gigantic amount of jobs each day
  • Your queue stays small and nimble
  • There is not a lot failure / chaos
  • You want to easily throw anything on the queue
  • You don’t want to setup Redis

And in my case: a short list of outstanding jobs, i am using a database already, not sure about redis, performance is not that important, i do not expect a lot of failure, i do not want to setup redis (yet :).

So to use the delayed_job gem in a rails3 you will need to use the code from github (as the gem version 2.0.3 is not yet rails3 compatible). Add the following line to your Gemfile :

gem "delayed_job",  :git => 'git://github.com/collectiveidea/delayed_job.git'

and then run

bundle install

Because i am using ActiveRecord, i can just use the generator, that will create the table and add the script to run a worker:

rails generate delayed_job
rake db:migrate

Include the rake tasks from delayed-jobs into your Rakefile:

begin
  require 'delayed/tasks'
rescue LoadError
  STDERR.puts "Run `bundle:install` to install delayed_job"
end

Create an initializer file delayed_jobs_config.rb and write

# config/initializers/delayed_job_config.rb
Delayed::Worker.destroy_failed_jobs = false
#Delayed::Worker.sleep_delay = 60
#Delayed::Worker.max_attempts = 3
Delayed::Worker.max_run_time = 5.minutes

If you are happy with the defaults, you can leave it. For me it was important to keep a record of the jobs that failed.

Then you can just delay any function-call like this:

envelope.delay.query_status(delivery)

Just adding the .delay does all the magic for you! And starting a worker is as simple as

rake jobs:work

Devise seems to become the new authentication standard in rails land. Also the last version of the gem is for rails3, so it is very easy to start using it.

If you are using rspec2 for your tests, it is also really easy to test your controllers that are authenticated by devise.

Suppose you have a DoStuffController like this:

class DoStuffController < ApplicationController

  before_filter :authenticate_user!

  def index
  end

end

You need to test it as follows in do_stuff_controller_spec.rb:

require 'spec_helper'

describe DoStuffController do
  include Devise::TestHelpers

  before (:each) do
    @user = Factory.create(:user)
    sign_in @user
  end

  describe "GET 'index'" do
    it "should be successful" do
      get 'index'
      response.should be_success
    end
  end
end

And in your factory, users.rb:

Factory.define :user do |f|
  f.email "bla@blabla.com"
  f.password "blax123"
end

I am not testing the fact that the redirect will work, under the assumption that that is specific devise functionality which i assume to be tested and working.

Previously, in rails 2.3.8 i used the prototype-helpers link_to_remote and form_remote_for (amongst others).

These had the option to add callbacks as follows:

    link_to_remote "Add to cart",
      :url => { :action => "add", :id => product.id },
      :update => { :success => "cart", :failure => "error" }

(an example from the documentation).
This example would, upon success update the html-element with class “cart”, and upon failure the class “error”.

The possible callbacks were:

  • :loading: Called when the remote document is being loaded with data by the browser.
  • :loaded: Called when the browser has finished loading the remote document.
  • :interactive: Called when the user can interact with the remote document, even though it
    has not finished loading.
  • :success: Called when the XMLHttpRequest is completed, and the HTTP status code is in
    the 2XX range.
  • :failure: Called when the XMLHttpRequest is completed, and the HTTP status code is
    not in the 2XX range.
  • :complete : Called when the XMLHttpRequest is complete (fires after success/failure if
    they are present).

Now the modus operandi has changed, instead we write:

    link_to "Add to cart", :url => {:action => "add", :id => product.id},  :remote => true

and it seems there is no option to set the callbacks anymore.

Instead of a normal html, we now render javascript, like this (in jquery) :

    $('.cart').replaceWith(<%= escape_javascript(render :partial => 'cart') %>)

But how do you handle an error situation? Do i handle it in my controller, and use seperate views?
It would seem useful to me to somehow be able to mimic the behaviour we had before.

Luckily in this article I was able to find the solution. I had already found that in rails.js the following callbacks were checked:

  • ajax:loading : triggered before executing the AJAX request
  • ajax:success : triggered after a successful AJAX request
  • ajax:complete : triggered after the AJAX request is complete, regardless the status of the response
  • ajax:failure : triggered after a failed AJAX request, as opposite to ajax:success

But i had no idea how to provide these callbacks.
The javascript should be unobtrusive, so this coupling is not done straight in the HTML anymore.

From the same article i found a very clear example how to solve this. Take the following Rails 2.3.8 code :

    <% form_remote_tag :url => { :action => 'run' },
            :id => "tool-form",
            :update => { :success => "response", :failure => "error" },
            :loading => "$('#loading').toggle()", :complete => "$('#loading').toggle()" %>

That translates to this in Rails3 :

    <% form_tag url_for(:action => "run"), :id => "tool-form", :remote => true do %>

and inside some javascript (application.js), you bind the events

    jQuery(function($) {
      // create a convenient toggleLoading function
      var toggleLoading = function() { $("#loading").toggle() };

      $("#tool-form")
        .bind("ajax:loading",  toggleLoading)
        .bind("ajax:complete", toggleLoading)
        .bind("ajax:success", function(data, status, xhr) {
          $("#response").html(status);
        });
    });

I have created a Rails3 application, started with Haml/Sass and finding it awesome. I am also trying to do unobtrusive javascript.

Before, in Rails 2.3, I would have expected a remote-form to have an :update attribute, where you could specify a selector where the response of the remote method would be rendered.

Now it needs to be done differently: my controller function will render a “.js” view, which will do the necessary actions itself. This makes it more library agnostic, and i prefer jquery.

So for instance i have a controller action “search”, and i have a corresponding “search.js.erb” as follows:

$('.grid').replaceWith('<%= escape_javascript(render :partial => 'list') %>')

This works. It will replace the html of the element with a class grid with the html from my rendered partial.

I started out trying to achieve that in HAML, and failed at first. So first i created the above ERB code which worked. Now my task seemed simpler: translate this seemingly simple line to HAML.

My first naive approach was to do the following:

= "$('.grid').replaceWith('#{escape_javascript(render :partial => 'list')}')"

But this just places the html as a readable string inside my page. Even i do something simple like

= "$('.grid').replaceWith('<h3>Text</h3>')

i see the actual characters

<h3>Text</h3>

and not the markup. But i need to contain the javascript inside the double-quotes or haml will not recognise it, and can not interpolate my ruby there.

After looking through the HAML reference, actually the solution was incredible simple. Once more.

!= "$('.grid').replaceWith('#{escape_javascript(render :partial => 'list')}')"

The != unescapes HTML (as opposed to the standard =). This is exactly what we need of course.

With Rails3 i can create a project fully cut to my needs. I will write it down here, just so i remember it well and hopefully it will help some of you too.

In my projects, i want to use haml, rspec2, factory-girl, jquery, …

We need a few steps to complete this.

Create the project

For starters, create the folder, without Test::Unit (-T) and without prototype (-J).

rails new test-project -T -J

You could also specify the database, using option -d, [--database=DATABASE], with the following options:

  • mysql
  • oracle
  • postgresql
  • sqlite3 (default)
  • frontbase
  • ibm_db

Specify needed gems

We also have to specify the gems we need. To do that, we need to edit the Gemfile which is located in the root of your project. My file looks as follows:

source 'http://rubygems.org'

gem 'rails', '3.0.0.rc'

# Bundle edge Rails instead:
# gem 'rails', :git => 'git://github.com/rails/rails.git'

#gem 'pg'
gem 'sqlite3-ruby', :require => 'sqlite3'

# Use unicorn as the web server
# gem 'unicorn'

# Deploy with Capistrano
# gem 'capistrano'

# To use debugger
# gem 'ruby-debug'

gem 'rails3-generators'

gem "bson_ext"
gem "haml"
gem "haml-rails"
gem "jquery-rails"

gem "rcov"

# we need this here, see http://blog.davidchelimsky.net/2010/07/11/rspec-rails-2-generators-and-rake-tasks/
group :development, :test do
  gem "rspec-rails", ">= 2.0.0.beta.18"
end

# test-environment gems
group :test, :spec, :cucumber do
  gem "factory_girl_rails"
  gem "rspec",                   ">= 2.0.0.beta.18"
  gem "remarkable",              ">=4.0.0.alpha2"
  gem "remarkable_activemodel",  ">=4.0.0.alpha2"
  gem "remarkable_activerecord", ">=4.0.0.alpha2"
  gem "capybara"
  gem "cucumber"
  gem "database_cleaner"
  gem "cucumber-rails"
end

Run bundle install to install all needed gems.

Use wanted generators

Then now edit the file application.rb, located in the config folder, and add the following lines:

    # Configure generators values
    config.generators do |g|
      g.test_framework  :rspec, :fixture => true
      g.fixture_replacement :factory_girl, :dir=>"spec/factories"
    end

This will make sure that the generators use our own defaults. So now when you type

rails generate model TestModel name:string
rails generate controller TestModel

and it will create haml views, rspec tests, and factories. Awesome :)

Prepare to use jquery!

To start using jQuery in Rails3, you have to first get the rails.js specifically for jQuery (stored in the jquery-ujs project). Save it in your public\javascripts directory.

Download jquery, and make sure to include both in your application layout or view. In HAML it would look something like.

= javascript_include_tag 'jquery.min.js'
= javascript_include_tag 'rails'

BUT: there is a quicker route! We also installed the gem jqeury-rails, which has the following generator if we want to install jquery in one go!

rails g jquery:install #--ui to enable jQuery UI

Rails requires an authenticity token to do form posts back to the server. This helps protect your site against CSRF attacks. In order to handle this requirement the driver looks for two meta tags that must be defined in your page’s head. Luckily rails makes it easier for us, again, and we just need to include csrf_meta_tag somewhere inside your page’s head (rails3 does this default in your application.html.erb). Be sure to include it in your haml code too.

An example application.html.haml would look like this:

!!! Strict
%html{ "xml:lang" => "en", :lang => "en", :xmlns => "http://www.w3.org/1999/xhtml" }
  %head
    %title== Test-project #{@page_title}
    %meta{'http-equiv' => "Content-Type", :content => "text/html; charset=utf-8"}
    %link{ :href => "/favicon.ico", :rel => "shortcut icon" }

    = stylesheet_link_tag :all

    = javascript_include_tag 'jquery-1.4.2.min.js', 'rails.js', 'application.js', :cache => 'all_1'

    = csrf_meta_tag

    = yield :local_javascript

    = yield :head
  %body
    #banner
      =image_tag 'your-logo.png'
      .the-title
        %h1
          %p Your application
      #navigation
    #container
      #sidebar
      #contents
        =yield
    #footer
      == &copy; 2010 your company

Done! :)

Read “Pure Rspec”, a great slideshow by Jon “Lark” Larkowski.
It contains some really cool rspec tips and hints, how to write your tests more readable and compact.