low_type 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3541ef39f820d9554a2493481686ad0692f66d36a31920d2c4304a62cb55bbb9
4
- data.tar.gz: 19bebceb57fff771317130dae93e5caf86962028db3e81321b03855ccf3c42cb
3
+ metadata.gz: 5eeeaaa196eb1b1b9d4f4408ec91e5716c6d9d19599e78d9f3f1ddaff9c08181
4
+ data.tar.gz: 47259a30a8855c530f530ba10f03c298c29dbf5018146b5432a4b4c0b95ac491
5
5
  SHA512:
6
- metadata.gz: 61dace65facc045407b0300157a9e66e6269b14a3414ed943ed3bb5bd817ab313d4c10fd184cad0c2f6803c50a03c6c7d5929253ce68ba89b909a49f55d79fcb
7
- data.tar.gz: 207f390f476e60af068bffc5bed65c0f76f20bc4997e1d76905496779e63aeaf9022405fe95576d4d8f14ad35107ea6c07f8a98fb4ddfb447d50eff35a1a6921
6
+ metadata.gz: 4331980b4a8ab95a1bcf806a575580c05a9f8386da572cc16e0e978b68376f5609fadac585436e072bbd3d1dc77e09892e53abec6710dff77dbe67f165fb0eff
7
+ data.tar.gz: 6c62cd38cf093e6beef4c66bfa0cad1286da8d86feaa5bf6f2f229124ece2bcddb6ffc9acb216cc6b6c24f9b4935deeca39726c9f29ca27f38d0ed8ca97a508d
data/lib/file_parser.rb CHANGED
@@ -4,25 +4,25 @@ require 'prism'
4
4
 
5
5
  module LowType
6
6
  class FileParser
7
- attr_reader :parent_map, :instance_methods, :class_methods, :private_start_line
7
+ attr_reader :parent_map, :instance_methods, :class_methods, :line_numbers
8
8
 
9
- def initialize(file_path:)
9
+ def initialize(klass:, file_path:)
10
10
  @root_node = Prism.parse_file(file_path).value
11
11
 
12
12
  parent_mapper = ParentMapper.new
13
13
  parent_mapper.visit(@root_node)
14
14
  @parent_map = parent_mapper.parent_map
15
15
 
16
- method_visitor = MethodDefVisitor.new(@parent_map)
16
+ method_visitor = MethodDefVisitor.new(root_node: @root_node, parent_map:, klass:)
17
17
  @root_node.accept(method_visitor)
18
18
 
19
19
  @instance_methods = method_visitor.instance_methods
20
20
  @class_methods = method_visitor.class_methods
21
- @private_start_line = method_visitor.private_start_line
21
+ @line_numbers = method_visitor.line_numbers
22
22
  end
23
23
 
24
24
  def method_calls(method_names:)
25
- block_visitor = MethodCallVisitor.new(parent_map: @parent_map, method_names:)
25
+ block_visitor = MethodCallVisitor.new(parent_map:, method_names:)
26
26
  @root_node.accept(block_visitor)
27
27
  block_visitor.method_calls
28
28
  end
@@ -54,14 +54,15 @@ module LowType
54
54
  end
55
55
 
56
56
  class MethodDefVisitor < Prism::Visitor
57
- attr_reader :class_methods, :instance_methods, :private_start_line
57
+ attr_reader :class_methods, :instance_methods, :line_numbers
58
58
 
59
- def initialize(parent_map)
59
+ def initialize(root_node:, parent_map:, klass:)
60
60
  @parent_map = parent_map
61
+ @klass = klass
61
62
 
62
63
  @instance_methods = []
63
64
  @class_methods = []
64
- @private_start_line = nil
65
+ @line_numbers = { class_start: 0, class_end: root_node.end_line }
65
66
  end
66
67
 
67
68
  def visit_def_node(node)
@@ -75,7 +76,23 @@ module LowType
75
76
  end
76
77
 
77
78
  def visit_call_node(node)
78
- @private_start_line = node.start_line if node.name == :private && node.respond_to?(:start_line)
79
+ start_line = node.name == :private && node.respond_to?(:start_line) && node.start_line || nil
80
+ class_start = @line_numbers[:class_start]
81
+ class_end = @line_numbers[:class_end]
82
+
83
+ if start_line && class_start && class_end && start_line > class_start && start_line < class_end
84
+ @line_numbers[:private_start] = node.start_line
85
+ end
86
+
87
+ super
88
+ end
89
+
90
+ def visit_class_node(node)
91
+ if node.name == @klass.to_s.to_sym
92
+ @line_numbers = { class_start: node.class_keyword_loc.start_line, class_end: node.end_keyword_loc.end_line }
93
+ end
94
+
95
+ super
79
96
  end
80
97
 
81
98
  private
data/lib/low_type.rb CHANGED
@@ -5,27 +5,29 @@ require_relative 'types/complex_types'
5
5
  require_relative 'instance_types'
6
6
  require_relative 'local_types'
7
7
  require_relative 'redefiner'
8
- require_relative 'syntax'
8
+ require_relative 'syntax/syntax'
9
9
  require_relative 'type_expression'
10
10
  require_relative 'value_expression'
11
11
 
12
12
  module LowType
13
13
  # We do as much as possible on class load rather than on instantiation to be thread-safe and efficient.
14
14
  def self.included(klass)
15
+ require_relative 'syntax/union_types' if LowType.config.union_type_expressions
16
+
15
17
  class << klass
16
18
  def low_methods
17
19
  @low_methods ||= {}
18
20
  end
19
21
  end
20
22
 
21
- file_path = LowType.file_path(klass:)
22
- parser = FileParser.new(file_path:)
23
- private_start_line = parser.private_start_line
23
+ file_path = LowType.file_path
24
+ parser = FileParser.new(klass:, file_path:)
25
+ line_numbers = parser.line_numbers
24
26
 
25
27
  klass.extend InstanceTypes
26
28
  klass.include LocalTypes
27
- klass.prepend LowType::Redefiner.redefine(method_nodes: parser.instance_methods, klass:, private_start_line:, file_path:)
28
- klass.singleton_class.prepend LowType::Redefiner.redefine(method_nodes: parser.class_methods, klass:, private_start_line:, file_path:)
29
+ klass.prepend LowType::Redefiner.redefine(method_nodes: parser.instance_methods, klass:, line_numbers:, file_path:)
30
+ klass.singleton_class.prepend LowType::Redefiner.redefine(method_nodes: parser.class_methods, klass:, line_numbers:, file_path:)
29
31
 
30
32
  if (adapter = Adapter::Loader.load(klass:, parser:, file_path:))
31
33
  adapter.process
@@ -37,8 +39,8 @@ module LowType
37
39
  # Public API.
38
40
 
39
41
  def config
40
- config = Struct.new(:deep_type_check, :severity_level)
41
- @config ||= config.new(false, :error)
42
+ config = Struct.new(:deep_type_check, :severity_level, :union_type_expressions)
43
+ @config ||= config.new(false, :error, true)
42
44
  end
43
45
 
44
46
  def configure
@@ -47,11 +49,9 @@ module LowType
47
49
 
48
50
  # Internal API.
49
51
 
50
- def file_path(klass:)
51
- # Remove module namespaces from class.
52
- class_name = klass.to_s.split(':').last
53
- # The first class found regardless of namespace will be the class that did the include.
54
- caller.find { |callee| callee.end_with?("<class:#{class_name}>'") }.split(':').first
52
+ def file_path
53
+ includer_file = caller.find { |callee| callee.end_with?("in 'Module#include'") || callee.end_with?("in 'include'") }
54
+ includer_file.split(':').first
55
55
  end
56
56
 
57
57
  # TODO: Unit test.
@@ -60,7 +60,7 @@ module LowType
60
60
  end
61
61
 
62
62
  def basic_type?(type:)
63
- type.respond_to?(:new) || type == Integer || type == Symbol
63
+ type.class == Class
64
64
  end
65
65
 
66
66
  def complex_type?(type:)
data/lib/redefiner.rb CHANGED
@@ -4,7 +4,7 @@ require_relative 'factories/proxy_factory'
4
4
  require_relative 'proxies/file_proxy'
5
5
  require_relative 'proxies/method_proxy'
6
6
  require_relative 'proxies/param_proxy'
7
- require_relative 'syntax'
7
+ require_relative 'syntax/syntax'
8
8
  require_relative 'type_expression'
9
9
 
10
10
  module LowType
@@ -13,9 +13,9 @@ module LowType
13
13
  using Syntax
14
14
 
15
15
  class << self
16
- def redefine(method_nodes:, klass:, private_start_line:, file_path:)
16
+ def redefine(method_nodes:, klass:, line_numbers:, file_path:)
17
17
  create_proxies(method_nodes:, klass:, file_path:)
18
- define_methods(method_nodes:, private_start_line:)
18
+ define_methods(method_nodes:, line_numbers:)
19
19
  end
20
20
 
21
21
  private
@@ -32,13 +32,20 @@ module LowType
32
32
  end
33
33
  end
34
34
 
35
- def define_methods(method_nodes:, private_start_line:) # rubocop:disable Metrics
35
+ def define_methods(method_nodes:, line_numbers:) # rubocop:disable Metrics
36
+ class_start = line_numbers[:class_start]
37
+ class_end = line_numbers[:class_end]
38
+ private_start = line_numbers[:private_start]
39
+
36
40
  Module.new do
37
41
  method_nodes.each do |method_node|
42
+ method_start = method_node.start_line
43
+ next unless method_start > class_start && method_node.end_line <= class_end
44
+
38
45
  name = method_node.name
39
46
 
40
47
  define_method(name) do |*args, **kwargs|
41
- method_proxy = instance_of?(Class) ? low_methods[name] : self.class.low_methods[name]
48
+ method_proxy = instance_of?(Class) ? low_methods[name] : self.class.low_methods[name] || Object.low_methods[name]
42
49
 
43
50
  method_proxy.params.each do |param_proxy|
44
51
  # Get argument value or default value.
@@ -61,7 +68,7 @@ module LowType
61
68
  super(*args, **kwargs)
62
69
  end
63
70
 
64
- private name if private_start_line && method_node.start_line > private_start_line
71
+ private name if private_start && method_start > private_start
65
72
  end
66
73
  end
67
74
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LowType
4
+ module Syntax
5
+ refine Array.singleton_class do
6
+ def [](*types)
7
+ return LowType::TypeExpression.new(type: [*types]) if types.all? { |type| LowType.type?(type) }
8
+ super
9
+ end
10
+ end
11
+
12
+ refine Hash.singleton_class do
13
+ def [](type)
14
+ return LowType::TypeExpression.new(type:) if LowType.type?(type)
15
+ super
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,26 +1,10 @@
1
- # frozen_string_literal: true
2
-
3
- module LowType
4
- module Syntax
5
- refine Array.singleton_class do
6
- def [](*types)
7
- return LowType::TypeExpression.new(type: [*types]) if types.all? { |type| LowType.type?(type) }
8
-
9
- super
10
- end
11
- end
12
-
13
- refine Hash.singleton_class do
14
- def [](type)
15
- return LowType::TypeExpression.new(type:) if LowType.type?(type)
16
-
17
- super
18
- end
19
- end
20
- end
21
- end
22
-
23
- # Refine doesn't support inheritence.
1
+ ###
2
+ # Type expressions from union types.
3
+ #
4
+ # The "|" pipe syntax requires a monkey-patch but can be disabled if you don't need union types with default values.
5
+ # This is the only monkey-patch in the entire library and is a relatively harmless one.
6
+ # @see LowType.config.union_type_expressions
7
+ ###
24
8
  class Object
25
9
  # For "Type | [type_expression/type/value]" situations, redirecting to or generating a type expression from types.
26
10
  # "|" is not defined on Object class and this is the most compute-efficient way to achieve our goal (world peace).
data/lib/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LowType
4
- VERSION = '1.0.1'
4
+ VERSION = '1.0.3'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: low_type
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - maedi
@@ -32,7 +32,8 @@ files:
32
32
  - lib/proxies/param_proxy.rb
33
33
  - lib/proxies/return_proxy.rb
34
34
  - lib/redefiner.rb
35
- - lib/syntax.rb
35
+ - lib/syntax/syntax.rb
36
+ - lib/syntax/union_types.rb
36
37
  - lib/type_expression.rb
37
38
  - lib/types/complex_types.rb
38
39
  - lib/types/error_types.rb