3 little gems or how to start doing the hard work

I've been programming in Ruby (on Rails mostly) for about 3 years now, but until recently I had no idea how to create a gem (a Ruby plugin). Gems are an essential part of Ruby on Rails development and I often wondered how these little creatures were made. Once in a while I would look at the source code of one, trying to figure out what makes their heart beat. After stumbling on a Step-by-Step Guide to Building Your First Ruby Gem, I decided to finally bite the bullet and code a tiny gem. The official Make your own gem guide also came in handly while working on the gems. After the first one, making more wasn't much more work so I did 2 more!

token_attr

As part of my jobs and personal projects, I often had to code something to generate a random token for various objects. Random tokens are everywhere and they're pretty easy to setup without a gem, yet it doesn't feel efficient to recode this over and over again. It was perfect for a first gem.

Getting the gem to work was easy enough by following the guide. The hard part was figuring out how to properly test it. A gem without specs is worthless as you won't know when you break something, it makes it much more difficult for others to contribute and it's even quite inneficient to code it.

I like how this one turned out:

class User < ActiveRecord::Base  
  include TokenAttr::Concern
  token_attr :token
end

user = User.new  
user.valid?  
user.token # => "b8bd30ff"  

scoped_id

Sometimes you don't want to expose to the world the database id of your objects. Similar to the token_attr gem, this one gives you a different option for giving unique identifiers to objects. This one is especially useful if you offer a hosted service and auto incrementing identifiers has value to your customers. For example, on GitHub the issues and pull requests numbers increment per repo.

class Project < ActiveRecord::Base  
  include ScopedId::Concern
  scoped_id :per_owner_id, scope: :owner_id
end

jacks_project = Project.create(owner_id: 1)  
jacks_project.per_owner_id # => 1

johns_project = Project.create(owner_id: 2)  
johns_project.per_owner_id # => 1  

time_will_tell

The third and last one for the moment was inspired by a post I wrote a while ago on coderwall. As part of a previous job I had to deal with formatting date ranges and timespans for humans.

Oct 3 - 8, 2012
Jan 30 - Feb 5, 2013
Dec 26, 2012 - Jan 3, 2013

The code was already written, all I had to do was wrap it in a gem, easy right? Not so fast, as part of the gem creation I tried to make it more flexible for people who would end up using it. Implementing a gem with locales wasn't as simple as I thought/expected. The documentation was nearly non-existent and I had to rely on looking at the code of other gems to figure out how it should be done. Not to mention that there are dozens of ways to make it work, but I didn't want the implementation to be a big hack. Fortunately I eventually figured it out and managed to publish the gem (which appears to be in use by a few people and even had 2 pull requests so far!).

The hard work

Creating and publishing a gem was harder than I thought it would be, but now if I ever have to create a gem I can simply start with one of these. The hard part, the bump, is behind me and plenty of possibilities are now open to me. So next time you're scared of doing something that sounds just a bit out of your comfort zone, just do it and enjoy the learning experience.

comments powered by Disqus