Alias example group #1236

Closed
wants to merge 5 commits into
from

2 participants

@michihuber

allows adding custom aliases for example_group through the configuration

this is very much work in progress but I'll be offline for a bit, so maybe somebody can take a first look in the meantime
/cc @JonRowe @myronmarston

@myronmarston
RSpec member

Hey @michihuber -- the build is erroring because of some recent travis/bundler/rubygems changes that broke some things. Can you rebase against master? That should pull in .travis.yml changes that will fix the the install errors.

@myronmarston myronmarston commented on the diff Jan 1, 2014
lib/rspec/core/dsl.rb
module Core
+ # DSL defines methods to group examples, most notably `describe`,
+ # and exposes them as class methods of {RSpec}.
+ # They can also be exposed globally (on main and Module) through
@myronmarston
RSpec member
myronmarston added a line comment Jan 1, 2014

I think this line should be:

# They can be exposed globally (on `main` and instances of `Module`) through
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@myronmarston myronmarston commented on the diff Jan 1, 2014
lib/rspec/core/dsl.rb
+
+ def self.expose_example_group_alias(name)
+ example_group_aliases << name
+
+ (class << RSpec; self; end).send(:define_method, name) do |*args, &example_group_block|
+ RSpec::Core::ExampleGroup.send(name, *args, &example_group_block).register
+ end
+ end
+
+ # Defines a named context for one or more examples
+ # @example_group
+ # RSpec.example_group do
+ # it "does something" do
+ # end
+ # end
+ expose_example_group_alias(:example_group)
@myronmarston
RSpec member
myronmarston added a line comment Jan 1, 2014

This line is pretty odd...you are exposing example_group as an alias of example_group?

I'm a bit on the fence about whether or not it's a good thing to add example_group as a new method to the DSL. Thoughts from others on that?

@JonRowe
RSpec member
JonRowe added a line comment Jan 3, 2014

It's pretty odd, personally I like the idea of it being example group internally, then use this mechanism to expose the DSL as describe and context. I personally don't think we should add example_group to the top level DSL, but this should easily allow those that wish to expose it that way can do so.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@myronmarston myronmarston commented on the diff Jan 1, 2014
lib/rspec/core/dsl.rb
module DSL
+ # @private
+ def self.example_group_aliases
+ @example_group_aliases ||= []
+ end
+
+ def self.expose_example_group_alias(name)
+ example_group_aliases << name
+
+ (class << RSpec; self; end).send(:define_method, name) do |*args, &example_group_block|
+ RSpec::Core::ExampleGroup.send(name, *args, &example_group_block).register
@myronmarston
RSpec member
myronmarston added a line comment Jan 1, 2014

Please use __send__ rather than send. While send works fine here, elsewhere we've had to use __send__ to deal with objects that have redefined send (e.g. Email#send), and it's good to be consistent and use __send__ everywhere -- that way we're less likely to forget to use it for places where it's important.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@myronmarston myronmarston commented on the diff Jan 1, 2014
lib/rspec/core/dsl.rb
@@ -35,26 +66,32 @@ def self.exposed_globally?
def self.expose_globally!
return if exposed_globally?
- to_define = proc do
- def describe(*args, &block)
- ::RSpec.describe(*args, &block)
+ example_group_aliases.each do |method_name|
+ to_define = proc do
+ send(:define_method, method_name) do |*args, &block|
+ ::RSpec.send(method_name, *args, &block)
@myronmarston
RSpec member
myronmarston added a line comment Jan 1, 2014

Same here; __send__ rather than send, please.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@myronmarston myronmarston commented on the diff Jan 1, 2014
lib/rspec/core/dsl.rb
@@ -62,5 +99,5 @@ def self.remove_globally!
end
end
-# cature main without an eval
+# capture main without an eval
@myronmarston
RSpec member
myronmarston added a line comment Jan 1, 2014

Thanks for correcting this typo :).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@myronmarston myronmarston commented on the diff Jan 1, 2014
lib/rspec/core/example_group.rb
@@ -124,13 +124,21 @@ def alias_example_to name, extra={}
(class << self; self; end).define_example_method name, extra
end
+ def alias_example_group_to(name, metadata={})
+ (class << self; self; end).send(:define_method, name) do |*args, &block|
@myronmarston
RSpec member
myronmarston added a line comment Jan 1, 2014

Same here: __send__ please.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@myronmarston myronmarston commented on the diff Jan 1, 2014
lib/rspec/core/example_group.rb
@@ -124,13 +124,21 @@ def alias_example_to name, extra={}
(class << self; self; end).define_example_method name, extra
end
+ def alias_example_group_to(name, metadata={})
+ (class << self; self; end).send(:define_method, name) do |*args, &block|
+ combined_metadata = metadata.dup
+ combined_metadata.merge!(args.pop) if args.last.is_a? Hash
@myronmarston
RSpec member
myronmarston added a line comment Jan 1, 2014

This does handle symbols-as-metadata (with true values) properly. Here's how we do it in alias_example_to:

options = Metadata.build_hash_from(args)
options.update(metadata)

I think it would make sense to do the same here. It'll need a spec as well, as I think it's important behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@myronmarston myronmarston commented on the diff Jan 1, 2014
lib/rspec/core/example_group.rb
@@ -242,7 +250,8 @@ def self.describe(*args, &example_group_block)
end
class << self
- alias_method :context, :describe
+ alias_method :describe, :example_group
+ alias_method :context, :example_group
@myronmarston
RSpec member
myronmarston added a line comment Jan 1, 2014

Now that we have a more general mechanism for declaring example group aliases, any reason not to leverage that here rather than just alias_method?

@myronmarston
RSpec member
myronmarston added a line comment Jan 1, 2014

There are a number of other built-in aliases I would like to see (for parity with the example aliases):

  • xdescribe (shortcut for making an example group pending, like xit)
  • xcontext (same)
  • fdescribe (shortcut for focusing on an example group, like fit)
  • fcontext (same)

I'm not sure I want to add fexample_group and xexample_group, though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@myronmarston myronmarston commented on the diff Jan 1, 2014
spec/rspec/core/dsl_spec.rb
+ shared_examples_for "a dsl method" do
+ it "is added to the main object and Module when expose_dsl_globally is enabled" do
+ in_sub_process do
+ RSpec.configuration.expose_dsl_globally = true
+ expect(main).to respond_to(method_name)
+ expect(Module.new).to respond_to(method_name)
+ end
+ end
+
+ it "is added to the RSpec DSL" do
+ expect(::RSpec).to respond_to(method_name)
+ end
+
+ it "is not added to every object in the system" do
+ expect(Object.new).not_to respond_to(method_name)
+ end
@myronmarston
RSpec member
myronmarston added a line comment Jan 1, 2014

There's nothing here that specs the fact that main and Module do not get these methods added when expose_dsl_globally is false. Would be good to add that.

@JonRowe
RSpec member
JonRowe added a line comment Jan 3, 2014

Also there's nothing I've seen that shows that this new code is safe against the rspec loading issue.
E.g. when you specify the aliases after rspec has loaded, it looks like they won't be exposed off main, Module unless someone toggles the expose method manually.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@myronmarston
RSpec member

@michihuber -- I'd love to get this in 3.0.0.beta2, which we'll be releasing soon. Are you able to get to get back to this in the next couple days? If not, that's fine; I'll plan to take it across the finish line so we can merge it. I just wanted to check first to make sure we don't step on each other's toes.

@myronmarston
RSpec member

I'm going to take a stab at getting this across the finish line. Thanks for getting this started, @michihuber!

@myronmarston myronmarston referenced this pull request Jan 22, 2014
Merged

Alias example group #1255

@myronmarston
RSpec member

Closing in favor of #1255.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment