Continued from Let’s Write a Gem, Part 1
In this post we’ll finish the gem. Along the way I’ll point out some confusing pitfalls you can run into when testing gems locally.
9. Behavior Driven Development
Next up it’s time to pick a testing framework for our Gem. Let’s go with RSpec just because.
9.1 Modify the gemspec to include rspec and rake.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | |
9.2 Modify the Rakefile to include rspec and default to test
This will let us just run rake to test our gem.
1 2 3 4 5 6 7 | |
9.3 Run bundle update to install the gems for development
1 2 3 4 5 6 7 8 9 10 | |
There. Now running rake will run our specs.
1 2 | |
10. Write some tests
To keep this tutorial about the gem, let’s keep the testing very simple.
- Create a
/specdirectory - Create
/spec/spec_helper.rb
1
| |
- Create
/spec/lib/periodic_table_spec.rb
1 2 3 4 5 6 7 8 9 10 | |
Now running rake shows that our tests are being loaded, executed, and our gem is being automatically pulled in.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
Of course running rspec spec will work too.
Note: you don’t need to have the gem installed in order to run the tests. The files are loaded and tested directly from /lib. This is great because it really encourages a good “red, green, refactor” development process.
If you really want to run the gem in the irb and you don’t want to deal with
uninstall/reinstall then you can just run bundle console. That command will
land you in a console environment with the gem already loaded.
11. Write the gem to pass the tests.
Hey, this is the actual gem writing part!
Ok, we’ll use savon to talk to the periodic table SOAP API, so that dependency is next.
- add savon as a runtime dependency
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | |
- bundle install to get savon
1
| |
Now we just need to code up enough of the API to pass the test.
- create
lib/periodic_table/periodic_table_api.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | |
- add the periodic_table_api to the main gem file
1 2 3 4 5 6 7 8 | |
- test
1 2 3 4 5 6 | |
Yeah! Passing tests (ok test). Must be time to launch.
Aside: bundle console
While developing a gem don’t forget about that bundle console command. Running
it will drop you in an irb session with the current bundler (not system)
environment loaded. Since you’re in a gem with a good gemspec, that means that
the PeriodicTable files will all be already loaded. Check it out.
1 2 3 4 5 6 7 8 9 10 11 | |
1 2 3 | |
This is super handy while you are writing your gem and you want to dive into a piece of it without having to jump through any hoops to get the lib directory loaded.
12. Test the gem locally
Ok. We’ve got a gem that’s ready for release. This is where all most gem writing tutorials will jump right to putting gems on RubyGems.org. Now RubyGems is amazing and awesome, but you might not be ready for the public to get to this code. What if you want to test your gem locally? What if you want to keep your gem in a private github repo? No problem! Here’s how to keep on rolling.
Let’s say we’re in another project on the same machine as the gem we just developed. The magic of bundler means we can just include the new gem’s path right in that project’s Gemfile. Similarly you can push the gem git repo up to a private git repository and put that repo URL in the Gemfile.
:path- load the gem from a local path:git- load the gem from an accessible git repo
A neat thing about adding your gems this way is that you won’t need to keep
rebuilding the gem with the rake build or rake install commands because the
gem files will be loaded directly. Just like they are when we run our rake
tests.
12.1 Include the gem in a Gemfile with :path or :git
1
| |
1 2 | |
After that, just a quick bundle install will ensure that the gem source is all
loaded and ready to roll.
There’s just one thing to keep in mind. Because the gem won’t actually be
installed to the “system” gems (i.e. your rvm gemset) it won’t be listed in gem
list and won’t be directly loadable in an irb session or a script.
Let me repeat that, because I spent way too much time going crazy trying to figure this out.
When you install a gem via :path or :git it will not be listed in gem list
and will throw a LoadError if you try to require it directly in irb.
What’s the answer? How can you actually use the gem? Two bundler commands.
12.2 Start irb and run scripts using the bundle command
bundle exec: if you want to run a script that requires your gembundle console: if you want to use your gem in irb
“But Stephen”, you might say, “you’ve already told me about bundle console twice already.”
Yes, because it’s really frustrating when you have a gem that has passing tests
and runs beautifully from a rake install, but will simply not behave when you
try to run it after dropping a :path or :git to it from other project’s
Gemfile.
bundle exec
Say you’ve got the following contrived script.
1 2 3 | |
Nothing fancy. But if we’ve included the periodic_table gem via :git or :path it
will absolutely not work if we try and run it directly.
1 2 3 4 | |
Bundle exec to the rescue!
1 2 3 4 5 | |
Woo!
bundle console
Just like we’ve already gone over. bundle console will load up an irb session
in your current bundler environment. That is, all the gems specified in the
Gemfile will be loaded and not just the system gems.
1 2 3 4 5 6 | |
13. Release to RubyGems.org
Ok, I guess this tutorial wouldn’t be complete without the requisite releasing of the gem to RubyGems.
1 2 | |
Bundler will walk you through everything you need from there.
It really is that easy.
Wrap it up
Well readers, I hope this long two part tutorial hasn’t completely scared you away from the Ruby gem creation process. It really is very easy, there are just a few steps and practices to keep in mind.
Once you have the process down though, you can actually take a gem from idea to worldwide distribution as fast as you can code it. That’s absolutely astonishing and one of my favorite aspects of Ruby. Anyone, anyone can write some code and have it hosted on a high-speed, always-on, super easy to use code distribution platform available to every single Ruby developer out there with an Internet connection. Think about that. What an awesomely powerful aspect of our community!
So get out there and share an idea. :-)
Check out the “Let’s Write a Gem” Reddit discussion