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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a340469d2c2f4945b043ab3cd4973f14cf12f9d80336b7939bdb85b0da5b739e
4
- data.tar.gz: 2e51c25e1b944dff67fb85f4582a2f3f38eca299fe58d4cc464c4b8c162a9d8a
3
+ metadata.gz: 5872bc6f373425ddfc4342d0421dd09e4e3bd6a5e7e0b4d3ba1d1c19cd077a5c
4
+ data.tar.gz: 9b5543ba7bded1c2a043966376956316624408a101f2b30ec9a6b16d07976973
5
5
  SHA512:
6
- metadata.gz: 8857cb2e301aa2e7f4ddf167fe622d3988d7d28ff223361a50a056f28ea248e4ad81ecc48fc2174f2f8845c7af7e8fad072ac9bf3ed4eea5a0bad37a06ab9b6d
7
- data.tar.gz: c401324adf4f5aa4b4da5a1c6f19d5b3daa095e7ff28a43a9075f28f4d7a8947a13fcba500bab252eece331b51d65e89fee09efd99e1d61f5e0290faae51dab7
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
- referenced_object = type_expression.default_value.dup
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
- local_proxy = LowType::LocalProxy.new(type_expression:, name: self, file:)
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: referenced_object, proxy: local_proxy)
16
+ type_expression.validate!(value:, proxy:)
22
17
 
23
- define_with_type(referenced_object:)
18
+ return value.value if value.is_a?(ValueExpression)
24
19
 
25
- return referenced_object.value if referenced_object.is_a?(ValueExpression)
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(:local_types, :deep_type_check)
47
- @config ||= config.new(false, 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
@@ -56,7 +56,15 @@ module LowType
56
56
  end
57
57
 
58
58
  def valid_types
59
- types = @types.map { |type| type.inspect.to_s }
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LowType
4
- VERSION = '0.9.0'
4
+ VERSION = '1.0.0'
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: 0.9.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