-
-
Notifications
You must be signed in to change notification settings - Fork 102
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
Extract expectations differ to support #36
Extract expectations differ to support #36
Conversation
attr_reader :color | ||
alias_method :color?, :color | ||
|
||
def initialize(opts={}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer it if we made these explicit rather than a hash...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additional thought, given the extra methods required, perhaps we could have a Differ
and ColouredDiffer
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also traditionally initialize
is at the top of the class...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For clarity, you mean something like this:
def initialize(stringifier=nil, color=nil)
@stringifier = stringifier || lambda {|string| string })
@color = color || false
end
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I think if stringify must be a block let's do
def initialize(colour = false, &block)
@colour = colour
@stringifier = block || proc { |string| string }
end
Although it's not clear to me what stringifier does?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You'd still want a default though, right? Unless I misunderstand, I'm not sure how that would reduce complexity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It reduces the work initialize
does and in my eyes might make things look nicer, there is also a minor performance cost capturing a block when we don't need it. See L#169 for my suggestion...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly, I prefer the options has over the ordered arguments. There isn't an obvious natural ordering. (Compare that to something like Date.new(2014, 2, 4)
where the ordering of the arguments is extremely obvious). From the caller, Differ.new(:color => true)
is preferrable than Differ.new(true)
-- for the latter, it's unclear what true
means. Furthermore, these two values are options (not data members like what you get with a struct or value object) and these just happen to be the two currently supported options. In the future we may gain more. An options hash sets us up better for the future as it will easily flex to support new options. This won't:
def initialize(colour = false, &block)
end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps instead of being a lambda or block it should be a method, with the idea that Differ can be subclassed?
I'm 👎 on this idea. I think subclassing is a fine technique when it's localized within a single code base, but it leads to the brittle base class problem when it crosses code bases (i.e. base class in one code base, subclass in another). We're running into this in rspec-core where @JonRowe is working on updating the formatter classes but we have to be uber careful not to break existing formatters that subclass formatter base classes.
I much prefer the composition approach of passing the lambda option.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly, I prefer the options has over the ordered arguments.
I do too.
I much prefer the composition approach of passing the lambda option.
Agreed, I was trying to brainstorm ideas.
@myronmarston will want to review this further, but I made a start :) Thanks for helping with this @phiggins |
Travis reckons you have a syntax error too ;) |
Yikes, I was running the tests locally, I'm not sure what happened... |
end | ||
|
||
def object_to_string(object) | ||
object = @stringifier.call(object) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Follow up to above), how about changing this to object = yield(object) if block_given?
and passing in the stringifier if it's needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly, I prefer this how it is. Usually blocks (in a case like Differ.new { }
) will always (or nearly always) be used but here the more common case is probably that it's not used. It's unclear at the call site what the block does or represents. As this grows there may be additional lambda options needed, and using a block for the object prep gives it a place of prominence that I don't think makes sense relative to its role here.
This is looking good to me. That said, it's really hard for me to judge if you properly ported everything from rspec-expectations, including the recent changes after you started working on this. Is there a simple command I can use to diff what you have here against the corresponding files in rspec-expectations master to see the diff from there? |
I tried diffing the changes made after I extracted the code from expectations to support and gave up after a few minutes, it is either beyond me or not easily done with git. I could make the cleanup changes to expectations, get those accepted, and then extract to support. This would be a much cleaner diff but would require some not insignificant rework on my part. |
Use a normal differ :) ( |
Here's a diff of the diff_presenter.rb from rspec-expectations's master and my branch of rspec-support: https://gist.github.com/phiggins/9735292 Sorry this fell off my radar, getting a new job will do that to you! I'd like to get this closed out soon just so it's not hanging around in limbo. |
This also needs rebasing I'm afraid. |
@@ -0,0 +1,204 @@ | |||
require "rspec/support/encoded_string" | |||
require "rspec/support/hunk_generator" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should leverage our new require stuff.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there somewhere where the "new require stuff" is discussed? I don't keep up with rspec development I'm afraid.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See: https://github.com/rspec/rspec-support/blob/master/lib/rspec/support.rb#L14-29 (especially L29 as that shows a require using the optimised path)
This has been rebased. |
It'd be good to squash this too. |
Also the build is failing, due to a missing require? |
BTW, I think there have been some changes in the original files in rspec-expectations. For example: rspec/rspec-expectations@859eedf These sorts of changes will need to be pulled in here. |
Good catch. I'll make sure to post full diffs of the files I moved when I get all the other feedback sorted out. |
It looks like the travis builds are failing because it depends on changes I've made in my expectations branch. Is there a good way to work around this? |
Commit a change to the Gemfile that uses your branch for rspec-expectations. We'll just have to remember to revert it before merging. |
I'm curious to know what changes... This should be independent of rspec-expectations? |
Here's the diffs of files I changed: I just realized I did the diffs backwards. :/ |
I renamed some classes. |
To fix some travis failures in rspec-expectations I added #empty? to the list of methods EncodedString delegates to the underlying string: https://travis-ci.org/rspec/rspec-expectations/jobs/21486966 |
But that shouldn't be breaking the support build, this should be additional code isolated from expectations. |
I renamed Differ to HunkGenerator and DiffPresenter to Differ. rspec-expectations used DiffPresenter. After renaming the class in rspec-support, rspec-support's tests wouldn't run because they use rspec-expectations. |
I'd expect them to be namespaced such that using different names for them in rspec-support shouldn't cause any problems for rspec-expectations. e.g. in rspec-expectations they were |
I think you guys might be correct, that might have been a red herring. I'll poke at it when I have a few minutes. |
I removed the dependence on my branch of rspec-expectations and everything seemed to pass except for one strange failure on REE. Except for squashing the commits, is there anything that needs to be done on either PR? |
Yep, assuming this now matches the changes to the differ in expectations, just needs squashing. |
Squashed. |
…upport Extract expectations differ to support
Thanks for having the patience to get this across the line @phiggins! |
Thanks for your patience while I took a six week hiatus. 🍹 |
This was done to support rspec/rspec-mocks#434 in conjunction with rspec/rspec-expectations#441.
There's still a few things I need to do, but I wanted to get this out there.