<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Scott Rutherford &#187; Rails</title>
	<atom:link href="http://blog.thatsuseful.com/category/rails/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.thatsuseful.com</link>
	<description>Life on and off the Rails</description>
	<lastBuildDate>Wed, 21 Jul 2010 16:35:05 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Simple Rails Oauth Functional &amp; Integration Testing</title>
		<link>http://blog.thatsuseful.com/2010/06/07/simple-rails-oauth-functional-integration-testing/</link>
		<comments>http://blog.thatsuseful.com/2010/06/07/simple-rails-oauth-functional-integration-testing/#comments</comments>
		<pubDate>Tue, 08 Jun 2010 01:09:42 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Rails]]></category>

		<guid isPermaLink="false">http://blog.caronsoftware.com/?p=9807</guid>
		<description><![CDATA[We are (UserVoice that is) about to release a new api that is based on a combination of 2-legged and 3-legged oauth. While checking the functional tests that had been written I noticed that they contained a lot of mocked methods, which while not a bad thing in itself, in this case meant a lot [...]]]></description>
			<content:encoded><![CDATA[<p>We are (<a href="http://www.uservoice.com">UserVoice</a> that is) about to release a new api that is based on a combination of 2-legged and 3-legged oauth. While checking the functional tests that had been written I noticed that they contained a lot of mocked methods, which while not a bad thing in itself, in this case meant a lot of functionality (specifically authorization) was not being checked.</p>
<p>So, I had a poke about in the gem (Oauth (0.3.6)) and wrote a couple of helper methods to allow for testing oauth based requests without mocking too much out, or in the case of integration tests, mocking nothing.</p>
<p><span id="more-9807"></span><br />
First off add this to your test_helper</p>
<pre>
<code>include OAuth::Helper

# Functional test helpers
def oauth_helper_for_client(client)
  # gonna do everything but actually create a sig, cause its a pain
  OAuth::Signature::HMAC::SHA1.any_instance.stubs(:==).returns(true)
  OAuth::Client::Helper.new(@request, {:consumer =&gt; client})
end

def oauth_helper_for_client_and_user(client, user)
  token = AccessToken.find_or_create_by_user_id_and_client_id(user.id, client.id)
  OAuth::Signature::HMAC::SHA1.any_instance.stubs(:==).returns(true)
  OAuth::Client::Helper.new(@request, {:consumer =&gt; client, :token =&gt; token})
end

# Integration test helper
def oauth_header_for_method_uri_client_user_and_params(method, uri, client, user, params={})
  token = AccessToken.find_or_create_by_user_id_and_client_id(user.id, client.id)
  request = OAuth::RequestProxy.proxy(
     "method"       =&gt; method,
     "uri"               =&gt; uri,
     "parameters" =&gt; {
       "oauth_consumer_key"       =&gt; client.key,
       "oauth_token"                     =&gt; token.token,
       "oauth_signature_method" =&gt; "HMAC-SHA1",
       "oauth_timestamp"             =&gt; generate_timestamp,
       "oauth_nonce"                    =&gt; generate_nonce,
       "oauth_version"                  =&gt; '1.0'
     }.merge(params).reject { |k,v| v.to_s == "" }
  )

  signature = OAuth::Signature.sign request,
    :consumer_secret =&gt; client.secret,
    :token_secret       =&gt; token.secret

  request.parameters["oauth_signature"] = signature
  request.oauth_header
end</code>
</pre>
<p>This gives you 3 new helper methods. The first can be used in functional tests to create a 2-legged oauth request and the second to create a 3-legged oauth as so:</p>
<pre>
<code>def test_index_xml
  get :index, {:format =&gt; 'xml'}.merge(oauth_helper_for_client(clients(:some_consumer)).oauth_parameters)
  assert_response :success
end</code>
</pre>
<p>The third method (which actually should have a parallel no user version for 2-legged, but that is left as an exercise for the reader..) allows you to simply create a fully signed request as shown below:</p>
<p>[NB. that the parameters are passed as strings not symbols, the Oauth library is not so happy with symbols]</p>
<pre>
<code>post( url, { :value =&gt; value }, {
  'Accept'=&gt; 'application/json', 'Authorization' =&gt; oauth_header_for_method_uri_client_user_and_params(
    "POST", URI.parse(url), clients(:uservoice),users(:scott),{"value" =&gt; value}
  )
})</code>
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.thatsuseful.com/2010/06/07/simple-rails-oauth-functional-integration-testing/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Exclude Records With UltraSphinx</title>
		<link>http://blog.thatsuseful.com/2008/08/20/exclude-records-with-ultrasphinx/</link>
		<comments>http://blog.thatsuseful.com/2008/08/20/exclude-records-with-ultrasphinx/#comments</comments>
		<pubDate>Wed, 20 Aug 2008 17:47:00 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Home]]></category>
		<category><![CDATA[Rails]]></category>

		<guid isPermaLink="false">http://blog.caronsoftware.com/2008/08/20/exclude-records-with-ultrasphinx</guid>
		<description><![CDATA[So been using Sphinx with the UltraSphinx plugin and I came across the requirement to use filters to reject records. Unfortunately in the plugin the filters are hardwired to submit the &#8216;exclude&#8217; parameter as &#8216;false&#8217; i.e include. Time for a quick hack I think&#8230;&#8230;. Very easy to fix, just open up the &#8216;internals.rb&#8217; file in [...]]]></description>
			<content:encoded><![CDATA[<p>So been using <a href="http://www.sphinxsearch.com/">Sphinx</a> with the <a href="http://blog.evanweaver.com/files/doc/fauna/ultrasphinx/files/README.html">UltraSphinx</a> plugin and I came across the requirement to use filters to reject records. Unfortunately in the plugin the filters are hardwired to submit the &#8216;exclude&#8217; parameter as &#8216;false&#8217; i.e include. Time for a quick hack I think&#8230;&#8230;.</p>
<p>Very easy to fix, just open up the &#8216;internals.rb&#8217; file in the UltraSphinx plugin or include the gem using &#8216;rake gems:unpack <span class="caps">GEM</span>=ultrasphinx&#8217; (need to have config.gem &#8220;ultrasphinx&#8221; set in envrinoment.rb) and then find that file.</p>
<p>Ok, so found the file? On line 97 is this loop:</p>
<pre>
Array(opts['filters']).each do |field, value|
  ....
 begin
     case value
     when Integer, Float, BigDecimal, NilClass, Array
       # XXX Hack to force floats to be floats
       value = value.to_f if type == 'float'
       # Just bomb the filter in there
       request.filters &lt;&lt; Riddle::Client::Filter.new(field, Array(value), false)
     when Range
     ....
end
</pre>
<p>The &#8216;Riddle::Client::Filter.new(field, Array(value), false)&#8217; call is the one we need to change (the &#8216;false&#8217; is the exclude param).</p>
<p>            <span id="more-9719"></span></p>
<p>So what I did was to change that loop to:</p>
<pre>
        Array(opts['filters']).each do |field, value|

          field = field.to_s
          type = Fields.instance.types[field]

          # Special derived attribute
          if field == 'distance' and options['location']
            field, type = '@geodist', 'float'
          end

          raise UsageError, "field #{field.inspect} is invalid" unless type

# ADDED FROM HERE
          exclude = false
          # check for exclude flag
          if value.is_a? Hash
            exclude = value[:exclude]
            value = value[:value]
          end
# TO HERE

          begin
            case value
              when Integer, Float, BigDecimal, NilClass, Array
                # XXX Hack to force floats to be floats
                value = value.to_f if type == 'float'
                # Just bomb the filter in there

# CHANGE CALL HERE TO PASS THE exclude PARAMETER
                request.filters &lt;&lt; Riddle::Client::Filter.new(field, Array(value), exclude)
              when Range
                # Make sure ranges point in the right direction
                min, max = [value.begin, value.end].map {|x| x._to_numeric }
                raise NoMethodError unless min &lt;=&gt; max and max &lt;=&gt; min
                min, max = max, min if min &gt; max
                # XXX Hack to force floats to be floats
                min, max = min.to_f, max.to_f if type == 'float'

# CHANGE CALL HERE TO PASS THE exclude PARAMETER
                request.filters &lt;&lt; Riddle::Client::Filter.new(field, min..max, exclude)
              when String
                # XXX Hack to move text filters into the query
                opts['parsed_query'] &lt;&lt; " @#{field} #{value}"
              else
                raise NoMethodError
            end
          rescue NoMethodError =&gt; e
            raise UsageError, "Filter value #{value.inspect} for field #{field.inspect} is invalid"
          end
        end
</pre>
<p>Now I can do searches like</p>
<pre>
    @search = Ultrasphinx::Search.new(
      :query =&gt; params[:q],
      :page =&gt; page,
      :class_names =&gt; ["User"],
      :filters =&gt; {:id =&gt; {:value =&gt; current_user.id, :exclude =&gt; true}}
    )
</pre>
<p>And the current_user gets excluded, cool.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.thatsuseful.com/2008/08/20/exclude-records-with-ultrasphinx/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Hop Toad</title>
		<link>http://blog.thatsuseful.com/2008/08/20/hoptoad/</link>
		<comments>http://blog.thatsuseful.com/2008/08/20/hoptoad/#comments</comments>
		<pubDate>Wed, 20 Aug 2008 15:49:00 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Home]]></category>
		<category><![CDATA[Rails]]></category>

		<guid isPermaLink="false">http://blog.caronsoftware.com/2008/08/20/hoptoad</guid>
		<description><![CDATA[I&#8217;ve been using the Hop Toad web app for a while now to collect errors from my Rails apps and its been working out great. It drops in very simply and replaces the Exception Notifier plugin I was using. Big advantages are the archive of errors and the ability to mark them as dealt with. [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been using the <a href="http://www.hoptoadapp.com">Hop Toad</a> web app for a while now to collect errors from my Rails apps and its been working out great. It drops in very simply and replaces the Exception Notifier plugin I was using.</p>
<p>Big advantages are the archive of errors and the ability to mark them as dealt with. Of course I could do that with email folders and the like, but I&#8217;m lazy and this appears to do it all. Oh and its free!!!</p>
<p><img src="http://blog.caronsoftware.com/assets/2008/8/19/Picture_1.png" width="500px"></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.thatsuseful.com/2008/08/20/hoptoad/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Parsing Rails log files with JSON</title>
		<link>http://blog.thatsuseful.com/2008/07/28/parsing-rails-log-files-with-json/</link>
		<comments>http://blog.thatsuseful.com/2008/07/28/parsing-rails-log-files-with-json/#comments</comments>
		<pubDate>Mon, 28 Jul 2008 12:38:00 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Home]]></category>
		<category><![CDATA[Rails]]></category>

		<guid isPermaLink="false">http://blog.caronsoftware.com/2008/07/28/parsing-rails-log-files-with-json</guid>
		<description><![CDATA[So I needed to parse the log files for SlimTimer this weekend to correct a data loss issue due to a small issue with implementing https for subscribers. The issue required looking for requests that had returned something other than &#8220;200 OK&#8221; collecting the parameters and entering any data that had got lost. After a [...]]]></description>
			<content:encoded><![CDATA[<p>So I needed to parse the log files for <a href="http://slimtimer.com">SlimTimer</a> this weekend to correct a data loss issue due to a small issue with implementing https for subscribers. The issue required looking for requests that had returned something other than &#8220;200 OK&#8221; collecting the parameters and entering any data that had got lost. After a bit of messing around it occured to me the the format of the parameters string in the Rail&#8217;s logs was not a million miles from the <span class="caps">JSON</span> format, so I came up with this to turn the string into a useable hash:</p>
<pre>
params =~ /.*: Parameters: (\{.*\})$/
str_hash = JSON.parse(params.gsub('=&gt;',':'))
</pre>
<p>This provides a hash of the parameters used in the request. Of course the keys here are strings so to convert to symbols we can then use:</p>
<pre>
def create_symbol_hash(input)
  ret = input
  if input.is_a? Hash
    ret = {}
    input.each do |k, v|
      ret[k.to_sym] = create_symbol_hash(v)
    end
    ret
  else
    ret
  end
end
</pre>
<p>and simply pass in the output from the <span class="caps">JSON</span> library. Seemed quite neat to me anyway.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.thatsuseful.com/2008/07/28/parsing-rails-log-files-with-json/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Twitter API: User IDs</title>
		<link>http://blog.thatsuseful.com/2008/05/29/twitter-api-user-ids/</link>
		<comments>http://blog.thatsuseful.com/2008/05/29/twitter-api-user-ids/#comments</comments>
		<pubDate>Thu, 29 May 2008 15:31:00 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Home]]></category>
		<category><![CDATA[Rails]]></category>

		<guid isPermaLink="false">http://blog.caronsoftware.com/2008/05/29/twitter-api-user-ids</guid>
		<description><![CDATA[So, next lesson from TweetLists: the user id given in the xml from the public timeline is not a unique identifier. The screen name is the only way to identify a user. I ended up with 17 different ids for one user!!! Hence, gave up fixed it and dropped the database &#8211; too much effort [...]]]></description>
			<content:encoded><![CDATA[<p>So, next lesson from <a href="http://www.tweetlists.com">TweetLists</a>: the user id given in the xml from the public timeline is not a unique identifier. The screen name is the only way to identify a user. I ended up with 17 different ids for one user!!! Hence, gave up fixed it and dropped the database &#8211; too much effort to fix when its only been running for 12 hours.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.thatsuseful.com/2008/05/29/twitter-api-user-ids/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>TweetLists</title>
		<link>http://blog.thatsuseful.com/2008/05/29/tweetlists/</link>
		<comments>http://blog.thatsuseful.com/2008/05/29/tweetlists/#comments</comments>
		<pubDate>Thu, 29 May 2008 09:13:00 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Home]]></category>
		<category><![CDATA[Rails]]></category>

		<guid isPermaLink="false">http://blog.caronsoftware.com/2008/05/29/tweetlists</guid>
		<description><![CDATA[I released TweetLists last night, well actually this morning (about 3am), the idea being to try and capture a little of the zeitgeist on Twitter by aggregating the links people are talking about. At the moment there are 3 lists, the live feed, a popular feed (the links ordered by the number of times they [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://blog.caronsoftware.com/assets/2008/5/29/Picture_1.png" alt="" /></p>
<p>I released <a href="http://www.tweetlists.com">TweetLists</a> last night, well actually this morning (about 3am), the idea being to try and capture a little of the zeitgeist on <a href="http://www.twitter.com">Twitter</a> by aggregating the links people are talking about. At the moment there are 3 lists, the live feed, a popular feed (the links ordered by the number of times they appeared) and one I have chosen to call Twitterati (quite proud of that!) which is the links ordered by the number of followers people have.</p>
<p>At the moment there is no time aspect to the Twitterati or Popular lists (not relevant on the live one) but I guess I will make them for the last 7 days?</p>
<p>Lesson one form this has been: don&#8217;t try and be clever with your scheduling &#8211; cron just works. I tried to use the Rufus Scheduler and it had stopped running by the time I got up again this morning.</p>
<p>Anyhoo, I&#8217;ve found it to be fun and interesting so far, any ideas for other lists?</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.thatsuseful.com/2008/05/29/tweetlists/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Fields_for and Multiple Checkboxes</title>
		<link>http://blog.thatsuseful.com/2008/05/23/fields_for-and-multiple-checkboxes/</link>
		<comments>http://blog.thatsuseful.com/2008/05/23/fields_for-and-multiple-checkboxes/#comments</comments>
		<pubDate>Fri, 23 May 2008 09:06:00 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Home]]></category>
		<category><![CDATA[Rails]]></category>

		<guid isPermaLink="false">http://blog.caronsoftware.com/2008/05/23/fields_for-and-multiple-checkboxes</guid>
		<description><![CDATA[Found a bit of a gotcha when building a form recently using form_for, fields_for and the check_box helper. Because Rails adds a hidden field for each checkbox and fields_for for new objects produces ids like &#8216;users_invites__selected&#8217;, if you try to generate multiple new objects in the same form things can get a little confused. I [...]]]></description>
			<content:encoded><![CDATA[<p>Found a bit of a gotcha when building a form recently using form_for, fields_for and the check_box helper. Because Rails <a href="http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#M000928">adds a hidden field</a> for each checkbox and fields_for for new objects produces ids like &#8216;users_invites__selected&#8217;, if you try to generate multiple new objects in the same form things can get a little confused. I found that when selecting a single checkbox things worked ok, but when selecting multiple the values would bleed across each other. I haven&#8217;t investigated this very far just switched to check_box_tag instead (no hidden field).</p>
<p>Actually just found this <a href="http://artofmission.com/articles/2008/2/27/check_box-broken-in-rails-2-0-2">post</a> mentioning that the checkboxes might be broken in Rails 2.0.2. Which could be the cause of the issue.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.thatsuseful.com/2008/05/23/fields_for-and-multiple-checkboxes/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Added UserVoice to FCKeditor</title>
		<link>http://blog.thatsuseful.com/2008/05/02/added-uservoice-to-fckeditor/</link>
		<comments>http://blog.thatsuseful.com/2008/05/02/added-uservoice-to-fckeditor/#comments</comments>
		<pubDate>Fri, 02 May 2008 11:42:00 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[FCKeditor]]></category>
		<category><![CDATA[Home]]></category>
		<category><![CDATA[Rails]]></category>

		<guid isPermaLink="false">http://blog.caronsoftware.com/2008/05/02/added-uservoice-to-fckeditor</guid>
		<description><![CDATA[I added the UserVoice feedback system to the FCKeditor demo page today. So any bugs or ideas for the plugin can be stored in a central place. If you have found any issues recently (and can be bothered) it would be great if you could put them up there. I will copy over any ones [...]]]></description>
			<content:encoded><![CDATA[<p>I added the <a href="http://www.uservoice.com">UserVoice</a> feedback system to the <a href="http://fckeditor.caronsoftware.com">FCKeditor demo page</a> today. So any bugs or ideas for the plugin can be stored in a central place. If you have found any issues recently (and can be bothered) it would be great if you could put them up there. I will copy over any ones I find going back over the comments. Ta.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.thatsuseful.com/2008/05/02/added-uservoice-to-fckeditor/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Plugin of the Day &#8211; Asset Packager</title>
		<link>http://blog.thatsuseful.com/2008/04/01/plugin-of-the-day/</link>
		<comments>http://blog.thatsuseful.com/2008/04/01/plugin-of-the-day/#comments</comments>
		<pubDate>Tue, 01 Apr 2008 15:20:00 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Home]]></category>
		<category><![CDATA[Rails]]></category>

		<guid isPermaLink="false">http://blog.caronsoftware.com/2008/06/25/plugin-of-the-day</guid>
		<description><![CDATA[I think this is great, very useful and gets YSlow off your back&#8230;&#8230; Asset Packager Key Features Merges and compresses javascript and css when running in production. Uses uncompressed originals when running in development. Handles caching correctly. (No querystring parameters – filename timestamps) Uses subversion revision numbers instead of timestamps if within a subversion controlled [...]]]></description>
			<content:encoded><![CDATA[<p>I think this is great, very useful and gets YSlow off your back&#8230;&#8230;</p>
<p><a href="http://synthesis.sbecker.net/pages/asset_packager">Asset Packager</a></p>
<p>Key Features</p>
<ul>
<li>Merges and compresses javascript and css when running in production.</li>
<li>Uses uncompressed originals when running in development.</li>
<li>Handles caching correctly. (No querystring parameters – filename timestamps)</li>
<li>Uses subversion revision numbers instead of timestamps if within a subversion controlled directory.</li>
<li>Guarantees new version will get downloaded the next time you deploy.</li>
</ul>
<p>Thank you <a href="http://sbecker.tweetpeek.com/">Scott Becker</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.thatsuseful.com/2008/04/01/plugin-of-the-day/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Recommendbox Live!!!</title>
		<link>http://blog.thatsuseful.com/2008/04/01/recommendbox-live/</link>
		<comments>http://blog.thatsuseful.com/2008/04/01/recommendbox-live/#comments</comments>
		<pubDate>Tue, 01 Apr 2008 13:57:00 +0000</pubDate>
		<dc:creator>Scott</dc:creator>
				<category><![CDATA[Home]]></category>
		<category><![CDATA[Rails]]></category>

		<guid isPermaLink="false">http://blog.caronsoftware.com/2008/04/01/recommendbox-live</guid>
		<description><![CDATA[After a couple of months of intense coding Robert Loch and I launched RecommendBox yesterday. Its a site based around requesting and giving recommendations to your friends. There were a few immediate issues!! IE 6 is not yet fully supported, but on the whole it went ok. Now, sleep&#8230;&#8230;.]]></description>
			<content:encoded><![CDATA[<p><img src="http://blog.caronsoftware.com/assets/2008/4/1/logo.png" alt="" /></p>
<p>After a couple of months of intense coding <a href="http://internetpeeps.com">Robert Loch</a> and I launched <a href="http://www.recommendbox.com">RecommendBox</a> yesterday. Its a site based around requesting and giving recommendations to your friends. There were a few immediate issues!! <span class="caps">IE 6</span> is not yet fully supported, but on the whole it went ok. Now, sleep&#8230;&#8230;.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.thatsuseful.com/2008/04/01/recommendbox-live/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
