Specing the ApplicationController and appending routes
Recently I was writing some specs that needed to exercise a method on the ApplicationController. While researching approaches to doing this I came across this thread on Ruby-Forum in which Pat Maddox suggests creating a fake controller class in the spec and using that to exercise the behavior on the ApplicationController.
Something like:
application_controller_spec.rb:
application_spec.rb: describe ApplicationController, "storing locations" do class FooController < ApplicationController before_filter :assign_var def index; render :text => "foos"; end end controller_name :foo before(:each) do ActionController::Routing::Routes.draw do |map| map.resources :foo end end it "should assign the current user" do get :index assigns[:var].should_not be_blank end endapplication.rb:
class ApplicationController < ActionController::Base def assign_var @var = "Quantum Leap Rocks" end end
In the thread user Andy Croll points out that the call to ActionController::Routing:Routes.draw overwrites all of your applications existing routes and asks if there is a sane way to append a route.
I needed to be able to access my applications other routes in my specs as well and after a little digging in the routing API docs and source and reading through Jamis Buck's excellent post about Rails routing, I found this solution to the issue.
The Mapper class delegates calls to #connect to Routes#add_named_route which is a public method. So, to append a route we can put the following in our before_each method:
before(:each) do ActionController::Routing::Routes.add_named_route( 'fakies', '/fakies', :controller => 'fake', :action => 'index' ) end
Testing before_filters with RSpec
I recently noticed that I was repeating a lot of code in my controller specs where I would have a block describing a before_filter condition:
describe 'when not logged in' do it 'should redirect to the log in page' do ... end end
which would be repeated for each action to which the my :require_user before_filter was applied. Wanting to DRY up these controller specs I set out to find a way to specify this behavior in a way that could be shared by all controllers.
I took my initial inspiration from the post at: http://blog.wolfman.com/articles/2007/07/28/rspec-testing-all-actions-of-a-controller but ended up making some pretty significant changes.
Here is what I ended up with:
I created a spec_helper called before_filter_spec_helper.rb which gets included by spec_helper.rb and contains the methods that do the bulk of the work for testing before_filters:
module BeforeFilterSpecHelper # Returns an Array containing the actions for the current controller to which # the given filter has been applied def get_actions_to_test(filter_method) current_filter = controller.class.filter_chain.detect do |f| f.kind_of?( ActionController::Filters::BeforeFilter ) && f.method == filter_method end return [] unless current_filter if current_filter.options[:only] current_filter.options[:only] elsif current_filter.options[:except] current_filter.options[:except] else controller.class.public_instance_methods(false).reject{ |action| ['rescue_action'].include?(action) } end end # This method does the bulk of the work for testing before filters. For a given # filter_method, it gets a collection of actions that the filter has been applied # to setups expectations for the action then iterates over them calling get, # post, put, or delete with the appropriate parameters to execute the action. def test_before_filter( filter_method ) actions_to_test = get_actions_to_test(filter_method) return if actions_to_test.empty? # setting pre send expectations within the iteration over actions caused multiple # expectations to be created and all but the first would fail self.send( "#{filter_method.to_s}_pre_send_requirements" ) actions_to_test.each do |action| route = nil controller_short_name = controller.class.to_s.gsub('Controller', '').tableize ActionController::Routing::Routes.routes.each do |r| route = r if r.requirements[:controller] == controller_short_name && r.requirements[:action] == action end method = route.conditions[:method] if ['show', 'edit', 'update', 'delete'].include?( action ) self.send(method, action, :id => 1) else self.send(method, action) end self.send( "#{filter_method.to_s}_post_send_requirements" ) end end # ================ # = Expectations = # ================ # The #{filter_name}_pre_send_requirements and #{filter_name}_post_send_requirements # are where the expectations specific to each before_filter are setup. The pre_send # method is called only once before we start iterating over and calling the actions. # The post_send method is called once for each action immediately after we call get, # post, put, or delete for that action def require_user_pre_send_requirements controller.should_receive(:store_location).at_least(:once) end def require_user_post_send_requirements response.should_not be_success response.should redirect_to( login_url ) flash[:notice].should == BeforeFilterRequirements::LOGGED_IN_ERROR_MESSAGE end def require_no_user_pre_send_requirements controller.should_receive(:store_location).at_least(:once) end def require_no_user_post_send_requirements response.should_not be_success response.should redirect_to( user_path( assigns[:current_user] ) ) flash[:notice].should == BeforeFilterRequirements::LOGGED_OUT_ERROR_MESSAGE end def require_active_user_pre_send_requirements end def require_active_user_post_send_requirements response.should_not be_success response.should redirect_to( user_activation_path( assigns[:current_user] ) ) end end
I then created a module to contain the shared behaviors for testing before_filters in spec/controllers/shared/before_filter_behaviors_spec.rb:
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') module BeforeFilterBehaviors shared_examples_for "a controller with before_filters" do describe "with actions that require a user to be logged in" do it "should handle missing user" do test_before_filter( :require_user ) end end describe "with actions that require a user NOT to be logged in" do it "should handle a user being logged in" do login_as(mock_user) test_before_filter( :require_no_user ) end end describe "with actions which require an active user" do it "should handle inactive user" do login_as(mock_user(:active? => false)) test_before_filter( :require_active_user ) end end end end
Then in my controller specs I just require/include the shared behaviors and call it:
require File.expand_path(File.dirname(__FILE__) + '/shared/before_filter_behaviors_spec' ) include BeforeFilterBehaviors describe UsersController do it_should_behave_like "a controller with before_filters" end
Finding which version of OS X you are running via the command line
Just found out about the handy command, sw_vers, from the unClog blog.
Sample output:
jclark$ sw_vers ProductName: Mac OS X Server ProductVersion: 10.4.11 BuildVersion: 8S169
Really Really Install ImageMagick and RMagick on OS X from source
I just got done spending a day struggling to get ImageMagick and RMagick installed on our system. It wasn't all ImageMagick and RMagick's fault. Our system had been modified by several different sys admins who all did things a little differently so after a bit of futzing I ended up wiping everything we already had on there and starting over from scratch.
The resources that I used as references were: benr75's write up which was based on Marc-André Cournoyer's tutorial
Here is my process:
ImageMagick Delegates
At this point many of the other walk throughs will list out the exact commands they used to install the dependencies they used. I will not. I am going to tell you where to find them and what I did and let you do a little figuring on your own.
The ImageMagick ftp site ( and its mirrors ) all contain a directory called 'delegates'. For example: ftp://ftp.imagemagick.org/pub/ImageMagick/delegates/ This directory contains the versions of the delegate libraries that are recommended for use with ImageMagick.
Here is the basic process I used to install each library:
wget [URL to gzipped lib]
tar -zxvf [lib file name]
cd [lib directory]
make && sudo make install
cd ..
Rinse and repeat.
I installed the following libraries:
- Freetype
- PNG
- JPEG
- TIFF
- GhostScript
- WMF
Note: I also installed pkg-config to remove a warning in the ./configure output for libwmf.
Lastly it is important to install GhostScript Fonts or the RMagick install tests will fail. The process for GhostScript Fonts is a little different:
wget [URL to fonts archive] tar -zxvf [fonts archive name]
Installing ghostscript should have created the directory: /usr/local/share/ghostscript if not you might need to mkdir it.
sudo mv fonts /usr/local/share/ghostscript/
ImageMagick
Lets get the latest version of ImageMagick ( you might want to check what the latest version is when you install ):
wget ftp://ftp.imagemagick.org/pub/ImageMagick/ImageMagick-6.4.0-3.tar.gz
tar -zxvf ImageMagick-6.4.0-3.tar.gz
cd ImageMagick-6.4.0
./configure --prefix=/usr/local
make && sudo make install
cd ..
Note: you can also disable the perl and c++ extensions in the ./configure step. See ./configure --help or benr75's write up for more details
You can test your ImageMagick install with the following two commands:
convert logo: logo.gif identify logo.gif
RMagick
You can now either install RMagick using gem ( or try to anyway ). I would recommend using the -V flag so you can get as much info as possible if something goes wrong:
I've had problems with gems install before and I like to be able to see all my config options so I installed from source:
Note: this installs RMagick 2
wget http://rubyforge.org/frs/download.php/34654/RMagick-2.3.0.tar.gz tar -zxvf RMagick-2.3.0.tar.gz ruby setup.rb config sudo ruby setup.rb setup sudo ruby setup.rb install
If you get through all that with no errors you can test your RMagick install with the following:
irb irb(main):001:0> require 'rmagick' => true
If that returns, true you are good to go!
Prototype show/hide gotcha that got me twice
From Prototype and script.aculo.us:
• Remember that because of reliance on methods such as Element.hide( )
and Element.show( ), you will have to style initially-hidden elements
with an inline style="display: none" attribute. Using a CSS rule will
not work: the element will remain hidden.
Hopefully now that I have blogged it I will remember it. I need to use Prototype more frequently.
Migrating from WordPress to Mephisto
I just converted my WordPress db to Mephisto and now I can view the individual articles but when I go to the blog root page, no articles are found and the default blog title and settings are displayed. What gives?
update: Apparently, just writing this post using Mephisto and publishing it has corrected the problem.
Now I just need to reformat all the imported articles and write up a more detailed post about the migration.
OpenId 2.0 and Ruby on Rails 2.0.2
I just converted a website from the openid_consumer plugin to DHH's open_id_authentication plugin and patched it for OpenId 2.0.
I found face's port of Dr. Nic's sample open_id_authentication app to be a useful starting point. I ended up chopping down the open_id_helpers class to this:
module OpenIdsHelper # Pass optional :required and :optional keys to specify what sreg fields you want. # Be sure to yield registration, a third argument in the #authenticate_with_open_id block. # REMEMBER: a "required" field is not guaranteed to be returned by OpenID provider def open_id_authentication authenticate_with_open_id( params[:openid_url], :required => [ :nickname, :email ], :optional => [ :fullname ] ) do |result, identity_url, registration| if result.successful? successful_openid_login(identity_url, registration) else failed_openid_login(result.message || "Sorry could not log in with identity URL: #{identity_url}") end end private def successful_openid_login(identity_url, registration = {}) throw "Implement me in this controller!" end def failed_openid_login(message) throw "Implement me in this controller!" end end
implement the pass/fail methods in the controller that includes OpenIdsHelper and call open_id_authentication and you are off to the races.
I hit a couple hiccups here and there and submitted some patches to the patch ticket on Rails Trac. It is important to note that authenticating a Yahoo OpenId will fail unless you are authenticating from a server with a real hostname. Face made a post about this recently too which quotes yahoo's security policy.
I also modified Jainrain's ruby-openid gem to allow symbols as well as strings to be passed into sreg. Their trac instance is down right now but when it comes back up I will submit a patch and link to it.
Calling a rake task from rails code.
Require rake and load the rake files containing the tasks and dependencies you want to run:
require 'rake' load File.dirname(__FILE__) + '/../vendor/rails/railties/lib/tasks/misc.rake' load File.dirname(__FILE__) + '/../vendor/rails/railties/lib/tasks/databases.rake'
Invoke the task
Rake::Task["db:test:prepare"].invoke
Base classes get stored by ActiveRecord with type = NULL when using STI...
So you make sure you take that into consideration when crafting find_by_sql queries on STI tables.
For example when looking for all but one type in the table:
AND ( g.type != ? OR g.type IS NULL )
sudo !!
I just discovered this handy linux command to repeat the last command from history as sudo:
sudo !!
I'm sure this is probably old news to most people.
Solr pure negatvie queries
Over at SolrQuerySyntax on the Solr wiki they claim that you can do pure negative searches ( i.e. searches for all records that don't have a value for a given field ) by using the syntax:
-field:[* TO *]
However, using this was throwing errors for me until I switched to:
-field:[* TO ]
Now you know.
From snail mail to email
This service seems super cool: http://www.earthclassmail.com/. It basically scans your mail and sends it to you as a pdf with the option of having certain items recycled, shredded, or forwarded to you unopened.
I hate opening mail and dealing with filing/shredding/recycling things and this seems like it could make my life a tad easier.
Now if they would just add the option to have them call the companies sending you snail mail junk mail and ask them to remove you from the send list..
How to alias a class method in Ruby
class << self alias_method :new_do_thing, :old_do_thing end
Now I know.
XMLRPC::Client#do_rpc: Client-Type charset param incompatible with some blog's MetaWeblog APIs
We recently added an automated posting feature to Ma.gnolia which uses XMLRPC and the MetaWeblog API to post a summary of a user's bookmarks for a given time period to their blog. To implement the MetaWeblog client functionality we wrote a lightweight wrapper around Ruby's XMLRPC::Client class.
After we released the feature to beta testing we started getting reports of some users receiving invalid content-type faults when trying to configure their blogs for posting (notably users of Typo and TypePad). Trying to resolve this problem I dug into the XMLRPC::Client class and found that the method which does the heavy lifting of performing the XMLRPC request and parsing the response, #do_rpc, was setting the Content-Type header to: "text/xml; charset=utf-8".
A quick bit of Googling brought me to this forum thread where a user reported having a similar experience trying to communicate with a xmlrpc-c server. This user was able to resolve the by overriding the #do_rpc method to set the Content-Type header to: "text/xml" without the charset param.
Doing the same thing in our app has resolved the connection issues for our Typo user and we will soon find out if it solves the problem for out Typepad users as well.
I have bumped the thread in ruby-core back up to try to generate some interest in adding an accessor for at least the Content-Type header as overriding Ruby core methods in our Rails app is less than ideal.