Showing posts with label rails. Show all posts
Showing posts with label rails. Show all posts

Saturday, June 27, 2009

Using simple_auto_complete

I'm working on my first public Rails application for managing small skydiving businesses.  The primary feature is to track when a person gets in an aircraft to make a skydive.  In skydiver speak, that's: "Jumpers are manifested in a Slot on a Load". I'm using a model named Account to hold Jumpers, Pilots and in fact anyone who does business with the dropzone.

So, My models look like:
class Load < ActiveRecord::Base
  has_many :slots, :dependent => :destroy
end
and:
class Slot < ActiveRecord::Base
  belongs_to :account
end
The form for editing a load contains:
<%= render :partial => 'slot', :collection => @load.slots %>
and the _slots.html.erb partial contains something like:
<% fields_for "load[slot_attributes][]", slot do |slot_form| -%>
  <%= slot_form.label :account_name, 'Jumper:' %>
  <%= slot_form.text_field :account_name %>
<% end -%>

This all works great, so it's time for a little flair ... what I'd like is for the Account.name field to present a list of accounts that match the text that I've typed so far and allow me to pick one (like the Google search field does these days).

Enter simple_auto_complete.

The instructions in the README describe the steps to get the simplest example working but left me scratching my head.  What I needed was the following...

In SlotsController (something I didn't even need before):

class SlotsController < ApplicationController
  autocomplete_for :account, :name, :order => 'name ASC'
end

in _slot.html.erb, I changed to:

<% fields_for "load[slot_attributes][]", slot do |slot_form| -%>
  <%= slot_form.label :account_name, 'Jumper:' %>
  <%= slot_form.text_field :account_name, :class => 'autocomplete', 
      :autocomplete_url => autocomplete_for_account_name_slots_path %>
<% end -%>
I updated my routes.rb to include:
map.resources :slots, :collection => { :autocomplete_for_account_name => :get}
Finally, I added to my application.html.erb layout:
<%= stylesheet_link_tag 'site', 'jquery.autocomplete' %>
<%= javascript_include_tag 'jquery', 'jquery.autocomplete', 'application', 'prototype' %>
And it works like a champ!

Saturday, February 28, 2009

Permission denied - db/test.sqlite3

I'm investigating creating a plugin for Redmine and having a good old time when I decide to clear out the test database:

C:\dev\redmine>rake db:test:purge
(in C:/dev/redmine)
rake aborted!
Permission denied - db/test.sqlite3

Boo! 

I don't know what made me think of it but I tried unsetting my RAILS_ENV environment variable (I had it set to "test"):

C:\dev\redmine>set rails_env=

C:\dev\redmine>rake db:test:purge
(in C:/dev/redmine)

C:\dev\redmine>

Hurrah!

Wednesday, January 14, 2009

My first Rails bug – accepted!

At the time that I posted about My first Rails bug the tests that existed for Action Pack contained failures and the only test in integration_upload_test.rb was:

assert_equal(:multipart_form, SessionUploadTest.last_request_type)

I felt that my change could be submitted with no tests because:

  • The change was trivial (6 characters)
  • The change was for a well known Windows file issue
  • The tests that existed didn’t pass
  • There were no example tests for me to build on

Three months after I submitted the ticket, Pratik dismissed it by changing its state to incomplete with the comment “Missing a test case.”  Are these guys CRAZY?  No, no they are not.  Just because there were broken windows in the neighborhood did not give me the right to break any more.

So, I sucked it up, cloned the latest rails repository again and to my surprise … all the Action Pack tests passed! … and there was a nice simple example in multipart_params_parsing_test.rb:

  test "uploads and reads file" do
    with_test_routing do
      post '/read', :uploaded_data => fixture_file_upload(FIXTURE_PATH + "/hello.txt", "text/plain")
      assert_equal "File: Hello", response.body
    end
  end

This is too easy!  I added a test for my issue:

  test "uploads and reads a binary file in windows" do
    with_test_routing do
      fixture_file = FIXTURE_PATH + "/mona_lisa.jpg"
      post '/read', :uploaded_data => fixture_file_upload(fixture_file, "image/jpg")
      assert_equal 'File: '.length + File.size(fixture_file), response.content_length
    end
  end

Watched it fail.  Added the magic 6 characters (“, ‘rb’”).  Watched it pass.  Wrapped it up and shipped it off.  The following morning I pick up an email letting me know that Pratik has reopened the ticket and assigned it to Joshua Peek for review.  The change was committed before the day was out!

Many thanks to Pratik and Josh for attending to this so promptly.

Monday, January 12, 2009

Restful-Authentication with Cucumber features (Step 2)

Knowing that all the stories that ship with restful-authentication pass, I’m ready to convert to Cucumber features.  The process is actually fairly simple and is described quite clearly on this Cucumber wiki page.  I did have to tweak a couple of the step matchers and create ra_env.rb:

Before do
  Fixtures.reset_cache
  fixtures_folder = File.join(RAILS_ROOT, 'spec', 'fixtures')
  Fixtures.create_fixtures(fixtures_folder, "users")
end
 
# Make visible for testing
ApplicationController.send(:public, :logged_in?, :current_user, :authorized?)

I’d have liked to put this in the support directory but Cucumber loaded it before env.rb, so I put in with the step_definitions.

Once that was done, all that was left was to update authenticated_generator.rb to copy the template features instead of stories.

The result is available from my fork on github.

Enjoy!

Saturday, January 10, 2009

Restful-Authentication with Cucumber features (Step 1)

My latest exercise is to update the restful-authentication Rails plugin to generate Cucumber features instead of RSpec stories. There is already a Lighthouse ticket open for this, so I guess I'm not the first one to think of it. The first steps make sure that restful-authentication is set up correctly:
  1. Create an empty Rails application
  2. Put in version control (git init, git add ., git commit -a -m "init")
  3. Add plugins as shown on the Cucumber Rails wiki page
  4. Add my fork of restful-authentication in the same manner
  5. Complete the restful-authentication installation per the directions
  6. Create the database tables (rake db:migrate)
  7. Run the RSpec stories (stories\rest_auth_stories.rb)
Stories won't even run, let alone pass! I'm not going to show all the errors along the way, but the steps to get to passing stories were:
  1. Added AthenticatedSystem into ApplicationController with the following 2 lines:
      include AuthenticatedSystem
      helper :all # include all helpers, all the time
  2. Uncommented the routes.rb map.root line(map.root :controller => "welcome")
  3. Removed line 1 of user_steps.rb (require File.dirname(__FILE__) + '/../helper')
  4. Created a minimal WelcomeController with an index method
  5. Created a minimal layout
16 scenarios: 16 succeeded, 0 failed, 0 pending
Hurrah!

Sunday, October 19, 2008

Cucumber scenarios need a title

So I'm playing with Behavior Driven Development (BDD) and Ruby on Rails using Cucumber and Webrat. I thought I'd start with the simplest feature I could think of, "I want to see the application name in the title". So, off we go with features/site_layout.feature:
Feature: Site Layout
  In order to build familiarity
  As a user
  I want to see the application name in the title

  Scenario:
    Given I am on the home page
    Then the title tag should be "WOW App"
I create features/steps/site_steps.rb:
Given /^I am on (.*)$/ do |page|
  visits case page
         when "the home page"
           "/"
         else
           raise "Can't find mapping from \"#{page}\" to a path"
         end
end

Then /^the (.*) tag should be "(.*)"$/ do |tag, content|
  response.should have_tag(tag, content)
end
And run it with "rake features". What I get is:
C:\dev\temp>rake features
(in C:/dev/temp)
Feature: Site Layout  # features/site_layout.feature
  In order to build familiarity
  As a user
  I want to see the application name in the title
  Scenario: Given I am on the home page     # features/site_layout.feature:6
    Then the title tag should be "WOW App"  # features/steps/site_steps.rb:10
      You have a nil object when you didn't expect it!
      The error occurred while evaluating nil.content_type (NoMethodError)
      ...
      ./features/steps/site_steps.rb:11:in `Then /^the (.*) tag should be "(.*)"$/'
      features/site_layout.feature:8:in `Then the title tag should be "WOW App"'


1 steps failed
rake aborted!
Not quite what I had in mind, I was expecting a failure, but got an error. Nevermind, this is all very new to me, so I'll push on and it'll all become clear right? I create an app/views/layouts/application.html.erb:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
    <title>WOW App</title>
  </head>
  <body>
  </body>
</html>
Run the feature again ... no joy, same error. Hours of web searching leads me nowhere. Finally while looking at some examples I noticed that my Scenario doesn't have a description, so I add one on line 6:
Feature: Site Layout
  In order to build familiarity
  As a user
  I want to see the application name in the title

  Scenario: On the home page
    Given I am on the home page
    Then the title tag should be "WOW App"
Now I get:
C:\dev\temp>rake features
(in C:/dev/temp)
Feature: Site Layout  # features/site_layout.feature
  In order to build familiarity
  As a user
  I want to see the application name in the title
  Scenario: On the home page                # features/site_layout.feature:6
    Given I am on the home page             # features/steps/site_steps.rb:1
      No route matches "/" with {:method=>:get} (ActionController::RoutingError)
...
      features/site_layout.feature:7:in `Given I am on the home page'
    Then the title tag should be "WOW App"  # features/steps/site_steps.rb:10


1 steps failed
1 steps skipped
rake aborted!
A quick addition to config/routes.rb:
map.root :controller => 'example'
And a stub controller from "ruby script\generate controller Example index", I get:
C:\dev\temp>rake features
(in C:/dev/temp)
Feature: Site Layout  # features/site_layout.feature
  In order to build familiarity
  As a user
  I want to see the application name in the title
  Scenario: On the home page                # features/site_layout.feature:6
    Given I am on the home page             # features/steps/site_steps.rb:1
    Then the title tag should be "WOW App"  # features/steps/site_steps.rb:10


2 steps passed
Hurrah!

Saturday, September 20, 2008

My first Rails bug

UPDATE January 14, 2009 -> The patch for this bug was accepted!

I'm pretty new to Rails and my first project included uploading videos. Shouldn't be too difficult I thought, after a little Google searching, I came up with the perfect example by Jim Neath: Converting Videos with Rails: Converting the Video Wanting to practice new skills with RSpec and Cucumber I wrote my first feature spec:

Feature: Upload videos
  In order to provide videos to users after hours
  As a videographer
  I want to upload videos

  Scenario: A valid filename is provided
    Given I go to the new video page
    And I browse to the file "Movie_0001.avi"

    When I submit the upload

    Then I should see "success"
    And the file should be uploaded
and the supporting steps file, upload_steps.rb:
require 'ftools'
require 'mime/types'

When /I browse to the file \"(.+)\"/ do |path|
  @original_filepath = File.join('features/fixtures/', path)
  mime_types = MIME::Types.of(@original_filepath)

  attach_file 'video[source]', @original_filepath, mime_types[0].content_type
end

When 'I submit the upload' do
  click_button 'Create'
end

def uploaded_filepath
  uploaded_basename = File.basename(@original_filepath)
  File.join(RAILS_ROOT, "public/videos/1", uploaded_basename)
end

Then /the file should be uploaded/ do
  assert File.compare(@original_filepath, uploaded_filepath)
end
what I got was:
...
    And the file should be uploaded
       is not true. (Test::Unit::AssertionFailedError)
      c:/ruby/lib/ruby/1.8/test/unit/assertions.rb:48:in `assert_block'
      c:/ruby/lib/ruby/1.8/test/unit/assertions.rb:500:in `_wrap_assertion'
      c:/ruby/lib/ruby/1.8/test/unit/assertions.rb:46:in `assert_block'
      c:/ruby/lib/ruby/1.8/test/unit/assertions.rb:63:in `assert'
      c:/ruby/lib/ruby/1.8/test/unit/assertions.rb:495:in `_wrap_assertion'
      c:/ruby/lib/ruby/1.8/test/unit/assertions.rb:61:in `assert'
      ./features/upload/steps/upload_steps.rb:22:in `And /the file should be uploaded/'
      features/upload/upload.feature:14:in `And the file should be uploaded'
...
On closer inspection, the test was failing because the uploaded file was truncated in some bizarre way. However, if I ran the application and manually upload a file from the browser, everything worked fine. After much hunting I ended up in rails/actionpack/lib/action_controller/integration.rb where in multipart_body, the mode is not specified in the call to File.open, so it defaults to "r". This is all well and good on anything but Windows, which I happen to be using! Windows requires that the mode be specified as "rb" to ensure the file is read as binary. I submitted a patch but I'm not holding my breath for it to be pulled in anytime soon.