As Ruby developers, we often forget how good we have it. We’ve got a awesome library distribution system in Rubygems, we use a powerful and flexible language that just begs to be used for DSLs and a culture of open development and community improvement. In this article, I’ll be talking about the ways you can add that bit of extra polish to your useful new gem. We’ll look at some common usage patterns and how they’re achieved in popular gems.
None of these tips provide any extra functionality for your gem – but they do provide a good user experience. If you have ambitions for your gem to become popular, UI matters and code is a programmers UI.
The Config Block
If you’ve been a rubyist for even a short while, you’ve seen this. The method I’m going to show use is useful for global set up of the gem. Let’s take a look:
[gist id=’3332088′]
So, there’s a few things to notice here. Firstly, I’ve chosen to have the Configuration
object inherit from the fantastic hashie mash gem. This allows us to treat our Configuration
object as a hash internally, but means we can expose nice dot syntax in the block itself. In the example a.magic_number = 3
results in the equivalent of a[:magic_number] = 3
. Secondly, the config
method acts just like the instance
method that you’d write if you were making a singleton class, so we don’t have to worry about multiple instances of configuration turning up.
Chain Reaction
If you’re writing a gem which performs some kind of searching on an interface, then you’re presented with somewhat of a API challenge: How can I let my users create complex searches while avoiding a bad case of the hashes? To illustrate what I mean, let’s take a look at Active Record 2 vs 3:
[gist id=’3332092′]
In the Active Record 3 example, method chaining is used instead of a potentially massive hash to define our query. This also has the added benefit of allowing us to define our query easily over time and only execute it when we’re sure we need it by calling a method that sparks off the actual query execution – each
for example.
Let’s build a really simple method chaining search object:
[gist id=’3332095′]
As you can see, we’ve made a pretty convincing search interface here that feels a fair deal like Active Records 3’s query syntax. All you’ll need to do is return self
at the end of chainable methods to get the effect. Notice that each
is a nice extra here, as it removes the need for an explicit call to results
.
Include Everything, Where Appropriate.
As I’m sure you know, you can include
a module to add the module’s instance methods to a class or you can extend
a module to add class methods. Often, when writing gems, we need to do both. Adding a few lines to the documentation to explain to users that we need to include Library::InstanceMethods
and extend Library::ClassMethods
is one approach, but that is one extra thing for the user to worry about and there’s no reason the user experience can’t be improved.
HTTParty is perhaps the most used example of this pattern.
Default HTTParty usage looks something like this:
[gist id =’3332108′]
The class Thingy
will now have the very handy instance method post
for POST requests, but will also have the option to call post
on the class should your application not actually need to create new Thingy
s but just to use one.
How does this work? Let’s look at the source – copied from github with some bits chopped out for clarity:
[gist id =’3332112′]
self.included
is a method defined in the Ruby core Module
class which get called on when a module is included in another class. base
, in this example, is the class Thingy
. By overriding it, we’re now able to extend Thingy
with HTTPart::ClassMethods
saving the user an additional extend.
Also, because we’ve got a handle on Thingy
we’re able to perform some setup on the object – as in the example here we’re some default values are set.
The included
method, and it’s counterpart extended
, are really powerful – but easily abused. In places where you do not need to include and extend there’s really no need to muddy the water by overriding included
just so you can use include to bring in class methods – there be dragons down that road.
Be Helpful
This isn’t a code tip – just something I bumped into this week and can’t recommend enough. As developers, perhaps especially as web developers, we should all be aware that it take x seconds for a user to leave a page if they’re having a bad time. I would suspect that the same holds for programmers when they’re having a bad time setting up a gem.
Friendly exceptions are one way to be helpful. Consider replacing the exception message “No configuration file found” with “Cannot find configuration file: config/awesome.yml” – maybe you could even pop in a url to the documentation there.
Ultimately, the experience of your gem is mostly a function of code quality and usefulness. Even libraries which aren’t very user friendly can find great success in the same way as software which isn’t very user friendly can inexplicably thrive.
I’d be intrested to hear your experiances with popular gems. Is there anything that recently caught your eye as a great bit of programmer centric UI? Is there anything that any of the big gems do that’s annoying? As ever, I’ll be lurking around the comment section or on the Tweet machine – do get in touch.
Daniel Cooper is a web developer from Brighton, UK