Well, it was a little more work than I'd originally anticipated, but 5Valleys is now both my personal OpenID server and a consumer for visitors wishing to use OpenID to identify themselves when posting comments. I used the ruby-openid gem, but otherwise wrote all my own code, trying to stay as RESTful as possible (OpenID itself isn't particularly friendly to REST practices), and getting a thorough understanding of the internals before my presentation at next week's Missoula Web Discussion Group meeting. If you have an OpenID, go ahead and post a comment to try it out. Hint: if you have accounts at any of these sites, then you've got an OpenID you can (hopefully) use here, or anyplace else that proudly accepts them.

Comments: 0 [add comment]

My Solstice Holidays present to myself this year was a Nokia N810 Internet Tablet. It's kinda like an IPhone, but without the phone part and especially without the locked down you-can-only-do-what-steve-jobs-says-you-can-do garbage. It runs on Linux and is wide open. I was very interested in getting one right up until I discovered that somebody had already ported Ruby to it... then I just HAD to have one. Don't hold your breath for Ruby to come to the IPhone. It'll never happen.

So, I've had it for about a week now, and have already been having way too much fun. It's got a built in GPS (yah that was another reason I HAD to have it), and I've been playing with that a bit. Naturally, I wanted to write stuff for it in Ruby, and naturally I wanted to take advantage of the GPS to do some location based stuff, so the first thing I needed was a Rubyish way to get at the GPS data in a friendly object-oriented fashion, which leads me to...

I just released Rupl, the RUby Personal Locator. It's a Ruby library for accessing the GPS. It's what I intend to use in any apps that I write for this thing that need to know where they are in the world within 20-30 feet. I put it up on RubyForge in hopes to attract more Ruby developers to this platform, as well as hope that they'll take it a run with it to create some cool location oriented apps that even I hadn't thought of (but can't live without).

Obviously, Rupl has a way to lookup where the unit is (Rupl.last_known_location) which will return a hash of data on where the GPS was the last time it got a GPS fix and what time that was at. So for instance, if you go inside a building and the GPS looses the satelite signal, last_known_location will return the latitude and longitude of the front door and whatever time it was that you were there.

Secondly, Rupl provides a few convenience functions to do related calculations such as computing the distance and direction between two given coordinates.

And last but not least is the Trigger class. Your application can create an arbitrary number of triggers and pass a block of code that will be fired whenever the trigger conditions are met. For instance, conditions can be based on the distance you've moved away from where you were the last time the trigger fired. So the trigger will fire every time you move another 42 feet, or whatever. Or, conditions can be based on how far away you are from some arbitrary hotspot and fire when you get within some set radius of that point.

Rupl can be found on RubyForge at the follow addresses and is released under the MIT license. Enjoy!

Project Page: http://rubyforge.org/projects/rupl/
Documentation: http://rupl.rubyforge.org/
SVN Repository: http://rupl.rubyforge.org/svn/trunk/
Comments: 0 [add comment]

I just released version 0.2 of ARID (ActiveResourceIntegrationDsl) [formerly known as RestfulCrudIntegrationTester]. I've been working with this on three different projects for the past week and so far am very happy with it.

Besides the name change, there's a couple major enhancements.

A new sess.exercises_ method.

This allows one call that runs through all the CRUD actions of a RESTful controller, assuming the controller doesn't do anything weird. So something like

napoleon.exercises_llama({:name => 'Tina'},{:name => 'Trisha'})
is equivalent to the 0.1 method of...
napoleon.creates_lama({:name => 'Tina'}) do |page|
  @llama = page.assigns(:llama)
end     
napoleon.reads_lama(@llama)
napoleon.updates_llama(@llama,{:name => {Trisha'})
napoleon.destroys_llama(@llama)
which runs through a big chunk of a simple RESTful controller's code and views checking that forms are all setup correctly on new and edit pages and that the controller properly processes the submitted forms and respond with :success on reads and :redirect everything else.

It now handles AJAX calls.

Simply add {:via_ajax => true} to the options hash for the lower level goes_to (GET), posts_to and puts_to functions, and it'll pass it to your controller as an XML_HTTP_REQUEST, instead of a standard HTTP. It'll assert a :success, but It's up to you to check for anything else in the response. i.e.

napoleon.puts_to(llama_path(@llama),{:name => 'Pedro'},{:via_ajax => true}) do |page|
  assert_equal('Pedro',page.assigns(:llama).name)
  page.assert_select_rjs :replace_html, :llama
end 

Comments: 0 [add comment]

Took a little time today to give the site a makeover, and fix some small bugs. I still don't claim to have any eye for aesthetics, but I think this probably looks a lot nicer than before. Clean & simple.

I also released my first public Rails plugin this morning (with the wildly clever name of RestfulCrudIntegrationTester), a whole 36 hours sooner than I thought I would yesterday.

I've just finally started delving into Integration Testing (yah, I know, I'm a little late to the party.) I was writing tests for two different projects (one at home and one at work) and noticed even though they were very different projects, I was duplicating a lot of low level integration test code. So... I extracted it out into a plugin.

Now, on both projects, and any others I use the plugin in, I can just say things like...
new_session_as('jon','Exc3l1ent_p@s5w0rd!') do |jon|
  jon.updates_article(1,{:article => {:subject => 'New Subject'}})
end
And then the Plugin will go try to login as that user (using the sessions_controller), and exercise both the edit and update actions in the articles_controller, as well as confirm that the edit form has the correct action, method, and input elements for a RESTful update.

Note, that the plugin is only for very RESTful controllers (if you didn't get that already), and it makes a few big assumptions on how your Rails app works. I tried to make sure the assumptions followed Rails conventions as much as possible.

Some of the initial code came from Jamis Buck's post on the subject from over a year ago, (yah, yah, late to the party) so thanks to Jamis for getting me started on the right path.

Strangly, as long as Integration Testing has been around, I didn't find any existing plugins to provide this kind of basic framework. So, I'm hoping I didn't waste time reinventing the wheel, and instead have found an unfilled need and provided a worthwhile contribution to the Rails community. Time will tell on that.

Here's the complete README file from the 0.1 version of plugin.

RestfulCrudIntegrationTester
============================
Provides a framework of common integration
tests for RESTful CRUD interactions.

How To Use
==========
class ArticleTest < ActionController::IntegrationTest
  include RestfulCrudIntegrationTester
  
  def test_some_article_functionality
  	new_session_as('jonnyg','testing123') do |jon|
      params = {
      	:article => {
      		:title => 'Foo Bites Bar',
      		:content => 'Bar was bitten by Foo yesterday.' 
      	}
      }
      jon.creates_story(params)
      params = {:article => {:title => 'Bar Bitten Badly'}}
      jon.updates_story(1,params)
      jon.reads_story(1)
      jon.deletes_story(1)
    end
  end
end

Assumptions
===========
The plugin makes certain assumptions about your application.
Some of these can be bypassed or overridden if your app does
not conform.

* you have appropriate map.resources lines in your routes.rb
	for any controllers you intend to test with these methods.
* sessions_path helper method returns the path to a controller
	that controls user logins. (Provided automatically by
	Rails if you have a sessions_controller and routes.rb
	includes "map.resources :sessions".)
	@ Override by adding your own sessions_path method to
	application_helper
	
* your sessions controller accepts parameters in the form of...
	{:user => {:username => 'xxx', :password => 'yyy'}}
	@ Override by providing your own new_session_as method
		that generates an appropriate session for your app
		with the user provided.

Provided Methods
================
-Create Sessions-
-----------------
new_session:
	creates generic session (i.e. user not logged into your site).
	
	#example
	new_session do |guest|
		#guest.does_things here
	end
	
new_session_as:
	creates session as user with provided username and password.
	
	***assumes that you have a 'sessions_controller' and that it
	takes params in the form of... 
	{:user => {:username => 'xxx', :password => 'yyy'}}
	
-Do CRUD-
---------
All CRUD tasks can be passed a block to do additional assertions
sess.reads_():
	performs a GET to _path()
	asserts a :success response
	yields if you passed a block containing more assertions
	
sess.creates_():
	performs a GET to new__path
	asserts a :success response
	asserts page contains a form with a _path action
		and a 'post' method.
	asserts form contains appropriately named input elements
		for each of the  you provided.
		i.e. 'person[first_name]'
	performs a POST to _path passing 
	yields if you passed a block containing more assertions
	if you did NOT pass a block
		asserts a :redirect response
		follows redirect
		asserts a :success response
	
	
sess.updates_(,)
	performs a GET to edit__path()
	asserts a :success response
	asserts page contains a form with a _path action
		and a 'post' method.
        asserts form contains a hidden input field named '_method'
    	        and a value of 'put'.
	asserts form contains appropriately named input elements
		for each of the  you provided.
		i.e. 'person[first_name]'
	performs a PUT to _path passing 
	yields if you passed a block containing more assertions
	if you did NOT pass a block
		asserts a :redirect response
		follows redirect
		asserts a :success response
		
sess.deletes_():
	performs a DELETE to _path()
	yields if you passed a block containing more assertions
	if you did NOT pass a block
		asserts a :redirect response
		follows redirect
		asserts a :success response
Comments: 0 [add comment]

Got the power bill today, and you'll never guess what happened.

Ok, maybe you will. I thought I was doing a pretty bad job at unplugging the router and stuff when not in use the past month, so I didn't think I'd beat my previous record of 114 KwH. But I guess I was doing better than I thought, 'cause the latest bill shows that I'm just 3 away from the magical double digits. That's 102 KwH for an entire month. According to some of the stuff I've read lately, that's what the average inefficient homeowner uses in just two or three days. I'm not sure what to expect from the next bill. I broke down and ran the dryer a couple times since the end of that billing period, but I was also out of town for 5 days at RailsConf, and unplugged just about every damn thing I could before I left, so... we'll have to wait and see.

Speaking of RailsConf, part of my motivation for setting up this blog was to occasionally talk about Rails, which I have yet to do, so, here goes.

Thanks to the day job for paying all the registration fees and travel expenses for RailsConf. Considering I work for a non-profit now (and my previous job at a much bigger company would have done no such thing), that's pretty fucking amazing. Since this was the first conference of this sort I've been to, I wasn't sure what to expect. I was just one among lots and lots of geeks, and that wasn't surprising. But I think I expected more of the presentations to be low level "Let's dissect some code together" kind of things, and some of them certainly were. But there were also a lot of high level theory presentations that weren't even Rails specific. So, I think that kind of disappointed me. But that just means if I go next year I'll be a more discriminating about which presentations I go to.

One of my favorite presentations though was Josh Sussers's presentation on contributing to Rails Source. I'll not bore anyone with the details, but Josh gave point by point directions to get started and made it seem very easy to take part (assuming you can code), with the hardest part being having the patience to wait for someone to review your submissions. I guess there's a backlog of hundreds of tickets and patches waiting to be reviewed. I managed to corner Josh at the final reception in the lobby and get some tips. His big suggestion was to find a submitted patch that didn't come with a test. Then, write a test case that proves the patch fixes something, and add that to the ticket. So, that night back at the hotel I figured I'd browse around the rails bug tracking site and see what I could find. Well, before I knew it, 7 minutes before midnight Pacific Time, I'd submitted my first patch to Rails Source. So, now the waiting game begins.

There was one more thing that surprised me at RailsConf... a marching band... or at least that's what they called themselves. Twasn't anything like the marching band I was in in High School.

Comments: 0 [add comment]