low_type 0.9.0 → 1.0.0
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 +4 -4
- data/lib/adapters/sinatra_adapter.rb +4 -1
- data/lib/local_types.rb +5 -47
- data/lib/low_type.rb +5 -34
- data/lib/subclasses.rb +58 -0
- data/lib/type_expression.rb +9 -21
- data/lib/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5872bc6f373425ddfc4342d0421dd09e4e3bd6a5e7e0b4d3ba1d1c19cd077a5c
|
|
4
|
+
data.tar.gz: 9b5543ba7bded1c2a043966376956316624408a101f2b30ec9a6b16d07976973
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c3a31c05871cc0d4b5fde6fca3c03c94fbc7e2ccc2696eb7eab401f8817e0b0a1abbd0bf4aa91d72454a61c60265b22ba9997148161f7d3e796d2e9c790e9c81
|
|
7
|
+
data.tar.gz: 3629f1ac45bc9cbfce9979f85e933bb3b12b731747b714fb8eba6b7399cf139ac6098eb37667b97b624e95eda4e02c104ce9d7cd2b6013ea3fbb639e1e4a0125
|
|
@@ -50,13 +50,16 @@ module LowType
|
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
module Methods
|
|
53
|
+
# Unfortunately overriding invoke() is the best way to validate types for now. Though direct it's also very compute efficient.
|
|
54
|
+
# I originally tried an after filter and it mostly worked but it only had access to Response which isn't the raw return value.
|
|
55
|
+
# I suggest that Sinatra provide a hook that allows us to access the raw return value of a route before it becomes a Response.
|
|
53
56
|
def invoke(&block)
|
|
54
57
|
res = catch(:halt, &block)
|
|
55
58
|
|
|
56
59
|
low_validate!(value: res) if res
|
|
57
60
|
|
|
58
61
|
res = [res] if res.is_a?(Integer) || res.is_a?(String)
|
|
59
|
-
if res.is_a?(Array) && res.first.is_a?(Integer)
|
|
62
|
+
if res.is_a?(::Array) && res.first.is_a?(Integer)
|
|
60
63
|
res = res.dup
|
|
61
64
|
status(res.shift)
|
|
62
65
|
body(res.pop)
|
data/lib/local_types.rb
CHANGED
|
@@ -6,25 +6,18 @@ require_relative 'type_expression'
|
|
|
6
6
|
require_relative 'value_expression'
|
|
7
7
|
|
|
8
8
|
module LocalTypes
|
|
9
|
-
class AssignmentError < StandardError; end
|
|
10
|
-
|
|
11
9
|
def type(type_expression)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
raise AssignmentError, "Single-instance objects like #{referenced_object} are not supported" unless LowType.value?(referenced_object)
|
|
10
|
+
value = type_expression.default_value
|
|
15
11
|
|
|
16
12
|
last_caller = caller_locations(1, 1).first
|
|
17
13
|
file = LowType::FileProxy.new(path: last_caller.path, line: last_caller.lineno, scope: 'local type')
|
|
18
|
-
|
|
19
|
-
referenced_object.instance_variable_set('@local_proxy', local_proxy)
|
|
14
|
+
proxy = LowType::LocalProxy.new(type_expression:, name: self, file:)
|
|
20
15
|
|
|
21
|
-
type_expression.validate!(value
|
|
16
|
+
type_expression.validate!(value:, proxy:)
|
|
22
17
|
|
|
23
|
-
|
|
18
|
+
return value.value if value.is_a?(ValueExpression)
|
|
24
19
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
referenced_object
|
|
20
|
+
value
|
|
28
21
|
end
|
|
29
22
|
alias low_type type
|
|
30
23
|
|
|
@@ -32,39 +25,4 @@ module LocalTypes
|
|
|
32
25
|
LowType.value(type:)
|
|
33
26
|
end
|
|
34
27
|
alias low_value value
|
|
35
|
-
|
|
36
|
-
# Scoped to the class that includes LowTypes module.
|
|
37
|
-
class Array < ::Array
|
|
38
|
-
def self.[](*types)
|
|
39
|
-
return LowType::TypeExpression.new(type: [*types]) if types.all? { |type| LowType.type?(type) }
|
|
40
|
-
|
|
41
|
-
super
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
# Scoped to the class that includes LowTypes module.
|
|
46
|
-
class Hash < ::Hash
|
|
47
|
-
def self.[](type)
|
|
48
|
-
return LowType::TypeExpression.new(type:) if LowType.type?(type)
|
|
49
|
-
|
|
50
|
-
super
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
private
|
|
55
|
-
|
|
56
|
-
def define_with_type(referenced_object:)
|
|
57
|
-
def referenced_object.with_type=(value)
|
|
58
|
-
local_proxy = instance_variable_get('@local_proxy')
|
|
59
|
-
type_expression = local_proxy.type_expression
|
|
60
|
-
type_expression.validate!(value:, proxy: local_proxy)
|
|
61
|
-
|
|
62
|
-
# We can't reassign self in Ruby so we reassign instance variables instead.
|
|
63
|
-
value.instance_variables.each do |variable|
|
|
64
|
-
instance_variable_set(variable, value.instance_variable_get(variable))
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
self
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
28
|
end
|
data/lib/low_type.rb
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
require_relative 'adapters/adapter_loader'
|
|
4
4
|
require_relative 'basic_types'
|
|
5
5
|
require_relative 'instance_types'
|
|
6
|
+
require_relative 'local_types'
|
|
6
7
|
require_relative 'redefiner'
|
|
8
|
+
require_relative 'subclasses'
|
|
7
9
|
require_relative 'type_expression'
|
|
8
10
|
require_relative 'value_expression'
|
|
9
11
|
|
|
@@ -11,11 +13,6 @@ require_relative 'value_expression'
|
|
|
11
13
|
module LowType
|
|
12
14
|
# We do as much as possible on class load rather than on instantiation to be thread-safe and efficient.
|
|
13
15
|
def self.included(klass) # rubocop:disable Metrics/AbcSize
|
|
14
|
-
# Array[] and Hash[] class method returns a type expression only for the duration of this "included" hook.
|
|
15
|
-
array_class_method = Array.method('[]').unbind
|
|
16
|
-
hash_class_method = Hash.method('[]').unbind
|
|
17
|
-
LowType.redefine(hash_class_method:)
|
|
18
|
-
|
|
19
16
|
class << klass
|
|
20
17
|
def low_methods
|
|
21
18
|
@low_methods ||= {}
|
|
@@ -27,6 +24,7 @@ module LowType
|
|
|
27
24
|
private_start_line = parser.private_start_line
|
|
28
25
|
|
|
29
26
|
klass.extend InstanceTypes
|
|
27
|
+
klass.include LocalTypes
|
|
30
28
|
klass.prepend LowType::Redefiner.redefine(method_nodes: parser.instance_methods, klass:, private_start_line:, file_path:)
|
|
31
29
|
klass.singleton_class.prepend LowType::Redefiner.redefine(method_nodes: parser.class_methods, klass:, private_start_line:, file_path:)
|
|
32
30
|
|
|
@@ -34,26 +32,18 @@ module LowType
|
|
|
34
32
|
adapter.process
|
|
35
33
|
klass.prepend Adapter::Methods
|
|
36
34
|
end
|
|
37
|
-
ensure
|
|
38
|
-
Array.define_singleton_method('[]', array_class_method)
|
|
39
|
-
Hash.define_singleton_method('[]', hash_class_method)
|
|
40
35
|
end
|
|
41
36
|
|
|
42
37
|
class << self
|
|
43
38
|
# Public API.
|
|
44
39
|
|
|
45
40
|
def config
|
|
46
|
-
config = Struct.new(:
|
|
47
|
-
@config ||= config.new(false,
|
|
41
|
+
config = Struct.new(:deep_type_check, :severity_level)
|
|
42
|
+
@config ||= config.new(false, :error)
|
|
48
43
|
end
|
|
49
44
|
|
|
50
45
|
def configure
|
|
51
46
|
yield(config)
|
|
52
|
-
|
|
53
|
-
return unless config.local_types
|
|
54
|
-
|
|
55
|
-
require_relative 'local_types'
|
|
56
|
-
include LocalTypes
|
|
57
47
|
end
|
|
58
48
|
|
|
59
49
|
# Internal API.
|
|
@@ -89,24 +79,5 @@ module LowType
|
|
|
89
79
|
def value(type:)
|
|
90
80
|
TypeExpression.new(default_value: ValueExpression.new(value: type))
|
|
91
81
|
end
|
|
92
|
-
|
|
93
|
-
# TODO: Unit test.
|
|
94
|
-
def redefine(hash_class_method:)
|
|
95
|
-
Array.define_singleton_method('[]') do |*types|
|
|
96
|
-
TypeExpression.new(type: [*types])
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
Hash.define_singleton_method('[]') do |type|
|
|
100
|
-
# Support Pry which uses Hash[].
|
|
101
|
-
unless LowType.type?(type)
|
|
102
|
-
Hash.define_singleton_method('[]', hash_class_method)
|
|
103
|
-
result = Hash[type]
|
|
104
|
-
Hash.method('[]').unbind
|
|
105
|
-
return result
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
TypeExpression.new(type:)
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
82
|
end
|
|
112
83
|
end
|
data/lib/subclasses.rb
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
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
|
|
8
|
+
|
|
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
|
|
19
|
+
|
|
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
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.===(other)
|
|
28
|
+
return true if other == ::Hash
|
|
29
|
+
super
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def ==(other)
|
|
33
|
+
return true if other.class == ::Hash
|
|
34
|
+
super
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Scoped to the top-level for method type expressions to work. Could we bind/unbind? Yes. Should we? Probably not.
|
|
40
|
+
class Object
|
|
41
|
+
# For "Type | [type_expression/type/value]" situations, redirecting to or generating a type expression from types.
|
|
42
|
+
# "|" is not defined on Object class and this is the most compute-efficient way to achieve our goal (world peace).
|
|
43
|
+
# "|" is overridable by any child object. While we could def/undef this method, this approach is actually lighter.
|
|
44
|
+
# "|" bitwise operator on Integer is not defined when the receiver is an Integer class, so we are not in conflict.
|
|
45
|
+
class << self
|
|
46
|
+
def |(expression)
|
|
47
|
+
if expression.instance_of?(::LowType::TypeExpression)
|
|
48
|
+
# We pass our type into their type expression.
|
|
49
|
+
expression | self
|
|
50
|
+
expression
|
|
51
|
+
else
|
|
52
|
+
# We turn our type into a type expression and pass in their [type_expression/type/value].
|
|
53
|
+
type_expression = ::LowType::TypeExpression.new(type: self)
|
|
54
|
+
type_expression | expression
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
data/lib/type_expression.rb
CHANGED
|
@@ -56,7 +56,15 @@ module LowType
|
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
def valid_types
|
|
59
|
-
types = @types.map
|
|
59
|
+
types = @types.map do |type|
|
|
60
|
+
# Remove 'LowType::' namespaces in subtypes.
|
|
61
|
+
if type.is_a?(::Array)
|
|
62
|
+
'[' + type.map { |subtype| subtype.to_s.delete_prefix('LowType::') }.join(', ') + ']'
|
|
63
|
+
else
|
|
64
|
+
type.inspect.to_s.delete_prefix('LowType::')
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
60
68
|
types += ['nil'] if @default_value.nil?
|
|
61
69
|
types.join(' | ')
|
|
62
70
|
end
|
|
@@ -97,23 +105,3 @@ module LowType
|
|
|
97
105
|
end
|
|
98
106
|
end
|
|
99
107
|
end
|
|
100
|
-
|
|
101
|
-
# For "Type | [type_expression/type/value]" situations, redirecting to or generating a type expression from types.
|
|
102
|
-
# "|" is not defined on Object class and this is the most compute-efficient way to achieve our goal (world peace).
|
|
103
|
-
# "|" is overridable by any child object. While we could def/undef this method, this approach is actually lighter.
|
|
104
|
-
# "|" bitwise operator on Integer is not defined when the receiver is an Integer class, so we are not in conflict.
|
|
105
|
-
class Object
|
|
106
|
-
class << self
|
|
107
|
-
def |(expression)
|
|
108
|
-
if expression.instance_of?(::LowType::TypeExpression)
|
|
109
|
-
# We pass our type into their type expression.
|
|
110
|
-
expression | self
|
|
111
|
-
expression
|
|
112
|
-
else
|
|
113
|
-
# We turn our type into a type expression and pass in their [type_expression/type/value].
|
|
114
|
-
type_expression = ::LowType::TypeExpression.new(type: self)
|
|
115
|
-
type_expression | expression
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
end
|
data/lib/version.rb
CHANGED
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: 0.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- maedi
|
|
@@ -34,6 +34,7 @@ files:
|
|
|
34
34
|
- lib/proxies/param_proxy.rb
|
|
35
35
|
- lib/proxies/return_proxy.rb
|
|
36
36
|
- lib/redefiner.rb
|
|
37
|
+
- lib/subclasses.rb
|
|
37
38
|
- lib/type_expression.rb
|
|
38
39
|
- lib/value_expression.rb
|
|
39
40
|
- lib/version.rb
|