Microgems: Five minute Rubygems

A project I’m working on required a score to be calculated on an ActiveRecord model, and stored in the database. The User#calculate_score method calculates the score:

class User
  def calculate_score
    100
  end
end

In ActiveRecord models, a convention is that methods that update the database should have a bang, or an exclamation point appended to their names. To add a function that stores the score, we add User#calculate_score!:

class User
  def calculate_score
    100
  end

  def calculate_score!
    update_attribute!(:score, calculate_score)
  end
end

This introduces some duplication, which can be removed with some trickery. A generic helper function named #bang can take care of the updating, and can be reused in models throughout the rest of the application:

module Bang
  def bang(attributes)
    [*attributes].each do |attribute|
      key, value = attribute
      define_method("#{key}!") { update_attribute(value || key, send(key)) }
    end
  end
end

class User
  extend Bang

  bang :calculate_score => :score

  def calculate_score
    100
  end
end

After the same pattern arose in another project, I wanted to extract the library to make it reusable. I decided against copying the library and having two versions to maintain, so I needed to turn it into an actual gem. However, it felt too small to squat a gem name on RubyGems.

Instead, I put the library into a Gist. Gists are full Git repositories in disguise, so they can be cloned from and pushed to like other repositories. To use it as a Ruby gem, add a gemspec:

Gem::Specification.new do |s|
  s.name        = 'bang'
  s.version     = '0.1.0'
  s.platform    = Gem::Platform::RUBY
  s.author      = 'Jeff Kreeftmeijer'
  s.summary     = 'Bang!'
  s.description = 'Bangs existing model methods'

  s.files         = ['bang.rb']
  s.test_file     = 'bang_spec.rb'
  s.require_path  = '.'

  s.add_development_dependency('rspec', ["~> 2.0"])
end

Then, install the library with Bundler by adding it to a Gemfile in your project:

gem 'bang', :git => 'git://gist.github.com/1232884.git'