posts by tag

code (6)
Rails' StringInquirer

A recent commit to Rails. When the last method of a method chain ends with a question mark, the entire chain reads like a question.

$> Rails.env == 'production'
$> # => true

$> Rails.env.production?
$> # => true

The latter is much better. I love these smart little additions. Any string wrapped up in a StringInquirer has this functionality.

Rails plugin: has_params

This is yet another edition of pointless programming. Ready. Set. Go!

To me, checking if parameters exist via the params hash is ugly. My fingers start to quiver when I type params[:whatever] more than once on the same line. Sometimes I’ll add a private method to simply check if parameters exist. It just reads better, IMHO.

if using_open_id?
  # process OpenID authentication
end

private
  def using_open_id?
    !params[:openid_url].blank?
  end

Maybe you’ve done something similar, maybe you haven’t. Being the pointless programmer that I am, I wrote a plugin to take care of this for any and all parameters. I called it, has_params. You can find it on GitHub.

Example(s)

I suppose there’s no harm in duplicating the README, huh? Anyway, here’s how it works:

# the "old" way
Project.new(params[:project]) unless params[:project].blank?
Project.new(params[:project]) if params[:project]

# the "has_params" way
Project.new(params[:project]) unless blank_project_params?
Project.new(params[:project]) if has_project_params?
Project.new(params[:project]) if project_params?

You basically just plug the key that you’d otherwise use for params into the standard “has_params” format. So, for params[xxx] you’d have one of the following to choose from:

  • blank_xxx_params?
  • has_xxx_params?
  • xxx_params?

Personally, I think it cleans up controller code a little. Brick-by-brick my friends, brick-by-brick.

Gotcha!

If you’re using method_missing in your controller(s), make sure you call super at some point. This plugin also uses method_missing and at a higher point in the inheritance chain (unless you’re also mixing into ActionController::Base), so if super isn’t called, Rails will be perfectly satisfied with your version of method_missing, ignoring the has_params’ version. At least, I’m pretty sure that’s what would happen. Consider yourself warned.

Installation

I can only imagine how excited you are to install this sucker. Here’s how:

>> cd project_root/vendor/plugins
>> git clone git://github.com/rpheath/has_params.git 

And that concludes this edition of pointless programming. I hope you’ve enjoyed it :-)

Rails plugin: EasySearch

Have you ever written a plugin or a piece of code that was more fun to write than it was actually worth? I seem to do that a lot—I enjoy experimenting with Ruby. Recently, I needed to search ActiveRecord models individually, and was in the mood to write a simple DSL. It’s now in plugin form, called EasySearch, and can be found on GitHub.

It doesn’t support joins or shared indexes or anything else you might come to expect from a search plugin. I’m posting about it not because I think you should use it, but because I think it’s a pretty cool API for searching.

So, install the plugin and do something like this:

class Search
  include RPH::EasySearch
end

(I originally had a nice acts_as_easy_search class method that hooked up the behavior, but I didn’t feel right about mixing that into Object. I wanted this plugin to work without the need of an ActiveRecord superclass. Plus, AR would then be expecting a corresponding table in the database, which is another unnecessary need. So, manual inclusion is the answer until I come to terms with a better alternative. Continuing…)

The Search class now has the means to easily search any ActiveRecord model within an application. Let’s say I want to find all of the users who mention ‘ruby’. Let’s reword that: search users with the term ‘ruby’.

$> Search.users.with('ruby')
$> # => [<#User ...>, <#User ...>, ...]

Makes sense, huh?

I have a confession to make: I lied about only having to include the module. Before anything will actually work, I’ll need to tell EasySearch about the tables I want to easily search.

EasySearch Configuration

For any generic search plugin to work, there needs to be a way to specify which database columns the search terms should be matched against. Here’s an example of how it works:

# in Rails 2.0+ you could put this in 
# config/initializers/easy_search_setup.rb
# (otherwise just use config/environment.rb)

RPH::EasySearch::Setup.config do
  setup_tables do
    users    :first_name, :last_name, :email, :bio
    projects :title, :description
    groups   :name
  end
end

There, that’s it (for real this time).

If you notice, it looks like there are “users”, “projects”, and “groups” methods that are called from within the block. And actually, that’s correct. The only difference is those methods don’t exist until they’re called, and even then they don’t “exist” (which is kind of weird, I know, but incredibly awesome nonetheless). Don’t worry about how it works (read through the code if you’re interested), just follow the pattern (use the table names instead of the model names).

After this one-time configuration is done, I can do things like:

$> Search.users.with('ryan')
$> # => [<#User ...>]
$> Search.projects.with('design')
$> # => [<#Project ...>]
$> Search.groups.with('friends')
$> # => [<#Group ...>]

If you try to search a model that hasn’t been configured, EasySearch will let you know. After all, how else would it know which columns to look in? It’s not that magical.

And getting a hold of the current table configuration is easy:

$> RPH::EasySearch::Setup.table_settings
$> # => {"users" => [:first_name, :last_name, :email, :bio], "projects" => ...}

How it Works

Really, all EasySearch is doing is building a WHERE clause and using conventions to pass that on to the appropriate finder. So Search.users is really doing User.find with a custom conditions clause.

Let’s say I want to search my User model for “ryan heath ruby”.

Search.users.with('ryan heath ruby')

This would not only compare “ryan heath ruby” with each of the specified columns (you know, from the configuration), but it’d compare “ryan”, “heath”, and “ruby” individually against each of the specified columns, providing more accurate results.

This, however, is an incredible performance limitation and is why this plugin is not meant for those gigantic applications with tons and tons of database records. It builds a potentially large WHERE clause using LIKE, which is quite slow in the computer world. For those large applications, it’s far better to use a full text solution. But again, this is all (more-or-less) for fun with little profit. I had a need to search individual models separately in a small application, so there you go. I can always go back and rework the back-end to operate differently/more efficient.

Oh, and since a rather large WHERE clause is being constructed, checking each term individually, I want to ignore meaningless words to avoid the extra overhead. For example, let’s say I tweak my search to be:

Search.users.with('ryan heath is a ruby')

EasySearch would still only search “ryan”, “heath”, and “ruby”, ignoring the “is” and “a” (because they’re dull). You can easily see what keywords are considered “dull” by doing:

$> RPH::EasySearch::Setup.dull_keywords
$> # => ['the', 'a', ...]

Now I know what you’re thinking, “Who gets to decide what keywords are dull keywords?” The programmer, of course! By default, EasySearch has a predefined list of keywords that it thinks are meaningless. But you can add to that list quite easily. Using the same Setup.config block:

RPH::EasySearch::Setup.config do
  setup_tables do
    # ...
  end

  strip_keywords do
    ['other', 'words', 'you', 'do', 'not', 'want', 'to', 'search']
  end
end

That will append those keywords to the default list (and will only count a dull keyword once, so don’t worry about duplication). If I wanted to ignore the default list completely (instead of appending to it), I’d just have to pass true to the strip_keywords method, like so:

strip_keywords(true) do
  ['other', 'words', 'you', 'do', 'not', 'want', 'to', 'search']
end

So that’s about it. It’s a fun plugin to play with, and like I said, it was more fun to write than it’s probably worth. But honestly, I think this sort of experimentation is nothing but good for programmer experience. And besides, every now and then, an “experiment” turns into something awesome (see Ambition for a prime example).

Flickr Developer Site

Flickr launched a developer site recently (code.flickr). Dev sites intrigue me, especially those belonging to well-known companies. And for some reason, statements like this always seem to draw me in:

In the last week we deployed new code to Flickr 50 times, including 546 changes by 16 people. We issued over 2,000 new API keys, and third party developers made an average of 704 API calls per second, across 109 public API methods. We added 1 new API method, and updated 7 others. There are approximately 10,000 lines of open source code in our public subversion repository.

Sheesh, 704 API calls per second!? To interface with an API requires a slight bit of knowledge (and actual skill to do properly); I mean, it’s not something cousin Elroy could just “throw together” real quick. So to think there are that many people making that many requests per second is kind of crazy.

You can find running totals for those types of stats at the bottom of the main code.flickr page. Pretty cool.

Writing Readable Code

Writing code in Ruby is a real pleasure. It’s a very expressive language, and that allows me to make complex programming problems read like English.

For instance, at work recently I needed to authenticate against a separate system (written in .NET). I also needed to cache the user to the application’s DB for speed and convenience. And I also needed to make sure the cached version gets updated if the .NET version gets updated. Etc. Etc. Etc. The point is there was a lot to do there, but it didn’t have to appear that way.

User.process_login(username, password)

Once that complicated mess is abstracted, that’s all I have to do. Here’s a piece of the mixed in module that does a little more of the dirty work:

def process_login(username, password)
  returning Proxy.authenticate(username, password) do |user|
    cache! user if user && user.needs_cached?
  end
end

Proxy is a class that nicely handles the WSDL/SOAP communication to the .NET system; the authenticate method on the Proxy authenticates against the .NET system’s DB; the cache! method (you guessed it) does the caching based on a last-modified date comparison of the returned SOAP user versus the cached version (if there even is one). The returning part simply returns the user object or nil, depending on if the authentication was successful or not.

Hopefully you’re thinking, “there was really no need to explain all of that.” Because the goal here isn’t to focus on all of the behind-the-scenes mumbo-jumbo, it’s to give yet another example of how clearly a complex thing can be written in Ruby.

I might even be able to show that to my Mom and have her tell me what it’s doing. Well, maybe not, but the average (technically-minded) person would have no problem.

As an aside, this is what I’m really starting to love about Rspec. It sort of guides your mind into writing readable code, which I’m all about.

Motivations to refactor code

I read a post a few weeks ago about refactoring code and the motivations thereof. Since then, I’ve come across one or two other posts regarding the same topic, but they’ve all seemed to neglect one of my main reasons for refactoring code: self-satisfaction.

I’m the first to admit that I don’t write perfect code. Some may argue that I don’t even write good code (although that would be somewhat depressing, mind you). However, I do write the best code I know how to write. And if I learn a better way, I want to go back and re-write (er, refactor) that code. Knowing that I have a clean code base makes me more focused and motivated to do even better. It’s an infinite cycle.

I realize not every developer cares a whole lot about his/her code base, but I think the good one’s do. I get sick if I’ve left code uncommitted, and experience anxiety when thinking of something I need to go back and “do better”. There’s a whole ‘nother wave of motivation when trying to improve already-working code. It sometimes removes the problem-solving aspect and allows you focus on efficiency and readability. It’s fun to say the least.

So… self-satisfaction is near the top of my list of motivations to refactor code. The end.

2008 by Ryan Heath | Get In Touch

flickr

DesolateInfinityLooking upDazedBlurred