low_type 1.0.0 → 1.0.1

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: 5872bc6f373425ddfc4342d0421dd09e4e3bd6a5e7e0b4d3ba1d1c19cd077a5c
4
- data.tar.gz: 9b5543ba7bded1c2a043966376956316624408a101f2b30ec9a6b16d07976973
3
+ metadata.gz: 3541ef39f820d9554a2493481686ad0692f66d36a31920d2c4304a62cb55bbb9
4
+ data.tar.gz: 19bebceb57fff771317130dae93e5caf86962028db3e81321b03855ccf3c42cb
5
5
  SHA512:
6
- metadata.gz: c3a31c05871cc0d4b5fde6fca3c03c94fbc7e2ccc2696eb7eab401f8817e0b0a1abbd0bf4aa91d72454a61c60265b22ba9997148161f7d3e796d2e9c790e9c81
7
- data.tar.gz: 3629f1ac45bc9cbfce9979f85e933bb3b12b731747b714fb8eba6b7399cf139ac6098eb37667b97b624e95eda4e02c104ce9d7cd2b6013ea3fbb639e1e4a0125
6
+ metadata.gz: 61dace65facc045407b0300157a9e66e6269b14a3414ed943ed3bb5bd817ab313d4c10fd184cad0c2f6803c50a03c6c7d5929253ce68ba89b909a49f55d79fcb
7
+ data.tar.gz: 207f390f476e60af068bffc5bed65c0f76f20bc4997e1d76905496779e63aeaf9022405fe95576d4d8f14ad35107ea6c07f8a98fb4ddfb447d50eff35a1a6921
@@ -5,7 +5,7 @@ require 'prism'
5
5
  require_relative '../interfaces/adapter_interface'
6
6
  require_relative '../proxies/file_proxy'
7
7
  require_relative '../proxies/return_proxy'
8
- require_relative '../error_types'
8
+ require_relative '../types/error_types'
9
9
 
10
10
  module LowType
11
11
  module Adapter
@@ -27,7 +27,7 @@ module LowType
27
27
 
28
28
  pattern = arguments_node.arguments.first.content
29
29
 
30
- line = Parser.line_number(node: method_call)
30
+ line = FileParser.line_number(node: method_call)
31
31
  file = FileProxy.new(path: @file_path, line:, scope: "#{@klass}##{method_call.name}")
32
32
  next unless (return_proxy = return_proxy(method_node: method_call, pattern:, file:))
33
33
 
@@ -38,7 +38,7 @@ module LowType
38
38
  end
39
39
 
40
40
  def return_proxy(method_node:, pattern:, file:)
41
- return_type = Parser.return_type(method_node:)
41
+ return_type = FileParser.return_type(method_node:)
42
42
  return nil if return_type.nil?
43
43
 
44
44
  # Not a security risk because the code comes from a trusted source; the file that did the include. Does the file trust itself?
@@ -1,14 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../proxies/return_proxy'
4
- require_relative '../parser'
4
+ require_relative '../file_parser'
5
5
  require_relative '../type_expression'
6
6
 
7
7
  module LowType
8
8
  class ProxyFactory
9
9
  class << self
10
10
  def return_proxy(method_node:, file:)
11
- return_type = Parser.return_type(method_node:)
11
+ return_type = FileParser.return_type(method_node:)
12
12
  return nil if return_type.nil?
13
13
 
14
14
  # Not a security risk because the code comes from a trusted source; the file that did the include. Does the file trust itself?
@@ -3,7 +3,7 @@
3
3
  require 'prism'
4
4
 
5
5
  module LowType
6
- class Parser
6
+ class FileParser
7
7
  attr_reader :parent_map, :instance_methods, :class_methods, :private_start_line
8
8
 
9
9
  def initialize(file_path:)
data/lib/local_types.rb CHANGED
@@ -2,27 +2,32 @@
2
2
 
3
3
  require_relative 'proxies/file_proxy'
4
4
  require_relative 'proxies/local_proxy'
5
+ require_relative 'types/error_types'
5
6
  require_relative 'type_expression'
6
7
  require_relative 'value_expression'
7
8
 
8
- module LocalTypes
9
- def type(type_expression)
10
- value = type_expression.default_value
9
+ module LowType
10
+ module LocalTypes
11
+ def type(type_expression)
12
+ value = type_expression.default_value
11
13
 
12
- last_caller = caller_locations(1, 1).first
13
- file = LowType::FileProxy.new(path: last_caller.path, line: last_caller.lineno, scope: 'local type')
14
- proxy = LowType::LocalProxy.new(type_expression:, name: self, file:)
14
+ last_caller = caller_locations(1, 1).first
15
+ file = FileProxy.new(path: last_caller.path, line: last_caller.lineno, scope: 'local type')
16
+ proxy = LocalProxy.new(type_expression:, name: self, file:)
15
17
 
16
- type_expression.validate!(value:, proxy:)
18
+ type_expression.validate!(value:, proxy:)
17
19
 
18
- return value.value if value.is_a?(ValueExpression)
20
+ return value.value if value.is_a?(ValueExpression)
19
21
 
20
- value
21
- end
22
- alias low_type type
22
+ value
23
+ rescue NoMethodError
24
+ raise ConfigError, "Invalid type expression, likely because you didn't add 'using LowType::Syntax'"
25
+ end
26
+ alias low_type type
23
27
 
24
- def value(type)
25
- LowType.value(type:)
28
+ def value(type)
29
+ LowType.value(type:)
30
+ end
31
+ alias low_value value
26
32
  end
27
- alias low_value value
28
33
  end
data/lib/low_type.rb CHANGED
@@ -1,18 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'adapters/adapter_loader'
4
- require_relative 'basic_types'
4
+ 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 'subclasses'
8
+ require_relative 'syntax'
9
9
  require_relative 'type_expression'
10
10
  require_relative 'value_expression'
11
11
 
12
- # Include this module into your class to define and check types.
13
12
  module LowType
14
13
  # We do as much as possible on class load rather than on instantiation to be thread-safe and efficient.
15
- def self.included(klass) # rubocop:disable Metrics/AbcSize
14
+ def self.included(klass)
16
15
  class << klass
17
16
  def low_methods
18
17
  @low_methods ||= {}
@@ -20,7 +19,7 @@ module LowType
20
19
  end
21
20
 
22
21
  file_path = LowType.file_path(klass:)
23
- parser = LowType::Parser.new(file_path:)
22
+ parser = FileParser.new(file_path:)
24
23
  private_start_line = parser.private_start_line
25
24
 
26
25
  klass.extend InstanceTypes
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../interfaces/error_interface'
4
- require_relative '../error_types'
4
+ require_relative '../types/error_types'
5
5
 
6
6
  module LowType
7
7
  class LocalProxy < ErrorInterface
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../interfaces/error_interface'
4
- require_relative '../error_types'
4
+ require_relative '../types/error_types'
5
5
 
6
6
  module LowType
7
7
  class ParamProxy < ErrorInterface
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../interfaces/error_interface'
4
- require_relative '../error_types'
4
+ require_relative '../types/error_types'
5
5
 
6
6
  module LowType
7
7
  class ReturnProxy < ErrorInterface
data/lib/redefiner.rb CHANGED
@@ -4,41 +4,57 @@ 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
8
  require_relative 'type_expression'
8
9
 
9
10
  module LowType
10
11
  # Redefine methods to have their arguments and return values type checked.
11
12
  class Redefiner
13
+ using Syntax
14
+
12
15
  class << self
13
- def redefine(method_nodes:, klass:, private_start_line:, file_path:) # rubocop:disable Metrics
16
+ def redefine(method_nodes:, klass:, private_start_line:, file_path:)
17
+ create_proxies(method_nodes:, klass:, file_path:)
18
+ define_methods(method_nodes:, private_start_line:)
19
+ end
20
+
21
+ private
22
+
23
+ def create_proxies(method_nodes:, klass:, file_path:)
24
+ method_nodes.each do |method_node|
25
+ name = method_node.name
26
+ line = FileParser.line_number(node: method_node)
27
+ file = FileProxy.new(path: file_path, line:, scope: "#{klass}##{name}")
28
+ params = param_proxies(method_node:, file:)
29
+ return_proxy = ProxyFactory.return_proxy(method_node:, file:)
30
+
31
+ klass.low_methods[name] = MethodProxy.new(name:, params:, return_proxy:)
32
+ end
33
+ end
34
+
35
+ def define_methods(method_nodes:, private_start_line:) # rubocop:disable Metrics
14
36
  Module.new do
15
37
  method_nodes.each do |method_node|
16
38
  name = method_node.name
17
- line = Parser.line_number(node: method_node)
18
- file = FileProxy.new(path: file_path, line:, scope: "#{klass}##{method_node.name}")
19
- params = Redefiner.params_with_type_expressions(method_node:, file:)
20
- return_proxy = ProxyFactory.return_proxy(method_node:, file:)
21
-
22
- klass.low_methods[name] = MethodProxy.new(name:, params:, return_proxy:)
23
39
 
24
40
  define_method(name) do |*args, **kwargs|
25
- klass.low_methods[name].params.each do |param_proxy|
41
+ method_proxy = instance_of?(Class) ? low_methods[name] : self.class.low_methods[name]
42
+
43
+ method_proxy.params.each do |param_proxy|
26
44
  # Get argument value or default value.
27
45
  value = param_proxy.position ? args[param_proxy.position] : kwargs[param_proxy.name]
28
46
  if value.nil? && param_proxy.type_expression.default_value != :LOW_TYPE_UNDEFINED
29
47
  value = param_proxy.type_expression.default_value
30
48
  end
31
- # Validate argument type.
49
+
32
50
  param_proxy.type_expression.validate!(value:, proxy: param_proxy)
33
- # Handle value(type) special case.
34
51
  value = value.value if value.is_a?(ValueExpression)
35
- # Redefine argument value.
36
52
  param_proxy.position ? args[param_proxy.position] = value : kwargs[param_proxy.name] = value
37
53
  end
38
54
 
39
- if return_proxy
55
+ if (return_proxy = method_proxy.return_proxy)
40
56
  return_value = super(*args, **kwargs)
41
- return_proxy.type_expression.validate!(value: return_value, proxy: klass.low_methods[name].return_proxy)
57
+ return_proxy.type_expression.validate!(value: return_value, proxy: return_proxy)
42
58
  return return_value
43
59
  end
44
60
 
@@ -50,12 +66,11 @@ module LowType
50
66
  end
51
67
  end
52
68
 
53
- def params_with_type_expressions(method_node:, file:)
69
+ def param_proxies(method_node:, file:)
54
70
  return [] if method_node.parameters.nil?
55
71
 
56
72
  params = method_node.parameters.slice
57
- # Not a security risk because the code comes from a trusted source; the file that did the include. Does the file trust itself?
58
- proxy_method = eval("-> (#{params}) {}", binding, __FILE__, __LINE__) # rubocop:disable Security/Eval
73
+ proxy_method = proxy_method(method_node:)
59
74
  required_args, required_kwargs = required_args(proxy_method:)
60
75
 
61
76
  # Not a security risk because the code comes from a trusted source; the file that did the include. Does the file trust itself?
@@ -87,7 +102,11 @@ module LowType
87
102
  raise ArgumentError, "Incorrect param syntax: #{e.message}"
88
103
  end
89
104
 
90
- private
105
+ def proxy_method(method_node:)
106
+ params = method_node.parameters.slice
107
+ # Not a security risk because the code comes from a trusted source; the file that did the include. Does the file trust itself?
108
+ eval("-> (#{params}) {}", binding, __FILE__, __LINE__) # rubocop:disable Security/Eval
109
+ end
91
110
 
92
111
  def required_args(proxy_method:)
93
112
  required_args = []
@@ -1,42 +1,26 @@
1
- module LowType
2
- # Scoped to the class that includes LowType module.
3
- class Array < ::Array
4
- def self.[](*types)
5
- return LowType::TypeExpression.new(type: [*types]) if types.all? { |type| LowType.type?(type) }
6
- super
7
- end
1
+ # frozen_string_literal: true
8
2
 
9
- def self.===(other)
10
- return true if other == ::Array
11
- super
12
- end
13
-
14
- def ==(other)
15
- return true if other.class == ::Array
16
- super
17
- end
18
- end
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) }
19
8
 
20
- # Scoped to the class that includes LowType module.
21
- class Hash < ::Hash
22
- def self.[](type)
23
- return LowType::TypeExpression.new(type:) if LowType.type?(type)
24
- super
9
+ super
10
+ end
25
11
  end
26
12
 
27
- def self.===(other)
28
- return true if other == ::Hash
29
- super
30
- end
13
+ refine Hash.singleton_class do
14
+ def [](type)
15
+ return LowType::TypeExpression.new(type:) if LowType.type?(type)
31
16
 
32
- def ==(other)
33
- return true if other.class == ::Hash
34
- super
17
+ super
18
+ end
35
19
  end
36
20
  end
37
21
  end
38
22
 
39
- # Scoped to the top-level for method type expressions to work. Could we bind/unbind? Yes. Should we? Probably not.
23
+ # Refine doesn't support inheritence.
40
24
  class Object
41
25
  # For "Type | [type_expression/type/value]" situations, redirecting to or generating a type expression from types.
42
26
  # "|" is not defined on Object class and this is the most compute-efficient way to achieve our goal (world peace).
@@ -57,9 +57,9 @@ module LowType
57
57
 
58
58
  def valid_types
59
59
  types = @types.map do |type|
60
- # Remove 'LowType::' namespaces in subtypes.
60
+ # Remove 'LowType::' namespace in subtypes.
61
61
  if type.is_a?(::Array)
62
- '[' + type.map { |subtype| subtype.to_s.delete_prefix('LowType::') }.join(', ') + ']'
62
+ "[#{type.map { |subtype| subtype.to_s.delete_prefix('LowType::') }.join(', ')}]"
63
63
  else
64
64
  type.inspect.to_s.delete_prefix('LowType::')
65
65
  end
@@ -5,4 +5,5 @@ module LowType
5
5
  class LocalTypeError < TypeError; end
6
6
  class ReturnTypeError < TypeError; end
7
7
  class AllowedTypeError < TypeError; end
8
+ class ConfigError < TypeError; end
8
9
  end
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.0'
4
+ VERSION = '1.0.1'
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.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - maedi
@@ -19,23 +19,23 @@ extra_rdoc_files: []
19
19
  files:
20
20
  - lib/adapters/adapter_loader.rb
21
21
  - lib/adapters/sinatra_adapter.rb
22
- - lib/basic_types.rb
23
- - lib/error_types.rb
24
22
  - lib/factories/proxy_factory.rb
23
+ - lib/file_parser.rb
25
24
  - lib/instance_types.rb
26
25
  - lib/interfaces/adapter_interface.rb
27
26
  - lib/interfaces/error_interface.rb
28
27
  - lib/local_types.rb
29
28
  - lib/low_type.rb
30
- - lib/parser.rb
31
29
  - lib/proxies/file_proxy.rb
32
30
  - lib/proxies/local_proxy.rb
33
31
  - lib/proxies/method_proxy.rb
34
32
  - lib/proxies/param_proxy.rb
35
33
  - lib/proxies/return_proxy.rb
36
34
  - lib/redefiner.rb
37
- - lib/subclasses.rb
35
+ - lib/syntax.rb
38
36
  - lib/type_expression.rb
37
+ - lib/types/complex_types.rb
38
+ - lib/types/error_types.rb
39
39
  - lib/value_expression.rb
40
40
  - lib/version.rb
41
41
  homepage: https://codeberg.org/low_ruby/low_type
File without changes