I've been refactoring the specs for my VCR gem[1] to take advantage of the new shared example group parameterization. VCR supports both FakeWeb and WebMock, with an appropriate adapter class implemented for each. The adapter classes have nearly identical behavior, except for the differences in the feature set of FakeWeb and WebMock. WebMock supports a bunch of HTTP libraries that FakeWeb doesn't, and also supports matching requests on request headers and body. My specs basically boil down to this:
describe VCR::HttpStubbingAdapters::FakeWeb do it_should_behave_like 'an http stubbing adapter', ['net/http'], [:method, :uri, :host] end describe VCR::HttpStubbingAdapters::WebMock do it_should_behave_like 'an http stubbing adapter', %w[net/http patron httpclient em-http-request], [:method, :uri, :host, :body, :headers] end Basically, this is a shared example group that takes two parameters: an array of supported HTTP libraries, and an array of supported request match attributes. This design makes it easy to have a single shared example group that specs out the full behavior of both adapters. If I ever implement another adapter, it's very easy to spec it out this way, and pass arrays indicating the supported features. This works great on ruby 1.8.7 and above, but I'm getting an error on 1.8.6. This gist[2] demonstrates the error in far simpler form. The issue is due to the way I implemented module_eval_with_args on 1.8.6[3]. The lack of module_exec on 1.8.6 forced me to eval the block twice: once using instance_eval_with_args (so that the block is properly eval'd with arguments), and once with module_eval (so that we can extract the instance method definitions, in order to deal with the difference between module_eval and instance_eval). The error occurs in the module_eval: because it's not evaluated with any args, the methods we call on the args raise NoMethodErrors. Note that the "normal" use case of parameterized groups doesn't have this problem. shared_example_group_for :foo do |arg| it "can access the argument (#{arg}) in the doc string" { arg.should ... } end In this case, #it raises a no method error that is handled by our anonymous class. Plus the only method being called on the arg is #to_s, which is defined for every object. So this problem really only exists when you call methods on arguments that are not defined on Object, outside of the RSpec DSL methods. So...how should we deal with this? A few ideas come to mind: 1. Find a better way to fake module_exec on ruby 1.8.6. I'm not sure if this is even doable. 2. Leave things as they are...but I don't like this idea since the error message is fairly cryptic. 3. Rescue the error and raise a more clear error message. 4. Rescue the error and print a warning. I lean towards #4, because the #module_eval is only necessary to extract the instance method definitions. This error doesn't prohibit the shared example group from working on 1.8.6; as long as their are no instance method definitions, or the instance method definitions come before the error occurs, it can still work fine, I think. The warning can hopefully make it clear that you just need to put the instance method definitions first and everything will still work. The one really difficult part about options #3 and #4 is the wording of the waring/error: this is a fairly complex, nuanced error, and it's hard to boil it down into a sentence explaining the issue and how to fix it. Thoughts? Myron [1] http://github.com/myronmarston/vcr/commit/0f304c132eea7d0a7e32d6731e2557ce6805f31c [2] http://gist.github.com/539649 [3] http://github.com/rspec/rspec-core/blob/master/lib/rspec/core/extensions/module_eval_with_args.rb _______________________________________________ rspec-users mailing list rspec-users@rubyforge.org http://rubyforge.org/mailman/listinfo/rspec-users