What Is the Modern Way to Structure a Ruby Gem

What is the modern way to structure a ruby gem?

Some posts that I have found useful:

  • http://chneukirchen.github.com/rps/
  • http://tomayko.com/writings/require-rubygems-antipattern
  • http://yehudakatz.com/2009/07/24/rubygems-good-practice/
  • http://weblog.rubyonrails.org/2009/9/1/gem-packaging-best-practices

Edit (2012-01-10): An excellent all-around guide to gem best practices is RubyGems Guides. I would highly recommend starting here now.

To summarize the key points:

  • Use the basic lib/gem.rb and lib/gem/ structure for code.
  • Put any executables in bin, any data files in data and tests in test or spec.
  • Don't require or depend upon files outside of the load path. (VERSION files often seem to live in odd places in gems.)
  • Do not require 'rubygems'.
  • Do not tamper with the $LOAD_PATH.
  • If you find yourself writing require File.join(__FILE__, 'foo', 'bar'), you're doing it wrong.

What's the standard file structure of a Ruby project?

There isn't necessarily any set way to define a Ruby project. But, generally RubyGems have this file structure:

.
├── bin
│   └── mygem
├── Gemfile
├── lib
│   ├── mygem
│   │   └── version.rb
│   └── mygem.rb
├── LICENSE
├── mygem.gemspec
├── Rakefile
└── README.md

Of course, there is also other directories and files in the project root, for tests, .ruby-version, etc.

How should I structure my ruby gem command line service?

Largely, RubyGems will take care of this for you. You'll need to include your executable in the files list, and put it in the executables in your gemspec. It's common to put your executable in bin in your directory, e.g.:

$ ls
bin/ myapp.gemspec lib/ Rakefile
$ ls bin
bin/myapp

Your gemspec would then look like:

Gem::Specification.new do |s|
s.name = 'myapp'

# whatever else is in your gemspec

s.files = ["bin/myapp","lib/myapp.rb"] # or whatever other files you want
s.executables = ["bin/todo"]
end

At this point, when users install your app via RubyGems, myapp will be in their path, and lib will be in your app's loadpath, so your executable can simply start off with:

#!/usr/bin/env ruby

require 'myapp'
# whatever other requires

The only issue with this is that, during development, you cannot just do bin/myapp and have your app run. Some devs manipulate the load path via $: or $LOAD_PATH, but this is considered bad form.

If you are using bundler, it's easiest to just run your app locally with bundle exec, e.g. bundle exec bin/myapp. You can alternately use the RUBYLIB environment variable, e.g. RUBYLIB=lib bin/myapp, which will put lib in the load path.

In depth Ruby Gem development resources (book, video, sites)

Rubygems aren't related to distributed programming.

Can you please provide more details about what you're after, if you aren't asking a duplicate question? Related questions within Stack Overflow include:

  • Gotchas for writing rubygems
  • Ruby : How to write a gem ?
  • What are the steps needed to create and publish a rubygem of your own?
  • What is the modern way to structure a ruby gem?

(I know this is more of a comment than an answer, but it's too big to fit in the comments section)

Structure of Ruby code which is not intended to be a gem

1) It sounds like this code is not at all dependent on Rails. As such, do not create a Rails project for it. That would be adding ceremony and dependency that are unnecessary.

2) There is nothing wrong with having sample files in a gem's git repo. You can put them in a directory named samples or examples, for example.

3) To create a conventional gem directory structure, use bundle:

bundle gem my_gem_name

4) Your tests should go in a directory named test if they are Minitest, or spec if they are RSpec.

What is the recommended folder structure for a Ruby command-line app with tests?


Unit Tests for Gems

If you're using MiniTest or similar, the expected place within gem foo would be foo/test. If you're using RSpec or minitest/spec, then you'd use foo/spec instead.

When you generate a new gem skeleton with Bundler it provides the following defaults:

$ bundle gem foo; tree foo
foo
├── Gemfile
├── README.md
├── Rakefile
├── bin
│   ├── console
│   └── setup
├── foo.gemspec
├── lib
│   ├── foo
│   │   └── version.rb
│   └── foo.rb
└── spec
├── foo_spec.rb
└── spec_helper.rb

4 directories, 10 files

The default Rakefile is set up to run RSpec, but you can certainly implement the Rakefile's default task and your tests any way you'd like.

Ideal Ruby project structure

I think that is pretty much spot on. By default, Rubygems will add the lib directory to the loadpath, but you can push any directory you want onto that using the $: variable. i.e.

$:.push File.expand_path(File.dirname(__FILE__) + '/../surfcompstuff')

That means when you have say, surfer.rb in that dir, you can require "surfer" anywhere and the file will be found.

Also, as a convention, classes and singletons get a file and modules get a directory. For instance, if you had the LolCatz module and the LolCatz::Moar class that would look like:

lib/
appname.rb
lolcatz/
moar.rb

That is why there is an lib/appname folder because most libraries are in the appname namespace.

Additionally, if you try running the command newgem --simple [projectname] that'll quickly generate a scaffold for you with just the bare essentials for a Ruby project (and by extension a Ruby Gem). There are other tools which do this, I know, but newgem is pretty common. I usually get rid of the TODO file and all the script stuff.

How to create a new Ruby gem?


Use Bundler

From the command line:

bundle gem your_new_gem

This will create a directory called your_new_gem with just a basic set of files and directory structure that are now considered best-practice. It's quick, easy, and a great place to start.

How do I set up a basic Ruby project?

To get a good start, you can use the bundle gem command and rspec --init.

~/code $ bundle gem my_lib
create my_lib/Gemfile
create my_lib/Rakefile
create my_lib/LICENSE.txt
create my_lib/README.md
create my_lib/.gitignore
create my_lib/my_lib.gemspec
create my_lib/lib/my_lib.rb
create my_lib/lib/my_lib/version.rb
Initializating git repo in /Users/john/code/my_lib
~/code $ cd my_lib/
~/code/my_lib $ git commit -m "Empty project"
~/code/my_lib $ rspec --init
The --configure option no longer needs any arguments, so true was ignored.
create spec/spec_helper.rb
create .rspec
  • code goes in lib
  • specs go in spec
  • test data or documents go in spec/fixtures/
  • Require all your ruby files in lib/my_lib.rb. You can define your exceptions in that file, too, or in their own files -- according to your own preference.
  • C source files go in ext/my_lib
  • shell scripts and executables go in bin

When in doubt, just look at how other gems are laid out.


Further information:

You should add rspec as a development dependency in your gemspec to make things easier for other developers

  1. Edit my_lib.gemspec, adding gem.add_development_dependency 'rspec' and gem.add_development_dependency 'rake' near the bottom.
  2. Add Bundler.setup and require 'my_lib' to the top of spec/spec_helper.rb to ensure your gem dependencies are loaded when you run your specs.
  3. Add require "rspec/core/rake_task" and task :default => :spec to your Rakefile, so that running rake will run your specs.

While you're working on your newest creation, guard-rspec can save you time and hassle by automatically running your specs as files change, alerting you to spec failures.

~/code/my_lib $ git add spec/spec_helper.rb
~/code/my_lib $ git commit -am "Add RSpec"
~/code/my_lib $ vim my_lib.gemspec # add guard development dependency
~/code/my_lib $ bundle
~/code/my_lib $ bundle exec guard init
~/code/my_lib $ vim Guardfile # Remove the sections below the top one
~/code/my_lib $ git add Guardfile
~/code/my_lib $ git commit -am "Add Guard"

After you're happy with your creation, push it up to github

# create a github repository for your gem, then push it up
~/code/my_lib $ curl -u myusername https://api.github.com/user/repos -d '{"name":"my_lib"}'
~/code/my_lib $ git remote add origin git@github.com:myusername/my_lib.git
~/code/my_lib $ git push

Then, when you're ready to release your gem on Rubygems.org, run rake release, which will walk you through the steps.

~/code/my_lib $ rake release

Further References

  • The Rubygems patterns guide (and home page), from Matheus Moreira's answer. They're really great references
  • How I Start by Steve Klabnik
  • Exercise 46: A Project Skeleton from Zed Shaw's Learn Ruby The Hard Way
  • New Gem with Bundler video on Railscasts
  • docs


Related Topics



Leave a reply



Submit