-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
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 StructInheritance cop #1571
Conversation
31dd5af
to
1915c2b
Compare
if struct_construtor?(superclass) | ||
add_offense(node, superclass.loc.expression, MSG) | ||
end | ||
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.
As travis says, when running Rubocop against this file:
lib/rubocop/cop/style/struct_inheritance.rb:20:11: C: Use a guard clause instead of wrapping the code inside a conditional expression.
if struct_construtor?(superclass)
^^
So something like:
def on_class(node)
_name, superclass, _body = *node
return unless struct_construtor?(superclass)
add_offense(node, superclass.loc.expression, MSG)
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.
@bquorning done. Sorry, completely forgot to check back for Travis status.
1915c2b
to
e152a65
Compare
|
||
private | ||
|
||
def struct_construtor?(node) |
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.
Possible typo: struct_construtor?
→ struct_constructor?
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.
Fixed 😱
How did that happen... 😆
922ac13
to
b9cea85
Compare
|
||
it 'registers an offense when inheriting from Struct.new' do | ||
inspect_source(cop, | ||
['class Person < Struct.new(:first_name, :last_name)', |
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.
There should be an example using Struct.new ... do ... end
as well.
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.
@bbatsov there already is one or do you mean something else?: https://github.com/bbatsov/rubocop/pull/1571/files?diff=unified#diff-2acbcfc063c998c3c95c51657c537eebR26
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 can subclass the result of Struct.new do end
as well.
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.
@bbatsov ohhh... Got it, done 😄
Overall the code looks good (apart from my remarks). Next step - checking for classes that can be replaced by a struct. :-) |
e2936cf
to
33d6934
Compare
@bbatsov could you give an example of what you mean? 😄 |
I meant that once we're ready with this cop we could create another one checking for code like: class Person
attr_accessor :age, :name
def initialize(age, name)
...
end
end We could search for more complex patterns, but I think we shouldn't overdo this. Anyways, this was just a random comment. Let's wrap this PR first. |
d05ba1c
to
4e48b70
Compare
@bbatsov changed the message to match the updated one in ruby-style-guide. |
# | ||
# @example | ||
# | ||
# # bad |
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.
Shouldn't the example code be indented with two more spaces?
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.
@bbatsov 👍
4e48b70
to
c71d5b6
Compare
# # good | ||
# Person = Struct.new(:first_name, :last_name) | ||
class StructInheritance < Cop | ||
MSG = "Don't extend an instance initialized by Struct.new. Extending " \ |
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.
One final remark. Keep just the first sentence of the MSG (long messages look bad with some formatters) and wrap Struct.new
in backquotes.
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.
@bbatsov done 😄
c71d5b6
to
67743e0
Compare
👍 Thanks! |
There's a small gotcha with Person = Struct.new(:first, :last) do
SEPARATOR = ' '.freeze
def name
[first, last].join(SEPARATOR)
end
end is not equivalent to: class Person < Struct.new(:first, :last)
SEPARATOR = ' '.freeze
def name
[first, last].join(SEPARATOR)
end
end The former creates |
Why is it better not to inherit from |
https://github.com/bbatsov/ruby-style-guide#no-extend-struct-new |
@Koronen Thanks for the clarification (and for your very polite encouragement for me to read the docs) 😄 |
Just upgraded and got bit by this cop. Can the rule be updated to only address empty class blocks (e.g. no additional methods or constants)? As @tamird alluded to, there are valid use cases for extending Struct.new. Or at the very least, add more details about why this cop should be included and on by default. |
There's a link to the style guide already. |
@bbatsov Yes, I saw that, and it didn't clarify anything. |
Guess you've never seen |
I've seen that too -- it was in one of the examples earlier in this thread. But as was already pointed out, it's not equivalent. I'm just looking for a reason for the hate for this obviously useful pattern. |
I understand this well enough. Perhaps we should account for the constant definition, but outside of this corner-case the two setups are pretty much the same and it's generally better to prefer the simpler option (simpler inheritance chain). |
I just found this interesting article about defining constants inside a Struct: http://blog.55minutes.com/2012/01/trivia-constants-in-ruby-192-vs-193/ I think I agree with its conclusion: maybe you should just use a simple Class instead. |
Sounds good to me. @bquorning Please, log a ticket so we don't forget to implement a check for constants defined in structs eventually. |
I was unaware of the perils of constants defined inside blocks, and I agree that constants inside blocks are definitely a risky choice and should be discouraged. I'm not convinced the author of that article knew about the |
No description provided.