-
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
[Discussion] Put files into buckets of responsibilities/seams #145
base: main
Are you sure you want to change the base?
Conversation
lib/packwerk/run_context.rb
Outdated
# 1. file path to node | ||
# It needs to return ancestors relative to node | ||
node, ancestors = file_processor.call(file) | ||
|
||
# Inside NodeProcessor - @reference_extractor.reference_from_node(node, ancestors: ancestors, file_path: @filename) | ||
# 2. node to constant | ||
# 3. constant to reference | ||
@constant_name_inspectors.each do |inspector| | ||
constant_name = inspector.constant_name_from_node(node, ancestors: ancestors) | ||
break if constant_name | ||
end | ||
|
||
reference_from_constant(constant_name, node: node, ancestors: ancestors, file_path: file_path) if constant_name | ||
|
||
# Inside NodeProcessor | ||
# 4. reference to an offence | ||
@checkers.each_with_object([]) do |checker, violations| | ||
next unless checker.invalid_reference?(reference) | ||
offense = Packwerk::ReferenceOffense.new( | ||
location: Node.location(node), | ||
reference: reference, | ||
violation_type: checker.violation_type | ||
) | ||
violations << offense | ||
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.
If we will divide code's responsibility by their explicit "steps", something needs to have the responsibility of orchestration.
RunContext
is just one convenient place atm, but what's more important is to find out if this way of separation of responsibility is what we want to go for, and whether or not the interface between each step is clean and extensible.
Does this direction make sense to you guys? @exterm @dougedey-shopify
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.
Can we eliminate RunContext
and let ParseRun
orchestrate?
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.
yup, certainly! I'll first focus on isolating the responsibilities of data transformation within the RunContext
, then will follow up with the PR to eliminate the redundancy between RunContext
and ParseRun
.
# Packwerk::Checkers | ||
|
||
- Checker implements `invalid_reference?(reference)`, which takes a `Packwerk::Reference` and returns Boolean. | ||
|
||
### Packwerk::Reference | ||
|
||
- It contains the information about source package and destination package. | ||
|
||
|
||
## Example: DependencyChecker | ||
|
||
- Dependency means any outgoing dependency from the source package. | ||
- If source package is enforcing dependency, it will check if declared dependency includes the destination package. | ||
|
||
|
||
## Example: PrivacyChecker | ||
|
||
- Privacy means any incoming dependency towards the destination package. | ||
- If destination package is enforcing privacy, it till check if source package references non-public constants. |
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.
Checker
is an obvious candidate of what we can accept as 3rd party integration.
Does current implementation leak too much of Reference
and subsequent objects?
For example, relative_path
in Reference
doesn't seem to be used at all, but I might be wrong.
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 relative_path
is used in, or through, the offense
@@ -0,0 +1,19 @@ | |||
# Packwerk::Checkers | |||
|
|||
- Checker implements `invalid_reference?(reference)`, which takes a `Packwerk::Reference` and returns Boolean. |
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.
Nitpick: This is accurate, but it duplicates the interface definition in Packwerk::Checker
, so I think in the final docs we should just point out that the interface exists instead of putting the details here
@@ -0,0 +1,3 @@ | |||
### Configurations | |||
|
|||
Includes validators which are outside of primary `check` runs. |
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 would probably go with the comment in https://github.com/Shopify/packwerk/pull/144/files#diff-5ca9dcd54fd0b9eeef94e00ba6adc7822dc83e5dd2bfb9e8d96fd3c356de8b8aR10-R11
@@ -0,0 +1,4 @@ | |||
# Extracting references | |||
|
|||
- Given a `AST::Node`, this module extracts `Packwerk::Reference`. |
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.
If you put it like this, it sounds like everything here works in the context of a single node. However, ParsedConstantDefinitions
works on a whole syntax tree.
@@ -31,6 +33,7 @@ def call(file_path) | |||
node_processor = @node_processor_factory.for(filename: file_path, node: node) | |||
node_visitor = Packwerk::NodeVisitor.new(node_processor: node_processor) | |||
|
|||
# node to offence | |||
node_visitor.visit(node, ancestors: [], result: result) |
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.
Yeah, the content of this conditional doesn't really seem to fit here. I'd rather just hand off to something that processes the AST instead of instantiating processors and visitors here.
@@ -52,7 +54,32 @@ def initialize( | |||
|
|||
sig { params(file: String).returns(T::Array[T.nilable(::Packwerk::Offense)]) } | |||
def process_file(file:) | |||
file_processor.call(file) | |||
# 1. file path to 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.
it's more like file path to AST, but that contains nodes, so... you're not wrong
lib/packwerk/run_context.rb
Outdated
break if constant_name | ||
end | ||
|
||
reference_from_constant(constant_name, node: node, ancestors: ancestors, file_path: file_path) if constant_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.
yeah maybe at this point we'd have a list of references?
I think in general this makes sense but we can talk more tomorrow. I think there'll be more interesting questions coming up when you start putting it together. |
What are you trying to accomplish?
Trying to find a seam within the Packwerk module.
We have some rough exploration of how it works (see below diagram). This PR will acts as a self-annotated PR to invite more discussions on what module structure Packwerk should enjoy.
Responsibilities and its dependencies
Data/object transformation journey
What approach did you choose and why?
A couple of motivations to do this;
checkers
?)What should reviewers focus on?
Currently, the files completely ignore the proposed namespace. No need to commit editing each file until we're comfortable finalizing the proposed change.
Type of Change
Discussion only. The merge-able artifact should be created as a separate PR.