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@rubyforge.org >> http://rubyforge.org/mailman/listinfo/rspec-users > _______________________________________________ rspec-users mailing list rspec-users@rubyforge.org http://rubyforge.org/mailman/listinfo/rspec-users