Introducing Action Messager: Dead simple IM notifications for your app! 3

Posted by james
on Sunday, April 06

Sometimes email is the wrong choice for webapp notifications. Our inboxes are becoming increasingly cluttered, and especially for those of us who carry PDAs, it can be a pain to scroll through twelve facebook notifications just to get to the mail that we actually need to read. What's more, email just isn't that great of a tool for receiving short messages, since you have to open them each individually. At the very least, it's nice to have another choice.

Particularly with shorter notifications, instant messaging can be a great alternative to email. The messages don't clutter up your inbox, or need to be opened individually. Most people already use it on a daily basis. The only problem is implementation.

ActionMessager

ActionMessager is a simple framework for creating IM-based notifiers. It is structured like ActionMailer, so it's got virtually zero learning curve for most rails developers. What's more, because the syntax is the same, it's pretty easy to create a delegate class that acts like your mailer, but sends IMs as well. That means drop-in replacement!

All you have to do start sending IM notifications to your users is subclass ActionMessager::Base. Then, create a method that sets an array of recipients, and returns the message you'd like to send:

class JabberNotifier < ActionMessager::Base
  def friendship_request(friendship_request)
    @recipients = friendship_request.receiver.jabber_contact
    
    "You have received a friendship request from #{friendship_request.sender.name}! Click here to accept or decline: #{friendship_request.url}"
  end
end

Then, wherever you'd like to send the notification:

JabberNotifier.deliver_friendship_request(friendship_request)

That's all there is to it.

Caveats

Currently, only jabber is supported. It is possible to access other IM services over jabber, but I'm not 100% clear how it works, and I don't yet have need for it, so that may or may not come later.

The bigger caveat, though, is speed. Sending a notification takes anywhere from 1-2s (whether you send 1 or 5). It may be possible to improve performance by using a different jabber library, and I'll probably investigate that in the near future. Even with a performance boost, though, you should still take your notifications out of the request cycle, by using something like workling to process them asynchronously.

Get It!

Get action_messager in gem form:

$ sudo gem install action_messager

Or get the source, from github:

$ git clone git://github.com/giraffesoft/action_messager.git

Plugins I've Known and Loved #3: Ultrasphinx 17

Posted by james
on Monday, March 03

If you've ever implemented searching in your rails app, you probably noticed that it's a major hassle (unless, of course, you already know about Ultrasphinx). Ferret is probably the most commonly used indexing solution, but, it isn't anywhere near production ready. acts_as_solr, another option, is so full of show stopper bugs that it really isn't even worth bothering with. The worst part about both of those solutions is that they usually work great in your development environment. Try to put them in to production, though, and you're in for big problems.

Enter Ultrasphinx, by Evan Weaver. Sphinx is a super high performance search daemon, designed to suck information out of a mysql or pgsql database (though, it isn't limited to that). Normally, one would configure Sphinx with SQL queries that it uses to fetch the data. Apparently, the configuration file can be a major hassle to work with — I wouldn't know. Ultrasphinx provides you with a declarative API that it ultimately uses to generate the Sphinx configuration file for you, making the process quick and painless. A few lines of code, a couple of rake commands, and you're searching.

The Sphinx approach has several advantages over ferret, and acts_as_solr. First of all, Sphinx is extremely stable. They're nearing a 1.0 release, and its stability certainly merits that. You don't even really need to worry about monitoring the search daemon, because it just isn't going to crash. Also, if you're running any rails apps in production, you know that long running ActiveRecord callbacks can lead to your app performing very poorly. The ferret and solr solutions both rely on active record callbacks to inform the indexer of new data. Moreover, if the search daemon goes down for any period of time, or, say, the index becomes corrupted (ferret, I'm looking at you), your entire app is going to be down for the count. You set the Sphinx indexer to run every half hour on a cron job, and since it works with the db directly, its performance and stability characteristics have absolutely zero impact on your app.

Trying it Out

So, let's implement a search for our blog. First, we'll want to edit the paths to the index and logs in the default config file. Note that Ultrasphinx uses two types of config files: one that the programmer edits, and one that it generates; both are necessary on all machines that are accessing the index (seriously, not having the generated config on a slave machine caused me some trouble). Then, we'll want to declare our post model as indexed (note that you must declare the fields as strings, not symbols):

class Post < ActiveRecord::Base
  is_indexed :fields => ['title', 'body']
end

Then, we'll need to ask Ultrasphinx to generate a configuration file. You've got to re-run this rake task any time you make changes to the definition of your models' is_indexed declaration. Make sure to .gitignore (svn:ignore for people still stuck in svn land) the generated file in development. Evan recommends checking the production file in to version control, but I have it set to generate automatically on deploy with a cap recipe. That way, if I make changes to the indexing, I won't forget to re-generate the config file. All you have to do is run:

$ rake ultrasphinx:configure

Then, since it's our first time, we've got to run the indexer:

$ rake ultrasphinx:index

Finally, we'll need to start the search daemon:

$ rake ultrasphinx:daemon:start

Now, we can start searching.

@search = Ultrasphinx::Search.new(:query => params[:query])
@search.run
@search.results

So, it's really easy to build a basic search engine using Ultrasphinx. There are some gotchas, though.

Gotchas & Notes

  • More complex indexing with Ultrasphinx can be slightly more verbose, and SQL-focused than it would be with a solution that relies on AR callbacks to do its indexing.
  • Transforming data with Ruby is impossible; the data you index must be in your database (unless you use a stored procedure, which can actually be written in ruby if you're using pgsql, but I digress).
  • Ultrasphinx preloads your indexed models when it is initialized. So, if your models depend on any monkey patches that live in your app's lib directory, they must be loaded before the Ultrasphinx (in my experience, this has meant pluginizing my monkey patches). Because of the way that exceptions are caught in the preloading routine, you won't see the actual error that is stopping your model from loading. Instead, you'll just get a constant loading error, or a name error, or something. If you see something like that, and your models load fine without Ultrasphinx installed, look for dependency issues.
  • Ultrasphinx attempts to preload your indexed models using a regex that doesn't respect commenting (at the time of writing). If you're struggling with issues mentioned in the last gotcha, you'll probably try commenting out the is_indexed call to see whether that's what's causing the problem. That won't work. You can either delete the is_indexed call entirely when debugging, or pull from my git repo, where I've modified the regex to respect #-style commenting (but not the =begin/=end style).
  • If you see DivideByZeroErrors in production, it's probably because you're missing the generated configuration file on one or more of your app server machines.

Check it Out

You'll need to grab Sphinx. Then, the plugin...

Get it from svn:

$ svn export svn://rubyforge.org/var/svn/fauna/ultrasphinx/trunk vendor/plugins/ultrasphinx

Or pull from my git repo (for the change described in the gotchas section):

git clone git://github.com/giraffesoft/ultrasphinx.git

To Sum Up

Ultrasphinx is by far the most effective rails searching solution I've come across. Unlike most of the other options, the search daemon is incredibly stable, and the index never seems to become corrupted (I'm running it in a relatively high load production environment with absolutely zero trouble so far). Also, since Ultrasphinx doesn't rely on AR callbacks for indexing, your application isn't quite as coupled to your search daemon; if it dies, search functionality will break, but the rest of your app will still function. It's not without problems, and complex indexing can be trickier, but Ultrasphinx's stability and performance make the choice a no-brainer.

resource_controller 0.2: maintenance release - no more edge_compatible branch 2

Posted by james
on Friday, February 15

This is mainly a maintenance release.

  • The helpers have been broken out in to four separate files internally, to help with managing the deep nesting branch.
  • There have also been a few little refactorings in preparation for some new features to come shortly.
  • The biggest thing to note for users is that there is no longer an edge_compatible branch. Since rails 1.2.6 generates the same style of named routes as 2.0.2 (edit_tag_photo_path instead of tag_edit_photo_path), there is no longer a need to continue two separate streams of development (yay)!.
  • The generator has been updated to spit out the right filenames for templates (rhtml/haml vs html.erb/html.haml), and old-style migrations (t.column instead of t.string) for any users still stuck on 1.2.6, so the transition shouldn't be a problem.

Get It!

You can get the new version by exporting from svn:

svn export http://svn.jamesgolick.com/resource_controller/tags/stable vendor/plugins/resource_controller

Or, if you're using piston, you may need to switch to the new url if you were previously on edge compatible (this is untested, so it may be slightly wrong).

  piston switch http://svn.jamesgolick.com/resource_controller/tags/stable
  piston update

As always, everybody is encouraged to come join the discussion on the increasingly lively mailing list.

An Introduction to Association Proxy

Posted by james
on Sunday, February 10
@post.comments << Comment.new(:title => 'something')
Comment.find(:all,  :conditions => ['post_id = ? AND title like ?', @post.id, 'something'])
Comment.count(:all, :conditions => ['post_id = ?', @post.id])

There is an easier way to do all of that. So, if you're still manipulating ActiveRecord associations by hand, stop; there is a better way.

AssociationProxy

When you request a collection of associated models, ActiveRecord extends that array with several helpful methods. You can treat that array like a model class, except all of the methods are automatically scoped to the association's parent. It responds to methods like find, create, new, count, etc, and, it's called AssociationProxy.

So, what if we wanted to create a comment, scoped to our post model?

@post.comments.create(params[:comment])

Making use of AssociationProxy, the code reads better, and requires fewer keystrokes. We can also use AssociationProxy to find elements in our collection.

@post.comments.find(:all, :conditions => {:title => 'title we are looking for'})

We can even use dynamic, attribute-based finders through AssociationProxy.

@post.comments.find_by_title 'title we are looking for'

It Gets Better

Since assoc proxy responds to many of the same methods as a model class, the two can be interchanged for many operations. In resource_controller, for example, nested controllers depend heavily on association proxies. Let me show you what I mean.

If we had, for example, a typical show action.

def show
  @comment = Comment.find(params[:id])
end

With very little effort, we can allow comments to be shown nested, or unnested.

def show
  @comment = model.find(params[:id])
end

private
  def model
    params[:post_id] ? Post.find(params[:post_id]).comments : Comment
  end

Since we know that AssociationProxy responds to other model class methods, we can base our create method on this technique, too.

def show
  @comment = model.find(params[:id])
end

def create
  @comment = model.new(params[:comment])
  if @comment.save
    redirect_to @comment
  else
    render :action => 'new'
  end
end

private
  def model
    params[:post_id] ? Post.find(params[:post_id]).comments : Comment
  end

In fact, this is nearly exactly how resource_controller is built.

So, association proxy is useful in simple and complex situations alike. It can save you a lot of code, and increase readability. It's a win-win really.

Ruby Reddit 0

Posted by james
on Friday, January 25

When reddit announced that they were going to be offering users the ability to create their own reddits, I sent them an email asking for a beta account. Guess what? They gave me one!

So, rubyists everywhere, here it is: our very own ruby reddit!

A Response to Dreamhost 1

Posted by james
on Saturday, January 12

Dreamhost sure stirred things up with their article rant about rails deployment and performance issues. Their complaint is roughly that they are having to work too hard to support rails; it needs performance and deployment improvements. Dreamhost voiced all of these complaints on the company's blog.

As they put it...

What I do have personal knowledge of is how difficult it can be to get a Rails application up and running and to keep it running. DreamHost has over 10 years of experience running applications in most of the most popular web programming frameworks and Rails has and continues to be one of the most frustrating.
...the solution from the Rails community itself was quite honestly, stupid.
That suggestion shows either a complete lack of understanding of how web hosting works, or an utter disregard for the real world.
Ruby on Rails needs to be a helluva lot faster.
Ruby on Rails needs to more or less work in ANY environment.

Forgive me for chopping up their arguments. If you have the time, please read the article. These quotes don't quite do it justice; they serve merely to provide the tone of the piece.

Frankly, I think DHH responded well: rails core team is there to serve their own purposes. Ruby on Rails doesn't need to do anything, despite what a Dreamhost blogger might suggest. The people hacking on rails core don't target platforms like Dreamhost's. So, in fact, rails is not designed to run on shared hosting.

It's not just shared hosting, either. Dreamhost is trying to support rails on massively oversold servers. Configuring rails in a shared environment (particularly under apache) is difficult; configuring, and maintaining their servers to support rails probably costs Dreamhost a lot of money. Rails doesn't perform well in that environment, which probably costs them even more money in support calls from frustrated customers. So, their profit margins on rails service are likely narrowing.

Why is that the rails community's problem to solve? Dreamhost has a business challenge. Rails deployment isn't perfect — it has more than its fair share of problems; but, there are plenty of rails apps running just fine in production. This blog, a rails app, runs on a 20$ per month plan from slicehost. The identical app barely ran at Dreamhost. It's anecdotal evidence, sure, but the point is that rails deployment (at least on this scale) is far from impossible. Dreamhost just can't seem to squeeze it in to their oversold offerings. And, they want somebody else to fix the problem for them.

There are plenty of shared hosting offerings available that support rails nicely. Those companies don't seem to be having any trouble creating an environment that works. None of them have published angry articles pointing the finger at "the rails community". But, I digress.

If it were an individual complaining — a noobie who was having a tough time deploying his rails app on a server he could afford — then perhaps there would be a reason to look at this differently. But, Dreamhost is a corporation; they are simply "...looking to capitalize on a framework that's driving a lot of demand...", to borrow some words from DHH.

If this had been a PC manufacturer, complaining loudly (and rudely) that linux doesn't run on cheap enough hardware for them to sell PCs for 50$/each... If Linksys had posted a rant on their company blog, complaining that linux needed to be faster, because their routers weren't performing well enough... If IBM had posted a rant on their company blog, complaining that the linux community showed "...either a complete lack of understanding of how web hosting works, or an utter disregard for the real world."... Everybody would have said the same thing: so, fix it.

Plenty of businesses are capitalizing on open source technologies. But, part of the deal in the real world is that when something doesn't work right, you might have to fix it yourself; but you can, and that's part of what's so great about open source. Engine Yard just hired the entire rubinius team (and some people to hack merb, so I hear). Linux kernel developments are facilitated in large part by corporate sponsorship, bounties, etc. The system works, because companies can solve their own problems, while leveraging the work of countless others. That's how it works in the real world.

An Introduction to Ruby's Enumerable Module 4

Posted by james
on Sunday, January 06

If you're still writing for loops, stop; there's a better way. Exactly why merits some examples.

[1, 2, 3].each { |number| puts number }

That's a pretty straightforward one. It says: with each element in this array, execute the given block. But, what if we wanted to do something more complicated, like determine whether there were any even numbers in the array?

[1, 2, 3].any? { |number| number % 2 == 0 } # => true
[1, 3, 5].any? { |number| number % 2 == 0 } # => false

The any? method says: are there any elements in the array for which this block evaluates to true? We might also want to know whether all? of the elements were even.

[2, 4, 6].all? { |number| number % 2 == 0 } # => true
[1, 2, 3].all? { |number| number % 2 == 0 } # => false

We can see that making good use of Enumerable methods produces highly readable code. Before we move on, there are a couple more methods that are worth demonstrating.

We've already seen how we can easily look inside of an array, using any?, and all?. Conveniently, Enumerable provides similar methods for extracting objects. What if we wanted, for example, to retrieve all of the even numbers.

[1, 2, 3, 4].select { |number| number % 2 == 0 } # => [2, 4]

...or, the first even number...

[1, 2, 3, 4].detect { |number| number % 2 == 0 } # => 2

Let's pick up the pace.

Putting the Pieces Together

If I was writing some blogging software, I might want to display recent activity in the administration area. With a big array of notification objects, I can use select to easily divide them up for display, based on recency.

today     = @notifications.select { |notification| notification.created_at > 1.day.ago.to_date }
yesterday = @notifications.select { |notification| notification.created_at < 1.day.ago.to_date && notification.created_at > 2.days.ago.to_date }
earlier   = @notifications.select { |notification| notification.created_at < 2.days.ago.to_date }

I'm actually doing something very similar to this in an app I'm working on. Since I love fat models, I created some helper methods on AR::Base. They look (something) like this:

class ActiveRecord::Base
  def today?
    created_at > 1.day.ago.to_date
  end
  
  def yesterday?
    created_at < 1.day.ago.to_date && created_at > 2.days.ago.to_date
  end
  
  def earlier?
    created_at < 2.days.ago.to_date
  end
end

So, now, we can greatly simplify our use of the select method, making it far clearer what we're up to.

today     = @notifications.select(&:today?)
yesterday = @notifications.select(&:yesterday?)
earlier   = @notifications.select(&:earlier?)

...wait, what? The ampersand operator, as described by the PickAxe book:

If the last argument to a method is preceded by an ampersand, Ruby assumes that it is a Proc object. It removes it from the parameter list, converts the Proc object into a block, and associates it with the method

But, a symbol isn't a Proc object. Luckily, ruby calls to_proc on the object to the right of the ampersand, in order to try and convert it to one. Symbol#to_proc is defined in ActiveSupport; it creates a block that calls the method named by the symbol on the object passed to it. So, for the above examples, the blocks send today?, yesterday?, or earlier?, respectively, to the each object that is yielded. Written out by hand, the expanded blocks would look like this:

today     = @notifications.select { |notification| notification.today?  }
yesterday = @notifications.select { |notification| notification.yesterday? }
earlier   = @notifications.select { |notification| notification.earlier? }

For additional to_proc coolness, it's worth mentioning Reg Braithwaite's String#to_proc (or get the code). I won't go on any further about that today, though.

Let's look at one last example — this time from real software. In order to add support for polymorphic, deep nesting in resource_controller, I had to write an algorithm that would determine which of the possible sets of parent resources were present at the invocation of a controller action. This is done by testing to see which of the params (parent_type_id) are present. So, if I say belongs_to [:project, :message], [:project, :ticket] in my comments controller, I need to determine: for which of those two possible sets are both necessary params present? Since belongs_to is just a simple accessor, the algorithm is easy to write.

belongs_to.detect { |set| set.all? { |parent_type| params["#{parent_type}_id".to_sym].nil? } }

Recap

Enumerable's biggest advantage over the for loop, as we've seen, is clarity. Using Enumerable's looping power tools, like any?, all?, and friends makes your code read semantically. Especially combined with Symbol#to_proc, code "...seems to describe what I want done instead of how I want the computer to do it.", to quote Raganwald.

Finally, in order to take your Enumerable usage to the next level, there are a few more methods you should have in your toolbox.

I'd also recommend keeping the Enumerable, and Array documentation bookmarked, so that you can quickly refer to them. As a bonus, most of these methods exist in prototype.js, too; just take a look at the documentation.

Tuesday Trip to Toronto

Posted by james
on Sunday, December 16

I'm headed to Toronto for the next couple of weeks — leaving Tuesday as you might have guessed from the title. If any Torontonians want to meet up for a coffee (yes, I am a coffeegeek), a beer (but, really, preferably a coffee), or a hack session (preferably including coffee), shoot me an email (my address is listed top-right).

Introducing AttributeFu: Multi-Model Forms Made Easy 29

Posted by james
on Wednesday, December 05

Ever needed to create a form that works with two models? If you have, you know that it's a major pain. It really seems like it should be easier, doesn't it? Powerful, SQL-free associations join our models; helpers build our forms in fewer keystrokes than it takes to call somebody a fanboy in a digg comment. But, as soon as you want to put the pieces together — as soon as you seek multi-model form bliss, the whole system falls apart.

Ryan Bates has popularized a few recipes for cooking up just such a dish (we all owe that man a debt of gratitude, don't we?). Great solutions (thanks Ryan!). There are only two flaws to speak of.

  • AJAX-friendliness: What is this - web1? Maybe we should all build our sites in frames, and pepper them with blinking text.
  • It's not a plugin: You mean I have to write code!? I thought rails was going to wash my dishes, walk my dog, and build my websites, while I sat outside on the porch smoking cigars, praying to DHH.

Being the compulsive pluginizer that I am, I simply couldn't resist this great opportunity.

attribute_fu

attribute_fu makes multi-model forms so easy you'll be telling all of your friends about it. Unless your friends are serious geeks, though, most of them will probably hang up on you. So, I recommend resisting that urge, unless you're looking for a good bedtime story.

To get started, enable attributes on your association (only has_many for now).

class Project < ActiveRecord::Base
  has_many :tasks, :attributes => true
end

Your model will then respond to task_attributes, which accepts a hash; the format of the which is a little bit different from what Ryan describes in his tutorials. Here's an example.

@project.task_attributes => { task_one.id => {:title => "Wash the dishes"},
                              task_two.id => {:title => "Go grocery shopping"},
                              :new => {
                                "0" => {:title => "Write a blog post about AttributeFu"}
                              }}

Follow the rules, though, and attribute_fu will shield you from the ugly details.

Form Helpers

The plugin comes with a set of conventions (IM IN UR PROJECT, NAMIN UR PARTIALZ). Follow them, and building associated forms is as easy as it should be. Take note of the partial's name, and the conspicuously absent fields_for call.

## _task.html.erb
<p class="task">
  <label for="task_title">Title:</label><br/>
  <%= f.text_field :title %>
</p>

Rendering one or more of the above forms is just one call away.

## _form.html.erb
<div id="tasks">
  <%= f.render_associated_form(@project.tasks) %>
</div>

You may want to add a few blank tasks to the bottom of your form — formerly requiring an extra line in your controller.

<%= f.render_associated_form(@project.tasks, :new => 3) %>

This being web2.0, removing elements from the form with Javascript (but your boss calls it AJAX) is a must.

## _task.html.erb
<p class="task">
  <label>Task</label><br/>
  <%= f.text_field :title %>
  <%= f.remove_link "remove" %>
</p>

Adding elements to the form via Javascript doesn't require any heavy lifting either.

## _form.html.erb
<%= f.add_associated_link('Add Task', @project.tasks.build) %>

It's that easy.

Last but not least (okay, maybe least), if you're one of those people who just has to be different - who absolutely refuses to follow the rules, you can still use attribute_fu (I guess...). See the RDoc for how.

Plugins, Plugins, Get Your Plugins

$ piston import http://svn.jamesgolick.com/attribute_fu/tags/stable vendor/plugins/attribute_fu

Check out the RDoc for more details.

Come join the discussion on the mailing list.

Noisy Backtraces Got You Down? 3

Posted by james
on Monday, December 03

Bothered by all the noise in my tests' backtraces, I was thrilled when I first saw a thread on the shoulda mailing list with some discussion around making them a little bit easier on the eyes. Assuming that creating such a filter would be a long and tedious process of monkey-patching test/unit, I forgot about the idea, assuming the job better left for somebody with more time to spare than myself.

When Dan Croak revived the thread with some sample code, cooked up at a Boston.rb hackfest, it occurred to me that the job was far more manageable than I had originally conceived. I quickly fired Dan an email asking whether he'd be interested in a pluginization of their concept. With a resounding yes! from Dan, we set off to create quiet_stacktracebacktrace.

The rest of this post is cross-posted on GIANT ROBOTS

90% of this typical backtrace will not help you hunt and kill bad code:

1) Failure:
test: logged in on get to index should only show projects for the user's account. (ProjectsControllerTest)
[/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/test/unit/assertions.rb:48:in `assert_block'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/test/unit/assertions.rb:500:in `_wrap_assertion'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/test/unit/assertions.rb:46:in `assert_block'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/test/unit/assertions.rb:63:in `assert'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/test/unit/assertions.rb:495:in `_wrap_assertion'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/test/unit/assertions.rb:61:in `assert'
test/functional/projects_controller_test.rb:31:in `__bind_1196527660_342195'
/Users/james/Documents/railsApps/projects/vendor/plugins/shoulda/lib/shoulda/context.rb:98:in `call'
/Users/james/Documents/railsApps/projects/vendor/plugins/shoulda/lib/shoulda/context.rb:98:in `test: logged in on get to index should only show projects for the user's account. '
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/test/unit/testcase.rb:78:in `__send__'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/test/unit/testcase.rb:78:in `run'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/test/unit/testsuite.rb:34:in `run'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/test/unit/testsuite.rb:33:in `each'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/test/unit/testsuite.rb:33:in `run'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/test/unit/testsuite.rb:34:in `run'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/test/unit/testsuite.rb:33:in `each'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/test/unit/testsuite.rb:33:in `run'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/test/unit/ui/testrunnermediator.rb:46:in `run_suite'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/test/unit/ui/console/testrunner.rb:67:in `start_mediator'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/test/unit/ui/console/testrunner.rb:41:in `start'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/test/unit/ui/testrunnerutilities.rb:29:in `run'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/test/unit/autorunner.rb:216:in `run'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/test/unit/autorunner.rb:12:in `run'
/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/test/unit.rb:278
test/functional/projects_controller_test.rb:36]:
one or more projects shown does not belong to the current user's account.
&lt;false&gt; is not true.

Noisy backtraces must be ruthlessly silenced like political dissidents in Stalinist Russia. This much is clear.

Quiet Backtrace

Install the gem:

  sudo gem install quietbacktrace

Require quietbacktrace:

## test_helper.rb
require 'quietbacktrace'

Run your Test::Unit tests:

1) Failure:
test: logged in on get to index should only show projects for the user's account. (ProjectsControllerTest)
[test/functional/projects_controller_test.rb:31
vendor/plugins/shoulda/lib/shoulda/context.rb:98
vendor/plugins/shoulda/lib/shoulda/context.rb:98
test/functional/projects_controller_test.rb:36]:
one or more projects shown does not belong to the current user's account.
&lt;false&gt; is not true.

Ooh la la! Now we’re cooking with gas. However, those shoulda-related lines are cluttering an otherwise perfect backtrace. Luckily, Quiet Backtrace is designed to be extended by calling two types of blocks that yield one line of the backtrace at a time.

Silencers and filters

  1. Silencers let you specify conditions that, if true, will remove the line from the backtrace.
  2. Filters let you use Ruby’s succulent set of String methods to modify a line by slicing and stripping and chomping away at anything you deem ugly and unnecessary. (such as the :in `__bind_1196527660_342195’ in the original example)

Say you want to remove Shoulda-related lines… you create a new silencer and add it the Array of backtrace_silencers:

class Test::Unit::TestCase
  self.new_backtrace_silencer :shoulda do |line| 
    line.include? 'vendor/plugins/shoulda'
  end
  
  self.backtrace_silencers << :shoulda
end

Re-run your tests and bask in the sweet sounds of silence:

1) Failure:
test: logged in on get to index should only show projects for the user's account. (ProjectsControllerTest)
[test/functional/projects_controller_test.rb:31
test/functional/projects_controller_test.rb:36]:
one or more projects shown does not belong to the current user's account.
&lt;false&gt; is not true.

Exquisitely sparse. Quiet Backtrace clears distractions from the “getting to green” TDD process like a Buddhist monk keeping his mind clear during meditation.

Getting noisy again

On occasion, you’ll want to see the noisy backtrace. Easy:

class Test::Unit::TestCase
  self.quiet_backtrace = false
end

You can set Test::Unit::TestCase.quiet_backtrace to true or false at any level in your Test::Unit code. Stick it in your test_helper.rb file or get noisy in an individual file or test. More flex than a rubber band.

Using Quiet Bactrace with Rails

After you have installed the gem on your local machine, I recommend using the excellent gemsonrails plugin to freeze it to your vendor/gems directory and automatically add it to your load path.

Install gemsonrails and add it your Rails app if you don’t already have it:

gem install gemsonrails
cd rails-app-folder
gemsonrails

Then freeze quietbacktrace:

rake gems:freeze GEM=quietbacktrace

Quiet Backtrace will now work with your tests, but because this gem is meant to work on any Ruby project with Test::Unit, it does turn any Rails-specific silencers or filters on by default. However, there is one of each, ready to be switched on, that remove the most dastardly lines.

Add these lines to your /test/test_helper.rb file to get perfectly clean Rails backtraces:

class Test::Unit::TestCase
  self.backtrace_silencers << :rails_vendor
  self.backtrace_filters   << :rails_root
end

Ongoing development

Bug reports and patches are welcome at RubyForge. Talk about it on the mailing list. It’s is a safe place. A place where we can feel free sharing our feelings. A nest in a tree of trust and understanding.

resource_controller 0.1.6: API Enhancements 2

Posted by james
on Sunday, November 25

It's been a while since the last r_c release; I have been busy. I'm still busy, so this isn't a huge release, but these changes have been in SVN for a while, so I thought I'd share them.

There was a cool thread over at RefactorMyCode.com where we discussed some potential API improvements for r_c (it's still open - come share your ideas!). Here's what made it in to this release...

respond_to

respond_to now accepts symbols, and has a few extra names. All of these examples have the same effect...

create.response :html do |format|
  format.js
end

create.respond_to :html, :js

create.responds_to do |format|
  format.html
  format.js
end

create.wants.html
create.wants.js

Callbacks Accept Symbols

I've always liked that the filters, and callbacks in rails accept the name of a method or a block. Now, all the callbacks in resource_controller accept them both, too!

create.before { @post.author = current_user }

create.before :set_user

private
  def set_user
    @post.author = current_user
  end

Configuration-Style Syntax for model_name, route_name, and object_name

Now, rather than overriding a method to provide alternate model, object or route names, you can set them through a configuration-style interface (though, you can still override the methods if you prefer).

model_name  :post_tag
route_name  :tag
object_name :tag

Note: resource_name is gone as of this release. If you were overriding that in your controllers, you'll have to use a combination of the other helpers to get the same effect.

LOTS More Helper Methods Exposed

If you've had a problem with r_c helper methods not being exposed in your views, it should be solved now. Just about every possible r_c method is now available in your views!

Get It!

1.2.3+ Compatible

svn export http://svn.jamesgolick.com/resource_controller/tags/stable vendor/plugins/resource_controller

Edge/Rails 2.0 Compatible

svn export http://svn.jamesgolick.com/resource_controller/tags/edge_compatible/stable vendor/plugins/resource_controller

If you're looking for older versions, try browsing http://svn.jamesgolick.com/resource_controller/tags.

For more info, see the resource_controller section of my blog.

Come join the discussion on the mailing list.

Looking for Rails Work?

Posted by james
on Friday, November 09

I've posted this in a few places, and gotten some great responses. I don't know why I didn't think of posting it here earlier. (Of course, I give preference to anybody who reads my blog :)). Our little job blurb looks like this:

GiraffeSoft is looking to add an awesome rails developer to our team. If you live and breathe ruby & rails, are test(or spec)-obsessed (this one is absolutely 100% critical), want to work on a team that is actually agile, and get paid to work on our open-source projects (and maybe some of your own), we want to talk to you. We work from home, so, self-motivation, and management skills are critical. Where you are, and when you do the work, however, are not, so long as things get done on time. It will start out as a contract position, with the potential to become full-time down the line. If you're interested, send a short note, resume, and code samples to noticeme at giraffesoft dot ca.

Looking forward to hearing from you.

resource_controller: Redesign My API at refactormycode.com 1

Posted by james
on Monday, November 05

Now that I've gotten a few maintenance releases out the door, I'm starting to feel like r_c's feature set is pretty solid; I think most people's needs should be covered (still a few things left to include, but we're getting there). The next step in improving resource_controller is evolving the API. What better place to hold the discussion than Marc-André's refactormycode.com?

First Refactoring designing

In the comments of the last release post, Luigi Montanez brought up an issue that's been bugging me since I first released the plugin. Currently, some customizations (before/after callbacks, response blocks, etc) are made with a "DSL"-style API. Some customizations (like alternate model names), however, are made by overriding helper methods. It's not that I don't like overriding methods, but I find the API a bit inconsistent in that way.

Currently, providing an alternate model name looks like this...

class PostsController < ResourceController::Base
  private
    def model_name
      'blog_post'
    end
end

It would be really neat, though, if it was possible to set the model_name the same way you set the flash...

class PostsController < ResourceController::Base
  model_name :blog_post
end

Please, come join the discussion at RmC, and put some of your ideas on the table for resource_controller's API.

resource_controller update: Bug Fix, Generators, and Google Group! 1

Posted by james
on Thursday, October 25

A week after I released resource_controller, there are a few issues that needed to be resolved.

Specifying Actions

Hampton pointed out the fact that I hadn't mentioned any way of specifying which actions a controller should respond to. You might want to have a controller that only responds to show, and index, for example. It was possible (and documented - see the RDocs), but somewhat broken. This release fixes that bug.

You can either provide a list of actions, or use :all, :except => syntax.

class PostsController < ResourceController::Base
  actions :show, :index
  ## .. or ..
  actions :all, :except => :index
end

Generators

resource_controller comes with a scaffold_resource generator now. In the 1.2.3+ stream, it will override the built-in version. I figure - if you're using resource_controller, you probably won't have much need for the default one.

It's pretty clever, too. If you have Haml installed, it generates Haml. If you have Shoulda installed, it spits out functional tests that make use of Shoulda's wonderful should_be_restful macro (note: I'm not necessarily opposed to doing the same for RSpec, if there's demand - let me know). Otherwise, it spits out erb, and the same functional tests as the generator included with rails.

The syntax is exactly the same as the built-in generator.

script/generate scaffold_resource post title:string body:text

Get It

1.2.3+ Compatible

svn export http://svn.jamesgolick.com/resource_controller/tags/stable vendor/plugins/resource_controller

Edge/Rails 2.0 Compatible

svn export http://svn.jamesgolick.com/resource_controller/tags/edge_compatible/stable vendor/plugins/resource_controller

If you're looking for older versions, try browsing http://svn.jamesgolick.com/resource_controller/tags.

For more info, see the release announcement.

Community

I also wanted to announce that I've created a google group for resource_controller.

Come join it, and discuss what can be done to make r_c even better!

Introducing resource_controller: Focus on what makes your controller special. 18

Posted by james
on Friday, October 19

If you're using rails, and you've kept up with what's happening in the community, and in the framework, you're probably writing RESTful apps. You may have noticed, like I did, that most of your controllers follow the same basic pattern - the one that the scaffold_resource (just scaffold in edge/2.0) generator spits out.

I wanted a great way to hide that pattern, and describe the unique features of my controllers through a more domain-specific API. Since I tend to write a lot of nested, and polymorphic resources, I also wanted to DRY up all of that code, and make generating urls a lot more intuitive. Finally, I wanted all of that code to be well-tested, so that I could count on hacking it up when I needed new features, without breaking things.

So, I created it, and here it is.

resource_controller

A basic, out of the generator resource just requires inheriting from ResourceController::Base.
class PostsController < ResourceController::Base  
end

API

Before/after callbacks, responses, and flash messages are managed through an extremely flexible API. I tried my best, here, to make syntax succinct when your needs are succinct, and allow API calls to be organized in an intuitive way. What I came up with was a scoping system, where calls to the various API methods can be chained, or scoped with a block.

Take the create action, for example. You might want to add a before block, to assign the author attribute of your post model to the current user.
class PostsController < ResourceController::Base
  create.before do
    @post.author << current_user
  end
end
As your application matures, you may want to add an RJS response to your create method, for some AJAXy goodness. Easy.
class PostsController < ResourceController::Base
  create.before do
    @post.author << current_user
    
    response do |wants|
      wants.html
      wants.js
    end
  end
end
Actually, there's even a shortcut for adding responses. You can omit the response block, if you just want to add responses to what's already provided by resource_controller (just HTML) or has been added elsewhere.
class PostsController < ResourceController::Base
  create do
    before do
      @post.author << current_user
    end
    
    wants.js
  end
end
Since I commonly use the same RJS template for several actions, I've found this short-form really handy, because I can use a one-liner to add the same response to several actions.
class PostsController < ResourceController::Base
  [create, update].each { |action| action.wants.js {render :template => "post.rjs"} }
end
Oh yeah, and you can change the flash too.
class PostsController < ResourceController::Base
  create.flash "Wow! RESTful controllers are so easy with resource_controller!"
end
For more API details, and examples, see the RDocs.

Helpers

Helpers are used internally to manage objects, generate urls, and manage parent resource associations.

If you want to customize certain controller behaviour, like member-object, and collection fetching, overriding helper methods is all it takes.

Note:For certain resource_controller functionality to work properly, user-defined helpers must make use of the other r_c helpers. The following examples do not follow that convention for clarity purposes - see the docs for more details.

If you wanted, for example, to use a permalink for your post, you'd need to alter the way that posts are fetched. Just override the object method.
class PostsController < ResourceController::Base
  private
    def object
      @object ||= Post.find_by_permalink(param[:id])
    end
end
You'll probably also want to add pagination to your index method. In the same way we altered member object fetching, we can change collection fetching behavior.
class PostsController < ResourceController::Base
  private
    def collection
      @collection ||= Post.find(:all, :page => {:size => 10, :current => params[:page]})
    end
end
Details and examples in the RDocs.

Namespaced Resources

...are handled automatically, and any namespaces are available, symbolized, in array form from the namespaces helper method.

Nested Resources

Again, handled automatically. This can be a real pain, and it's a lot easier with r_c. With an ActiveRecord-like syntax, just say belongs_to :model, and resource_controller takes care of the associations for you.
class CommentsController < ResourceController::Base
  belongs_to :post
end

Polymorphic Resources

This is a concept that can be found in a lot of my apps. Prior to resource_controller, it was a real pain on various levels. I solved some of my problems with urligence, and took things the rest of the way with resource_controller. It really does pretty much everything for you.

Just use the belongs_to syntax in your controller, and r_c infers whichever association (if any) is present when an action is called. The arguments passed to belongs_to are single possible parents; there is no support for deeply nested resources (although, I'm not necessarily opposed to adding it, if there is demandwe).
class CommentsController < ResourceController::Base
  belongs_to :post, :product
end
In the above example, the controller will automatically infer the presence of either a parent Post, or Product, and scope all the comments to that parent. The controller will also respond without a parent if you have that in your routes.

Thanks to urligence, generating urls in your polymorphic controller's views is really easy. The object_url, and collection_url helpers will maintain your parent resource's scope automatically.
# /posts/1/comments
object_url          # => /posts/1/comments/#{@comment.to_param}
object_url(comment) # => /posts/1/comments/#{comment.to_param}
edit_object_url     # => /posts/1/comments/#{@comment.to_param}/edit
collection_url      # => /posts/1/comments

# /products/1/comments
object_url          # => /products/1/comments/#{@comment.to_param}
object_url(comment) # => /products/1/comments/#{comment.to_param}
edit_object_url     # => /products/1/comments/#{@comment.to_param}/edit
collection_url      # => /products/1/comments

# /comments
object_url          # => /comments/#{@comment.to_param}
object_url(comment) # => /comments/#{comment.to_param}
edit_object_url     # => /comments/#{@comment.to_param}/edit
collection_url      # => /comments
More in the RDocs.

Getting it

resource_controller is available under the MIT License.

It is currently available in two streams:

1.2.3+ Compatible

Install it:

svn export http://svn.jamesgolick.com/resource_controller/tags/stable vendor/plugins/resource_controller

SVN (stable): http://svn.jamesgolick.com/resource_controller/tags/stable

SVN (ongoing): http://svn.jamesgolick.com/resource_controller/trunk

Note: If you want to run the tests, cd in to the test directory, and type rake test.

Edge/Rails 2.0 Compatible

Install it:

svn export http://svn.jamesgolick.com/resource_controller/tags/edge_compatible/stable vendor/plugins/resource_controller

SVN (stable): http://svn.jamesgolick.com/resource_controller/tags/edge_compatible/stable

SVN (ongoing): http://svn.jamesgolick.com/resource_controller/branches/edge_compatible

Note: If you want to run the tests in the edge-compatible version, cd in to the test directory, and type rake rails:freeze:edge. I didn't want people to have to download all of edge rails just to get the plugin.

Also, check out the rdoc

Feedback

Drop me a line and let me know what you think. I'd love to hear your suggestions for API or implementation improvements, or anything else!