Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add snippet type to support different type of ruby snippets. #390

Merged
merged 6 commits into from
Mar 27, 2013

Conversation

rdvdijk
Copy link

@rdvdijk rdvdijk commented Mar 7, 2013

Take a look at these issues: cucumber/common#328 and cucumber/common#331.

The change

Ruby snippets have now changed from this form:

When /^there is a missing step$/ do
  pending # express the regexp above with the code you wish you had
end

to:

When(/^there is a missing step$/) do
  pending # express the regexp above with the code you wish you had
end

The choice

This pull request gives the command line option to change Ruby snippet formats.

Using --snippet-type regexp is the default, and will create snippets with parentheses.
Using --snippet-type legacy will create snippets the old way, without parentheses.
Using --snippet-type percent will create snippets the with percent-style regular expressions:

When %r{^there is a missing step$} do
  pending # express the regexp above with the code you wish you had
end

The disclaimer

I'm not too happy about adding a fourth argument to the Cucumber::RbSupport::RbLanguage#snippet_text function and the same function in all other supported languages. I couldn't find another way to get to this option, I hope this is acceptable.

@mattwynne
Copy link
Member

Thanks for your contribution @rdvdijk, I've had a chance to review it and I have a few comments.

The main one is that I'd like to see this patch use polymorphism rather than symbol keys for the different snippet types. I think that would also allow us to push the list of available snippet types down into the language. After 2.0, we'll only support Ruby and Wire languages anyway.

I'll add some comments to the code to point out what I mean.

@@ -184,6 +191,17 @@ def check_nil(o, proc)
o
end
end

def typed_snippet_pattern(pattern, type)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have this method do a hash lookup to the class that represents the snippet type:

{
  regexp: Snippet::Regexp,
  legacy: Snippet::Legacy,
  percent: Snippet::Percent,
}.fetch(type).new(pattern)

Now you could move the code from lines 104-111 onto a base clases for a Snippet.

@mattwynne
Copy link
Member

I hope those comments make sense - sorry for the delay. Just shout if you have any questions.

@rdvdijk
Copy link
Author

rdvdijk commented Mar 26, 2013

That makes a lot of sense. I had considered doing this, but was worried I would have to change too much code to make it work. I especially dislike passing around so many variables down to where the actual snippets are rendered.

I'll look into this, thanks for the review.

@mattwynne
Copy link
Member

Red, green, refactor. You've done the hard work and got this change to green, now the refactoring is the fun part!

I think pulling the snippet rendering out of the RbLanguage class will be a great improvement.

@rdvdijk
Copy link
Author

rdvdijk commented Mar 27, 2013

I've not only pulled all the snippet rendering into a base snippet class. I've moved the tests to a separate spec as well. Any more comments? Let me know if you'd like me to refactor some more 😄

@rdvdijk
Copy link
Author

rdvdijk commented Mar 27, 2013

Ah, I broke 1.8 compatibility. Let me fix that.

if(multiline_arg_class == Ast::Table)
multiline_class_comment = "# #{multiline_arg_class.default_arg_name} is a #{multiline_arg_class.to_s}\n "
end
snippet.code_keyword = code_keyword
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you either pass these three parameters (code_keyword, parttern, multiline_argument_class) into the snippet constructor, or pass them into #render? I find code is much easier to reason about when objects are as immutable as possible.

@mattwynne
Copy link
Member

Much better! Thanks for reworking this.

@rdvdijk
Copy link
Author

rdvdijk commented Mar 27, 2013

I fiddled with a builder pattern before settling on the current version. But I guess three arguments into a constructor isn't that bad,

I'll change it.

@rdvdijk
Copy link
Author

rdvdijk commented Mar 27, 2013

Here you go! More comments?


class BaseSnippet

attr_reader :code_keyword, :pattern, :multiline_argument_class
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These can be made private, right?

@rdvdijk
Copy link
Author

rdvdijk commented Mar 27, 2013

There, more clean up. I think we're getting there.

@mattwynne
Copy link
Member

Looking good! Now how about calling replace_and_count_capturing_groups! from the constructor, passing it the original pattern? Then we won't have that ugly mutation of the @pattern instance variable. And you could inline render_snippet into render (or even just call it to_s)

@rdvdijk
Copy link
Author

rdvdijk commented Mar 27, 2013

Your wish is my command! 😉

})
it "creates a legacy Snippet class" do
Snippet::Legacy.stub(:new => stub.as_null_object)
Snippet::Legacy.should_receive(:new)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you could change these two lines to:

Snippet::Legacy.should_receive(:new).and_return(snipped)

@mattwynne
Copy link
Member

Really great contribution @rdvdijk - thanks for having the patience to iterate on it with me!

mattwynne added a commit that referenced this pull request Mar 27, 2013
Add snippet type to support different type of ruby snippets.
@mattwynne mattwynne merged commit a667b4b into cucumber:master Mar 27, 2013
@rdvdijk
Copy link
Author

rdvdijk commented Mar 27, 2013

Thanks for all the help, and for merging, @mattwynne! 🎉

"Use different snippet type (Default: regexp). Available types:",
"regexp : Snippets with parentheses, e.g. \"When(/^missing step$/) do\"",
"legacy : Snippets without parentheses, e.g. \"When /^missing step$/ do\"",
"percent : Snippets with percent regexp, e.g. \"When %r{^missing step$} do\"") do |v|
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once refactoring enhancement I think you could consider doing in a separate PR is to move these descriptions down to the Snippet classes, and expose the list of snippet types (the values from the hash in the factory method) as a method on RbLanguage. Then this option parser would not have to know anything about the different types of snippets, and we could easily add more without having the change the option parser.

Do you see what I mean?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes a lot of sense! I'll work on it somewhere in the coming days.

@lock
Copy link

lock bot commented Oct 25, 2018

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked as resolved and limited conversation to collaborators Oct 25, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants