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:
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.
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:
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:
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
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:
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
Thingys 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:
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
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.
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.
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.