Pivotal Labs

Main menu

Skip to primary content
Skip to secondary content
  • About
  • Case Studies
  • Team
    • Executives
    • Locations
      • San Francisco (HQ)
      • Boston
      • Boulder
      • Denver
      • London
      • Los Angeles
      • New York
  • Community
    • Blogs
    • Tech Talks
    • Events
  • Careers
    • Lifestyle
    • Principles & Practices
    • Benefits
    • FAQ
    • Apply
  • Contact
    • Press Room
    • Press Releases
    • In The News
    • Press Kit
  • All
  • Labs
  • Standup
  • Tracker
Doc Ritezel

The Naked Gemspec

Doc Ritezel
Friday, December 14, 2012

Before we start, the .gemspec itself only appears once. Here it is, as generated by bundle init and hand-tweaked for relevance:

lib = File.expand_path("../lib", __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require "your_gem/version"

Gem::Specification.new do |s|
  s.name        = "your-gem"
  s.version     = YourGem::VERSION
  s.authors     = ["Your Name"]
  s.email       = ["you@example.com"]
  s.homepage    = "https://github.com/you/should-use-github"
  s.summary     = "Describe this gem like you're talking to me."
  s.description = "Describe this gem like you're talking to your mom."

  s.require_paths = ["lib"]
  s.files         = `git ls-files`.split("n")
  s.test_files    = `git ls-files -- spec/*`.split("n")

  s.add_dependency "hashie", "~> 2.0"
  s.add_development_dependency "rspec", "~> 2.12"
end

But what does all this mean? Moreover, how do all these crazy bits fit together?

Files

The most important component of any gemspec is the list of files that it includes when building the gem. After all, a .gem file is just a tarball with a metadata header written in Ruby. Here’s how we make that happen:

s.files         = ['file/one', 'file/two']
s.test_files    = ['spec/one', 'spec/two']

Let’s exploit part of git to give us the list of files. Don’t use git on your project? Start using git. Problem solved! Here’s what it gives us:

$ git ls-files
.gitignore
.rvmrc
Gemfile
LICENSE
README.md
your-gem.gemspec
lib/your_gem.rb
lib/your_gem/version.rb
spec/lib/your_gem.rb
spec/spec_helper.rb

We can make this output into a Ruby array of strings quite simply:

`git ls-files`.split("n")

Now, gems are laid out in a conventional way. That means a lib directory, a spec directory and some predictable files. That means nobody has to guess where your files are, which is fantastic!

Now let’s say you want to exclude your .rvmrc and .gitignore, because those files aren’t really all that important:

`git ls-files`.split("n") - %w(.rvmrc .gitignore)

Note: exclude Gemfile.lock from git, even though it might exist in your directory. This is conventional.

Naming

Your gem’s classes are called YourGem, while they live in files named your_gem. As a matter of taste, I believe gems should be named your-gem. There’s an argument to be made that gem names should match their requires (i.e., the gem should be named your_gem).

Version

The first component here is the version number for your gem. In this example, the your_gem/version.rb looks like this:

module YourGem
  VERSION = "0.1.0"
end

This is a Semantic Versioning string, and it’s the Simplest Thing that Could Possibly Work for a version.

Dependencies

This is the fun part. I’m of the opinion that dependencies should be as loose as possible until they’re not, but that throwing them away by doing >= 0 is the wrong approach.

For example, the above file will pull in RSpec as a dependency, but require any version that matches a pattern like 2.y.z, as long as y is above 12. Note that z is allowed to be anything, which allows patch versions to be included.

Of course, this means that everyone in the community has to play along and not break their gem on a minor version bump. Also, the community now includes you!

Maintenance

So now you’ve got a conventional gem with loosely-required dependencies. How do you know if these change? Well, if you’re on github, you can use Gemnasium to watch for new dependency versions and see if anything’s broken!

Dropping a new version of your gem is as easy as gem release with @svenfuchs‘s gem-release gem.

More Reading

  • Rubygems Patterns
  • Semantic Versioning
  • Clarifying the Roles of the gem spec and Gemfile
  • The Pessimistic Gem Version Operator
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Sean Beckett

Powering down gems.pivotallabs.com

Sean Beckett
Friday, May 13, 2011

We’re planning to decommission our gems.pivotallabs.com RubyGems server on Friday, June 17th. If you are dependent on gems hosted there, and the logs suggest there aren’t many of you, please find an alternate source for your gems.

Since Bundler has become available, there are now other options for making custom or forked gems available to Ruby applications, so we no longer have a need for our own dedicated Gem server. You can point to rubygems.org or direct bundler to the github repos for all of the gems we are currently hosting. We hope this doesn’t cause any significant inconvenience and please leave a comment if you have concerns.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

Bundler + Cruise… Take Two

Pivotal Labs
Wednesday, November 25, 2009

Responding to my last post, Josh pointed out what should probably be obvious: It’s likely a bad idea to bundle bundler. There’s a potential for version conflicts.

For our second attempt, we’re

As a second attempt, we’re now cribbing from the continuous integration setup from the Rails project. So far, so good:

Our RAILS_ROOT/cruise_config.rb…

require 'fileutils'

Project.configure do |project|
  project.build_command = 'sudo gem update --system && ruby lib/cruise/build.rb'
end

And, the referenced lib/cruise/build.rb (the important parts)…

#!/usr/bin/env ruby
require 'fileutils'
include FileUtils

def root_dir
    @root_dir ||= File.expand_path(File.dirname(__FILE__) + '/../..')
end

def rake(*tasks)
  tasks.each { |task| return false unless system("#{root_dir}/bin/rake", task, 'RAILS_ENV=test')}
end

build_results = {}

cd root_dir do
  build_results[:bundle] = system 'gem bundle'  # bundling here, rather than in a task (not in Rails context)
  build_results[:spec] = rake 'cruise:spec'
end

failures = build_results.select { |key, value| value == false }

if failures.empty?
  exit(0)
else
  exit(-1)
end

Thanks go to

  • Josh Susser for help via email
  • John Pignata for suggesting we look at the Rails project
  • Rails team for the reference scripts

More comments and suggestions are encouraged.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

Bundle(r) Yourself… and Get Ready to Cruise!

Pivotal Labs
Tuesday, November 17, 2009

On a current project, we’ve just switched from GemInstaller to Bundler for managing our application’s gems.

All in all, the transition was painless… in our development environments. Of course, in order to keep things on running smoothly on the continuous integration box, we amended our rake cruise:spec task to start by running sh "gem bundle".

So, wonderful! Any changes to our gem dependency list will picked up when cruise starts and made available for that “build”; there’s no need to log in and make any manual updates. Right?

Not quite. We’re using the disable_system_gems option and, in that case, Bundler (very intentionally) modifies your GEM_PATH such that only “vendor’d” gems are available to the application. Which of course means that Bundler itself, being “a gem to bundle gems”, is unavailable when that sh "gem bundle" command is run.

Our solution: bundle Bundler, obviously! That’s right, in our Bundler Gemfile we’ve included gem "bundler". Now, after a single manual execution of gem bundle to pick up the bundled Gem Bundler gem bundle (heh), any subsequent Gemfile changes (to gems other than Bundler) get picked up at the start of the build… and we’re cruisin’

Have another solution? Please let us know in the comments.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

hubcut your gems from github to gemcutter

Pivotal Labs
Friday, October 30, 2009

This week we moved our gems over to GemCutter. It’s very easy to claim the gems that were automatically synced over from RubyForge, but if you have gems on GitHub it takes a little more work. We had several gems to move over, some of which had quite a few versions we wanted to preserve. You can’t just push the .gem files over because GitHub built the gem namespaced with your username (e.g. “pivotal-apdex”). So Sam and I built a little script to pull down the gems from gems.github.com, fix the name in the spec, repack, and push up to GemCutter.

You can get the hubcut script at http://gist.github.com/220908

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

Standup 3/27/2009: Gem repo forked?

Pivotal Labs
Friday, March 27, 2009

Interesting Things

  • IE doesn’t allow you to change the type of an input. If you create an input with createElement, IE will not allow you to change that element to a button. This was discovered when a project’s javascript dom builder code was modified to generate inputs of type=button rather than type=submit. The cross-browser solution was to create some other temporary dom element such as a div and then set the innerHtml of that element to a type=button input, then extract that child element and return it in the builder call. Yeah!

Ask for Help

“What’s the best way to get gems for forked repos?”

There was quite a discussion on this. The specific issue is that the team is trying to use Compass (Chris Eppstein gave a talk on Compass at Pivotal on 3/18-look for the future video on our Talks Page.) For the moment, since compass depends on the edge version of sass you must first manually install sass before installing the compass gem.

  • One suggestion was to submit a fix for the gem. This is not a good solution in this case since the new version of sass/haml is expected to be released soon, fixing compass and simplifying its installation.
  • Pivotal will likely host its own internal gem server at some point to deal with issues like this.
  • The Has My Gem Built Yet? service might be useful in some situations, but not for this specific problem.
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Pivotal Labs

Standup 11/11/2008: Firefoxen goodness

Pivotal Labs
Thursday, November 13, 2008

Interesting Things

  • In response to our ask for help, Ray Baxter answered us in code with a script called Firefoxen. “It’s a script to automatically configure multiple installations of Firefox so that they open with different profiles.” You can grab Firefoxen on GitHub. Thanks Ray!
  • IE7 sends odd Accept headers (*.* instead of an explicit text/html) which can cause undesired behavior in Rails. Someone suggested manually setting the format in a before_filter:

    params[:format] ||= 'text/html'

    This was discouraged because it can cause problems elsewhere. A better solution was to put the html format at the top of any `respond_to` blocks:

    def show
      respond_to.html # run by default when type cannot be determined
      respond_to.js
    end
    
  • The Ruby MySQL Gem version 2.7 leaks memory for very large queries. The solution is to remove the 2.7 gem and manually install version 2.8 from source. This library is no longer a gem and must be installed from the mysql-ruby-2.8 tarball.

Ask for Help

“As a followup to Firefox SSL certificate problems…”

It turns out that our server running nginx had an old version of OpenSSL installed. Upgrading OpenSSL solved the problem.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Topics

  • agile (780)
  • rails (113)
  • testing (88)
  • ruby (83)
  • ruby on rails (70)
  • jobs (62)
  • javascript (55)
  • techtalk (44)
  • rspec (38)
  • ironblogger (32)
  • productivity (30)
  • activerecord (29)
  • gogaruco (29)
  • git (28)
  • nyc (27)
  • rubymine (26)
  • bloggerdome (23)
  • mobile (22)
  • process (21)
  • pivotal tracker (20)
  • cucumber (20)
  • jasmine (19)
  • design (18)
  • ios (18)
  • webos (17)
  • objective-c (17)
  • android (16)
  • palm (16)
  • "soft" ware (16)
  • fun (15)
  • tracker ecosystem (15)
  • ci (15)
  • cedar (15)
  • rails3 (14)
  • performance (14)
  • bdd (14)
  • gem (13)
  • css (13)
  • tdd (13)
  • selenium (12)
  • goruco (12)
  • bundler (12)
  • meetup (11)
  • railsconf (11)
  • nyc-standup (11)
  • capybara (10)
  • mac (10)
  • mojo (10)
  • chef (10)
  • api (10)
Subscribe to gems Feed
  • About
  • Case Studies
  • Team
  • Community
  • Careers
  • Contact
  • Labs
  • Events

Contact Us

contact@pivotallabs.com
+1 415-77-PIVOT
TwitterLinkedInFacebook

Pivotal Tracker

Tracker is the award-winning agile project management tool that enables real-time collaboration around a shared, prioritized backlog.
Visit pivotaltracker.com >