Let's say I have a gem called foo, with a file structure like so:
foo.gemspec
test_foo.rb
lib/foo.rb
lib/foo/file1.rb
lib/foo/file2.rb
The file test_foo.rb
contains some code which I use to try out my gem. It accesses the gem's code with the following line:
require './lib/foo'
Then, lib/foo.rb
acceses the other files required for the gem like so:
require './lib/foo/file1'
require './lib/foo/file2'
Since test_foo.rb
is in the root of the gem directory, it is necessary to require the files in lib/foo
with the complete path from the root directory.
This all works fine, and allows me to immediately play around with my gem by changing the code used in test_foo.rb
.
However, if I then want to build the gem, I then have to change my calls to require
, like so:
require 'foo/file1'
require 'foo/file2'
instead of
require './lib/foo/file1'
require './lib/foo/file2'
Which is kind of tedious to do every time I want to build the gem.
So, I thought of another way of trying it out, which was to use rake
to automate the build and install of the gem, something like this:
task :build do
`gem build foo.gemspec`
`gem uninstall foo`
`gem install ./foo-0.0.0.gem`
end
and then when I made changes to my code, and wanted to try it out, just run rake build
, and call require 'foo'
in test_foo.rb
.
But that is quite a slow process, and feels a bit like it defeats the point of ruby being a language where you don't have to build your code before trying it out.
So, my question is, what's the best workflow to use when actively developing a gem, and testing it out?
The rub test includes rubbing your ruby across a smooth (but hard) surface, like glass, and seeing if the gem leaves any color behind. Glass only has a hardness of 5 on the Mohs scale, so it's much softer than actual rubies. Real rubies and some gems shouldn't leave any color behind, but cheap fakes or imitations can.
Install BundlerSelect Tools | Bundler | Install Bundler from the main menu. Press Ctrl twice and execute the gem install bundler command in the invoked popup. Open the RubyMine terminal emulator and execute the gem install bundler command.
It's error-prone and confusing having to mess with require
paths within your source files in order to make the code run in different situations (local testing, "production" as a gem etc.).
There are some tricks you can use to "write once, run anywhere" (sorry Java, for stealing). This is also what you should aim for, using these standard practices makes it easier for others to understand your code or help you out if something doesn't work as expected.
Your code already follows the common gem directory layout, nothing needs to be changed there. But, as a general rule of thumb, you should use require
to load "libraries" and require_relative
(if you are on Ruby >1.9) to load source files relative to the one you are working on.
1) Your file test_foo.rb should only contain a line:
require 'foo'
That is you omit the './lib' part from the require path. The entry point for the gem (the "require_path") is commonly chosen to be "lib". More on that later (3).
2) Next, in foo.rb, use require_relative
(again assuming Ruby 1.9) to pull in the relative source files:
require_relative 'foo/file1'
require_relative 'foo/file2'
3) To set the require_path to lib for your gem, it's considered good practice to add a gemspec file.
To test your code (for example with your test_foo.rb), you have several options:
4) You can run test_foo.rb with ruby's -I
option to include the lib/ directory in its load path:
ruby -I lib test_foo.rb
This is much less obtrusive than adding additional includes directly in your code.
5) Bundler is an even better way to manage your code/gem in order to test it under "real-life" conditions, especially if you develop two or more gems at once that rely on each other. Bundler gives you the option to reference these gems locally, so there's no need to deploy them formally in order to test their functionality during development. To use Bundler, you would add a Gemfile to the root directory of your gem code.
6) Finally, for convenience, it makes sense to integrate common steps in your development process into a Rakefile, using rake to drive the processes.
I use these techniques quite a bit in my own projects, here are some links for your reference to give you some concrete examples:
1) Test files just contain a "require 'foo'"
2) Use "require_relative" from your "main" file to pull in subsequent parts
3) Example gemspec with require_path set to "lib"
5) Example Gemfile
6) Example Rakefile
Of course you don't need to use all of these techniques, you'll probably be fine with using steps 1-4, but as your project grows larger, the additional effort will definitely pay off.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With