Skip to content

Commit

Permalink
Merge pull request #11 from euglena1215/refactoring
Browse files Browse the repository at this point in the history
Refactoring
  • Loading branch information
euglena1215 authored Jan 7, 2024
2 parents 831984f + f71d5a2 commit 8b69d50
Show file tree
Hide file tree
Showing 11 changed files with 213 additions and 99 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## [Unreleased]

- Some refactoring https://github.com/euglena1215/packwerk-yard/pull/11

## [0.2.0] - 2024-01-07

- Add packwerk to dependencies https://github.com/euglena1215/packwerk-yard/pull/6
Expand Down
2 changes: 2 additions & 0 deletions lib/packwerk_yard.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

require_relative "packwerk_yard/version"
require_relative "packwerk_yard/parser"
require_relative "packwerk_yard/constantize_type"
require_relative "packwerk_yard/yard_handler"

module PackwerkYard
end
43 changes: 43 additions & 0 deletions lib/packwerk_yard/constantize_type.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# typed: strict
# frozen_string_literal: true

module PackwerkYard
class ConstantizeType
extend T::Sig

# Array Syntax e.g. Array<String>
ARRAY_REGEXP = T.let(/\AArray<(.+)>/.freeze, Regexp)
private_constant :ARRAY_REGEXP

# Hash Syntax e.g. Hash<String, String>
HASH_REGEXP = T.let(/\AHash<([^,]*),\s?(.*)>/.freeze, Regexp)
private_constant :HASH_REGEXP

sig { params(yard_type: String).void }
def initialize(yard_type)
@yard_type = yard_type
end

sig { returns(T::Array[String]) }
def types
split_type(@yard_type).select { |type| constantize?(type) }.uniq
end

private

sig { params(name: String).returns(T::Boolean) }
def constantize?(name)
Object.const_get(name) # rubocop:disable Sorbet/ConstantsFromStrings
true
rescue NameError
false
end

sig { params(type: String).returns(T::Array[String]) }
def split_type(type)
matched_types = Array(ARRAY_REGEXP.match(type).to_a[1])
matched_types = Array(HASH_REGEXP.match(type).to_a[1..2]) if matched_types.empty?
matched_types.empty? ? [type] : matched_types.map { |t| split_type(t) }.flatten
end
end
end
47 changes: 3 additions & 44 deletions lib/packwerk_yard/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,6 @@ class Parser
extend T::Sig
include Packwerk::FileParser

# Array Syntax e.g. Array<String>
ARRAY_REGEXP = T.let(/\AArray<(.+)>/.freeze, Regexp)
private_constant :ARRAY_REGEXP

# Hash Syntax e.g. Hash<String, String>
HASH_REGEXP = T.let(/\AHash<([^,]*),\s?(.*)>/.freeze, Regexp)
private_constant :HASH_REGEXP

sig { params(ruby_parser: T.nilable(Packwerk::Parsers::Ruby)).void }
def initialize(ruby_parser: Packwerk::Parsers::Ruby.new)
@ruby_parser = ruby_parser
Expand All @@ -24,11 +16,11 @@ def call(io:, file_path: "<unknown>")
source_code = io.read
return to_ruby_ast(nil.inspect, file_path) if source_code.nil?

types = extract_from_yard_to_types(source_code)
yard_handler = YardHandler.from_source(source_code)
types = yard_handler.return_types | yard_handler.param_types

to_ruby_ast(
types.map { |type| to_evaluable_type(type) }.flatten
.reject { |type| to_constant(type).nil? }
types.map { |type| ConstantizeType.new(type).types }.flatten
.inspect.delete('"'),
file_path,
)
Expand All @@ -41,39 +33,6 @@ def match?(path:)

private

sig { params(source_code: String).returns(T::Array[String]) }
def extract_from_yard_to_types(source_code)
YARD::Registry.clear
YARD::Logger.instance.enter_level(YARD::Logger::ERROR) do
YARD::Parser::SourceParser.parse_string(source_code)
end

types = YARD::Registry.all(:method).each_with_object([]) do |method_object, arr|
method_object.tags("param").each do |tag|
arr.concat(tag.types) if tag.types
end

return_tag = method_object.tag("return")
arr.concat(return_tag.types) if return_tag&.types
end

types.uniq
end

sig { params(type: String).returns(T::Array[String]) }
def to_evaluable_type(type)
matched_types = Array(ARRAY_REGEXP.match(type).to_a[1])
matched_types = Array(HASH_REGEXP.match(type).to_a[1..2]) if matched_types.empty?
matched_types.empty? ? [type] : matched_types.map { |t| to_evaluable_type(t) }.flatten
end

sig { params(name: T.any(Symbol, String)).returns(T.untyped) }
def to_constant(name)
Object.const_get(name) # rubocop:disable Sorbet/ConstantsFromStrings
rescue NameError
nil
end

sig { params(code: String, file_path: T.untyped).returns(T.untyped) }
def to_ruby_ast(code, file_path)
T.must(@ruby_parser).call(
Expand Down
46 changes: 46 additions & 0 deletions lib/packwerk_yard/yard_handler.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# typed: strict
# frozen_string_literal: true

module PackwerkYard
class YardHandler
extend T::Sig

sig { returns(T::Array[String]) }
attr_reader :return_types

sig { returns(T::Array[String]) }
attr_reader :param_types

sig { params(return_types: T::Array[String], param_types: T::Array[String]).void }
def initialize(return_types, param_types)
@return_types = return_types
@param_types = param_types
end

class << self
extend T::Sig

sig { params(code: String).returns(YardHandler) }
def from_source(code)
YARD::Registry.clear
YARD::Logger.instance.enter_level(YARD::Logger::ERROR) do
YARD::Parser::SourceParser.parse_string(code)
end

return_types = []
param_types = []

YARD::Registry.all(:method).each do |method_object|
method_object.tags("param").each do |tag|
param_types << tag.types if tag.types
end

return_tag = method_object.tag("return")
return_types << return_tag.types if return_tag&.types
end

new(return_types.flatten.uniq, param_types.flatten.uniq)
end
end
end
end
9 changes: 0 additions & 9 deletions test/fixtures/yard/nested_array.rb

This file was deleted.

9 changes: 0 additions & 9 deletions test/fixtures/yard/nested_hash.rb

This file was deleted.

6 changes: 0 additions & 6 deletions test/fixtures/yard/not_exists_class_args.rb

This file was deleted.

45 changes: 45 additions & 0 deletions test/unit/packwerk_yard/constantize_type_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# typed: false
# frozen_string_literal: true

require "test_helper"

module PackwerkYard
class ConstantizeTypeTest < Minitest::Test
def test_types_returns_constantize_types_with_primitive_value
types = PackwerkYard::ConstantizeType.new("String").types

assert_equal(1, types.size)
assert_equal("String", types[0])
end

def test_types_returns_constantize_types_with_simple_array
types = PackwerkYard::ConstantizeType.new("Array<String>").types

assert_equal(["String"], types)
end

def test_types_returns_constantize_types_with_nested_array
types = PackwerkYard::ConstantizeType.new("Array<Array<String>>").types

assert_equal(["String"], types)
end

def test_types_returns_constantize_types_with_simple_hash
types = PackwerkYard::ConstantizeType.new("Hash<Integer, String>").types

assert_equal(["Integer", "String"], types)
end

def test_types_returns_constantize_types_with_nested_hash
types = PackwerkYard::ConstantizeType.new("Hash<Integer, Hash<String, Integer>>").types

assert_equal(["Integer", "String"], types)
end

def test_types_returns_constantize_types_with_not_existing_class
types = PackwerkYard::ConstantizeType.new("NotExistsClass").types

assert_equal([], types)
end
end
end
31 changes: 0 additions & 31 deletions test/unit/packwerk_yard/parser_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,36 +14,5 @@ def test_call_returns_node_with_valid_file
assert(node.type, :array)
assert(node.children[0].children[1], :String)
end

def test_call_return_node_with_not_exists_class_args_file
parser = ::PackwerkYard::Parser.new
io = StringIO.new(File.read("test/fixtures/yard/not_exists_class_args.rb"))
node = parser.call(io: io, file_path: "test/fixtures/yard/not_exists_class_args.rb")

assert_instance_of(::Parser::AST::Node, node)
assert(node.type, :array)
assert(node.children.size, 0)
end

def test_call_return_node_with_nested_array_file
parser = ::PackwerkYard::Parser.new
io = StringIO.new(File.read("test/fixtures/yard/nested_array.rb"))
node = parser.call(io: io, file_path: "test/fixtures/yard/nested_array.rb")

assert_instance_of(::Parser::AST::Node, node)
assert(node.type, :array)
assert(node.children[0].children[1], :String)
end

def test_call_return_node_with_nested_hash_file
parser = ::PackwerkYard::Parser.new
io = StringIO.new(File.read("test/fixtures/yard/nested_hash.rb"))
node = parser.call(io: io, file_path: "test/fixtures/yard/nested_hash.rb")

assert_instance_of(::Parser::AST::Node, node)
assert(node.type, :array)
assert(node.children[0].children[1], :String)
assert(node.children[1].children[1], :Integer)
end
end
end
72 changes: 72 additions & 0 deletions test/unit/packwerk_yard/yard_handler_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# typed: false
# frozen_string_literal: true

module PackwerkYard
class YardHandlerTest < Minitest::Test
def test_from_source_with_simple_param_and_return
code = <<~RUBY
class Foo
# @param [String] foo
# @return [String]
def bar(foo)
foo
end
end
RUBY

handler = PackwerkYard::YardHandler.from_source(code)

assert_equal(["String"], handler.param_types)
assert_equal(["String"], handler.return_types)
end

def test_from_source_with_multiple_param
code = <<~RUBY
class Foo
# @param [String] foo
# @param [Integer] bar
def baz(foo, bar)
end
end
RUBY

handler = PackwerkYard::YardHandler.from_source(code)

assert_equal(["String", "Integer"], handler.param_types)
end

def test_from_source_with_nested_array
code = <<~RUBY
class Foo
# @param [Array<Array<String>>] foo
# @return [Array<Array<String>>]
def bar(foo)
foo
end
end
RUBY

handler = PackwerkYard::YardHandler.from_source(code)

assert_equal(["Array<Array<String>>"], handler.param_types)
assert_equal(["Array<Array<String>>"], handler.return_types)
end

def test_from_source_with_polymorphic_param_and_return
code = <<~RUBY
class Foo
# @param [String, Integer] foo
# @return [String, Integer]
def bar(foo)
foo
end
end
RUBY

handler = PackwerkYard::YardHandler.from_source(code)

assert_equal(["String", "Integer"], handler.param_types)
assert_equal(["String", "Integer"], handler.return_types)
end
end
end

0 comments on commit 8b69d50

Please sign in to comment.