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

David Chelimsky dchelimsky at gmail.com
Fri Jul 30 10:10:20 EDT 2010


On Jul 30, 2010, at 9:03 AM, David Chelimsky wrote:

> 
> On Jul 30, 2010, at 5:13 AM, Ashley Moran wrote:
> 
>> Hi
>> 
>> I finally looked into why this is not currently possibly in RSpec 2 (beta 19):
>> 
>> shared_examples_for "Etymology" do
>>   describe "The etymology of foo" do
>>     it "is followed by #{after_foo}" do
>>       # ...
>>     end
>>   end
>> end
>> 
>> describe "foo", focus: true do
>>   it_should_behave_like "Etymology" do
>>     def self.after_foo
>>       "bar"
>>     end
>>   end
>> end
>> 
>> It's because of the current implementation of ExampleGroup.define_shared_group_method, which evaluates the shared example block before the customisation block:
>> 
>>  shared_group = describe("#{report_label} \#{name}", &shared_block)
>>  shared_group.class_eval(&customization_block) if customization_block
>> 
>> (This is behaviour I found surprising.)
>> 
>> However, with a little more metaprogramming jiggery-pokery, you can have them evaluated in the other order:
>> 
>> module RSpec
>>   module Core
>>     class ExampleGroup
>>       # ...
>> 
>>       def self.define_shared_group_method(new_name, report_label=nil)
>>         report_label = "it should behave like" unless report_label
>>         module_eval(<<-END_RUBY, __FILE__, __LINE__)
>>           def self.#{new_name}(name, &customization_block)
>>             shared_block = world.shared_example_groups[name]
>>             raise "Could not find shared example group named \#{name.inspect}" unless shared_block
>> 
>>             compound_block = lambda do |*args|
>>               module_eval &customization_block if customization_block
>>               module_eval &shared_block
>>             end
>> 
>>             shared_group = describe("#{report_label} \#{name}", &compound_block)
>>             shared_group
>>           end
>>         END_RUBY
>>       end
>> 
>>       # ...
>>     end
>>   end
>> end
>> 
> 
> Or ...
> 
>      def self.define_shared_group_method(new_name, report_label=nil)
>        report_label = "it should behave like" unless report_label
>        module_eval(<<-END_RUBY, __FILE__, __LINE__)
>          def self.#{new_name}(name, &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} \#{name}" do
>              module_eval &customization_block if customization_block
>              module_eval &shared_block
>            end
>          end
>        END_RUBY
>      end
> 
>> Would this be a useful improvement to RSpec 2?
> 
> Yes
> 
>> Any opinions on the order of the block evaluation for shared examples
> 
> Makes perfect sense to me. Wanna make a patch with an additional scenario in the cuke?

Actually - maybe I spoke to soon. Ordering things this way would mean that you couldn't do this:

shared_examples_for Enumerable do
  def enumerable
    raise "you must provide an enumerable method that returns the object which you're specifying should behave like Enumerable"
  end
  it "..." { .. }
end

Although, if you were going to do that, I guess you could do this:

shared_examples_for Enumerable do
  unless defined?(:enumerable)
    raise "you must provide an enumerable method that returns the object which you're specifying should behave like Enumerable"
  end
  it "..." { .. }
end

Maybe that, or a DSL that wraps that, is the better way, so we can get the best of both worlds?

shared_examples_for Enumerable do
  require_instance_method :foo, "gotta have foo instance method"
  require_class_method :foo, "gotta have foo class method"
  require_instance_variable "@foo", "gotta have an instance variable named @foo"
  it "..." { .. }
end

Thoughts?

> 
>> 
>> Cheers
>> Ash
>> 
>> -- 
>> http://www.patchspace.co.uk/
>> http://www.linkedin.com/in/ashleymoran
>> 
>> _______________________________________________
>> rspec-users mailing list
>> rspec-users at rubyforge.org
>> http://rubyforge.org/mailman/listinfo/rspec-users
> 



More information about the rspec-users mailing list