The Best of Cheat

There’s some real gold in Cheat. Like, nuggets. Ever since we got tagged by mustache it’s been just wild.

Ever wonder how to make hot chocolate? We’ve got a tasty recipe.

Need some night time reading to go along with your cocoa? May I suggest Into the Code?

The DataMapper sheet is superb in its brevity: has_many :class => “ClassName”

(By the way, this post has been highly optimized with the SEO sheet.)

The Foo sheet is the foo sheet is the foo sheet.

Hot chocolate not hitting the spot? How about some Gazpacho soup?

If you’re a designer, you’ll love this: lipsum. All of it.

Ever wanted to talk like you’re in the military, or own a walkie talkie? Now you can with this handy military alphabet.

Oh, an entire Cheat Emacs mode. Remember: Never quit Emacs.

I’m sure you’ve been wondering, so here it is: every TLD ever.

In the spirit of three letter acronyms, there’s also the GTD sheet.

One of my favorites, the Runescape sheet:

Hot keys

1 selling
2 buing
3 buy
4 noob

For the serious kids in the room, there’s the Unix permissions and Unix redirection sheets. Oh, and the notify trick then the complete Firebug sheet.

Finally, the nonsense sheet imparts on us some non-nonsense wisdom: “and that’s why cheat also is an anagram for teach!”

Who Needs an API?

GitHub is a pretty fun site to work on, I’m not gonna lie. On more than one occasion, we thought it would be pretty cool to setup a service allowing public projects to receive donations.

Sounds like an Itch

Pledgie being the venerable service it is, I decided one night it couldn’t possibly be that hard to integrate it with GitHub. It’s a pretty standard Rails site with some simple forms to make the magic happen (eg. setup a donation page).

They don’t have an API (yet), but in this day and age, you really don’t need one if you procure the proper tools.

Enter Mechanize. You can do all I’m about to describe with just Net::HTTP, but seriously, who wants to do that?

Start Scratching

Step 0: Drive girlfriend to airport, buy a case of Anchor Steam, and turn off the Xbox.

Step 1: Sign up for a Pledgie account, cause GitHub’s a regular user after all.

Step 2: Write the interface on GitHub to accept the user’s Paypal address.

Step 3: Figure out the form fields I should be filling out to login and create a new pledge on Pledgie.

Step 4: Write the Mechanize code:

def pledgify(email)
  agent = WWW::Mechanize.new
  page  = agent.get('http://pledgie.org/accounts/login')
  form  = page.forms[1]
  form['account[login]']    = GitHub::PledgieUser
  form['account[password]'] = GitHub::PledgiePass
  page  = agent.submit(form)

  link  = page.links.text(/Create A Campaign/)
  page  = agent.click(link)
  form  = page.forms[1]
  form['campaign[title]']        = "FooBarz" 
  form['campaign[paypal]']       = email
  form['campaign[description]']  = "The best project evar!" 
  form['campaign[end_date(1i)]'] = 10.years.from_now.year.to_s
  page = agent.submit(form, form.buttons.last)
  update_attribute(:pledgie, page.uri.to_s[/\d+/])
end

Step 5: Take the pledgie attribute we just grabbed and put a cool badge in their repository’s detail box.

Step 6: Watch the millions pour in for GitHub’s hard-working open source committers.

The obvious caveat here is that it relies on Pledgie not drastically changing the structure of its HTML, but it’s incredibly satisfying to throw something together like this in such a short amount of time.

PS. If you’re on GitHub and wondering how you missed the original announcement, the post is here: http://github.com/blog/57-getting-paid-the-open-source-way

Huba Huba

Surely, by now, you’ve heard of GitHub. (Don’t call me surely.) It’s totally the Indiana Jones of repository hosts. Feel free to stalk Pj and I to see what we’re up to. Blogging be damned!

If you haven’t heard of GitHub, there are tons of posts explaining the hows and whys of its awesomeness. This is not one of the posts. Instead, I want to quickly share some oft overlooked but tasty GitHub tidbits.

The GitHub Gem

GitHub supports gems, which is cool, and also means we can install the official GitHub gem with ease:

$ gem install defunkt-github --source=http://gems.github.com/

Great. At this point, possibilities become reality. The gem has a few cool features, all of which are displayed via $ github -h, but the best feature by far is pull.

Here’s how it works: I have my fork of technoweenie’s exception_logger. I’ve cloned it and am sitting in the working directory. Suddenly I discover ryanb (of RailsCasts fame) has sent me a pull request. Open source’s finest moment.

So, I type $ github pull ryanb. A remote is added, a new branch is created, and Ryan’s changes are pulled into that branch. (It’s probably named ryanb/master.) I then review the changes and, if they rock, either rebase or merge them back into master. Like this:

$ git checkout master
$ git merge ryanb/master
$ git push

Already reviewed the changes on the web and know they’re legit? Just $ github pull –merge ryanb. This’ll grab the changes and merge them into master for you. Oh, right, you can also specify a branch. The assumption is master, but you know what they say about assumptions: you’re a jerk.

Thus: $ github pull—merge ryanb weird_branch

And just like that, GitHub pull requests are no longer a pain in the ass.

But really, this is just start. Please please please fork the gem and add awesome features. github clone, anyone?

Keyboard Shortcuts

Let’s say you want to peep some Rails changes. In classic vi style, j and k navigate between changes. c, t, and p lead you to the selected change’s commit, tree, or parent. h and l navigate between pages.

In fact, h and l will always go back and forward on any paginated page. We’ve written an evil twin which adds those hotkeys to any will_paginate call.

Also cool: s. If you’re logged in, hitting s will display and focus the search bar. I use this one the most.

Ranged Code Highlighting

Clicking on any line number then shift clicking a higher value line number selects a range. Super useful for code discussion. Discussion such as, “Dude, nonzero? is so awesome. Check it out!” (People definitely talk like that.)

Sweet.

Keep Your Dotfiles in Git

Okay, this isn’t strictly related to GitHub, but it’s good. You should be keeping your dotfiles in Git. Here are the steps to do so:

1. Create a ‘dotfiles’ directory.

2. Move your dotfiles to this new directory, sans leading dot. For example, to keep your ˜/.vimrc under version control, do this:

$ mv ˜/.vimrc ˜/Projects/dotfiles/vimrc. Rinse and repeat as necessary.

3. Add the following file to your dotfiles project, then run it: http://pastie.org/195036

4. Finally: $ git init && git add . Then: $ git commit -m ‘new dotfiles project’

You’re all set. Now your dotfiles that live in ˜ are symlinked to their counterparts in ˜/Projects/dotfiles. As a bonus, any time you git commit it will automatically git push. One of the entire points of keeping your files under version control is to back them up regularly.

I push to a private ‘dotfiles’ repo on GitHub. Others have created public repos. Your call.

For posterity’s sake, here’s my version controlled dotfiles:

bashrc
gitconfig
irbrc
railsrc
sake
screenrc
ssh
vim
vimrc

The best part?

The best part about GitHub, f’sure, is all the outrageously cool open source projects hosted on it. _why’s stuff, the jQuery plugins and mirrors, all the LISP projects, newer languages like Io, and of course the assorted GitHub-related projects.

Got something cool hosted there? Let us know.

Til next time, keep on hubbin’.

Update: GitHub Open Sores

I just created the GitHub account and did two things: created and pushed up some extractions from GitHub itself (like the jQuery hotkeys plugin) and also forked all the projects that are used on GitHub which we’re using on GitHub. Dude, meta. Anyway, have fun with that.

Sugary Adapters

Very recently, Simon Harris had an idea: nil? for Ambition. Tasty sugar.

Let’s figure out what it takes to make

User.select { |x| x.nil? }

behave just like

User.select { |x| x == nil }

in Ambition.

Short and Sweet

Simon’s approach was to modify Ambition directly to add support for nil?. While this is for sure ambitious, nil? is just another method. Not special. The adapter should decide what to do with it.

Easy. Here’s what we added to the ActiveRecord adapter’s Select translator:

def nil?(column)
  left = "#{owner.table_name}.#{quote_column_name column}" 
  negated? ? not_equal(left, nil) : self.==(left, nil)
end

See it in action on lines 84 to 87.

The tests, of course, can be found in types_test.

Chaining Stuffs

So, how does this work?

Every adapter’s Select translator has a special chained_call method. Ambition invokes chained_call and passes it an array of symbols when a chained.method.call is executed on itself.

In this case, the chain is m.name.nil?. Ambition knows that m is itself and ignores it, passing [ :name, :nil? ] to chained_call.

The ActiveRecord adapter’s chained_call method takes the passed array and, if it can find the second element, sends it the first element.

Basically:

# methods = [ :name, :nil? ]
if respond_to? methods[1]
  send(methods[1], methods.first)
end

Which translates to:

self.nil? :name

Cool. Adapters don’t need to set themselves up this way, but it works for ActiveRecord.

Notice: the ActiveRecord adapter doesn’t support anything more than chains two methods deep. It calls the second element and passes the first, ignoring the rest. Almost discouraging, but chin up – this is ActiveRecord specific. Ambition itself supports chains of arbitrary length, and your adapter can, too.

So array.include?, right?

The thing is, chained_call is only invoked when a chained method call is executed on an object Ambition owns.

User.select { |x| x.nil? }

In the above, Ambition owns the x. It’s self as far as the translator is concerned.

User.select { |x| [1,2,3].include? x.id }

Ambition does not own the array, only the x.id. So what happens?

Well, it’s the same as [1,2,3] == x.id to Ambition. The dude really doesn’t care. Any time there is something like left op right, Ambition calls op(left, right) on your translator.

Here’s an idea of the call:

include?([1,2,3], x.id)

Luckily x.id is translated for you prior to this. The call really looks more like:

include?([1,2,3], 'users.id')

The include? definition, then, on ActiveRecord’s translator is very straightforward:

def include?(left, right)
  left = left.map { |element| sanitize element }.join(', ')
  "#{right} IN (#{left})" 
end

Beautiful.

Join the Fun

While the Err twitter is great for general stuff, you should really hop on the Ambition mailing list if you want in on this action. Or just watch the project on GitHub.

Til next time.

Adapting Ambitiously

It’s funny, really. All these people walking around, talking about Ambition. “Oh, Ambition? Yeah, pretty cool.” “Ambition? Impedance mismatch.” “I’m happy with SQL-92 the way it is, thank you very much.” Outrageous!

I know, I know. We’ve said some crazy things ourselves. Like how we wanted Ambition to be a Rack for databases. Or, far fetched as it sounds, how we hoped Ambition could evolve into something akin to LINQ. But we’re done talking.

Today we want to show you some plain jane Ruby and how Ambition empowers it to leverage its inherent synergy. Er, I mean, we want to show you something kickass.

New School

This is what we’re used to:

>> SQL::User.select { |m| m.name == 'jon' && m.age == 21 }.to_s
=> "SELECT * FROM users WHERE users.name = 'jon' AND users.age = 21" 

This is what’s new:

>> LDAP::User.select { |m| m.name == 'jon' && m.age == 21 }.to_s
=> "(&(name=jon)(age=21))" 

Adapter School

As of 0.5, Ambition is no longer a SQL wrapper. Rather, it is an API for writing your own adapters. If you’d like to continue using the ActiveRecord version of Ambition, please install the ambitious-activerecord gem:

$ gem install ambitious-activerecord

Then, of course, use it:

require 'rubygems'
require 'ambition/adapters/active_record'

You can, too, install and use the older 0.3 series:

$ gem install ambition -v 0.3.2

Anyway, you heard right: Ambition now supports arbitrary data stores. Anything. Ambition adapters are just gems which depend on ambition and use its amazing API powers for the greater good.

What other adapters are underway? Oh, I dunno. How about ActiveLDAP, CouchDB, Facebook FQL, XPath, and DataMapper, to name a few. Why, just the other night the Boston.rb guys started working on a Sphinx adapter. Check it out with git:

$ git clone git://technicalpickles.com/ambitious_sphinx.git

We’ve also got two example gems: ambitious-activeldap and ambitious-activerecord.

There’s basic documentation for Ambition’s API over at ambition.rubyforge.org, which you are free to peruse as well.

We’re just starting out, but it’s not a bad start. Got an idea? Something crazy? We’re all about it. Jump on the mailing list or join #ambition on irc.freenode.net then chime in.

Dream School

Let’s take the youtube-g gem, as an example. There’s no finished adapter for it yet so we’re going to pretend.

Using the new Ambition, we could (behind the scenes) turn a query like this:

Videos.select { |video| video.user == 'liz' }

Into this:

YouTubeG::Client.new.videos_by(:user => 'liz')

We could turn a query like this:

Videos.select { |video| video.tags.include? 'apple' }

Into this:

YouTubeG::Client.new.videos_by(:tags => 'apple')

And we could even turn a query like this:

Videos.select do |video| 
  video.tags.include?('football') && !video.tags.include?('soccer')
end

Into this:

YouTubeG::Client.new.videos_by :tags => { 
    :include => ['football'], 
    :exclude => ['soccer'] 
  }

Not bad. It even comes with a generator, courtesy of Dr Nic, for spitting out an adapter scaffold:

$ ambition_adapter ambitious_youtube

Future School

Got an idea for an adapter, or some code to show? Throw it in the comments. You better believe we’ll keep the rest of you abreast of cool adapters, fancy tricks, and new features.

Want to get involved? Like I said, there’s always the list and the GitHub repo. Bugs can go to Lighthouse and you can clone my repo thisaways:

$ git clone git://github.com/defunkt/ambition.git

Ah, how far we’ve come. And how far we’ll go! Here’s to it.

Feeds for Free

And money for nothing. Or something like that? Sorry, Mark Knopfler. I’ll pay more attention next time.

Anyways, let us be painfully aware that we can get Atom feeds for free. Not as in beer or speech, but as in ‘zero lines of code.’ How? Microformats.

You and meFormats

Almost a year has past since we last spoke of microformats, and way more than a year since our first encounter. Seems like only yesterday.

Remember hAtom? It’s like Atom, only embedded into your existing content’s HTML pages. The mofo site references the following example:

A normal, typical blog post:

<div class="post">
  <h3>Megadeth Show Last Night</h3>
  <span class="subtitle">Posted by Chris on June 4th</span>
  <div class="content">
    Went to a show last night. Megadeth. 
    It was alright.
  </div>
</div>

The same post with hAtom superpowers:

<div class="post hentry">
  <h3 class="entry-title">Megadeth Show Last Night</h3>
  <span class="subtitle">Posted by 
    <span class="author vcard fn">Chris</span> 
    on 
    <abbr class="updated" title="2006-06-04T10:32:10Z">June 4th</abbr>
  </span>
  <div class="content entry-content">
    Went to a show last night. Megadeth. It was alright.
  </div>
</div>

To you and I, eagerly searching for a review of last summer’s Megadeth show, there is no difference between the two. Our browsers render them the same. To a machine, however, the second post is chock full of semantic goodness.

This semantic goodness represents, in our HTML, the same information an Atom feed would provide. This leaves us with two paths of action for gettin’ our feed on: we can wait for feed readers to start speaking hAtom fluently, or we can have someone translate hAtom to Atom for us.

Subtlely Free Feeds

One year ago today Subtlety was released. Today it is re-released with a new feature: it can convert a page containing hAtom entries into an Atom feed. This means your feeds are now officially free.

We’ve actually been doing this for a while right here on Err. Our Feedburner feed points to this url: http://subtlety.errtheblog.com/O_o/29f.xml. It’s an Atom feed generated by Subtlety after parsing the hAtom elements on this site. On Err the Blog.

My ozmm blog is a static blog with no special RSS code. Instead, I point the Feedburner URL at a Subtlety Atom feed which is generated from the hAtom in the posts. Our Dynamite blog uses the same trick. See the pattern?

There’s no reason to ever write your own Atom feeds anymore. Sorry.

But what if I don’t want you hosting my feeds?

That’s fine, and acceptable. How about I just hand you the technology to do this on your own?

It goes like this:

$ gem install mofo
$ cd rails_app/vendor/plugins
$ gem unpack mofo

Then, here’s your controller:

class PostsController < ApplicationController
  def index
    @posts = Post.find(:all)
  end

  def atom
    target = url_for(:action => :index)
    render :xml => hEntry.find(target).to_atom(:title => 'whatever')
  end
end

You can use this trick for dynamically generated feeds (changelogs or activity feeds, perhaps) or whatever else. Thanks, mofo.

Last Step: Cut the Code

Now go through your app and remove all the Atom code. Drop those extra plugins, remove those xml templates, cut out all the special logic, and enjoy simple Subtlety or profound mofo.

Have fun.

The jSkinny on jQuery

j-j-j-jQuery. It’s on everyone’s lips, right? You love it or you hate it, or you’ve never tried it but you love it, or you’ve never tried it but you super-hate it. Yeah, we know.

Well, PJ and I launched FamSpam a bit ago and made the bold move of powering all the jabbascript with jQuery. We even wrote our own Facbeook-style lightbox library in jQuery (Facebox). So while this is a Ruby blog, indulge me for a moment as we dance with Ruby’s ugly-cool half-sister: Javascript.

whatQuery?

There are a bajillion posts about jQuery, all of which introduce you gently, so I will be brief: jQuery is all about a single namespace and kickass querying. (Get it?)

Our buddy Hpricot, you may remember, was heavily influenced by jQuery’s selector syntax. Which was, in turn, heavily influenced by CSS selectors. As such, some of this may look familiar:

$('#id').hide()
$('.class').css('height', 20)
$('#posts li > a').addClass('dark')

And so forth. One of the fun things is that any of those $() queries may return 0, 1, or more elements—yet the code stays the same. That’s right: our css() call would affect the height of all matched elements. Same with the addClass. But, if nothing is found, it’ll all silently fail. jAwesome!

niceQuery()

While some of the recent “jQuery vs <insert_framework_here>” blog posts might not be so nice, jQuery itself certainly is: it (mostly) easily works alongside other libraries. That means you can start dipping your toe into the jSauce while your Prototype or MooTools code doesn’t suspect a thing.

It’s easy:

jQuery.noConflict()

My above examples would now be written like this:

jQuery('#id').hide()
jQuery('.class').css('height', 20)
jQuery('#posts li > a').addClass('dark')

The noConflict() call causes jQuery to defer ownership of $() to Moo or Proto, leaving your current js intact. How thoughtful. Check more at the comprehensive doc site.

chainQuery()

jQuery is all about chaining, in a big way. Here’s an example from FamSpam:

var person_email = $(this).parent().find('#person_email').val()

Pretty self explanatory. The find is scoped to the receiver, in this case the parent of the current element.

Another cool chain:

$('#invite_error').show().text('Please enter an email and a name.')  

Hrm, we should probably put text() before show(), yeah? I love these kind of questions!

Finally, a slightly more advanced chain:

$('#facebox .body').children().hide().end().
append('<div class="loading"><img src="'+$s.loading_image+'"/></div>')

Get it? The end() reverts the most recent ‘destructive’ (read: find) operation. So we start with the .body, then find its children, then hide its children, then go back to .body and append some html. Slick, I think. Real slick.

Like I said, the doc site is super great.

ujsQuery()

Okay, here’s the segue: jQuery has unobtrusiveness built in. And it feels smooth. Real smooth.

Here, a snippet straight from FamSpam’s javascript:

$('.reset_invite_form').click(function() {
  $('#new_person').resetForm()
  $('#invite_another').hide()
  $('#invite_another > span').remove()
  $('#new_person').show()
  return false
})

Pretty simple, right? And clear, to boot. What we do is slip this code inside of a function passed to $(document).ready(), which will be run when the, erm, document is, uh, ready.

Like this:

$(document).ready(function {
  $('.reset_invite_form').click(function() {
    ... stuff ...
  })
})

So on and so forth. We attach Facebox to links the same way:

if ($.facebox)
  $('a[rel*=facebox]').facebox()

If the Facebox plugin is loaded, we find any links with a rel of “facebox” and convert them from normal links into jsery’d Facebox links. Easy as pie.

Which brings us, of course, to the segue.

spamQuery()

How are we using jQuery on FamSpam? jRails? Something custom? By hand?!

Yeah, well, by hand. We add all our behavior unobtrusively using the method detailed above. As far as I know, there’s no javascript in our html. If there is it’s on the run. For its life.

If you want to peep around, the js is (predictably) right here: http://famspam.com/javascripts/application.js.

Something to note: as of writing (1.2.2), jQuery doesn’t play nicely with Rails’ respond_to. But, hold the phone, it’s okay: a simple fix. Right here:

jQuery.ajaxSetup({ 
  'beforeSend': function(xhr) {xhr.setRequestHeader("Accept",
    "text/javascript")} 
})  

You’re now ready to rock.

Oh, one more thing before we move on: if you want some ajax-flavored will_paginate, check out this short guide. It was mentioned in another post, and now it’s mentioned here.

plugQuery()

jQuery, you see, has a wonderfully simple plugin system. We take full advantage of it by using a few choice plugins. Here’s a taste to wet your pallet.

The most essential plugin is the jQuery Form Plugin. With it, you can unobtrusively convert normal forms into ajax forms. The (obvious) advantage of this is graceful degradation, which is very kind but also very courageous.

$('#new_person').ajaxForm(function() { alert('Atta boy!') })

Seriously. Simple. And just so perfect for Rails—all the form’s attributes stay the same, including its action and method, just now it’s submitted through ajax. respond_to and jQuery are so in love it’s making me sick.

Another plugin we use is the Tablesorter. While we don’t have much tabular data on the promo or family sides of FamSpam, our admin interface is full of it. Want to sort your families by number of members? Conversations? Photos? It’s one line of code with this plugin. Sure, we’ll have to do some more complicated server-side sorting as our database grows, but this does the trick so quick right now.

$('#sorted_table').tablesorter()

See?

Another plugin we really love is the anti-aliased rounded corner plugin. Unfortunately this is not the most popular rounded corner plugin for jQuery, and that’s a shame. A damn shame. Because it’s definitely the best. We use it on the home page and other places we thought could use some class.

As usual, it’s dead simple once installed:

$('.corner').corner()  

You don’t have to be so generic with it, but we like to be.

Finally, the brand new autocomplete plugin by ReinH and wycats is simple, small, and slick. See a pattern here? We’re using this on our admin site and couldn’t be happier. It speaks JSON, baby. Sign me up.

thatsitQuery()

Thanks for letting us stray for a moment from our normally dreary discourse. Got any other cool jQuery tips or treats? Leave ‘em in the comments.

Oh, a parting gift. More code to chew on: our tour.js. We use it to power the FamSpam tour. Enjoy.

Paginatin’ Christmas

In a timely holiday manner, we present to you a short list of will_paginate resources. Please enjoy responsibly.

will paginate

Sightings

Since its inception, millions of people have paginated billions of records using will_paginate. A few notable examples:

Know a site that belongs on this list? Let us know in the comments.

New Features

Just in time for the holidays, Mislav has gifted us all with a bucket o’ new features for will_paginate, mostly dealing with customizing the output. Take a look at his announcement and dive in.

Testing Your Views

For those of us constantly asking the view testing question, stern but fair will_paginate maintainer Mislav comes to the rescue with his aptly titled will_paginate and view testing article. It’s in-depth, so be sure to check it out. Also: subscribe to his blog. Immediately.

Ajax Pagination

This Ajax thing is going to be huge! Get in on the action with Matt Aimonetti’s Ajax Pagination in less than 5 minutes article. He’ll teach you how to unobtrusively add Ajax behavior to will_paginate using Prototype, LowPro, and RJS.

If you’re more of the jQuery type, check out the Ajax will_paginate, jq-style article over at ozmm.

The RailsCast

The prolific Ryan Bates has a screencast explaining the basics of WP. As always, it’s to the point and very well done. And hey, you can even watch it on your iPod! Have a look.

will_paginate without ActiveRecord

There’s only a few things I like more than websites with tildes in the URL. Like, say, twisting Rails plugins into non-Rails uses. Lucky for us, Erin Ptacek provides both in an entry titled Erin’s Adventures with Rails: will_paginate without ActiveRecord. In it, you’ll learn how to paginate a collection of OpenStruct objects. No ActiveRecord required. What a rush.

Ferret Integration

Brandon Keepers, that handsome devil, wrote a popular article detailing the steps necessary to paginate your Ferret search results using good ol’ WP. If you’re using Ferret, this is definitely the way to go.

Solr Integration

While I don’t know what ‘The Pug Automatic’ means, and I certainly know nothing of the RoboPug in said blog’s header, Henrik Nyh’s article on paginating acts_as_solr with will_paginate is an undeniable must read for any Java lovin’, Apache huggin’ Solr user wanting to add a bit of style and flair into his app.

acts_as_taggable Integration

Perhaps as contentious as the comments and this thread suggest, Jim Morris’ Paginating acts_as_taggable article offers a few ways to make both the on_steroids variant of acts_as_taggable plugin and will_paginate play nicely together.

Rails Plugins

A number of Rails plugins have been released with support for will_paginate lovingly baked in. Like a Santa shaped sugar cookie.

SpinBits’ SimplySearchable helps you search in style while providing options to paginate using our friend WP.

UltraSphinx, Evan Weaver’s preeminent Sphinx search engine plugin, longs to be installed alongside will_paginate.

While will_paginate, due to popular demand, plays nicely with scope_out, Nick Kallen’s similar HasFinder works technically and conceptually well with everyone’s favorite paginator.

And finally, how can we neglect to mention Will_Paginate_Search, which hooks into both WP and acts_as_indexed to the benefit of all involved parties.

The Bugtracker

Found a bug? Got an idea? Of course we’d love to hear it. Our Lighthouse tracker, which we love is the place for all of it.

The Google Group

Did you catch it above? Yep, you did. So observant: there’s now a Google Group for will_paginate. Be sure to join up and chime in.

Nightly RDoc

Finally, RDoc is now generated nightly from the latest code in Subversion. Check it out at the Rock. It’s like Christmas every day!

That’s a wrap

Hey, where did 2007 go? I’m getting sucked into 2008 faster than Perl into obscurity.

Keep paginatin’, Railers. See you next year.

Evil Twin Plugin

Seriously, I think I have something against Rails’ lib directory. We jumped from keeping gems in lib to vendor/gems back in March. Then we jumped from keeping generic Rake tasks in lib/tasks to Sake. Now we’re gonna jump again.

Hacking Plugins

It’s really not that big of a deal, and pretty common—you want to change the behavior of some existing plugin. Maybe you Piston it and commit your changes. Sure. But maybe you just want to leave the original code alone.

A classic approach has been to stick some kind of hack in the lib directory. Issues abound, for sure. First: the load order. Who gets loaded first? Who reloads and who doesn’t? Second: location. You’ve got one bit of code messing with another bit of code in a totally separate location. Third: testing. Are you testing it? Maybe.

None of these things are deal breakers, but we can certainly address them. And we will.

The Evil Twin Plugin

Evil Twin

Here’s the simple solution: create a plugin called whatever_hacks, where whatever is the name of the plugin you’re hacking. That’s it. An evil twin, if you will.

Adding the _hacks suffix ensures it will always be loaded after the target plugin (assuming you haven’t messed with the default plugin load order—alphabetical). Keeping it right next to the target plugin also ensures anyone who peers into vendor/plugins will instantly know tomfoolery is afoot.

You can now build out a tested, hack happy plugin. Or, y’know, just stick it all in init.rb. With caution.

Caution: init.rb

Caution: init.rb does not always do what you expect it to do. It’s loaded in the context of Rails::Plugin in 2.0 and Rails::Initializer in 1.2.5, not Object. Come again? Like this: re-opening existing classes isn’t as straightforward as elsewhere.

=> init.rb

class Hash
end
puts Hash.inspect

Guess what that prints. Ready?

$ ./script/runner 
Rails::Plugin::Hash

That’s right—we didn’t re-open Hash, we created a new Rails::Plugin::Hash class. Any methods we add in there won’t be added to Hash proper.

If we want to grab a real class and stuff some methods in it, we need to use class_eval or module_eval:

=> init.rb

Hash.class_eval do
  def duck_punched?
    true
  end
end  

puts({}.duck_punched?)

As expected:

$ ./script/runner 
true

Doing it this way (class_eval) forces a constant lookup, making Ruby happily run up the chain and find the class or module in question.

attachment_fu_cropper

Okay, time for a real example. I wanted to change attachment_fu’s ImageScienceProcessor to crop thumbnails before resizing them. As this is a hack I use on all my apps, I also want to keep it out of my models. Hence, attachment_fu_hacks.

=> vendor/plugins/attachment_fu_hacks/init.rb

klass = Technoweenie::AttachmentFu::Processors::ImageScienceProcessor
klass.module_eval do
  ##
  # Hacked to use image_science's #cropped_thumbnail method
  def resize_image(img, size)
    # create a dummy temp file to write to
    filename.sub! /gif$/, 'png'
    self.temp_path = write_to_temp_file(filename)
    grab_dimensions = lambda do |img|
      self.width  = img.width  if respond_to?(:width)
      self.height = img.height if respond_to?(:height)
      img.save temp_path
      callback_with_args :after_resize, img
    end

    size = size.first if size.is_a?(Array) && size.length == 1
    if size.is_a?(Fixnum) || 
        (size.is_a?(Array) && size.first.is_a?(Fixnum))
      if size.is_a?(Fixnum)
        img.cropped_thumbnail(size, &grab_dimensions)
      else
        img.cropped_thumbnail(size.first, &grab_dimensions)
      end
    else 
      new_size = [img.width, img.height].dim size.to_s

      img.cropped_thumbnail(new_size.first, &grab_dimensions)
    end
  end
end 

Works like a charm.

When heavysixer wanted to hack acts_as_taggable, he took the same approach: http://pastie.caboo.se/119904. Feel free to follow suit.

View Testing 2.0

I’ve come to realize how anti-web 2.0 this blog really is. Why are we, the authors, doing all the work?! Where is the user generated content? How can we pretend to be Rails developers when we waste our time writing all these blog posts!

No longer. Today all that changes. It’s time you guys start pulling your weight.

Let’s Talk About Testing Views

There seem to be about a bajillion different solutions to the ‘problem’ of testing views. If you’re into testing and also into Rails, my guess is you’ve tried a few different styles before settling on your current method. (hey, me too)

What I would love is for you to post your current, favorite, flavor-of-the-month style of view testing. What library do you use, where can we download it, and how about some sample code?

When commenting, wrap your code in <code>code tags</code> and use "textile":http://textism.com/tools/textile/ for links.

Everyone has different taste, but the hope is we’ll have enough options for people to find something they like. Something tasty.

Hit me!