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
andlib/gem/
structure for code. - Put any executables in
bin
, any data files indata
and tests intest
orspec
. - 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
- Edit my_lib.gemspec, adding
gem.add_development_dependency 'rspec'
andgem.add_development_dependency 'rake'
near the bottom. - Add
Bundler.setup
andrequire 'my_lib'
to the top of spec/spec_helper.rb to ensure your gem dependencies are loaded when you run your specs. - Add
require "rspec/core/rake_task"
andtask :default => :spec
to your Rakefile, so that runningrake
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
How to Set Up Cascade Deleting in Rails
Creating Routes with an Optional Path Prefix
How Mix in Routes in Sinatra for a Better Structure
What Does the Fail Keyword Do in Ruby
Ruby Rails - Select Only Few Columns from the Data Base
Rbenv Irb History Is Not Saving
Rails: Render View from Outside Controller
Ruby 2.0 How to Uninclude a Module Out from a Module After Including It
Could Not Find 'Cocoapods' (>= 0) Among N Total Gem(S) (Gem::Loaderror)
Directory Layout for Pure Ruby Project
How to Use Jquery-Tokeninput and Acts-As-Taggable-On
Ruby Methods and Optional Parameters
How to Check If a Variable Is a Number or a String