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

Refactor - simplify orchestration layer #147

Merged
merged 1 commit into from
Oct 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions lib/packwerk.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@ module Packwerk
autoload :ApplicationValidator
autoload :AssociationInspector
autoload :OffenseCollection
autoload :Checker
autoload :Cli
autoload :Configuration
autoload :ConstantDiscovery
autoload :ConstantNameInspector
autoload :ConstNodeInspector
autoload :DependencyChecker
autoload :DeprecatedReferences
autoload :FileProcessor
autoload :FilesForProcessing
Expand All @@ -36,7 +34,6 @@ module Packwerk
autoload :ParsedConstantDefinitions
autoload :Parsers
autoload :ParseRun
autoload :PrivacyChecker
autoload :Reference
autoload :ReferenceExtractor
autoload :ReferenceOffense
Expand Down Expand Up @@ -77,4 +74,18 @@ module Generators
autoload :InflectionsFile
autoload :RootPackage
end

module ReferenceChecking
extend ActiveSupport::Autoload

autoload :ReferenceChecker

module Checkers
extend ActiveSupport::Autoload

autoload :Checker
autoload :DependencyChecker
autoload :PrivacyChecker
end
end
exterm marked this conversation as resolved.
Show resolved Hide resolved
end
17 changes: 0 additions & 17 deletions lib/packwerk/checker.rb

This file was deleted.

26 changes: 0 additions & 26 deletions lib/packwerk/dependency_checker.rb

This file was deleted.

53 changes: 39 additions & 14 deletions lib/packwerk/file_processor.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# typed: false
# typed: true
# frozen_string_literal: true

require "ast/node"

module Packwerk
class FileProcessor
extend T::Sig

class UnknownFileTypeResult < Offense
def initialize(file:)
super(file: file, message: "unknown file type")
Expand All @@ -16,24 +18,47 @@ def initialize(node_processor_factory:, parser_factory: nil)
@parser_factory = parser_factory || Packwerk::Parsers::Factory.instance
end

sig do
params(file_path: String).returns(
T::Array[
T.any(
Packwerk::Reference,
Packwerk::Offense,
Copy link
Contributor Author

@kenzan100 kenzan100 Oct 14, 2021

Choose a reason for hiding this comment

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

FileProcessor still have chance to early-return Offence, in cases of UnknownFileTypeResult or ParseError.

I don't particularly like this shared responsibility of the class, and more than happy to revisit the further separation.

)
]
)
end
def call(file_path)
parser = @parser_factory.for_path(file_path)
return [UnknownFileTypeResult.new(file: file_path)] if parser.nil?
return [UnknownFileTypeResult.new(file: file_path)] if parser_for(file_path).nil?

node = File.open(file_path, "r", external_encoding: Encoding::UTF_8) do |file|
parser.call(io: file, file_path: file_path)
rescue Parsers::ParseError => e
return [e.result]
end
node = parse_into_ast(file_path)
return [] unless node

references_from_ast(node, file_path)
rescue Parsers::ParseError => e
[e.result]
end

private

result = []
if node
node_processor = @node_processor_factory.for(filename: file_path, node: node)
node_visitor = Packwerk::NodeVisitor.new(node_processor: node_processor)
def references_from_ast(node, file_path)
references = []

node_visitor.visit(node, ancestors: [], result: result)
node_processor = @node_processor_factory.for(filename: file_path, node: node)
node_visitor = Packwerk::NodeVisitor.new(node_processor: node_processor)
node_visitor.visit(node, ancestors: [], result: references)
kenzan100 marked this conversation as resolved.
Show resolved Hide resolved

references
end

def parse_into_ast(file_path)
File.open(file_path, "r", nil, external_encoding: Encoding::UTF_8) do |file|
parser_for(file_path).call(io: file, file_path: file_path)
end
result
end

def parser_for(file_path)
@parser_factory.for_path(file_path)
end
Copy link
Contributor

Choose a reason for hiding this comment

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

this class looks so much better now.

end
end
31 changes: 9 additions & 22 deletions lib/packwerk/node_processor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,22 @@ class NodeProcessor
params(
reference_extractor: ReferenceExtractor,
filename: String,
checkers: T::Array[Checker]
).void
end
def initialize(reference_extractor:, filename:, checkers:)
def initialize(reference_extractor:, filename:)
@reference_extractor = reference_extractor
@filename = filename
@checkers = checkers
end

sig { params(node: Parser::AST::Node, ancestors: T::Array[Parser::AST::Node]).returns(T::Array[Offense]) }
def call(node, ancestors)
return [] unless Node.method_call?(node) || Node.constant?(node)
reference = @reference_extractor.reference_from_node(node, ancestors: ancestors, file_path: @filename)
check_reference(reference, node)
sig do
params(
node: Parser::AST::Node,
ancestors: T::Array[Parser::AST::Node]
).returns(T.nilable(Packwerk::Reference))
Copy link
Contributor

Choose a reason for hiding this comment

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

❤️

sharpening the definition of the node processor to only generate references - I love it

end

private

def check_reference(reference, node)
return [] unless reference
@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
def call(node, ancestors)
Copy link
Contributor Author

@kenzan100 kenzan100 Oct 14, 2021

Choose a reason for hiding this comment

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

Result of this change, identifies further refactoring opportunity;

NodeProcessor and its factory is becoming almost negligible wrapper on reference_extractor. To be worked on as a follow-up of this PR.

return unless Node.method_call?(node) || Node.constant?(node)
@reference_extractor.reference_from_node(node, ancestors: ancestors, file_path: @filename)
end
end
end
2 changes: 0 additions & 2 deletions lib/packwerk/node_processor_factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,12 @@ class NodeProcessorFactory < T::Struct
const :root_path, String
const :context_provider, Packwerk::ConstantDiscovery
const :constant_name_inspectors, T::Array[ConstantNameInspector]
const :checkers, T::Array[Checker]

sig { params(filename: String, node: AST::Node).returns(NodeProcessor) }
def for(filename:, node:)
::Packwerk::NodeProcessor.new(
reference_extractor: reference_extractor(node: node),
filename: filename,
checkers: checkers,
)
end

Expand Down
3 changes: 2 additions & 1 deletion lib/packwerk/node_visitor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ def initialize(node_processor:)
end

def visit(node, ancestors:, result:)
result.concat(@node_processor.call(node, ancestors))
reference = @node_processor.call(node, ancestors)
result << reference if reference

child_ancestors = [node] + ancestors
Node.each_child(node) do |child|
Expand Down
53 changes: 0 additions & 53 deletions lib/packwerk/privacy_checker.rb

This file was deleted.

2 changes: 1 addition & 1 deletion lib/packwerk/reference.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
# frozen_string_literal: true

module Packwerk
Reference = Struct.new(:source_package, :relative_path, :constant)
Reference = Struct.new(:source_package, :relative_path, :constant, :source_location)
end
21 changes: 21 additions & 0 deletions lib/packwerk/reference_checking/checkers/checker.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# typed: true
# frozen_string_literal: true

module Packwerk
module ReferenceChecking
module Checkers
module Checker
extend T::Sig
extend T::Helpers

interface!

sig { returns(ViolationType).abstract }
def violation_type; end

sig { params(reference: Reference).returns(T::Boolean).abstract }
def invalid_reference?(reference); end
end
end
end
end
30 changes: 30 additions & 0 deletions lib/packwerk/reference_checking/checkers/dependency_checker.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# typed: strict
# frozen_string_literal: true

module Packwerk
module ReferenceChecking
module Checkers
class DependencyChecker
extend T::Sig
include Checker

sig { override.returns(ViolationType) }
def violation_type
ViolationType::Dependency
end

sig do
override
.params(reference: Packwerk::Reference)
.returns(T::Boolean)
end
def invalid_reference?(reference)
return false unless reference.source_package
return false unless reference.source_package.enforce_dependencies?
return false if reference.source_package.dependency?(reference.constant.package)
true
end
end
end
end
end
Loading