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