On Aug 1, 2010, at 11:40 AM, Myron Marston wrote: >> The particular issue of simple values being used in the docstrings and the >> examples themselves (i.e. exposed to everything in the block scope) could be >> handled like this: >> >> shared_examples_for "blah" do |a,b| >> ... >> end >> >> it_should_behave_like "blah", 1, 2 > > Fantastic idea. I'm sold. I'm not sure why this simple idea didn't > occur to me earlier :(. > >> That's no different from methods that have default values for arguments: >> >> def foo(bar, baz = :default) >> >> If you provide only 1 arg, all is well, but the first one is required. >> Here's the same idea expressed in a group: >> >> shared_examples_for "foo" do >> unless defined?(:bar) >> raise "you need to supply a bar() method" >> end >> >> unless defined?(:baz) >> def baz; :default; end >> end >> end > > This does indeed work, to the extent that the methods the consumer > needs to override are ones the author of the shared example ground had > in mind, and coded as such. This isn't an issue when the shared > example group and the consuming code are in the same code base. But > the idea has been brought up that shared example groups could be > provided by a library for users to use to enforce a contract of some > class or object they write that the library interacts with. I think > it's likely that library authors won't declare their helper methods > using the "unless defined?" idiom, because they can't anticipate all > the needs of their users, and they probably aren't even aware that > they have to declare their methods this way to allow them to be > overridden.
<disclaimer>I kind of jumped around from one part of this thread to the other - apologies if my responses seem to lack cohesion</disclaimer> That would be the tricky gotta RTFM part - I'd rather have that burden on the shared group/library author than its consumer. > Suddenly it's _impossible_ for consumers of the shared > example group to override any of the helper methods. > > I _love_ how flexible ruby is, and the fact that every method can be > overridden, without the original author of the a method having to do > anything special to allow it. Your suggestion above seems (to me > anyway) to be more in line with a language like C#, where methods are > not overriddable by default, and developers have to use the virtual > keyword to make them so. Seems like your mental model is that of a customization block being a subclass or re-opening of the shared block. What you say makes sense in that model, but that's not the same model I have. If we were going to go with the subclass model, I think we'd be best served by going all the way there. So this structure: describe "foo" do it_behaves_like "bar", "with baz == 3" do def baz; 3 end end end Would result in: foo # top level group it behaves like bar # nested group generated by it_behaves_like using "bar" in the report with baz == 3 # 3rd level nested group generated using the customization block This would make the whole relationships between things much more transparent. I don't love this idea either, but we're searching for balance here. At least I am :) > So, all of that is just to say that I'm still in favor of eval'ing the > customization block last. To me, the primary need for eval'ing the > customization block first was to allow it define class helper methods > that the shared example group could use to interpolate into doc > strings, and this need is solved much more elegantly with David's > suggestion. Assuming that can work. I've taken a closer look and getting that to work would take some serious re-architecting that I'm not sure is a good idea. Consider the code as it is now: http://github.com/rspec/rspec-core/blob/cc72146205fb93ca11e1f290d3385151b51181ad/lib/rspec/core/example_group.rb#L61 I've restructured it a bit after some of this conversation, but right now the customization block is still eval'd last. I think this code is very easy to grok for a reasonably advanced Rubyist (i.e. if you can get past module_eval(<<-END_RUBY), then the content of the String is no problem), but if we start adding gymnastics to support various combinations of nice-to-haves, then this code will quickly become harder to read. In my experience with RSpec, readability/simplicity of the internals _does_ matter to end users, not just contributors and maintainers, because many want to understand what's going on under the hood. That is a strong motivator for me to keep things exactly as they are now (simple and readable). In terms of end-users, the consumer of the shared group would not need to be any different in either of these two scenarios: shared_examples_for "foo" do # with customization_block eval'd before unless defined?(:bar) raise "you need to supply a bar() method" end end shared_examples_for "foo" do # with customization_block eval'd after def bar raise "you need to supply a bar() method" end end In either case, this will get you the same error: it "does something" do it_behaves_like "bar" end > I like eval'ing it last so that helper methods can be > overridden, _without_ anything special being done in the shared > example group. I can appreciate that but I'd rather have burden placed on the shared group author than its consumer. Cheers, David > Of course, if there are other things that will only work by eval'ing > the block first, then I'm completely fine with it--I'm just not seeing > the need right now. > > Myron > _______________________________________________ > 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