<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/css" href="/stylesheets/rss.css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
  <channel>
    <title>Wolfmans Howlings</title>
    <link>http://blog.wolfman.com</link>
    <language>en-us</language>
    <ttl>40</ttl>
    <description>A programmers Blog about Ruby, Rails and a few other issues</description>
    <item>
      <title>OpenMoko Freerunner after a few weeks</title>
      <description>&lt;p&gt;Well I have had this thing for a few weeks now, and I have burned a lot
of hours playing with it :) (Wish I could bill someone for those
hours it would have paid for the phone 4 times over!)&lt;/p&gt;

&lt;p&gt;I started with 2007.2 the built in image, and upgraded it initially
with dfu-util, then with opkg update &amp;amp;&amp;amp; opkg upgrade.&lt;/p&gt;

&lt;p&gt;I installed a bunch of things and tweaked it to death (literally).&lt;/p&gt;

&lt;p&gt;I got &lt;a href="https://wiki.evolvis.org/jalimo/index.php/OpenMoko"&gt;Jalimo&lt;/a&gt;
java installed and working, and wrote the simple SWT app which worked nicely.
The only change to the instructions are you need to add
&lt;code&gt;-force-depends&lt;/code&gt; to the opkg command.&lt;/p&gt;

&lt;p&gt;As reported in my 
&lt;a href="http://blog.wolfman.com/articles/2008/07/11/openmoko-freerunner-first-impressions"&gt;other blog article&lt;/a&gt;
I got WIFI/WPA2 working, and finally got GPRS working.&lt;/p&gt;

&lt;p&gt;I have not tried to pair with a bluetooth headset yet, although got
far enough to see that the device could be seen using the scan
command.&lt;/p&gt;

&lt;p&gt;I played with all the varieties of on screen keyboards available, but
really couldn't use any of them as they are so small, and my eyes are
not that good anymore. I have seen some promising mentions of works in
progress for keyboards, but the ones in the Qtopia distribution are
the best, plus they have handwriting recognition which is a little
fussy but looks like it can be trained.&lt;/p&gt;

&lt;p&gt;The GPS problems were fixed by an amazing community and OpenMoko
effort, which I have never seen before. There is a S/W fix, and a H/W
fix which I suspect most people will not be able to do, soldering
surface mount components is not for the faint of heart!&lt;/p&gt;

&lt;p&gt;I spent several hours trying to build the OpenEmbedded and
the MokoMakefile development environments, so I could start to
contribute, but have still not been able to get them to finish
building the native toolchain needed to build any of the apps.&lt;/p&gt;

&lt;p&gt;I get several errors, some of which I found workarounds to by googling
but eventually hit dead ends in both cases which I could not solve and
have not been reported or solved by the community. I am using a stock
KUbuntu 8.08 Hardy Heron desktop, which should be pretty mainstream,
so I don't know what is wrong. The pre built toolchain OpenMoko
provides for building apps does work however, but you can't build
soft keyboards, or system components that route (at least I couldn't
find a way to do it). I'll continue to hammer away at MokoMakefile and
see if I can eventually get it going, of course I'll post my findings
to the Wiki or dev mailing list if I succeed.&lt;/p&gt;

&lt;p&gt;If someone could provide a VMPlayer image of a working OE or
Mokomakefile environment that would help a lot of us.
(Trolltech/Qtopia do provide exactly that for their development
tools).&lt;/p&gt;

&lt;p&gt;So after several opkg upgrades and tweaks, I finally killed my highly
customized rootfs, X would no longer boot, sound was dead etc etc.&lt;/p&gt;

&lt;p&gt;I tried to backup the image using
&lt;a href="http://wiki.openmoko.org/wiki/Pre-Flash_Backup"&gt;these&lt;/a&gt; instructions,
but always got an error halfway through, I suspect my flash image of
rootfs may have been corrupted. Anyway I lost all that work :(&lt;/p&gt;

&lt;p&gt;I tried flashing ASU, but it seems too much a work in progress more so
than 2007.8.&lt;/p&gt;

&lt;p&gt;Then I tried
&lt;a href="http://www.qtopia.net/modules/mydownloads/index.php"&gt;Qtopia&lt;/a&gt;
now this is much more to my liking. The interface is clean, intuitive
and seems to work pretty well. There are still a few rough edges which
Trolltech seem to be taking care of, but it mostly works pretty well. The
on screen input methods (which there are several of) are very good,
even the keyboard has a nice touch where it zooms into the keys you
are touching. It also has the tiny tiny QWERTY keyboard if you prefer
that style and your eyes still work.&lt;/p&gt;

&lt;p&gt;The downside is of course you lose access to all the applications
currently under development for the GTK based 2007.2, but Qtopia does
have a growing number of applications, and of course you can write
your own.&lt;/p&gt;

&lt;p&gt;I am going to experiment a little with trying to run GTK based apps
under Qtopia, I know I can do that on my KDE (aka QT) desktop, so why
not under Qtopia? I'll update this if I get it to work.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;UPDATE&lt;/em&gt; Oh ok the reason you can't do that is that Qtopia is not
running X Windows, so running GTK is not an option, bummer. I suppose you
could render GTK into a QT canvas or something but that is more work
than I am prepared to do at the moment.&lt;/p&gt;

&lt;p&gt;The current show stoppers for me, which stops me being able to use
this a phone are...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nasty buzzing noise on both ends of the call&lt;/li&gt;
&lt;li&gt;Bluetooth pairing with a headset not easily available.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm sure people are working on these issues, and I wait patiently for
them to get fixed so I can dump my aging Motorola V600. I'd actually
try to pitch in myself but I can't get the development environment to
work!&lt;/p&gt;

&lt;p&gt;Oh well guess I better get back to my paying job :)&lt;/p&gt;

&lt;p&gt;&lt;a href="http://technorati.com/tag/openmoko+freerunner" rel="tag"&gt;&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Tue, 22 Jul 2008 13:20:08 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:d67881c3-2148-421d-b561-7d15d3f820d7</guid>
      <author>Jim Morris</author>
      <link>http://blog.wolfman.com/articles/2008/07/22/openmoko-freerunner-after-a-few-weeks</link>
      <category>Openmoko</category>
      <category>Linux</category>
      <category>openmoko</category>
      <category>freerunner</category>
      <trackback:ping>http://blog.wolfman.com/articles/trackback/342</trackback:ping>
    </item>
    <item>
      <title>Upgrading Ubuntu Gutsy to Hardy</title>
      <description>&lt;p&gt;I did my duty and upgraded from Gutsy to Hardy, after letting Hardy settle for a while.
For the most part it was painless (unlike the last upgrade to Gutsy!).&lt;/p&gt;

&lt;p&gt;Unfortunately the sound was now broken I have a HDA-Intel AD198x Analog chip set.&lt;/p&gt;

&lt;p&gt;Then I discovered (just when I needed it of course) that my Samsung
ML-2010 USB printer didn't print anymore.&lt;/p&gt;

&lt;p&gt;I would get this weird error...&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Unable to open device hal:///org/freedesktop/Hal/devices/...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;After Googling I found &lt;a href="http://www.plasticboy.de/?p=35"&gt;this&lt;/a&gt;, he was
half right, I don't know why but it works, except that I needed to do...&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sudo chmod 700 /usr/lib/cups/backend/usb
sudo chmod 700 /usr/lib/cups/backend/hal
sudo killall -HUP cupsd
sudo /etc/init.d/cupsys restart
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;ie both usb and hal.&lt;/p&gt;

&lt;p&gt;That fixed it... Amazing!&lt;/p&gt;

&lt;p&gt;Now why won't my audio work???&lt;/p&gt;

&lt;p&gt;Ok now it does work, go figure. The only things I changed were added this to /etc/modprobe.d/alsa-base&lt;/p&gt;

&lt;p&gt;options snd-hda-intel model=3stack-dig&lt;/p&gt;

&lt;p&gt;and made sure that Front was checked in the mixer and turned up.&lt;/p&gt;

&lt;p&gt;Not sure which of those fixed it though.&lt;/p&gt;</description>
      <pubDate>Sun, 13 Jul 2008 04:32:03 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:5ac0dcdd-fc59-4f1f-80b8-88d68b3fb58f</guid>
      <author>Jim Morris</author>
      <link>http://blog.wolfman.com/articles/2008/07/13/upgrading-ubuntu-gutsy-to-hardy</link>
      <category>Linux</category>
      <category>ubuntu</category>
      <category>hardy</category>
      <category>cups</category>
      <trackback:ping>http://blog.wolfman.com/articles/trackback/341</trackback:ping>
    </item>
    <item>
      <title>OpenMoko Freerunner first impressions</title>
      <description>&lt;p&gt;OK so I just got my shiny new OpenMoko Freerunner GTA02.&lt;/p&gt;

&lt;p&gt;This is an Open source GSM cell phone, running Linux and OpenMoko S/W stack.&lt;/p&gt;

&lt;p&gt;This phone is really a developer release although it implies on the
website it is usable for the masses.&lt;/p&gt;

&lt;p&gt;We will see...&lt;/p&gt;

&lt;p&gt;Turning it on you see all this Linux boot up tiny tiny text scroll by,
ya the average Joe blo is going to love that ;)&lt;/p&gt;

&lt;p&gt;So I followed the 
&lt;a href="http://wiki.openmoko.org/wiki/Getting_Started_with_your_Neo_FreeRunner#Turning_on_the_Neo_Freerunner"&gt;startup guide&lt;/a&gt;
and plugged in the usb cable to my Ubuntu desktop. (If you don't have
Linux think again, maybe that $400's can go towards a tank of gas).&lt;/p&gt;

&lt;p&gt;I followed the usb networking guide, and ssh'd into the phone, and
Holy Crap! it just worked!&lt;/p&gt;

&lt;p&gt;First off it doesn't come with much, It can make a phone call, but no
other apps seem to be loaded.&lt;/p&gt;

&lt;p&gt;Oh one gripe is there is nowhere to stow a stylus, it comes with a
huge pen/laser pointer/stylus, but who is going to keep that in their
pocket, and those with fat fingers are going to have a hard time doing
anything.&lt;/p&gt;

&lt;p&gt;I actually had to RTFM to figure out how to close an application! BTW
this &lt;a href="http://openmoko.togaware.com/survivor/openmoko.html"&gt;guide&lt;/a&gt;
seems to be pretty good. (You click the power button to close an app!
it really needs a close button somewhere where people expect one).&lt;/p&gt;

&lt;p&gt;After loading most of the needed apps via the opkg app, which is slow
via USB 1.1 (whats up with that guys? its 2008! USB 2 is &lt;em&gt;REQUIRED&lt;/em&gt;)
I found that most of them are pretty much unusable. The default
2007.2 UI is unintuitive, and most of the necessary apps simply don't
work well. Adding a contact is virtually impossible, try setting the
phone number to home instead of work for instance.&lt;/p&gt;

&lt;p&gt;The web browser works but you can't input anything into forms, so
don't try logging into gmail.&lt;/p&gt;

&lt;p&gt;The calendar makes no sense whatsoever.&lt;/p&gt;

&lt;p&gt;OK so I am being harsh here, but the damn thing costs $400! and they
imply your average user could use it as a phone, so lets get over that
issue, and take it that this is a work in progress, very early stages,
and open source. It needs a good UI guide, come on we may not have a
super UI design guy, but copy the iphone or any other phone on the
market to get a clue how to make an intuitive UI. I am not a UI guy,
but even I can see this needs some work. BTW I do like the way the
scrolling works, that is very cool, you flick up or down and it
scrolls with some friction looks great, and works even better.&lt;/p&gt;

&lt;p&gt;The platform itself has plenty of potential, built in GPS (which
doesn't work without an external antenna), accelerometers, which no one
has figured out how to use yet. A really nice VGA display, although
its invisible in bright sunlight, wifi which I was totally unable to
get to connect to a WPA/PSK station, and bluetooth, with no S/W support
for headsets.&lt;/p&gt;

&lt;p&gt;Once all these issues are resolved I think it would make a great
primary handset.&lt;/p&gt;

&lt;p&gt;I'll do my part and write something for it and release it as open
source, but I'm disappointed I can't use this as a phone yet.&lt;/p&gt;

&lt;p&gt;I'll continue to RTFM if I can find one that describes the current UI
(the WIKI describes an obsolete UI). Maybe some of my issues are
simply not knowing how to use the UI, but that is a bad sign as I am a
developer and can use most UI's without RTFM'ing.&lt;/p&gt;

&lt;p&gt;I still think this is a great (if expensive) toy for now, especially
for us Linux Geeks, and one day it may actually be a great phone too.&lt;/p&gt;

&lt;h3&gt;GPRS&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;UPDATE&lt;/em&gt; I got GPRS working, followed the instructions on the
&lt;a href="http://wiki.openmoko.org/wiki/Manually_using_GPRS"&gt;WIKI&lt;/a&gt;, it is manual but it works.
My tmobile chatscript is...&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# File: /etc/ppp/chatscripts/tmobile
TIMEOUT 20
ABORT BUSY
ABORT "NO ANSWER"
ABORT "NO DIALTONE"
ABORT VOICE
ABORT ERROR
ABORT RINGING
SAY 'Starting GPRS connect script\n'
"" +++
OK ATZ
OK ATE1
OK AT+CFUN=1
OK AT+COPS
SAY 'Setting APN\n'
OK AT+CGDCONT=1,"IP","wap.voicestream.com"
ABORT 'NO CARRIER'
SAY 'Dialing...\n'
OK ATD*99***1#
CONNECT /n/d
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;and the script &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# File: /etc/ppp/peers/tmobile
#
/dev/ttySAC0
115200
crtscts
lock
hide-password
defaultroute # set the default route
usepeerdns
holdoff 3
ipcp-accept-local
lcp-echo-failure 12
lcp-echo-interval 3
noauth
noipdefault
novj
novjccomp
replacedefaultroute
persist
debug

connect "/usr/sbin/chat -v -f /etc/ppp/chatscripts/tmobile"
disconnect "/usr/sbin/chat -v -f /etc/ppp/chatscripts/tmobile-disconnect"
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;and a script to run it all...&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;/etc/init.d/gsmd stop
echo "1" &amp;gt; /sys/bus/platform/devices/neo1973-pm-gsm.0/power_on
chown uucp.uucp /dev/ttySAC0
stty -F /dev/ttySAC0 crtscts
pppd call tmobile
&lt;/code&gt;&lt;/pre&gt;

&lt;h3&gt;GPS&lt;/h3&gt;

&lt;p&gt;I did get a fix with the GPS using the internal antenna, however it
takes some concerted effort, see &lt;a href="http://wiki.openmoko.org/wiki/GPS_Problems"&gt;this page&lt;/a&gt; 
for hints. NOTE without the sd card I get a fix within 1 minute.&lt;/p&gt;

&lt;h3&gt;WIFI&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;UPDATE&lt;/em&gt; Got WIFI and WPA/PSK working, again there are two sets of
instructions on the WIKI, the first set doesn't work for me, however
the second set does... adding&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;iface eth0 inet dhcp
wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;to /etc/network/interfaces and then do ifdown usb0 and ifup eth0 of
course you need to setup &lt;code&gt;/etc/wpa_supplicant/wpa_supplicant.conf&lt;/code&gt;
appropriately. However when I ssh into the phone via the wifi after
disconnecting the USB, it looses its connection regularly and usually
within a few minutes.  Not sure why that is, my WIFI is generally very
stable with my laptop. I wonder if the bluetooth is interfering with
it?&lt;/p&gt;

&lt;p&gt;Finally I would like to say that I love the idea of an open spurce
cell phone. I can see that these issues will probably get fixed pretty
quickly as it is open source and there are thousands of eyes on the
code, and if there is something you don't like you can fix it. Lets
see you do that on your I-Phone ;)&lt;/p&gt;

&lt;p&gt;&lt;a href="http://technorati.com/tag/openmoko+freerunner" rel="tag"&gt;&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Fri, 11 Jul 2008 13:53:11 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:61b198bd-81f1-410c-8165-29aaad9dbf9b</guid>
      <author>Jim Morris</author>
      <link>http://blog.wolfman.com/articles/2008/07/11/openmoko-freerunner-first-impressions</link>
      <category>Openmoko</category>
      <category>Linux</category>
      <category>openmoko</category>
      <category>freerunner</category>
      <trackback:ping>http://blog.wolfman.com/articles/trackback/340</trackback:ping>
    </item>
    <item>
      <title>XPath matchers for rspec</title>
      <description>&lt;p&gt;&lt;em&gt;UPDATE&lt;/em&gt; I found
&lt;a href="http://github.com/kamal/rspec_hpricot_matchers/tree/master"&gt;this&lt;/a&gt;
which is a nicer way of doing it using hpricot, which is faster. I
have created a slightly different version that does have_xpath, for XML.&lt;/p&gt;

&lt;p&gt;I've been working on a project that is mostly Java for the last many
months, so haven't had much Ruby or Rails stuff to share.&lt;/p&gt;

&lt;p&gt;However one thing I found when working on my tests in Java was an
xpath matcher for JUnit 4.0 using the
&lt;a href="http://code.google.com/p/hamcrest"&gt;Hamcrest&lt;/a&gt; libraries.&lt;/p&gt;

&lt;p&gt;When I dropped back into Ruby to write some cron scripts that process
information from the database and generate xml files I wanted to check
the script with an rspec, and check the xml files it was generating.&lt;/p&gt;

&lt;p&gt;To do this I wanted to use a similar matcher to the Hamcrest ones, but
use it in RSpec.&lt;/p&gt;

&lt;p&gt;I Googled around and found a simple
&lt;a href="http://gensym.org/2007/7/15/custom-xpath-matcher-for-rspec"&gt;example&lt;/a&gt;,
but it wasn't very sophisticated and didn't check what I needed so I
upgraded it to do the kind of matches I needed, the results are here.&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;
&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;rexml/document&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;rexml/element&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;

&lt;span class="keyword"&gt;module &lt;/span&gt;&lt;span class="module"&gt;Spec&lt;/span&gt;
  &lt;span class="keyword"&gt;module &lt;/span&gt;&lt;span class="module"&gt;Matchers&lt;/span&gt;

    &lt;span class="comment"&gt;# check if the xpath exists one or more times&lt;/span&gt;
    &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;HaveXpath&lt;/span&gt;
      &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;initialize&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;xpath&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
        &lt;span class="attribute"&gt;@xpath&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;xpath&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;

      &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;matches?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;response&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
        &lt;span class="attribute"&gt;@response&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;response&lt;/span&gt;
        &lt;span class="ident"&gt;doc&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;response&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;is_a?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;REXML&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Document&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;?&lt;/span&gt; &lt;span class="ident"&gt;response&lt;/span&gt; &lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="constant"&gt;REXML&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Document&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="attribute"&gt;@response&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
        &lt;span class="ident"&gt;match&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;REXML&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;XPath&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;match&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;doc&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="attribute"&gt;@xpath&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
        &lt;span class="keyword"&gt;not&lt;/span&gt; &lt;span class="ident"&gt;match&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;empty?&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;

      &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;failure_message&lt;/span&gt;
        &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Did not find expected xpath &lt;span class="expr"&gt;#{@xpath}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;

      &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;negative_failure_message&lt;/span&gt;
        &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Did find unexpected xpath &lt;span class="expr"&gt;#{@xpath}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;

      &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;description&lt;/span&gt;
        &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;match the xpath expression &lt;span class="expr"&gt;#{@xpath}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;have_xpath&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;xpath&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
      &lt;span class="constant"&gt;HaveXpath&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;xpath&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="comment"&gt;# check if the xpath has the specified value&lt;/span&gt;
    &lt;span class="comment"&gt;# value is a string and there must be a single result to match its&lt;/span&gt;
    &lt;span class="comment"&gt;# equality against&lt;/span&gt;
    &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;MatchXpath&lt;/span&gt;
      &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;initialize&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;xpath&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;val&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
        &lt;span class="attribute"&gt;@xpath&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;xpath&lt;/span&gt;
        &lt;span class="attribute"&gt;@val&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;val&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;

      &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;matches?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;response&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
        &lt;span class="attribute"&gt;@response&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;response&lt;/span&gt;
        &lt;span class="ident"&gt;doc&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;response&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;is_a?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;REXML&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Document&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;?&lt;/span&gt; &lt;span class="ident"&gt;response&lt;/span&gt; &lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="constant"&gt;REXML&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Document&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="attribute"&gt;@response&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
        &lt;span class="ident"&gt;ok&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt;
        &lt;span class="constant"&gt;REXML&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;XPath&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;doc&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="attribute"&gt;@xpath&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;e&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
          &lt;span class="attribute"&gt;@actual_val&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="keyword"&gt;case&lt;/span&gt; &lt;span class="ident"&gt;e&lt;/span&gt;
          &lt;span class="keyword"&gt;when&lt;/span&gt; &lt;span class="constant"&gt;REXML&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Attribute&lt;/span&gt;
            &lt;span class="ident"&gt;e&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_s&lt;/span&gt;
          &lt;span class="keyword"&gt;when&lt;/span&gt; &lt;span class="constant"&gt;REXML&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Element&lt;/span&gt;
            &lt;span class="ident"&gt;e&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;text&lt;/span&gt;
          &lt;span class="keyword"&gt;else&lt;/span&gt;
            &lt;span class="ident"&gt;e&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_s&lt;/span&gt;
          &lt;span class="keyword"&gt;end&lt;/span&gt;
          &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="constant"&gt;false&lt;/span&gt; &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="attribute"&gt;@val&lt;/span&gt; &lt;span class="punct"&gt;==&lt;/span&gt; &lt;span class="attribute"&gt;@actual_val&lt;/span&gt;
        &lt;span class="keyword"&gt;end&lt;/span&gt;
        &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="ident"&gt;ok&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;

      &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;failure_message&lt;/span&gt;
        &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;The xpath &lt;span class="expr"&gt;#{@xpath}&lt;/span&gt; did not have the value '&lt;span class="expr"&gt;#{@val}&lt;/span&gt;'&lt;span class="escape"&gt;\n&lt;/span&gt;It was '&lt;span class="expr"&gt;#{@actual_val}&lt;/span&gt;'&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;

      &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;description&lt;/span&gt;
        &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;match the xpath expression &lt;span class="expr"&gt;#{@xpath}&lt;/span&gt; with &lt;span class="expr"&gt;#{@val}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;match_xpath&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;xpath&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;val&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
      &lt;span class="constant"&gt;MatchXpath&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;xpath&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;val&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="comment"&gt;# checks if the given xpath occurs num times&lt;/span&gt;
    &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;HaveNodes&lt;/span&gt;  &lt;span class="comment"&gt;#:nodoc:&lt;/span&gt;
      &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;initialize&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;xpath&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;num&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
        &lt;span class="attribute"&gt;@xpath&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;xpath&lt;/span&gt;
        &lt;span class="attribute"&gt;@num&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;num&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;

      &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;matches?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;response&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
        &lt;span class="attribute"&gt;@response&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;response&lt;/span&gt;
        &lt;span class="ident"&gt;doc&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;response&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;is_a?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;REXML&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Document&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;?&lt;/span&gt; &lt;span class="ident"&gt;response&lt;/span&gt; &lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="constant"&gt;REXML&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Document&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="attribute"&gt;@response&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
        &lt;span class="ident"&gt;match&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;REXML&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;XPath&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;match&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;doc&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="attribute"&gt;@xpath&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
        &lt;span class="attribute"&gt;@num_found&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;match&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;size&lt;/span&gt;
        &lt;span class="attribute"&gt;@num_found&lt;/span&gt; &lt;span class="punct"&gt;==&lt;/span&gt; &lt;span class="attribute"&gt;@num&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;

      &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;failure_message&lt;/span&gt;
        &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Did not find expected number of nodes &lt;span class="expr"&gt;#{@num}&lt;/span&gt; in xpath &lt;span class="expr"&gt;#{@xpath}&lt;/span&gt;&lt;span class="escape"&gt;\n&lt;/span&gt;Found &lt;span class="expr"&gt;#{@num_found}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;

      &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;description&lt;/span&gt;
        &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;match the number of nodes &lt;span class="expr"&gt;#{@num}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;have_nodes&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;xpath&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;num&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
      &lt;span class="constant"&gt;HaveNodes&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;xpath&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;num&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The first matcher &lt;code&gt;HaveXPath&lt;/code&gt; was pretty much the original I found on
the net, it simply checks that an XPath exists, I don't use this one.&lt;/p&gt;

&lt;p&gt;The next matcher &lt;code&gt;MatchXPath&lt;/code&gt; is more like what I was using in Java,
it gets an element from the xpath and checks the value is equal to the
one expected string. I will eventually add regex matching and arrays
of strings or regexs to check against.&lt;/p&gt;

&lt;p&gt;The last one &lt;code&gt;HaveNodes&lt;/code&gt; I find handy to make sure a given xpath matches the
expected number of nodes.&lt;/p&gt;

&lt;p&gt;I even wrote an rspec to check the matchers, and this also is handy to
show the way to use them.&lt;/p&gt;

&lt;p&gt;Note that you can pass the matchers a String containing the XML or (much faster)
a REXML::Document.&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;matchers&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;

&lt;span class="ident"&gt;describe&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;test matchers&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
  &lt;span class="ident"&gt;before&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:each&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="attribute"&gt;@xml&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="constant"&gt;EOFXML&lt;/span&gt;&lt;span class="string"&gt;
    &amp;lt;?xml version='1.0'?&amp;gt;
      &amp;lt;claims&amp;gt;
        &amp;lt;testnode1&amp;gt;
          &amp;lt;day&amp;gt;
            &amp;lt;rank order='1' value='0' userid='26' alias='user25'/&amp;gt;
            &amp;lt;rank order='2' value='0' userid='93' alias='user92'/&amp;gt;
            &amp;lt;rank order='3' value='0' userid='55' alias='user54'/&amp;gt;
            &amp;lt;sometext&amp;gt;this is text&amp;lt;/sometext&amp;gt;
          &amp;lt;/day&amp;gt;
        &amp;lt;/testnode1&amp;gt;
      &amp;lt;/claims&amp;gt;
&lt;/span&gt;&lt;span class="constant"&gt;    EOFXML&lt;/span&gt;

    &lt;span class="attribute"&gt;@doc&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;REXML&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Document&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="attribute"&gt;@xml&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;

  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="ident"&gt;it&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;should test xpath&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="attribute"&gt;@xml&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;have_nodes&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;/claims/*&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="attribute"&gt;@doc&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;have_nodes&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;/claims/*&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="attribute"&gt;@doc&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;have_nodes&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;/claims/testnode1/day/rank&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="number"&gt;3&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="attribute"&gt;@doc&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;have_xpath&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;/claims/testnode1/day/rank[@order='1']&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt;
    &lt;span class="attribute"&gt;@doc&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should_not&lt;/span&gt; &lt;span class="ident"&gt;have_xpath&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;/claims/testnode1/day/rank[@order='10']&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt;
    &lt;span class="attribute"&gt;@doc&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;match_xpath&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;/claims/testnode1/day/rank[1]/@order&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt;
    &lt;span class="attribute"&gt;@doc&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;match_xpath&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;/claims/testnode1/day/rank[2]/@value&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;0&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt;
    &lt;span class="attribute"&gt;@doc&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;match_xpath&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;/claims/testnode1/day/rank[3]/@alias&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;user54&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt;
    &lt;span class="attribute"&gt;@doc&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;match_xpath&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;/claims/testnode1/day/sometext&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;this is text&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="http://technorati.com/tag/rspec+xpath" rel="tag"&gt;&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Wed, 02 Jan 2008 13:32:50 -0800</pubDate>
      <guid isPermaLink="false">urn:uuid:f6ea0f80-0c0a-4613-b44f-a0f233d8b470</guid>
      <author>Jim Morris</author>
      <link>http://blog.wolfman.com/articles/2008/01/02/xpath-matchers-for-rspec</link>
      <category>RSpec</category>
      <category>Rails</category>
      <category>rspec</category>
      <category>xpath</category>
      <category>matcher</category>
      <trackback:ping>http://blog.wolfman.com/articles/trackback/339</trackback:ping>
    </item>
    <item>
      <title>Bit Vector Preferences</title>
      <description>&lt;p&gt;In my latest web project I potentially have a lot of boolean
preferences, which I use for enabling or disabling various email
notifications to users.&lt;/p&gt;

&lt;p&gt;Rather than having to add a migration everytime I want to add a new
preference, I thought I would use the &lt;code&gt;composed_of&lt;/code&gt; feature in my model
and compose the boolean preferences from a bitvector. That way I can
simply modify my model to add new preferences rather than add new
columns to the database.&lt;/p&gt;

&lt;p&gt;I also wanted this to be easy to add new boolean preferences, so I use
some Macros (I guess you could also call it Meta-Programming) to do
all the repetitive code.&lt;/p&gt;

&lt;p&gt;The result is a little class in my Person model called Preferences,
one integer field in my persons database called preferences, and a
composed_of :preferences in the Person model, and of course the
following class in the person.rb model.&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Person&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&lt;/span&gt; &lt;span class="constant"&gt;ActiveRecord&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Base&lt;/span&gt;

  &lt;span class="comment"&gt;#...&lt;/span&gt;

  &lt;span class="comment"&gt;# place to store bit vector preferences&lt;/span&gt;
  &lt;span class="comment"&gt;# to add a new preference:-&lt;/span&gt;
  &lt;span class="comment"&gt;#   add symbol of preference to @@bits with bit allocation&lt;/span&gt;
  &lt;span class="comment"&gt;#   update initialize defaults if initial default is true&lt;/span&gt;
  &lt;span class="keyword"&gt;class &lt;/span&gt;&lt;span class="class"&gt;Preferences&lt;/span&gt;
    &lt;span class="attribute"&gt;@@bits&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt;
      &lt;span class="symbol"&gt;:comment_notifications&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;
      &lt;span class="symbol"&gt;:friendship_notifications&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;2&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;
      &lt;span class="symbol"&gt;:event_notifications&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;4&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;
      &lt;span class="symbol"&gt;:misc_notifications&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="number"&gt;8&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;

    &lt;span class="comment"&gt;# create a reader for each preference&lt;/span&gt;
    &lt;span class="attribute"&gt;@@bits&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each_key&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
      &lt;span class="ident"&gt;attr_reader&lt;/span&gt; &lt;span class="ident"&gt;a&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="comment"&gt;# Initialize from integer or Hash&lt;/span&gt;
    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;initialize&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;prefs&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
      &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;prefs&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;nil?&lt;/span&gt;
        &lt;span class="comment"&gt;# set the defaults to false if not been set before&lt;/span&gt;
        &lt;span class="attribute"&gt;@@bits&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;v&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
          &lt;span class="ident"&gt;instance_variable_set&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;@&lt;span class="expr"&gt;#{a}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="constant"&gt;false&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;  
        &lt;span class="keyword"&gt;end&lt;/span&gt;
        &lt;span class="comment"&gt;# override default here        &lt;/span&gt;
        &lt;span class="attribute"&gt;@comment_notifications&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt;
        &lt;span class="attribute"&gt;@event_notifications&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt;
      &lt;span class="keyword"&gt;elsif&lt;/span&gt; &lt;span class="ident"&gt;prefs&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;is_a?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Hash&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
        &lt;span class="comment"&gt;# initialize from parameter Hash, and default to false if absent from hash&lt;/span&gt;
        &lt;span class="attribute"&gt;@@bits&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;v&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
          &lt;span class="ident"&gt;instance_variable_set&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;@&lt;span class="expr"&gt;#{a}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="constant"&gt;false&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;  
        &lt;span class="keyword"&gt;end&lt;/span&gt;

        &lt;span class="ident"&gt;prefs&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;k&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt;&lt;span class="ident"&gt;v&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
          &lt;span class="keyword"&gt;raise&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;ArgumentError&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Unknown preference &lt;span class="expr"&gt;#{k}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt; &lt;span class="keyword"&gt;unless&lt;/span&gt; &lt;span class="attribute"&gt;@@bits&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;has_key?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;k&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_sym&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
          &lt;span class="ident"&gt;instance_variable_set&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;@&lt;span class="expr"&gt;#{k}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;v&lt;/span&gt; &lt;span class="punct"&gt;==&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
        &lt;span class="keyword"&gt;end&lt;/span&gt;
      &lt;span class="keyword"&gt;else&lt;/span&gt;
        &lt;span class="comment"&gt;# create from integer bit vector&lt;/span&gt;
        &lt;span class="attribute"&gt;@@bits&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;v&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
          &lt;span class="ident"&gt;instance_variable_set&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;@&lt;span class="expr"&gt;#{a}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;prefs&lt;/span&gt; &lt;span class="punct"&gt;&amp;amp;&lt;/span&gt; &lt;span class="ident"&gt;v&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;!=&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt; &lt;span class="punct"&gt;?&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt; &lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="constant"&gt;false&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;  
        &lt;span class="keyword"&gt;end&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="comment"&gt;# returns bit vector of preferences&lt;/span&gt;
    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;preferences&lt;/span&gt;
      &lt;span class="ident"&gt;bv&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;
      &lt;span class="attribute"&gt;@@bits&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;v&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
        &lt;span class="ident"&gt;bv&lt;/span&gt; &lt;span class="punct"&gt;|=&lt;/span&gt; &lt;span class="ident"&gt;instance_variable_get&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;@&lt;span class="expr"&gt;#{a}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt; &lt;span class="punct"&gt;?&lt;/span&gt; &lt;span class="ident"&gt;v&lt;/span&gt; &lt;span class="punct"&gt;:&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;  
      &lt;span class="keyword"&gt;end&lt;/span&gt;
      &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="ident"&gt;bv&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="comment"&gt;# create a predicate for each preference&lt;/span&gt;
    &lt;span class="attribute"&gt;@@bits&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each_key&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
      &lt;span class="ident"&gt;alias_method&lt;/span&gt;&lt;span class="punct"&gt;((&lt;/span&gt;&lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_s&lt;/span&gt; &lt;span class="punct"&gt;+&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;?&lt;/span&gt;&lt;span class="punct"&gt;').&lt;/span&gt;&lt;span class="ident"&gt;to_sym&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="comment"&gt;# access preferences as bit vector&lt;/span&gt;
  &lt;span class="ident"&gt;composed_of&lt;/span&gt; &lt;span class="symbol"&gt;:preferences&lt;/span&gt;

  &lt;span class="comment"&gt;#...&lt;/span&gt;

&lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;All I need to do to add new preferences is add it to the &lt;code&gt;@@bits&lt;/code&gt;
class variable, which is a Hash of the preference name as a symbol and
the bit it sets in the integer (actually the value of the bit, bit0 is
1, bit1 is 2 etc). The rest of the code is derived from the class
variable.&lt;/p&gt;

&lt;p&gt;To make things easier I also add a predicate for each preference, so I
can access &lt;code&gt;@person.preferences.comment_notifications?&lt;/code&gt; to see if any
comment notifications are required for instance.&lt;/p&gt;

&lt;p&gt;One other thing I do in the initialize method is set up defaults for
the preferences. This is only really needed if it is being added as an
after thought, and the column &lt;code&gt;preferences&lt;/code&gt; is NULL in the database.&lt;/p&gt;

&lt;p&gt;Because &lt;code&gt;composed_of&lt;/code&gt; classes are immutable you must always create a
whole new one to update them, so I also allow initialize to be called
with a Hash, which can come straight from the controller. The last
case of initialize is being passed the integer from the database,
expanding it into the various boolean instance variables. The
&lt;code&gt;preferences&lt;/code&gt; method does the reverse and converts the boolean
instance variables into the bit vector. Calls to these are all taken
care of by ActiveRecord.&lt;/p&gt;

&lt;p&gt;An example of it being called from the controller is...&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@person.preferences= Person::Preferences.new(params[:preferences])
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;presuming you have a bunch of check boxes in your view which are
passed in as part of the preferences hash.&lt;/p&gt;

&lt;p&gt;Because I added this later I had one migration to initially add the
new column...&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;add_column :people, :preferences, :integer
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a href="http://technorati.com/tag/composed_of" rel="tag"&gt;&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Tue, 07 Aug 2007 23:53:09 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:4fb543c1-0bf5-4881-9fc5-aabe2d0789c2</guid>
      <author>Jim Morris</author>
      <link>http://blog.wolfman.com/articles/2007/08/07/bit-vector-preferences</link>
      <category>Rails</category>
      <category>rails</category>
      <category>preferences</category>
      <category>composed_of</category>
      <category>bitvector</category>
      <trackback:ping>http://blog.wolfman.com/articles/trackback/338</trackback:ping>
    </item>
    <item>
      <title>Paginating acts_as_taggable with will_paginate</title>
      <description>&lt;p&gt;A question I see asked a lot is how do I paginate acts_as_taggable
(on steroids)?&lt;/p&gt;

&lt;p&gt;I haven't seen any answers I liked, so I created my own, which I'm
sure a few people won't like either ;) But it works for me (tm).&lt;/p&gt;

&lt;p&gt;I use &lt;a href="http://errtheblog.com/post/4791"&gt;will_paginate&lt;/a&gt;, but this does
not work with custom finds that plugins define themselves, as is the
case with
&lt;a href="http://agilewebdevelopment.com/plugins/acts_as_taggable_on_steroids"&gt;acts_as_taggable&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you do use a custom find_by_sql you have to hit the database twice, once to find the total number
of items and then the paginated find.&lt;/p&gt;

&lt;p&gt;I have a situation where I generate a 
&lt;a href="http://blog.wolfman.com/articles/2007/06/23/developing-a-social-networking-site-part-3-tag-cloud"&gt;tag cloud&lt;/a&gt;
with every page, and part of that tag cloud has already calculated the
number of tags for each classification I use.&lt;/p&gt;

&lt;p&gt;I combined this with the &lt;a href="http://paginator.rubyforge.org/"&gt;paginator gem&lt;/a&gt; 
to get myself pages without too many hits to the database.&lt;/p&gt;

&lt;p&gt;The first thing I do is pass the total count I get from the tag cloud
to the action that renders the index for all those items matching the
cloud... If you look at the article cited above I make this
modification, this is dumbed down a bit for the sake of simplicity...&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;  &lt;span class="ident"&gt;tags&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Post&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;tag_counts&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:order&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;tags.name&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt;
  &lt;span class="ident"&gt;tags&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
    &lt;span class="ident"&gt;link_to&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;h&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;),&lt;/span&gt; &lt;span class="ident"&gt;tagged_post_path&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:tag&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:cnt&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;count&lt;/span&gt;&lt;span class="punct"&gt;))&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This passes the count I have already calculated to the action that
will list the paginated results.&lt;/p&gt;

&lt;p&gt;In my controller I do this to get the paginated results using
will_paginate and the Pagination gem...&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;    &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;tagged&lt;/span&gt;
    &lt;span class="punct"&gt;...&lt;/span&gt;
      &lt;span class="comment"&gt;# page if we can&lt;/span&gt;
      &lt;span class="ident"&gt;size&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:cnt&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
      &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;size&lt;/span&gt;
        &lt;span class="ident"&gt;per_page&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="number"&gt;10&lt;/span&gt;

        &lt;span class="comment"&gt;# use Paginator gem to do the actual paging&lt;/span&gt;
        &lt;span class="ident"&gt;pager&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Paginator&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;size&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;per_page&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;offset&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;per_page&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
          &lt;span class="constant"&gt;Post&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find_tagged_with&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:tag&lt;/span&gt;&lt;span class="punct"&gt;],&lt;/span&gt; &lt;span class="symbol"&gt;:limit&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ident"&gt;per_page&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:offset&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ident"&gt;offset&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
        &lt;span class="keyword"&gt;end&lt;/span&gt;

        &lt;span class="comment"&gt;# default to page 1 if not specified&lt;/span&gt;
        &lt;span class="ident"&gt;page&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:page&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;||&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;

        &lt;span class="comment"&gt;# gets a paged array of posts&lt;/span&gt;
        &lt;span class="attribute"&gt;@posts&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;returning&lt;/span&gt; &lt;span class="constant"&gt;WillPaginate&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Collection&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;page&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;per_page&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;size&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;p&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
          &lt;span class="ident"&gt;p&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;replace&lt;/span&gt; &lt;span class="ident"&gt;pager&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;page&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;page&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;items&lt;/span&gt;
        &lt;span class="keyword"&gt;end&lt;/span&gt;
      &lt;span class="keyword"&gt;else&lt;/span&gt;
        &lt;span class="comment"&gt;# fall back if we don't know the size&lt;/span&gt;
        &lt;span class="attribute"&gt;@posts&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Post&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find_tagged_with&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:tag&lt;/span&gt;&lt;span class="punct"&gt;])&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;

      &lt;span class="ident"&gt;render&lt;/span&gt; &lt;span class="symbol"&gt;:action&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;index&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;    &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This fits in nicely with the tag cloud I need to calculate, and it
uses will_paginate just like the regular index action does.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;UPDATE&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I refactored this to be more generally useful, I added the following as a
protected method in &lt;code&gt;application.rb&lt;/code&gt;...&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;  &lt;span class="comment"&gt;# paginate a call to find_tagged_with&lt;/span&gt;
  &lt;span class="comment"&gt;# klass is the tagged class&lt;/span&gt;
  &lt;span class="comment"&gt;# tag is the tag to find&lt;/span&gt;
  &lt;span class="comment"&gt;# count is the total number of items with that tag, if nil count_tags is called&lt;/span&gt;
  &lt;span class="comment"&gt;# per_page is numbe rof items per page&lt;/span&gt;
  &lt;span class="comment"&gt;# page is the page we are on&lt;/span&gt;
  &lt;span class="comment"&gt;# order is the order to return the items in&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;tag_paginator&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;klass&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;tag&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;count&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt;&lt;span class="constant"&gt;nil&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;per_page&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt;&lt;span class="number"&gt;10&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;page&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt;&lt;span class="number"&gt;1&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;order&lt;/span&gt;&lt;span class="punct"&gt;='&lt;/span&gt;&lt;span class="string"&gt;updated_at DESC&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt;
    &lt;span class="ident"&gt;count&lt;/span&gt; &lt;span class="punct"&gt;||=&lt;/span&gt; &lt;span class="ident"&gt;klass&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;count_tags&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;tag&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;pager&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Paginator&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;count&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;per_page&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;offset&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;per_page&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
      &lt;span class="ident"&gt;klass&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;find_tagged_with&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;tag&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:order&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ident"&gt;order&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:limit&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ident"&gt;per_page&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:offset&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ident"&gt;offset&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;

    &lt;span class="ident"&gt;page&lt;/span&gt; &lt;span class="punct"&gt;||=&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt;

    &lt;span class="ident"&gt;returning&lt;/span&gt; &lt;span class="constant"&gt;WillPaginate&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Collection&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;page&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;per_page&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;count&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;p&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
      &lt;span class="ident"&gt;p&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;replace&lt;/span&gt; &lt;span class="ident"&gt;pager&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;page&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;page&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;items&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I call it from one of my other actions like this...&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@faqs= tag_paginator(Post, 'FAQ', nil, per_page, params[:page], 'updated_at DESC')
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Passing in nil as the third parameter causes the &lt;code&gt;tag_paginator&lt;/code&gt;
method to call &lt;code&gt;Post.count_tags&lt;/code&gt; which is not part of the
&lt;code&gt;acts_as_taggable&lt;/code&gt; methods, I added it to the SingletonMethods module
myself...&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;module &lt;/span&gt;&lt;span class="module"&gt;ActiveRecord&lt;/span&gt;
  &lt;span class="keyword"&gt;module &lt;/span&gt;&lt;span class="module"&gt;Acts&lt;/span&gt; &lt;span class="comment"&gt;#:nodoc:&lt;/span&gt;
    &lt;span class="keyword"&gt;module &lt;/span&gt;&lt;span class="module"&gt;Taggable&lt;/span&gt; &lt;span class="comment"&gt;#:nodoc:&lt;/span&gt;
      &lt;span class="keyword"&gt;module &lt;/span&gt;&lt;span class="module"&gt;SingletonMethods&lt;/span&gt;

      &lt;span class="punct"&gt;...&lt;/span&gt;

        &lt;span class="comment"&gt;# Return the count of tag tags in this class&lt;/span&gt;
       &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;count_tags&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;tag&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
         &lt;span class="ident"&gt;count_by_sql&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;select count(*) FROM tags, taggings WHERE &lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="punct"&gt;+&lt;/span&gt; &lt;span class="ident"&gt;sanitize_sql&lt;/span&gt;&lt;span class="punct"&gt;(['&lt;/span&gt;&lt;span class="string"&gt;name = ? AND tags.id = taggings.tag_id AND taggable_type = ?&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="ident"&gt;tag&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;]))&lt;/span&gt;
       &lt;span class="keyword"&gt;end&lt;/span&gt;

      &lt;span class="punct"&gt;....&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;If you don't want to hack &lt;code&gt;acts_as_taggable&lt;/code&gt; then simply leave that
part out and call the &lt;code&gt;count_by_sql&lt;/code&gt; yourself.&lt;/p&gt;

&lt;p&gt;My refactored tagged action from above now looks like this...&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;tagged&lt;/span&gt;
  &lt;span class="punct"&gt;...&lt;/span&gt;
  &lt;span class="ident"&gt;tag&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:tag&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
  &lt;span class="ident"&gt;per_page&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="number"&gt;10&lt;/span&gt;
  &lt;span class="ident"&gt;size&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:cnt&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
  &lt;span class="attribute"&gt;@posts&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;tag_paginator&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Post&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;tag&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;size&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="number"&gt;10&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;params&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:page&lt;/span&gt;&lt;span class="punct"&gt;])&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="http://technorati.com/tag/acts_as_taggable+will_paginate" rel="tag"&gt;&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Mon, 30 Jul 2007 15:04:50 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:e7b7bbf9-0707-48d6-a1ed-7603966b049b</guid>
      <author>Jim Morris</author>
      <link>http://blog.wolfman.com/articles/2007/07/30/paginating-acts_as_taggable-with-will_paginate</link>
      <category>Rails</category>
      <category>acts_as_taggable</category>
      <category>will_paginate</category>
      <trackback:ping>http://blog.wolfman.com/articles/trackback/337</trackback:ping>
    </item>
    <item>
      <title>RSpec testing all actions of a controller</title>
      <description>&lt;p&gt;A pattern I find very helpful is to find all the actions in a
controller and apply a test to all those actions. &lt;/p&gt;

&lt;p&gt;For instance this is useful for automatically testing all actions are
protected from unauthorized access when using a login system.&lt;/p&gt;

&lt;p&gt;One nice feature of this pattern is that if you add an action to a
controller it will automatically be tested. This is less helpful if
you use&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;before_filter :login_required, :except =&amp;gt; {...}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;as it will automatically be protected, but there are other use cases
where this is not the situation. Just as in the except clause above
you need to explicitly add any action that does not need to be tested
to an exception list, which is supported by this pattern.&lt;/p&gt;

&lt;p&gt;Here are the methods I use to test for login accessibility.&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;module &lt;/span&gt;&lt;span class="module"&gt;MySpecHelper&lt;/span&gt;

  &lt;span class="comment"&gt;# get all actions for specified controller&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;get_all_actions&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;cont&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;c&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;Module&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;const_get&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;cont&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_s&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;pluralize&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;capitalize&lt;/span&gt; &lt;span class="punct"&gt;+&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Controller&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt;
    &lt;span class="ident"&gt;c&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;public_instance_methods&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;false&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;reject&lt;/span&gt;&lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;action&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;rescue_action&lt;/span&gt;&lt;span class="punct"&gt;'].&lt;/span&gt;&lt;span class="ident"&gt;include?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;action&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="comment"&gt;# test actions fail if not logged in&lt;/span&gt;
  &lt;span class="comment"&gt;# opts[:exclude] contains an array of actions to skip&lt;/span&gt;
  &lt;span class="comment"&gt;# opts[:include] contains an array of actions to add to the test in addition&lt;/span&gt;
  &lt;span class="comment"&gt;# to any found by get_all_actions&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;controller_actions_should_fail_if_not_logged_in&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;cont&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;opts&lt;/span&gt;&lt;span class="punct"&gt;={})&lt;/span&gt;
    &lt;span class="ident"&gt;except&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;opts&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:except&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;||&lt;/span&gt; &lt;span class="punct"&gt;[]&lt;/span&gt;
    &lt;span class="ident"&gt;actions_to_test&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;get_all_actions&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;cont&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;reject&lt;/span&gt;&lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;except&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;include?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
    &lt;span class="ident"&gt;actions_to_test&lt;/span&gt; &lt;span class="punct"&gt;+=&lt;/span&gt; &lt;span class="ident"&gt;opts&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:include&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;opts&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:include&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
    &lt;span class="ident"&gt;actions_to_test&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
      &lt;span class="comment"&gt;#puts &amp;quot;... #{a}&amp;quot;&lt;/span&gt;
      &lt;span class="ident"&gt;get&lt;/span&gt; &lt;span class="ident"&gt;a&lt;/span&gt;
      &lt;span class="ident"&gt;response&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should_not&lt;/span&gt; &lt;span class="ident"&gt;be_success&lt;/span&gt;
      &lt;span class="ident"&gt;response&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;redirect_to&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;http://test.host/login&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt;
      &lt;span class="ident"&gt;flash&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:warning&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="punct"&gt;==&lt;/span&gt; &lt;span class="attribute"&gt;@login_warning&lt;/span&gt;
   &lt;span class="keyword"&gt;end&lt;/span&gt;
 &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I put this in my spec_helper.rb and include it as shown here:&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="ident"&gt;describe&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;When Logged out&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
  &lt;span class="ident"&gt;include&lt;/span&gt; &lt;span class="constant"&gt;MySpecHelper&lt;/span&gt;
  &lt;span class="ident"&gt;controller_name&lt;/span&gt; &lt;span class="symbol"&gt;:events&lt;/span&gt;

  &lt;span class="ident"&gt;before&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:each&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="ident"&gt;controller&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;stub!&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:current_user&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;and_return&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:false&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="attribute"&gt;@login_warning&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;You need to be logged in to do that&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="comment"&gt;# test all actions require login except the ones specified&lt;/span&gt;
  &lt;span class="comment"&gt;# add new_comment as it is not seen by the automatic collector&lt;/span&gt;
  &lt;span class="ident"&gt;it&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;actions should fail&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="ident"&gt;controller_actions_should_fail_if_not_logged_in&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:input&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; 
                              &lt;span class="symbol"&gt;:except&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;index&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;show&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;tagged&lt;/span&gt;&lt;span class="punct"&gt;'],&lt;/span&gt; 
                              &lt;span class="symbol"&gt;:include&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;new_comment&lt;/span&gt;&lt;span class="punct"&gt;'])&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;
&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;get_all_actions&lt;/code&gt; method collects all the public un-inherited
methods in the given controller, these will consist of all the
accessible actions in that controller. I explicitly exclude
&lt;code&gt;rescue_action&lt;/code&gt; as it is created by RSpec itself and should not be
tested. Note it will not see any actions that are in application.rb so
you need to add those to the list manually of you want them tested.
(See the :include option in the example).&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;controller_actions_should_fail_if_not_logged_in&lt;/code&gt; could be put in
the spec itself rather than the spec_helper, but as I call this from
all my controller specs it is more DRY to put it here. This method
takes the controller name and an option array of actions names to
ignore. This method tests all the actions and makes sure I get the
expected result of the filter failing due to not being logged in.&lt;/p&gt;

&lt;p&gt;I show an example spec that uses this to test my events controller, it
mocks the login calls to say I am not logged in, and then tests them
with the exceptions of the actions in this controller that do not
require one to be logged in.&lt;/p&gt;

&lt;p&gt;This pattern can be extended to test all sorts of things, and is
especially useful for testing things where you can add an action and
forget to do something in a filter to protect it. Make sure the
default is on the side of caution though. IE you need to explicitly
except actions rather than include actions.&lt;/p&gt;

&lt;p&gt;Another example is something I recently stumbled upon in my RESTful
controllers. In many cases it is good to use a verify statement to
make sure that the RESTful actions actually can only be called with
PUT, POST or DELETE and fail if called with GET. I use this statement
in my controllers to enforce this...&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="comment"&gt;# GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)&lt;/span&gt;
&lt;span class="ident"&gt;verify&lt;/span&gt; &lt;span class="symbol"&gt;:method&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:put&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:only&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt; &lt;span class="symbol"&gt;:update&lt;/span&gt; &lt;span class="punct"&gt;],&lt;/span&gt; &lt;span class="symbol"&gt;:add_flash&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="symbol"&gt;:error&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Operation Failed&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="punct"&gt;},&lt;/span&gt; &lt;span class="symbol"&gt;:redirect_to&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="symbol"&gt;:action&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:index&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
&lt;span class="ident"&gt;verify&lt;/span&gt; &lt;span class="symbol"&gt;:method&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:post&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:only&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt; &lt;span class="symbol"&gt;:create&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:new_comment&lt;/span&gt; &lt;span class="punct"&gt;],&lt;/span&gt; &lt;span class="symbol"&gt;:add_flash&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="symbol"&gt;:error&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Operation Failed&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="punct"&gt;},&lt;/span&gt; &lt;span class="symbol"&gt;:redirect_to&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="symbol"&gt;:action&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:index&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
&lt;span class="ident"&gt;verify&lt;/span&gt; &lt;span class="symbol"&gt;:method&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:delete&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:only&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt; &lt;span class="symbol"&gt;:destroy&lt;/span&gt; &lt;span class="punct"&gt;],&lt;/span&gt; &lt;span class="symbol"&gt;:add_flash&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="symbol"&gt;:error&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;Operation Failed&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="punct"&gt;},&lt;/span&gt; &lt;span class="symbol"&gt;:redirect_to&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="symbol"&gt;:action&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="symbol"&gt;:index&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I test this in my specs using this in the MySpecHelper Module&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;controller_actions_should_fail_with_get&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;cont&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;except&lt;/span&gt;&lt;span class="punct"&gt;=[])&lt;/span&gt;
    &lt;span class="ident"&gt;actions_to_test&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;get_all_actions&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;cont&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;reject&lt;/span&gt;&lt;span class="punct"&gt;{&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt; &lt;span class="ident"&gt;except&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;include?&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;}&lt;/span&gt;
    &lt;span class="ident"&gt;actions_to_test&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;a&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
      &lt;span class="comment"&gt;#puts &amp;quot;... #{a}&amp;quot;&lt;/span&gt;
      &lt;span class="ident"&gt;get&lt;/span&gt; &lt;span class="ident"&gt;a&lt;/span&gt;
      &lt;span class="ident"&gt;response&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="ident"&gt;redirect_to&lt;/span&gt;&lt;span class="punct"&gt;(&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;http://test.host/&lt;span class="expr"&gt;#{cont.to_s.pluralize}&lt;/span&gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt;
      &lt;span class="ident"&gt;flash&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:error&lt;/span&gt;&lt;span class="punct"&gt;].&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="punct"&gt;==&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Operation Failed&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;and an example of its use in a spec...&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;  &lt;span class="ident"&gt;it&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;actions should fail if not post or put&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="ident"&gt;controller_actions_should_fail_with_get&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:event&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;['&lt;/span&gt;&lt;span class="string"&gt;index&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;show&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;edit&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;new&lt;/span&gt;&lt;span class="punct"&gt;'])&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;                                                                                     &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now whenever I add an action, the default is that it will fail with a
GET, unless I add it to the exclude list in the spec, this will remind
me to check if the action required PUT, POST or DELETE instead and to
add it to the verify if so or add it to the specs exclude list if not.&lt;/p&gt;

&lt;p&gt;These automatic tests keep me honest, especially in the last case
where you really don't want a GET to be able to delete something.&lt;/p&gt;

&lt;p&gt;I hope this pattern is useful to you.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://technorati.com/tag/rspec+controllers" rel="tag"&gt;&lt;/a&gt;
&lt;a href="http://technorati.com/tag/rspec+rails" rel="tag"&gt;&lt;/a&gt;
&lt;a href="http://technorati.com/tag/rspec" rel="tag"&gt;&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Sat, 28 Jul 2007 14:23:07 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:d49575f0-142f-4311-b52f-380bcedf63e9</guid>
      <author>Jim Morris</author>
      <link>http://blog.wolfman.com/articles/2007/07/28/rspec-testing-all-actions-of-a-controller</link>
      <category>RSpec</category>
      <category>Rails</category>
      <category>rails</category>
      <category>rspec</category>
      <category>controllers</category>
      <trackback:ping>http://blog.wolfman.com/articles/trackback/336</trackback:ping>
    </item>
    <item>
      <title>Using RSpec to test HAML helpers</title>
      <description>&lt;p&gt;&lt;em&gt;UPDATED&lt;/em&gt; for HAML 2.0 and RSpec 1.1.5 - Changed open to haml_tag, prefix helper. to all rspec calls...&lt;/p&gt;

&lt;p&gt;The most recent release of &lt;a href="http://haml.hamptoncatlin.com/"&gt;HAML&lt;/a&gt;
introduced a neat feature that allows you to use HAML-like syntax in
your helpers to generate HTML
&lt;a href="http://haml.hamptoncatlin.com/docs/rdoc/classes/Haml/Helpers.html#M000019"&gt;HAML#haml_tag&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A question on the HAML news group asked how to test a helper that uses
HAML#haml_tag (used to be open/puts) and thanks to Nathan on that
&lt;a href="http://groups.google.com/group/haml/browse_thread/thread/e2c6cba548a4eb00/4e3ac1007485e902#4e3ac1007485e902"&gt;list&lt;/a&gt;
I finally got RSpec to do it. As shown below.&lt;/p&gt;

&lt;p&gt;However a really good point was made that really in RSpec the way to
test anything is to use mocks to mock any call to an outside method
thus focusing the test on the specific module under test.
Generally I agree with that philosophy. But this is way cool so I
thought I'd do it anyway, and also as it is a new feature in HAML one
may not want to simply trust HAML to generate the correct HTML.&lt;/p&gt;

&lt;p&gt;So in my application_helper.rb I have a simple helper...&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="keyword"&gt;module &lt;/span&gt;&lt;span class="module"&gt;ApplicationHelper&lt;/span&gt;

 &lt;span class="punct"&gt;...&lt;/span&gt;

  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;display_flash&lt;/span&gt;
    &lt;span class="keyword"&gt;for&lt;/span&gt; &lt;span class="ident"&gt;name&lt;/span&gt; &lt;span class="keyword"&gt;in&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:notice&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:warning&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:error&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
      &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;flash&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
        &lt;span class="ident"&gt;haml_tag&lt;/span&gt; &lt;span class="symbol"&gt;:div&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;flash&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;],&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt;&lt;span class="symbol"&gt;:class&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ident"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_s&lt;/span&gt;&lt;span class="punct"&gt;}&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="constant"&gt;nil&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="punct"&gt;...&lt;/span&gt;

&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is called in my views as...&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;- display_flash
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Notice the - instead of =, this is because the open (and puts) write
output directly to the HAML buffer, and so this routine should return
nothing. (This is also a very simply case and does not show off the
utility of the open/puts methods, I'll show one of those later on).&lt;/p&gt;

&lt;p&gt;The RSpec helper test that tests this is as follows...&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;&lt;span class="comment"&gt;# File: spec/helpers/application_helper_spec.rb&lt;/span&gt;
&lt;span class="ident"&gt;require&lt;/span&gt; &lt;span class="constant"&gt;File&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;dirname&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;__FILE__&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt; &lt;span class="punct"&gt;+&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;/../spec_helper&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;

&lt;span class="ident"&gt;describe&lt;/span&gt; &lt;span class="constant"&gt;ApplicationHelper&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;

  &lt;span class="ident"&gt;before&lt;/span&gt; &lt;span class="symbol"&gt;:each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="ident"&gt;helper&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;extend&lt;/span&gt; &lt;span class="constant"&gt;Haml&lt;/span&gt;
    &lt;span class="ident"&gt;helper&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;extend&lt;/span&gt; &lt;span class="constant"&gt;Haml&lt;/span&gt;&lt;span class="punct"&gt;::&lt;/span&gt;&lt;span class="constant"&gt;Helpers&lt;/span&gt; 
    &lt;span class="ident"&gt;helper&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;send&lt;/span&gt; &lt;span class="symbol"&gt;:init_haml_helpers&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

  &lt;span class="ident"&gt;it&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;should display flash&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="keyword"&gt;for&lt;/span&gt; &lt;span class="ident"&gt;name&lt;/span&gt; &lt;span class="keyword"&gt;in&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:notice&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:warning&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:error&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
      &lt;span class="ident"&gt;flash&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;]=&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;flash &lt;span class="expr"&gt;#{name.to_s}&lt;/span&gt; message&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;
      &lt;span class="ident"&gt;helper&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;capture_haml&lt;/span&gt;&lt;span class="punct"&gt;{&lt;/span&gt;
        &lt;span class="ident"&gt;helper&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;display_flash&lt;/span&gt;
      &lt;span class="punct"&gt;}.&lt;/span&gt;&lt;span class="ident"&gt;should&lt;/span&gt; &lt;span class="punct"&gt;=~&lt;/span&gt; &lt;span class="punct"&gt;/&lt;/span&gt;&lt;span class="regex"&gt;&amp;lt;div class='&lt;span class="expr"&gt;#{name.to_s}&lt;/span&gt;'&amp;gt;&lt;span class="escape"&gt;\s&lt;/span&gt;*&lt;span class="expr"&gt;#{flash[name]}&lt;/span&gt;&lt;span class="escape"&gt;\s&lt;/span&gt;*&amp;lt;&lt;span class="escape"&gt;\/&lt;/span&gt;div&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;/&lt;/span&gt;
      &lt;span class="ident"&gt;flash&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="ident"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;]=&lt;/span&gt; &lt;span class="constant"&gt;nil&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;

&lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Excellent, a simple test for the HTML generated by my haml helper.
NOTE the setup required in the before :each, this sets up the haml helpers in the helpers context&lt;/p&gt;

&lt;p&gt;Why would I want to use HAML#open you ask?&lt;/p&gt;

&lt;p&gt;Well it makes the helpers look so much tidier IMHO, take this example
from my previous post on 
&lt;a href="http://blog.wolfman.com/articles/2007/06/23/developing-a-social-networking-site-part-3-tag-cloud"&gt;tag clouds&lt;/a&gt;,
the re-factored helper now looks like this...&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;  &lt;span class="comment"&gt;# display a tag cloud for the given model&lt;/span&gt;
  &lt;span class="keyword"&gt;def &lt;/span&gt;&lt;span class="method"&gt;tag_cloud&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;model&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;title&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="constant"&gt;nil&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="ident"&gt;m&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;model&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_s&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;camelcase&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;constantize&lt;/span&gt;
    &lt;span class="ident"&gt;plural&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;model&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;to_s&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;capitalize&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;pluralize&lt;/span&gt;
    &lt;span class="ident"&gt;title&lt;/span&gt; &lt;span class="punct"&gt;||=&lt;/span&gt; &lt;span class="ident"&gt;plural&lt;/span&gt;
    &lt;span class="ident"&gt;tags&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;m&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;tag_counts&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:order&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;tags.name&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt;
    &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="constant"&gt;false&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;tags&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;empty?&lt;/span&gt;
    &lt;span class="ident"&gt;urlmeth&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;tagged_&lt;span class="expr"&gt;#{model.to_s.pluralize}&lt;/span&gt;_path&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;.&lt;/span&gt;&lt;span class="ident"&gt;to_sym&lt;/span&gt;
    &lt;span class="ident"&gt;haml_tag&lt;/span&gt; &lt;span class="symbol"&gt;:div&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt;&lt;span class="symbol"&gt;:class&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;tagcloud&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;}&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
      &lt;span class="ident"&gt;haml_tag&lt;/span&gt; &lt;span class="symbol"&gt;:h3&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="ident"&gt;title&lt;/span&gt;
      &lt;span class="ident"&gt;tags&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;each&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt; &lt;span class="punct"&gt;|&lt;/span&gt;&lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;|&lt;/span&gt;
        &lt;span class="keyword"&gt;next&lt;/span&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; &lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;name&lt;/span&gt; &lt;span class="punct"&gt;==&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;FAQ&lt;/span&gt;&lt;span class="punct"&gt;'&lt;/span&gt;
        &lt;span class="ident"&gt;haml_tag&lt;/span&gt; &lt;span class="symbol"&gt;:span&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt;&lt;span class="symbol"&gt;:style&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;font-size:&lt;span class="expr"&gt;#{calc_size(t.count)}&lt;/span&gt;%&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;}&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
          &lt;span class="ident"&gt;puts&lt;/span&gt; &lt;span class="ident"&gt;link_to&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;h&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;),&lt;/span&gt; &lt;span class="constant"&gt;self&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;send&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="ident"&gt;urlmeth&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:tag&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="ident"&gt;t&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;name&lt;/span&gt;&lt;span class="punct"&gt;))&lt;/span&gt;
        &lt;span class="keyword"&gt;end&lt;/span&gt;
      &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;end&lt;/span&gt;
    &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;      &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;So much cleaner, plus I can return a boolean to indicate if there was
anything output or not, which tells me if I need to output an &lt;code&gt;&amp;lt;hr/&amp;gt;&lt;/code&gt; or
not.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://technorati.com/tag/rspec+haml" rel="tag"&gt;&lt;/a&gt;
&lt;a href="http://technorati.com/tag/haml+haml_tag" rel="tag"&gt;&lt;/a&gt;&lt;/p&gt;</description>
      <pubDate>Sat, 14 Jul 2007 16:40:00 -0700</pubDate>
      <guid isPermaLink="false">urn:uuid:0697a7d8-7846-4c80-b786-603324e4e321</guid>
      <author>Jim Morris</author>
      <link>http://blog.wolfman.com/articles/2007/07/14/using-rspec-to-test-haml-helpers</link>
      <category>RSpec</category>
      <category>Rails</category>
      <category>HAML</category>
      <category>test</category>
      <category>haml</category>
      <category>rspec</category>
      <category>helpers</category>
      <trackback:ping>http://blog.wolfman.com/articles/trackback/332</trackback:ping>
    </item>
    <item>
      <title>RSpec testing views for escaped HTML</title>
      <description>&lt;p&gt;For my social networking site &lt;a href="http://snowdogsr.us"&gt;snowdogsr.us&lt;/a&gt; I
decided to escape all user input that gets displayed. I know people
like to trick out their profiles with HTML but I want to avoid the
various hacks that it allows.&lt;/p&gt;

&lt;p&gt;So thinking I had done a good job of using h everywhere I output user
input fields, I decided to see if I could actually test this with
RSpec view tests.&lt;/p&gt;

&lt;p&gt;I recently switched to &lt;a href="http://rspec.rubyforge.org"&gt;RSpec&lt;/a&gt; for my
testing needs, its cool :)&lt;/p&gt;

&lt;p&gt;One thing it does is isolate the various things for testing using built
in mocking, and views can be entirely tested standalone without
accessing a model or a controller.&lt;/p&gt;

&lt;p&gt;So how would it do testing for escaped user input I wondered?&lt;/p&gt;

&lt;p&gt;Very well actually.&lt;/p&gt;

&lt;p&gt;An example is worth a thousand words, so here is my RSpec for my home
page.&lt;/p&gt;

&lt;p&gt;BTW I found about 4 places where embedded HTML in user input was
bleeding through, so it was well worth the effort.&lt;/p&gt;

&lt;p&gt;So this goes in spec/views/home/home_spec.rb...&lt;/p&gt;

&lt;div class="typocode"&gt;&lt;pre&gt;&lt;code class="typocode_ruby "&gt;  &lt;span class="ident"&gt;it&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;should escape all user input&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt; &lt;span class="keyword"&gt;do&lt;/span&gt;
    &lt;span class="attribute"&gt;@place&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;mock_model&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Place&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:name&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;place name&amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:location&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;place location&amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:tag_list&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;place taglist &amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="symbol"&gt;:rated?&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;false&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="attribute"&gt;@event&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;mock_model&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Event&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:name&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;event name&amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:where&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;event where&amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:tag_list&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;event taglist &amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="symbol"&gt;:date_time&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;DateTime&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;now&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:hosted_by&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Event host&amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt;
    &lt;span class="attribute"&gt;@post&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;mock_model&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Input&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:input&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;input body &amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:tag_list&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;post taglist &amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="symbol"&gt;:updated_at&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;DateTime&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;now&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:created_at&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;DateTime&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;now&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:created_by&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;post created by person&amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:rated?&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;false&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="attribute"&gt;@picture&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;mock_model&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Picture&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:public_filename&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;filename&amp;lt;b&amp;gt;.png&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;)&lt;/span&gt;
    &lt;span class="attribute"&gt;@pictures&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="attribute"&gt;@picture&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
    &lt;span class="attribute"&gt;@pet&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;mock_model&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Pet&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:name&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;pet name&amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:owned_by&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;pet owner &amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="symbol"&gt;:breed&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;breed &amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:description&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;pet description &amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;,&lt;/span&gt; &lt;span class="symbol"&gt;:neutered&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;true&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:gender&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;M&amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:pictures&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="attribute"&gt;@pictures&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:owned_by?&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="constant"&gt;false&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;

    &lt;span class="attribute"&gt;@posts&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="attribute"&gt;@post&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
    &lt;span class="attribute"&gt;@events&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="attribute"&gt;@event&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
    &lt;span class="attribute"&gt;@places&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="attribute"&gt;@place&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
    &lt;span class="attribute"&gt;@top_places&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="attribute"&gt;@place&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
    &lt;span class="attribute"&gt;@new_pets&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="attribute"&gt;@pet&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;

    &lt;span class="attribute"&gt;@comment&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="ident"&gt;mock_model&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;Comment&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="attribute"&gt;@comment&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;stub!&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:user&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;and_return&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="attribute"&gt;@user&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="attribute"&gt;@comment&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;stub!&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:created_at&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;and_return&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="constant"&gt;DateTime&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;now&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;
    &lt;span class="attribute"&gt;@comment&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;stub!&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:comment&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;and_return&lt;/span&gt;&lt;span class="punct"&gt;('&lt;/span&gt;&lt;span class="string"&gt;comment body &amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;')&lt;/span&gt;
    &lt;span class="attribute"&gt;@comments&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="attribute"&gt;@comment&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt;
    &lt;span class="attribute"&gt;@post&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should_receive&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="symbol"&gt;:comments&lt;/span&gt;&lt;span class="punct"&gt;).&lt;/span&gt;&lt;span class="ident"&gt;and_return&lt;/span&gt;&lt;span class="punct"&gt;(&lt;/span&gt;&lt;span class="attribute"&gt;@comments&lt;/span&gt;&lt;span class="punct"&gt;)&lt;/span&gt;

    &lt;span class="attribute"&gt;@new_stuff&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[]&lt;/span&gt;
    &lt;span class="attribute"&gt;@new_stuff&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt;&lt;span class="symbol"&gt;:list&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="attribute"&gt;@posts&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:title&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Posts&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:link&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;#&lt;/span&gt;&lt;span class="punct"&gt;'}&lt;/span&gt;
    &lt;span class="attribute"&gt;@new_stuff&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt;&lt;span class="symbol"&gt;:list&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="attribute"&gt;@events&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:title&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Events&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:link&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;#&lt;/span&gt;&lt;span class="punct"&gt;'}&lt;/span&gt;
    &lt;span class="attribute"&gt;@new_stuff&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt;&lt;span class="symbol"&gt;:list&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="attribute"&gt;@places&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:title&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Places&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:link&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;#&lt;/span&gt;&lt;span class="punct"&gt;'}&lt;/span&gt;

    &lt;span class="attribute"&gt;@top&lt;/span&gt;&lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="punct"&gt;[]&lt;/span&gt;
    &lt;span class="attribute"&gt;@top&lt;/span&gt; &lt;span class="punct"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="punct"&gt;{&lt;/span&gt;&lt;span class="symbol"&gt;:list&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="attribute"&gt;@top_places&lt;/span&gt;&lt;span class="punct"&gt;,&lt;/span&gt; &lt;span class="symbol"&gt;:title&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;Hot Places&lt;/span&gt;&lt;span class="punct"&gt;',&lt;/span&gt; &lt;span class="symbol"&gt;:link&lt;/span&gt; &lt;span class="punct"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="punct"&gt;'&lt;/span&gt;&lt;span class="string"&gt;#&lt;/span&gt;&lt;span class="punct"&gt;'}&lt;/span&gt;

    &lt;span class="ident"&gt;assigns&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:new_stuff&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="attribute"&gt;@new_stuff&lt;/span&gt;
    &lt;span class="ident"&gt;assigns&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:top&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="attribute"&gt;@top&lt;/span&gt;
    &lt;span class="ident"&gt;assigns&lt;/span&gt;&lt;span class="punct"&gt;[&lt;/span&gt;&lt;span class="symbol"&gt;:new_pets&lt;/span&gt;&lt;span class="punct"&gt;]&lt;/span&gt; &lt;span class="punct"&gt;=&lt;/span&gt; &lt;span class="attribute"&gt;@new_pets&lt;/span&gt;

    &lt;span class="ident"&gt;render&lt;/span&gt; &lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;&lt;span class="string"&gt;/home/logged_in&lt;/span&gt;&lt;span class="punct"&gt;&amp;quot;&lt;/span&gt;   
    &lt;span class="comment"&gt;#puts excerpt(response.body, &amp;quot;&amp;lt;b&amp;gt;&amp;quot;)&lt;/span&gt;
    &lt;span class="ident"&gt;response&lt;/span&gt;&lt;span class="punct"&gt;.&lt;/span&gt;&lt;span class="ident"&gt;should_not&lt;/span&gt; &lt;span class="ident"&gt;have_text&lt;/span&gt;&lt;span class="punct"&gt;(/&lt;/span&gt;&lt;span class="regex"&gt;&amp;lt;b&amp;gt;&lt;/span&gt;&lt;span class="punct"&gt;/)&lt;/span&gt;
  &lt;span class="keyword"&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Its quite complex as the home page renders a lot of summaries of the
various lists I have.&lt;/p&gt;

&lt;p&gt;First I mock the models that are called, and stub out the calls that
are made to them. I force them all to return an embedded &lt;code&gt;&amp;lt;b&amp;gt;&lt;/code&gt; which I
don't use anyway, and with the new CSS oriented web styles shouldn't
be used in HTML anyway.&lt;/p&gt;

&lt;p&gt;Then I just test that &lt;code&gt;&amp;lt;b&amp;gt;&lt;/code&gt; does not appear anywhere. If I have
correctly used h to escape all the inputs then it should be rendered
as &lt;code&gt;&amp;amp;lt;b&amp;amp;gt;&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;response.should_not have_text(/&amp;lt;b&amp;gt;/)&lt;/code&gt; should do that test.&lt;/p&gt;

&lt;p&gt;One cool thing is the mocking will tell you if any new inputs (ie
calls to model attributes) have been added, or if you have forgotten
any. So this should keep you honest in the future if you add new
attributes that need escaping.&lt;/p&gt;

&lt;p&gt;The &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;assigns[:new_stuff] = @new_stuff
assigns[:top] = @top
assigns[:new_pets] = @new_pets
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Sets the assigns to the variables that my view uses,simulating what
the controller woul