This was originally written for Coding in the Crease. View the original post here.
Over the past few weeks Iâve spent a lot of time working on a few of the modules we use here at Sport Ngin - mostly Ruby gems such as Opsicle, our OpsWorks CLI gem. As Iâve acquainted myself with architecting and testing gems locally, the use of git in gemspecs caught my attention.
Gemspecs and âgit ls-filesâ
If youâre unfamiliar with gems, a .gemspec
file contains a Specification class with setter methods to assign various
bits of information on a gem. This class is then used to build the gem. One of the attributes, named files
, contains
all of the files you wish to include in your build.
Itâs very common to see a shell command with git used for files
. Usually some variation
of git ls-files
is used, which outputs a full list of your projectâs files.
If you use Bundler to generate a new gem scaffold by running bundle gem <gemname>
, youâll get a gemspec
file like this:
An issue you might git
Using git ls-files
to get all of the files in the directory seemed pretty clever to me.
Yet regardless of git being the de facto VCS of choice for most gems
(Here, here,
here, everywhereâŚ), I still found it interesting that a shell command to an
external tool with its own configuration would be used. I shrugged my shoulders and continued working, until I ran
in to a small issue that took me a few moments to figure out:
Using git ls-files
, you get all of the files in the git index and the working tree (documentation,
here). That means if youâre playing around with a gem for use in another
project, you canât leave your musings in untracked files to test.
For example, say youâre tinkering with âexamplegemâ and you add a new file:
/examplegem/lib/examplegem/newfile.rb
. If youâre using something like gem "examplegem", :path => "../examplegem"
in the Gemfile of a project in a sibling directory for local development, that file wonât be included in the link.
If itâs something really basic, you wonât get any error for the functionality being missing when you try and test it.
Youâve got [at least] a few options:
-
Add your files to the index
Even if you donât plan on actually committing yournewfile.rb
, usinggit add newfile.rb
will add it to the index and thus be included in your gemspecâs files. -
Add flags to your git command
You could add--deleted
,--modified
, or--others
(i.e. untracked files) to your git command in the gemspec, though this sort of solution seems hacky to me. -
Stop using git for your gemspecâs files
You donât need it.Instead of using git, you could use something along the lines of this:
spec.files = Dir.glob("{bin,lib}/**/*") + %w(LICENSE README.md)
Or if youâre looking for a well-known example, hereâs the gemspec from rails/actionpack:
spec.files = Dir['CHANGELOG.md', 'README.rdoc', 'MIT-LICENSE', 'lib/**/*']
Using git affects your project
This subtle little side effect may seem insignificant, but I do like to know the consequences of my decisions, even the minor ones. Itâs probably not often that you run in to issues with completely unindexed files in your gem, using it in the situation I managed to find myself in.
Still, this got me curious and I looked around at other projects and how the use of git may be a good or bad thing.
Take Bundlerâs gemspec, where they have to append /man/
because they want it in the gem, but not under source control when you generate it locally:
On the opposite end of the spectrum, check out Vagrantâs gemspec,
where Mitchell Hashimoto isnât afraid to put forth some effort to get exactly what he wants based on .gitignore
without actually using git:
A more âcorrectâ solution?
As is the case with most things in development, the answer to the correctness of any of these methods is It Dependsâ˘. Do you care whether or not a bunch of extra files are in your gem? Do you want to meticulously pick out which files you want in the gem? For many projects this may be a fairly inconsequential issue, and you shouldnât spend a great deal of time thinking about the perfect solution unless you are working on a very large project with specific needs.
Using git ls-files
is a fine default for Bundler to feed you when you want to throw together a gem quickly -
but no matter what you actually go with (Iâm going with git, myself) considering these small details and their [minor] consequences
is important when trying to architect something well.