ruby rails workflow

We make an effort of extracting shared code into Gems. While FOSS code goes to of course, our business-internal logic needs a private Gem server. This article focusses on my workflow with Gemfury1 as the private Gem host but you can also roll your own with bundler/gemstash or Gem in a Box.

1Note: I am not affiliated with nor am I sponsored by said provider.

Gem Skeleton

In order to create all the boilerplate code and to make sure I am following the latest conventions, I use Bundler’s gem command:

bundle gem lolwat --no-coc --no-mit

When I cd into the created directory and load the generated lolwat.gemspec into my editor, I can see that it already includes a setting for allowed_push_host. This prevents me from accidentally pushing proprietary code to

Unfortunately, doesn’t support a RubyGems compliant API to actually push my released Gem so there’s no need to bother with changing the example host to something real.

Instead, we’ll add the following line to the project’s Rakefile:

ENV['gem_push'] = 'no'

We’ll get back to it later.

Gemfury Support

In order to easily push to, there a few more additions. First we have to include the gemfury Gem to our lolwat.gemspec file:

spec.add_development_dependency "gemfury"

The second file we need to edit is our Rakefile again. Add the following code to the end of the file:

# Rakefile
desc "Push lolwat-#{Lolwat::VERSION}.gem to"
task :gemfury do
  package = "pkg/lolwat-#{Lolwat::VERSION}.gem"
  if File.exist? package
    system "fury push #{package}"
    STDERR.puts "E: gem `#{package}' not found."
    exit 1

Here’s a set of shell commands that will automate all this:

GEMNAME=$(basename $(pwd))
tail -r <$GEMNAME.gemspec \
  |sed -E '1s/(.+)/\1\'$'\n  spec.add_development_dependency "gemfury"/' \
  |tail -r >_new.gemspec \
  && mv _new.gemspec $GEMNAME.gemspec

GEMVERSION="$(grep module lib/$GEMNAME/version.rb|sed 's/module //')::VERSION"
cat >>Rakefile <<EOF

desc "$GEMNAME-#{$GEMVERSION}.gem to"
task :gemfury do
  package = "pkg/$GEMNAME-#{$GEMVERSION}.gem"
  if File.exist? package
    system "fury push #{package}"
    STDERR.puts "E: gem \`#{package}' not found."
    exit 1

desc "Gemfury"
task :release do

Note: This has only been tested on Mac OS which uses BSD sed. GNU sed (Linux) may behave differently.

Prepare Release

While I’m hacking, I track completed features in a Changelog section in my I list the changes under a HEAD subsection.

## Changelog
### HEAD (not yet released)
* Add foo to bar
* Fix: Baz not correctly responds to boo

Be sure to add your git origin as explained by GitHub, GitLab, Bitbucket or whichever provider you chose.

For the first version, lib/lolwat/version.rb is already preconfigured with 0.1.0. That’s a sensible default for a first version to play around with. For later updates, increase the version number based on Semantic Versioning (»Semver«).

I prefer tracking the Changelog using the GitHub’s Release system (I’ll get to that in a bit). To that end, I remove the bullet points in my temporary »HEAD (not yet released) and commit the changed together with the changed lib/lolwat/version.rb:

git commit -m 'Release v0.2.0'

Release the Kraken Gem!

Together with our changes to the gemspec and Rakefile, releasing the version is as simple as:

rake release

It will create a git version tag, push everything to your git remote, package the gemfile and upload the Gem file to Gemfury.

The last step is to head over to your GitHub project page, select releases and Draft a new Release in the upper right corner.

GitHub Releases Overview

The ensuing form is pretty self-explanatory (check the explanations on the right side). For the release name, I just repeat the tag name (e.g. v0.2.0). The text area gets all the info from my temporary Changelog tracker in the file.


  1. Create gem skeleton using bundle gem
  2. Adapt Rakefile and *.gemspec to prevent pushes to public
  3. Hackedy-hack-hack + track features/bugfixes in README
  4. Update version.rb, remove changelog info, commit and rake release
  5. Create release on GitHub and paste changelog data


comments powered by Disqus