-
Notifications
You must be signed in to change notification settings - Fork 111
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
Pull privacy concerns out of packwerk #247
Conversation
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.
When building the plugin itself, if you are copying code from this repository don't forget to include the same copyright and license in the plugin since it is a substantial copy of this project.
b4d3402
to
212f417
Compare
lib/packwerk/checker_interface.rb
Outdated
@@ -0,0 +1,46 @@ | |||
# typed: strict |
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.
This was just moved from Packwerk::ReferenceChecking::Checkers::Checker
. The main reason for that is that the extensive module nesting felt like an implementation detail and now that extending the checker is a supported public API, I felt like the more concise Packwerk::CheckerInterface
was an improved public API.
@@ -0,0 +1,52 @@ | |||
# typed: strict |
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.
This was moved from lib/packwerk/application_validator/helpers.rb
, as this is now a ValidatorInterface
concern. I also made these proper module methods so they can be included as helpers in the ValidatorInterface
. I could also just inline them into ValidatorInterface
if folks prefer.
@@ -2,7 +2,7 @@ | |||
buyers: | |||
"::Buyers::Document": | |||
violations: | |||
- dependency | |||
- some_checker_type |
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.
This was originally used to test when a dependency violation turned into a privacy violation. I changed it to privacy originally and then realized we could use any arbitrary string to represent a different checker's violation.
@rafaelfranca @gmcgibbon I think this is in a good state for review now! |
085e1b6
to
b41eab5
Compare
lib/packwerk/checker_interface.rb
Outdated
# frozen_string_literal: true | ||
|
||
module Packwerk | ||
module CheckerInterface |
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.
module CheckerInterface | |
module CheckerBehaviour |
This defines behaviour, so it technically isn't an interface, is it? Maybe AbstractChecker
makes more sense? Does Sorbet have a standard for this?
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.
According to the sorbet docs, it's an abstract class: https://sorbet.org/docs/abstract
Sorbet doesn't have a standard as far as I can see, but using the language of sorbet (i.e. AbstractChecker
) does probably make sense. I've always liked including the word "interface" in my interfaces to make it clear that the user has to implement certain methods, but I think abstract
signifies the same thing here. Also cc @mclark and also maybe @paracycle who may have suggestions on naming conventions for sorbet interfaces.
lib/packwerk/validator_interface.rb
Outdated
extend T::Sig | ||
extend T::Helpers | ||
|
||
include Helpers |
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.
This will load helpers immediately, so there is no need to autoload it. Also curious if we should leave it up to the concrete implementation to include this if they need it?
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.
When you say there is no need to autoload it -- do you mean replace with a vanilla require statement (since something needs to require it somehow)? I made that change.
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'm inclined to keep it because the fact that it's in a separate module is mostly just an implementation detail -- in fact I wonder if it would be better to just inline it into AbstractValidator
. I think a lot of validators will need these methods (as the existing ones did) and wanted to avoid an extra step for the clients to be able to implement they validation.
That being said I'm open to changing this so the concrete implementation has to include it if you feel strongly about it!
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.
When you say there is no need to autoload it -- do you mean replace with a vanilla require statement (since something needs to require it somehow)? I made that change.
If you tell ruby to manually autoload something (via autoload :Helpers
and then include Helpers
the autoload triggers immediately and defeats the purpose of using autoload. So I think we should use a require instead at the top of this file.
That being said I'm open to changing this so the concrete implementation has to include it if you feel strongly about it!
No, I think it is fine to keep, as long as we require it properly. I would say we could make it a private_constant
, but if we want to document these methods it is better off as -is. I just want to make sure we're defining our initial public API 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.
Gotcha -- added the require and made Helpers
a private_constant
!
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.
Gotcha, thanks for the explanation. Ya know, realizing that the module Helpers is basically private and the API is on AbtractValidator
, I went ahead with experimenting just inlining Helpers
into AbstractValidator
and I think it looks a lot better – it makes it clear what API is available for an AbstractValidator
and removes this class which I think is just obscuring the available API.
end | ||
|
||
sig { returns(PackageSet) } | ||
def package_set |
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 had to define the package set on the run_context in parse_run to cleanup todos. I wonder if we DRY things up somehow.
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'm not sure exactly. We could have ParseRun
take in a fully initialized RunContext
, and then pass in the package_set
from here into the initialization of RunContext
. Right now RunContext
is only used for parsing related functionality, so this would have us be sharing variables used in validation and parsing in a somewhat new way, and getting the package set is using package set public API anyways, so I'm not sure how to do this in a way that simplifies the design.
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.
Let me know if you have suggestions. I also think it could be something we come back to in a separate PR since it's a structural/non-behavioral change only.
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 think we would be passing the package set via this method down to where I was using it. But yeah, we can deal with later.
@gmcgibbon I believe I've responded to and/or incorporated all of your feedback! |
6fe5589
to
1896030
Compare
lib/packwerk/abstract_validator.rb
Outdated
end | ||
|
||
sig { abstract.params(package_set: PackageSet, configuration: Configuration).returns(ApplicationValidator::Result) } | ||
def call(package_set, configuration) |
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.
def call(package_set, configuration) | |
def validate(package_set, configuration) |
I think naming this method will help if the object grows in complexity. Similar to how checkers respond to multiple methods. We can't substitute this for a proc or a simpler object because it needs to define permitted_keys as well so I don't see the benefit of calling this method call
.
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.
Makes a lot of sense, I'll make this change next week before merging.
lib/packwerk/abstract_validator.rb
Outdated
def call(package_set, configuration) | ||
end | ||
|
||
sig { params(configuration: Configuration, setting: T.untyped).returns(T.untyped) } |
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.
sig { params(configuration: Configuration, setting: T.untyped).returns(T.untyped) } | |
sig { params(configuration: Configuration, setting: String).returns(T.untyped) } |
If we're reading a YAML file, there's a very good chance the key will be a string. You could also use Object
if you want it to be generalized.
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.
Good call, I think going with string is good, at least until we have a use case for anything else.
lib/packwerk/abstract_validator.rb
Outdated
class << self | ||
extend T::Sig | ||
|
||
sig { params(base: Class).void } |
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.
This isn't technically true. If we want to guarantee that these are classes we could consider making these abstract module abstract classes. We could also drop the abstract wording and call them Validator
and Checker
which would make AbstractChecker.all
-> Checker.all
which is more straightforward API in my opinion. However, I don't feel too strongly about this point so I'll leave the decision up to you. Since we've defined more behaviour in this module it seems appropriate to me that it should be an abstract 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.
I'm not sure I'm totally following, but I think for this to be a sorbet interface is has to be a module. Are you saying that this in theory could be implemented by a module so it's not necessarily a class that gets passed in? If so I think that's technically true but the only supported and documented use would be including in a class.
I like checker and validator too actually -- does seem like a cleaner public API for use and the fact that it's abstract feels like an implementation detail a bit? As far as packwerk's sorbet signatures go, it's just a "checker" and "validator" so I'll change that next week before merging.
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 think for this to be a sorbet interface is has to be a module
Classes should work, but they can't be interfaces. The concept of an abstract class is in other languages too. Usually, in typed languages, interfaces can't define behaviour and therefore can't be instantiated, while abstract classes are the middle-ground between an interface and a the implementation (partially defining behaviour, but also defining a contract).
The only difference is you would have to use an inherited hook instead.
but the only supported and documented use would be including in a class.
Yes, we expect it to only be included in classes, but a developer could make their own and try include that module in their concrete classes instead. They may assume it will work (but we expect a direct ancestor line from the class, so it wouldn't). An abstract class has a similar flaw if they wanted to share behaviour with subclasses though.
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.
the fact that it's abstract feels like an implementation detail a bit?
Yes, most gem APIs don't mention concepts like that, but since we use Sorbet we walk a fine line between Ruby conventions and strongly typed language conventions.
009534e
to
2a439d4
Compare
For those interested, there is summary of the reasoning behind these changes outlined in the follow blog post: (Full details in this discussion)
|
Do not merge yet
I think sequencing should look something like this:
deprecated_references.yml
andbin/packwerk update-deprecations
in favor ofpackage_todo.yml
andbin/packwerk update-todo
, and a migration hint:What are you trying to accomplish?
This PR removes privacy concerns out of the
packwerk
repository. It moves them topackwerk-privacy
in this PR: rubyatscale/packwerk-extensions#1What approach did you choose and why?
I completely removed all of the privacy related code along with tests that should be moved to the other repo.
What should reviewers focus on?
Is there something that I didn't remove that I should, or something I shouldn't have removed that I did?
Type of Change
Additional Release Notes
Users will need to include
packwerk-privacy
in theirGemfile
and addpackwerk-privacy
to therequire
directive inpackwerk.yml
. USAGE.md includes more detail for how to do this.This PR will require a major version bump, and the upgrade guide should go into a bit more detail on this.
We'll also want to couple a major version bump with the removal of references to
deprecated_references.yml
.Checklist