If you don’t already know, Bort has now been updated to Rails 2.2. For more information on exactly what has changed, please see Jim’s post.

I have also updated my email-as-login fork of bort to include this release.

Please give us your feedback over on our Uservoice page.

When testing Controller actions for access restriction, I was building a set of tests for each controller that were in a similar format to this:


describe "access control" do
  it "should prevent access by non-logged-in users"
  it "should prevent access by normal users"
  it "should prevent access by editor users"
  it "should prevent access by admin users"
  it "should allow access by super admin users"
end

Unforunately, this was neither dry, complete, nor very readable, so I have come up with the following helper that allows you to specify the access restrictions for a number of methods:

In your spec_helper.rb file, add the following function:


def access_control (code, options={})
  options = {:allow => [], :disallow => []}.merge(options)

  options[:allow].each do |user|
    it "#{code} should allow #{user.to_s}" do
      login_as(user)
      eval code
      response.should_not redirect_to(login_path)
    end
  end

  options[:disallow].each do |user|
    it "#{code} should disallow #{user.to_s}" do
      login_as(user)
      eval code
      response.should redirect_to(login_path)
    end
  end
end

Then in the specs for your controller, call the function as follows:


access_control("get :index", {:allow => [:super_admin], :disallow => [:quentin, :admin]})

This allows you to very quickly build a set of specs that fully describe how access control to your methods should restrict your users:


[ "get :show, :id => 0",
      "get :new",
      "post :create, :setting_value => {:value => 'A new setting', :group => 0}",
      "get :edit",
      "put :update, :id => SettingValue.first.id, :setting_value => {:value => 'changed'}",
      "delete :destroy, :id => SettingValue.first.id"
    ].each do |action|
      access_control(action, {:allow => [:super_admin], :disallow => [:quentin, :admin]})

In order to DRY-up our process of creating new rails projects over at Fudge, we recently created Bort – a base rails application that contains all of the bits that we push together when starting out.

Jim has more about the base system, but there is also a fork that contains all of the bort goodness, with the notable difference that instead of using usernames to authenticate, the users email address is used instead.

Enjoy.

A recent release of Bouldr threw a bit of a spanner in the works when the excellent Asset Packager began choking when attempting to create our concatenated and compressed javascript file.

It turns out that as I am developing in Windows (gah!), I need to run dos2unix in order to ensure that the js files are in the appropriate file format (our server is running CentOS). In order to automate this process, I added the following to our deploy script:


# Need to ensure files are in UNIX format for the asset packager to run correctly
run "cd #{release_path}/public/javascripts && find . -name '*.js' -exec dos2unix '{}' \\;"

# Run the asset packager to create the production javascript and css files
run "cd #{release_path} && rake asset:packager:build_all RAILS_ENV=production"

I recently came across a small problem in RSH whereby on loading a page with a state in the url (e.g. localhost.com/test_page#test) would not trigger the history listener function on it’s initial load. To fix this, I needed to modify my window onload function to include a call to my history listener, as follows:


history_listener: function (newLocation, historyData) {
...
},

// Window.onload calls this
initialize_history: function () {
       	dhtmlHistory.initialize();
       	dhtmlHistory.addListener(this.history_listener.bindAsEventListener(this));

       	// Added this line to force a call to my history listener
       	this.history_listener(dhtmlHistory.getCurrentLocation());
}

This ensures that when the page is freshly loaded, the history listener is called with the contents of our initial location.

This one had me for a while: When attempting to install the MySQL gem on my shiny new Slicehost slice, I was getting the folowing error:


Building native extensions.  This could take a while...
ERROR:  Error installing mysql:
   ERROR: Failed to build gem native extension.

/usr/local/bin/ruby extconf.rb install mysql -- --with-mysql-include=/usr/include/mysql --with-mysql-lib=/usr/lib/mysql
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lm... yes
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lz... yes
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lsocket... no
checking for mysql_query() in -lmysqlclient... no
checking for main() in -lnsl... yes
checking for mysql_query() in -lmysqlclient... no
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of
necessary libraries and/or headers.  Check the mkmf.log file for more
details.  You may need configuration options.

Provided configuration options:
   --with-opt-dir
   --without-opt-dir
   --with-opt-include
   --without-opt-include=${opt-dir}/include
   --with-opt-lib
   --without-opt-lib=${opt-dir}/lib
   --with-make-prog
   --without-make-prog
   --srcdir=.
   --curdir
   --ruby=/usr/local/bin/ruby
   --with-mysql-config
   --without-mysql-config
   --with-mysql-dir
   --without-mysql-dir
   --with-mysql-include=${mysql-dir}/include
   --with-mysql-lib=${mysql-dir}/lib
   --with-mysqlclientlib
   --without-mysqlclientlib
   --with-mlib
   --without-mlib
   --with-mysqlclientlib
   --without-mysqlclientlib
   --with-zlib
   --without-zlib
   --with-mysqlclientlib
   --without-mysqlclientlib
   --with-socketlib
   --without-socketlib
   --with-mysqlclientlib
   --without-mysqlclientlib
   --with-nsllib
   --without-nsllib
   --with-mysqlclientlib
   --without-mysqlclientlib

Gem files will remain installed in /usr/local/lib/ruby/gems/1.8/gems/mysql-2.7 for inspection.
Results logged to /usr/local/lib/ruby/gems/1.8/gems/mysql-2.7/gem_make.out

It turns out, all I needed was to run the following:


gem install mysql -- --with-mysql-config=/usr/bin/mysql_config

Hope this saves someone the hour or so I spent scratching my head!

OK, so say you have a form as follows:


<% form_tag '/', :method => :get do -%>
<p><%= text_field_tag 'q', params[:q] ||= '' %> <%= submit_tag 'Search' %></p>
<% end %>

That form will send the contents of the text box to the page at ‘/’. Unfortunately, it will also send through the value of the submit button, which is ‘Search’ in this case. The url generated will be:

/?q=something&commit=Search

In this case, we would want only the ‘q’ parameter passing through, and in order to do this, all we need do is nullify the name parameter:


<% form_tag '/', :method => :get do -%>
<p><%= text_field_tag 'q', params[:q] ||= '' %> <%= submit_tag 'Search', :name => nil %></p>
<% end %>

This will create the following url on submission:

/?q=something

Just a quick tip I recently came across:

If you want to see the docs for any of the gems installed on your dev machine, type the following in the command line…


gem_server

…and then browse over to http://localhost:8808/!

Railscasts are a Rails developers wet-dream.

Since Ryan created the site, it has been a fount of knowledge for anyone wishing to learn about the latest and greatest tips and tricks. To make things even better, they are free, though PeepCode is also very much worth a look for those of us wanting to glean a little (or a lot) of in-depth knowledge; $9 is a menial sum to pay for what amounts to a full-on lecture!

If you haven’t done already, go take a look – there is even a bunch of screencasts dedicated to Rails 2.0!

Given that the back button has been around for many years, and users have grown accustomed to it’s use, it’s a shame to see so many web applications neglecting this feature. As an example, I’ll pick on Lightbox, a tool that I like, yet one that is flawed in it’s disregard for the back button. For example:

  1. You navigate to a page that contains a thumbnailed image
  2. You click the image, and Lightbox shows it centered in your browser, full size
  3. You click back, press the back button on your mouse or whatever

Now, I realise that Lightbox has a close button in the image dialog box, but I’m not looking at the box, I’m looking at the image, and furthermore, my natural path back to the originating page is to press the back button, not to close the Lightbox. I therefore end up at the page I was on before I saw the thumbnail.

I’ve seen this with many users – the back button has been scarred into our mind, and to break it now is frivolous, and will ultimately lead to frustration, and lost users.

It’s with relief therefore, that I found Really Simple History, a JavaScript library for dealing with this exact problem – to create a JS history system that can run across many browsers, and provide developers with a little hope that our apps will no longer break the most fundamental user assumptions.

We tried to get the browser history working in Bouldr, and to an extent, we succeeded. The difference between coding something proprietary, and using an open source solution, though is very great indeed, so without doubt, we’ll be refactoring to use this library in the future.