[rspec-users] Evaluating shared example customisation block before shared block

David Chelimsky dchelimsky at gmail.com
Sun Aug 1 18:52:31 EDT 2010


On Aug 1, 2010, at 5:12 PM, Ashley Moran wrote:

> 
> On 1 Aug 2010, at 3:43 PM, David Chelimsky wrote:
> 
>> shared_examples_for "blah" do |a,b|
>> ...
>> end
>> 
>> it_should_behave_like "blah", 1, 2
>> 
>> That wouldn't have worked with the old implementation, but it would work perfectly well now. This would also "just work" with hash-as-keyword-args:
>> 
>> shared_examples_for "blah" do |options|
>> it "blah #{options[:a]}" do
>>   ..
>> end
>> end
>> 
>> it_should_behave_like "blah", :a => 1
>> 
>> Now you can do this:
>> 
>> [1,2,3].each do |n|
>> it_should_behave_like "blah", :a => n
>> end
>> 
>> And we can still use the customization block to define methods, hooks (before/after) and let(). Now it just feels like the rest of RSpec.
>> 
>> Thoughts?
> 
> One thought: me.facepalm :)
> 
> The only thing it lacks is a DSL to define the requirements.  Would it still be desirable to be able to write:
> 
>  shared_examples_for "blah" do |options|
>    require_argument options[:a]
>    it "blah #{options[:a]}" do
>      ..
>    end
>  end
> 
> Or some such?
> 
> Also, after staring at this for a while, I'm puzzled by something.  In this code:
> 
>  it_should_behave_like "blah", :a => 1
> 
> how does :a => 1 get passed to the "options" block, as `shared_block` in the code is never called with arguments?  Would this need another code change?  (Apologies if I'm being thick, it's late and I should probably go to bed, but I wanted to review this first...)

Actually, I just discovered that ruby 1.8.7 actually added module_exec. What does this mean? It means this:

      def self.define_shared_group_method(new_name, report_label=nil)
        module_eval(<<-END_RUBY, __FILE__, __LINE__)
          def self.#{new_name}(name, *args, &customization_block)
            shared_block = world.shared_example_groups[name]
            raise "Could not find shared example group named \#{name.inspect}" unless shared_block

            describe("#{report_label || "it should behave like"} \#{name}") do
              module_exec *args, &shared_block
              module_exec *args, &customization_block if customization_block
            end
          end
        END_RUBY
      end

What does that mean? It means this:

shared_examples_for "foo" do |a,b,c|
  it "#{a} #{b} #{c}s" do
    a.should do_something_with(b, c)
  end
end

describe "something" do
  it_behaves_like "foo", 1, 2, 3
end

Ta da!!!!!!

Two problems to solve at this point:

1. order of evaluation of blocks
2. what to do about ruby 1.8.6

re: order of evaluation of blocks, I think I'm inclined to go one way one minute, and another the next. Somebody convince me of one or the other.

re: 1.8.6, we've got a home-grown implementation of instance_exec that runs in 1.8.6 (although I just discovered that it's broken - fix coming shortly). I could

a) add such a thing for module_exec as well, though I haven't quite figured out how that works yet. 
b) only support parameterized shared groups in ruby 1.8.7 or better
c). the most drastic option, would be to drop support for 1.8.6 entirely, but I don't think that's really feasible yet.

Thoughts?

David

> Cheers
> Ash



More information about the rspec-users mailing list