Skip to main content

Mobomo webinars-now on demand! | learn more.

This post is a part of Polishing Rubies, an end-to-end guide to Ruby open source development.

« Back to Part 2: Creating a Gem

A good toolchain is important for any development project. It makes the lives of developers easier by abstracting away or automating repetitive tasks. You should always spend some time at the outset of a project making sure you're using all of the best tools available. On an open source project this is 10 times more important.

Instead of building a toolchain that will be used by your company or small team, you're building a toolchain that will potentially be used by dozens, hundreds, or even thousands of other developers. An annoyance that costs five minutes, multiplied by all those developers, equals hours and hours of lost potential productivity. Now that we've bootstrapped the directory structure of our gem, it's time to get our toolchain up and running so that development for you (and all of your contributors some day) is as painless and fast as possible.

Test Setup

Having tests for your gem is fundamentally important. If you have no tests, you have no way to vet incoming pull requests, check for regressions between versions, or really have any control over the quality of your project. Luckily, the Ruby community has a strong testing culture so there are great tools to use and widespread adoption of testing practices.

I'm going to walk through how to set your gem up for testing via RSpec. In general the Ruby community nearly universally uses either RSpec or the built-in Test::Unit as its framework of choice. I would love to cover Test::Unit as well, but I don't have enough of a grasp on best practices to be able to tell you how to do that here. If you would like to contribute such a section to this guide, by all means please fork it on GitHub and I'll be happy to update the content.

The first step to using RSpec in your tests is to add it to your project's gemspec:

gem.add_development_dependency 'rspec', '~> 2.9' 

This tells RubyGems that RSpec is a necessary library for developers who are going to try to contribute to your gem. Once you've added the dependency, run bundle to update your dependencies with Bundler. Note that because Bundler is capable of reading dependencies from a gemspec you don't need to manually specify them in your Gemfile.

With RSpec, it's a good practice to specify the most recent minor version (the second number in the MAJOR.MINOR.PATCH versioning system) for your dependency. This way if RSpec updates to a new major version that breaks existing functionality, you can review any breakages and fix them with a new release of your gem rather than having unstable tests in the wild.

Now that you have RSpec in your project's bundle, you should run bundle exec rspec --init. This is a simple generator that will bootstrap your project's RSpec setup. The output should look something like this:

create   spec/spec_helper.rb create   .rspec 

The .rspec file is an options file that lets you specify the default command-line options (such as output format and color) that will be used by RSpec's runner in your project. If you've written tests before, you should be familiar with spec/spec_helper.rb as a central place to bootstrap your test suite and add any dependent library requires, etc.

Because we're writing a gem we will want to set up our spec_helper.rb file to work with our gem's structure. Alter the file to look like this:

$:.unshift File.dirname(__FILE__) + '/../lib' require 'my-gem'  RSpec.configure do |config|   config.treat_symbols_as_metadata_keys_with_true_values = true   config.run_all_when_everything_filtered = true   config.filter_run :focus end 

The first line adds our gem's lib directory to Ruby's load path. This means that when we say something like require 'some-file' Ruby will automatically look in our gem's lib directory to see if that file exists. The second line, require 'my-gem', is the same line that your end users will type when they want to start using your gem. This allows us to write tests from the perspective of someone who has installed the gem and required it, either manually or automatically through a tool like Bundler.

Now that we have our spec_helper all set up, let's go ahead and create our first spec. Create a file spec/my_gem_spec.rb (replace my_gem with your gem's name) and put this in it:

require 'spec_helper'  describe MyGem do   it 'requires additional testing' end 

Now simply run the rspec command in your project's root directory. You should see output that looks like this:

Run options: include {:focus=>true}  All examples were filtered out; ignoring {:focus=>true} *  Pending:   MyGem requires additional testing     # Not yet implemented     # ./spec/my_gem_spec.rb:4  Finished in 0.00015 seconds 1 example, 0 failures, 1 pending 

Congratulations! You've got your gem's test suite up and running.

Testing With Rake

Another standard practice amongst all Ruby projects (apps and gems alike) is to make it possible to run the tests for a project via a Rake task. This is remarkably easy; all you need to do is open up the Rakefile in your project's directory and add the following to the bottom of your Rakefile:

require 'rspec/core/rake_task' RSpec::Core::RakeTask.new task :default => :spec 

This does two things: first, it creates a new RSpec rake task that, by default, can be run by typing rake spec at your project's root. Second, we assign the :default task to be :spec. This means that in your project's root you can simply run rake and all of the tests for your project will run. This is a convention in the Ruby community and a good practice for all projects.

To verify everything we've done we can simply run rake -T which is a command to list the available Rake tasks. You should see something like this:

rake build    # Build my-gem-0.0.1.gem into the pkg directory rake install  # Build and install my-gem-0.0.1.gem into system gems rake release  # Create tag v0.0.1 and build and push my-gem-0.0.1.gem to Ru... rake spec     # Run RSpec code examples 

As you can see, we have four tasks available. Three of them are provided to us by Bundler for packaging and releasing our gem, and one of them is provided by RSpec for testing our gem.

Autotesting With Guard

Guard is a gem that allows you to observe your project's files for change and perform actions based on that change. The first thing you will need to do is add Guard (and its helpers for Bundler and RSpec) to the end of your Gemfile:

gem 'guard' gem 'guard-rspec' gem 'guard-bundler' 

Note that, differently from RSpec, I prefer to add Guard and its related gems to my project's Gemfile, not its gemspec. This choice comes down to personal preference, but while RSpec is required for someone to do development on my gem, Guard is really more of a nice to have and so I relax the dependency somewhat. The effect is the same for contributors: simply running bundle in the project's directory will install all of the dependencies from the gemspec and the Gemfile. Speaking of which, you should now run bundle in your project root to install Guard if it isn't already.

Now we need to initialize Guard and configure it for our project. Luckily, Guard comes with its own command line helpers:

guard init guard init bundler guard init rspec 

Guard works similarly to Bundler and Rake by creating a Guardfile in your project's root directory. These commands automatically add example configuration for each guard type to the Guardfile (after guard init creates the file to begin with). While I'm going to tell you explicitly what to put in your Guardfile, the init commands can really help jog your memory if you're trying to do it from scratch. Let's modify our Guardfile to look like this:

guard 'bundler' do   watch('Gemfile')   watch(/^.+.gemspec/) end  guard 'rspec', :version => 2 do   watch(%r{^spec/.+_spec.rb$})   watch(%r{^lib/(.+).rb$})     { |m| "spec/#{m[1]}_spec.rb" }   watch('spec/spec_helper.rb')  { "spec" } end 

Guard works by watching certain files in your project and then performing actions when those files change. The first guard tells us to watch for the Gemfile and the gemspec to change, and re-bundle the application when that happens. The second guard tells us to watch all of our RSpec test files, our spec_helper, and the corresponding library files for each of our RSpec tests. This means that when you change a file, Guard can automatically re-run the tests for that specific file only.

Now that your Guardfile is properly configured, you can just run bundle exec guard from your project's root directory. This will get Guard up and running and you will now automatically run tests and re-bundle as you build your gem. Running Guard makes the development feedback loop as tight and automatic as possible.

Continuous Integration

Continuous Integration is the process of automatically running the tests for a project when new code is pushed to the central repository. Luckily, a team of developers has built Travis CI that provides free continuous integration for open source projects of all types.

Why do you need continuous integration for an open source project?

  • Travis allows you to test your code across multiple versions of Ruby. Even if you don't personally develop on older versions of Ruby or alternative implementations such as JRuby or Rubinius, Travis can run your tests on these Rubies and notify you of any build failures.
  • Travis gives you the confidence to merge pull requests from the web. If someone submits a pull request you can merge it into your repository knowing that if it breaks your test build Travis will let you know. In the future, Travis will even support testing pull requests before you merge them.

In addition to the benefits provided by Travis is it exceptionally easy to implement. Here's how:

  1. Go to http://travis-ci.org and sign in with your GitHub account. Travis needs access to your GitHub info to be able to set up the automatic build process for each repository.
  2. Hover over your name once logged in and go to your profile. This lists out your open source repositories; from here you can simply "switch on" CI for each project.
  3. Set up a .travis.yml file in your project.

We haven't yet created a GitHub repository for our project, so steps 1 and 2 will actually come a bit later. However, we can set up the configuration file so that when we're ready to push to GitHub we will get continuous integration immediately. To set up Travis for your project you need to create one more configuration file in your repository. Make a file called .travis.yml (the leading dot is not a typo) and add this to it:

rvm:   - 1.9.3   - 1.9.2   - jruby   - rbx script: "bundle exec rake" 

As you can see, the configuration is very straightforward. The rvm key allows us to specify the different versions of Ruby we want to test against. The script key tells Travis what command to run for the build. Any command that exits with a non-zero status code (a Unix standard for a command that had errors) will automatically fail the build in Travis. In our case we're going to run bundle exec rake which, as explained above, has been aliased to running our RSpec tests via Rake.

Once you've added your Travis configuration file, it's time to create another commit:

git add . git commit -m "Adds RSpec, Rake testing, Guard, and Travis." 

All right! Now we've not only created a gem but we've added robust tools for testing that will give us and other contributors the power to develop quickly and in a test-driven manner. We've also set up continuous integration to let us test against many different versions of Ruby, even if we don't have them installed! We're well on our way to building a gem. Stay tuned for the next installment!

Continue to Part 4: Writing Library Code »

Categories
Author

RSpec macros (like those in the fantastic Remarkable library) ease spec writing for repetitive tasks, but is that process more effort than it’s worth? No — it’s actually quite easy to write and include macros for your specs to do some of the standard heavy lifting.

This is a great resource to get started with how to write the actual macros (or custom matchers) themselves.

What tripped me up was how to actually include them in my examples. Monkeypatching is rarely a clean process. And, while the comments on that post mentioned a public API, how to use that API wasn’t immediately obvious (as with many things about RSpec).

In my case, I was looking to define a quick login! macro that would allow me to easily create a before block to log in as a specified user or just as a FactoryGirl generated one in a TwitterAuth based app. Here’s the module I wrote and stuck in my spec_helper.rb:

module LoginMacro   def login!(user=Factory(:user))     before do       @current_user = user       controller.stub!(:current_user).and_return(@current_user)       session[:user_id] = @current_user.id     end   end end

So that was relatively straightforward and now what I wanted to be able to do was call it in my controller specs like this:

describe UsersController do   describe "#create" do     login!          it 'should etc. etc.'   end end

As it turns out, RSpec offers the ability to add macros and matchers via the config.extend and config.include methods on the RSpec configuration object (extend is for class-level macros like the one I wrote here, include is for instance-level matchers). You just have to add this to your spec helper’s configuration portion:

config.extend(LoginMacro)

Voila! Now your specs will be able to use the login! macro to set up a user in no time.

Experienced RSpec users are already familiar with this, but I find that certain things can be hard to come across if you don’t already know how to use them. So I thought I’d blog for the benefit of others like me: this is an easy way to use RSpec macros.

Categories
Author

In some cases you may have the need to run multiple Rails applications with shared functionality. While Rails 3 promises to bring “mounting apps in apps” and the ability to make the whole process simple, for now we’re stuck in the real world. However, it is possible to share components. This post will walk you through how to set up shared components that live in multiple Rails applications at once and even run specs properly.

A few notes before we begin:

  • This post focuses on models but the same could be applied to controllers, views, and more.
  • I use RSpec so the testing solutions come from that perspective.
  • My applications share a database, so I keep all of the migrations in one app and load from a duplicated schema.rb in the other.

Setting Up Your Application

First, you’ll need to create your shared directory. I’m mounting all shared components to my RAILS_ROOT/shared directory. So if I have app1 and app2 then I’ll do this in app1:

mkdir shared cd shared touch README git init git add . git commit -m "Initial import of shared repository." git remote add origin git@some.git:repository/online.git git push

At this point all we’ve actually done is create a git repository inside our app1 application in the shared directory and pushed it (with an empty README file) to a remote git repository.

What we need to do now is actually delete this git repository and re-add it as a submodule from the remote source (this is from the root of app1 again):

git submodule add git@some.git:repository/online.git shared git submodule init git add . git commit -m "Added shared directory as submodule"

What we did here is add the repository as a submodule using the git submodule command. We then ran git submodule init to update our .git/config to reflect the new submodule.
Finally we committed our changes.

So now we have a submodule living in our application directory, but right now it’s empty and Rails doesn’t know or care about it! Next we’ll set up Rails to make use of the components in the shared directory.

Setting Up Rails To Use Shared Components

Lets say that we’re going to create a shared model, SharedModel. We need to put it in the shared directory but still have it picked up by Rails’s lazy class loader. So in config/environment.rb you
will need to add the following:

config.load_paths += %W( #{RAILS_ROOT}/shared/models )

This tells Rails to look for classes in the shared models path. Now we create our model by creating shared/models/shared_model.rb:

class SharedModel < ActiveRecord::Base  end

When creating shared components I tend not to use Rails’s model generator, preferring instead to create the class by hand and generate a migration separately in my migration-having app.

This is actually all you need to do to get your shared components running in Rails. Next we’ll set up app2 to use the same code!

Setting Up The Second Application

To set up the second application, you basically need to simply repeat the same steps you did for the first application starting with git submodule add. So that would be:

  • Add the submodule and initialize it.
  • Add the shared directory to the load paths in config/environment.rb

As a note, if you are doing this to an existing application with multiple developers, other developers will simply need to pull from your main application once you’ve pushed it to a remote and run:

git submodule init git submodule update

To get the latest revision of the submodule locally for themselves.

Changing Shared Components

To modify shared components, just change them like you would normal files in your repository. The only difference is that when you want to commit changes you will need to do so from the shared directory, push and then make a new commit from the root directory. This way you are telling the root repository to track a new revision of the
submodule.

Testing Shared Components

So just because we’re sharing components doesn’t mean that we want to abandon TDD, does it? In fact, it brings up a somewhat interesting problem. I want to have specs that I can run for the shared components that can run in both applications, in fact I want these specs to run in both applications to make sure that the shared components aren’t
having any compatibility issues. While this isn’t extremely difficult to set up, it’s not easy, either.

The first step is to create a spec directory inside your shared submodule, and create a spec_helper.rb that simply points back out to the root application’s.

In shared/spec/spec_helper.rb:

require File.dirname(__FILE__) + '/../../spec/spec_helper'

We also need to create a pending spec for our SharedModel to make sure that these are running. In shared/spec/models/shared_model_spec.rb:

require File.dirname(__FILE__) + '/../spec_helper'  describe SharedModel do   it 'should have a pending spec' end

The good news is that if you run autospec from your shared directory, you should be able to see your pending spec run (you will need to create a second spec.opts file in the shared/spec directory for this to use your preferred options). You should push out the changes in your shared directory and get all of your applications up to date. The bad news is that this is the only place your specs will run at the moment. Let’s change that for the better.

Note: You will need to perform the following steps in each of your Rails apps that use the shared components.

First to get your specs running with rake spec we will need to modify the task found in lib/tasks/rspec.rake by performing the following substitution:

# When you see this... FileList['spec/**/*/*_spec.rb']  # Change it to this... FileList['spec/**/*/*_spec.rb', 'shared/spec/**/*/*_spec.rb']

That takes care of the spec rake tasks, but there’s still the matter of autotesting from your application root. This requires a custom .autotest file in your application root that looks like this:

Autotest.add_hook :initialize do |autotest|   autotest.add_mapping(%r%^shared/models/.*.rb$%) { |_, m|     autotest.files_matching %r%^shared/spec/.*_spec.rb$%   } end

This will automatically pick up changes to your shared models and re-run the specs for all of your shared models when they change. You could get more granular than this, but that’s a topic for another day.

Wrapping Up

Now that all of this is done you should be able to freely develop models in the shared directory and have them completely integerated into your normal development workflow. It’s quite a bit of work, but once you iron out the kinks it runs beautifully!

Categories
Author

RSpec is a great tool that has come to replace Test::Unit for many Rails developers. Autotest makes it go even faster, and has become an indispensable part of my development environment. However, it has always been somewhat-to-extremely difficult to use RSpec when developing Rails plugins. In this post I will walk through step-by-step how to get RSpec and Autotest working with your plugin.

This plugin is assuming that you are running Rails >= 2.1 and have already installed RSpec and RSpec::Rails as plugins in your Rails project like so:

script/plugin install git://github.com/dchelimsky/rspec.git script/plugin install git://github.com/dchelimsky/rspec-rails.git

And also gotten RSpec up and running by calling script/generate rspec.

Generate It

Luckily, I wasn’t the first person who ever wanted to create a plugin that was tested with RSpec. The Rspec Plugin Generator will do most of the heavy lifting for us when we start out. Just install it like so:

script/plugin install git://github.com/pat-maddox/rspec-plugin-generator.git

And you’re ready to get started. I’m assuming here that this is a brand new plugin, if it’s already in development you may need to run this in a fresh directory and then copy/paste files as needed to glue it together. Let’s say I’m writing a plugin called new_fu. I can generate an RSpec-ready plugin simply by calling:

script/generate rspec_plugin new_fu

This will generate the standard plugin structure as well as some extra files:

create  vendor/plugins/new_fu/spec create  vendor/plugins/new_fu/spec/spec_helper.rb create  vendor/plugins/new_fu/spec/new_fu_spec.rb

You can take a look at these to see how they work, but pretty simply they hook your plugin up so that it can be run with rake spec:plugins. Let’s add a simple example to our new_fu_spec.rb file:

require File.dirname(__FILE__) + '/spec_helper'  describe "NewFu" do   it "should have a pending spec" end

Now if you run rake spec:plugins you should see one pending spec. Congratulations, your plugin is now running on RSpec!

Autotest Like a Champ

Ok, so now we’re up and running with RSpec on our plugin. That’s great, but if you have several plugins in the same Rails app that all have specs, it starts to get messy when you run that rake spec:plugins. Not to mention how long it takes between runs! We need to get an autotest setup like we have for our main Rails app!

I struggled with getting this to work for a long time, so thanks to this post on Rails Symphonies for finally pointing me in the right direction. First we need to create an autotest/discover.rb file in our plugin’s lib directory. In that file, put this code:

$:.push(File.join(File.dirname(__FILE__), %w[.. .. rspec]))       Autotest.add_discovery do     "rspec"  end

This gets us almost exactly where we want to be. However, the first time I ran it I had two problems: some specs that I had written were strangely failing, and it wasn’t in color or following the rest of my spec.opts preferences from my main app!

To remedy this, we need a spec.opts in the spec directory of the plugin. You can either copy and paste it in from your Rails app (my recommendation if you are publishing your plugin) or you can just create a softlink back to it:

ln -s ../../../../spec/spec.opts spec.opts

That’s it! Now if you run autotest you should be running all of the specs for your plugin just as you would if you were running them for your app. Note that this doesn’t hook in to your app’s autotest, which may be desirable or undesirable to your specific needs.

Categories
Author
1
Subscribe to Rspec