tspec 0.3.0 → 0.4.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/tspec/core.rb +127 -0
- data/lib/tspec/version.rb +3 -0
- data/lib/tspec.rb +48 -99
- data/tspec.gemspec +4 -1
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 84d333dcb760ef858b22802125d730f1b92620e3
|
4
|
+
data.tar.gz: e584d70cc0359970c93dc0b5c4953a526a20720b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89a5b92412920594c39e4ea503a693e82036bbec41ef4447e2d4520267af63192c83e1fd663cf66949f006623ce956f363ba5b74979e5fb4e4f27e87ed4e6646
|
7
|
+
data.tar.gz: dbe9ac78c9a44d35889114b8365aca4cfd8ec85f8c35bc34daa2609ed79bc87cc5febb31f73fa3b20ec2555c968b9ea1a7e97066ff3062df93cc7e4febf90425
|
data/lib/tspec/core.rb
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
class Symbol
|
2
|
+
def return(*types)
|
3
|
+
self
|
4
|
+
end
|
5
|
+
|
6
|
+
def receive(*type, **types)
|
7
|
+
self
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class UnboundMethod
|
12
|
+
def return(*types)
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def receive(*type, **types)
|
17
|
+
self
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Method
|
22
|
+
def return(*types)
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def receive(*type, **types)
|
27
|
+
self
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module TSpec
|
32
|
+
module_function
|
33
|
+
|
34
|
+
def value_type_check(value, *types)
|
35
|
+
types.any? do |type|
|
36
|
+
if type.instance_of?(Array)
|
37
|
+
return false unless value.instance_of?(Array)
|
38
|
+
|
39
|
+
value.all? do |v|
|
40
|
+
type.any? do |t|
|
41
|
+
value_type_check(v, t)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
else
|
45
|
+
value.instance_of?(type)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def regist_type(method_id, ctx, keys)
|
51
|
+
types = ctx.local_variable_get(:types)
|
52
|
+
|
53
|
+
case method_id
|
54
|
+
when :return
|
55
|
+
regist_return_type(keys, types)
|
56
|
+
when :receive
|
57
|
+
regist_receive_type(keys, types, ctx.local_variable_get(:type))
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def get_keys(tp, btp)
|
62
|
+
ctx = tp.binding
|
63
|
+
keys = []
|
64
|
+
|
65
|
+
if %i(instance_method method).include?(btp[:method_id])
|
66
|
+
keys << "#{tp.self.owner}::#{tp.self.name}"
|
67
|
+
else
|
68
|
+
if btp[:method_id] == :singleton_method_added
|
69
|
+
klass = btp[:self].singleton_class
|
70
|
+
|
71
|
+
if @module_function_flags[btp[:self]]
|
72
|
+
keys << "#{btp[:self]}::#{ctx.receiver}"
|
73
|
+
end
|
74
|
+
else
|
75
|
+
klass = btp[:self]
|
76
|
+
end
|
77
|
+
|
78
|
+
keys << "#{klass}::#{ctx.receiver}"
|
79
|
+
end
|
80
|
+
|
81
|
+
keys
|
82
|
+
end
|
83
|
+
|
84
|
+
def regist_return_type(keys, types)
|
85
|
+
keys.each do |key|
|
86
|
+
@method_return_type_table[key] = types
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def regist_receive_type(keys, types, type)
|
91
|
+
keys.each do |key|
|
92
|
+
@method_arguments_type_table[key] = types
|
93
|
+
|
94
|
+
if @method_arguments_type_table[key].empty?
|
95
|
+
@method_arguments_type_table[key] = { type.__id__ => type }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def check_type(tp)
|
101
|
+
key = "#{tp.defined_class}::#{tp.method_id}"
|
102
|
+
|
103
|
+
if types = @method_arguments_type_table[key]
|
104
|
+
arguments = tp.binding.eval("method(:#{tp.method_id}).parameters.map(&:last)")
|
105
|
+
|
106
|
+
types.each do |name, type|
|
107
|
+
name = arguments.first if name == type.__id__
|
108
|
+
|
109
|
+
unless arguments.include?(name)
|
110
|
+
@type_error_flag = true
|
111
|
+
raise NotFoundArgumentNameError, "undefined arguments `#{name}' for #{key}"
|
112
|
+
end
|
113
|
+
|
114
|
+
value = tp.binding.local_variable_get(name)
|
115
|
+
|
116
|
+
unless value_type_check(value, *type)
|
117
|
+
@type_error_flag = true
|
118
|
+
if type.instance_of?(Array)
|
119
|
+
raise ArgumentTypeError, "##{tp.method_id} '#{name}' variable should be #{type.map(&:inspect).join(' or ')}, but actual '#{value.inspect}' - #{value.class}"
|
120
|
+
else
|
121
|
+
raise ArgumentTypeError, "##{tp.method_id} '#{name}' variable should be #{type.inspect}, but actual '#{value.inspect}' - #{value.class}"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
data/lib/tspec.rb
CHANGED
@@ -1,125 +1,74 @@
|
|
1
|
+
require 'tspec/version'
|
1
2
|
require 'tspec/type_error'
|
2
|
-
|
3
|
-
class Symbol
|
4
|
-
def return(*types)
|
5
|
-
self
|
6
|
-
end
|
7
|
-
|
8
|
-
def receive(*type, **types)
|
9
|
-
self
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
class UnboundMethod
|
14
|
-
def return(*types)
|
15
|
-
self
|
16
|
-
end
|
17
|
-
|
18
|
-
def receive(*type, **types)
|
19
|
-
self
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
class Method
|
24
|
-
def return(*types)
|
25
|
-
self
|
26
|
-
end
|
27
|
-
|
28
|
-
def receive(*type, **types)
|
29
|
-
self
|
30
|
-
end
|
31
|
-
end
|
3
|
+
require 'tspec/core'
|
32
4
|
|
33
5
|
module TSpec
|
34
6
|
@method_return_type_table = {}
|
35
7
|
@method_arguments_type_table = {}
|
36
8
|
@before_trace = {}
|
9
|
+
@module_function_flags = {}
|
10
|
+
@module_function_mode = {}
|
37
11
|
@type_error_flag = false
|
12
|
+
HOOK_EVENT = %i(call return c_call c_return line)
|
38
13
|
DEFINE_METHOD_SYMBOLS = %i(method_added singleton_method_added define_method instance_method method)
|
39
14
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
end
|
49
|
-
end
|
50
|
-
else
|
51
|
-
value.instance_of?(type)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
TracePoint.trace do |tp|
|
57
|
-
case tp.event
|
58
|
-
when :call
|
59
|
-
if %i(return receive).include?(tp.method_id) && DEFINE_METHOD_SYMBOLS.include?(@before_trace[:method_id])
|
60
|
-
ctx = tp.binding
|
61
|
-
|
62
|
-
if %i(instance_method method).include?(@before_trace[:method_id])
|
63
|
-
key = "#{tp.self.owner}::#{tp.self.name}"
|
15
|
+
@trace = TracePoint.new(*HOOK_EVENT) do |tp|
|
16
|
+
@trace.disable do
|
17
|
+
case tp.event
|
18
|
+
when :call
|
19
|
+
if %i(return receive).include?(tp.method_id) && DEFINE_METHOD_SYMBOLS.include?(@before_trace[:method_id])
|
20
|
+
ctx = tp.binding
|
21
|
+
keys = get_keys(tp, @before_trace)
|
22
|
+
regist_type(tp.method_id, ctx, keys)
|
64
23
|
else
|
65
|
-
|
66
|
-
key = "#{klass}::#{ctx.receiver}"
|
67
|
-
end
|
68
|
-
|
69
|
-
case tp.method_id
|
70
|
-
when :return
|
71
|
-
@method_return_type_table[key] = ctx.local_variable_get(:types)
|
72
|
-
when :receive
|
73
|
-
@method_arguments_type_table[key] = ctx.local_variable_get(:types)
|
74
|
-
|
75
|
-
if @method_arguments_type_table[key].empty?
|
76
|
-
@method_arguments_type_table[key] = {ctx.local_variable_get(:type).__id__ => ctx.local_variable_get(:type)}
|
77
|
-
end
|
24
|
+
check_type(tp)
|
78
25
|
end
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
if types = @method_arguments_type_table[key]
|
83
|
-
arguments = tp.binding.eval("method(:#{tp.method_id}).parameters.map(&:last)")
|
84
|
-
|
85
|
-
types.each do |name, type|
|
86
|
-
name = arguments.first if name == type.__id__
|
26
|
+
when :c_call
|
27
|
+
if tp.method_id == :module_function && tp.defined_class == Module
|
28
|
+
@module_function_mode[tp.self] = true
|
87
29
|
|
88
|
-
|
89
|
-
|
90
|
-
raise NotFoundArgumentNameError, "undefined arguments `#{name}' for #{key}"
|
91
|
-
end
|
30
|
+
line = File.readlines(tp.path)[tp.lineno-1]
|
31
|
+
names = line.scan(/:.+/)
|
92
32
|
|
93
|
-
|
33
|
+
if names.empty?
|
34
|
+
@module_function_flags[tp.self] = true
|
35
|
+
else
|
36
|
+
names.each do |name|
|
37
|
+
key = "#{tp.self}:#{name}"
|
94
38
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
raise ArgumentTypeError, "##{tp.method_id} '#{name}' variable should be #{type.inspect}, but actual '#{value.inspect}' - #{value.class}"
|
39
|
+
if type = @method_return_type_table[key]
|
40
|
+
@method_return_type_table["#{tp.self.singleton_class}:#{name}"] = type
|
41
|
+
end
|
42
|
+
if type = @method_arguments_type_table[key]
|
43
|
+
@method_arguments_type_table["#{tp.self.singleton_class}:#{name}"] = type
|
101
44
|
end
|
102
45
|
end
|
103
46
|
end
|
104
47
|
end
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
48
|
+
when :c_return
|
49
|
+
if tp.method_id == :module_function && tp.defined_class == Module
|
50
|
+
@module_function_mode[tp.self] = false
|
51
|
+
end
|
52
|
+
when :return
|
53
|
+
if !@type_error_flag
|
54
|
+
key = "#{tp.defined_class}::#{tp.method_id}"
|
109
55
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
56
|
+
if types = @method_return_type_table[key]
|
57
|
+
unless value_type_check(tp.return_value, *types)
|
58
|
+
@type_error_flag = true
|
59
|
+
raise ReturnValueTypeError, "`#{tp.method_id}' expected return #{types.map(&:inspect).join(' or ')}, but actual `#{tp.return_value.inspect}' - #{tp.return_value.class}"
|
60
|
+
end
|
114
61
|
end
|
115
62
|
end
|
116
|
-
|
117
|
-
end
|
63
|
+
end
|
118
64
|
|
119
|
-
|
65
|
+
@type_error_flag = false
|
120
66
|
|
121
|
-
|
122
|
-
|
67
|
+
if tp.defined_class != Symbol || !%i(return receive).include?(tp.method_id)
|
68
|
+
@before_trace = { self: tp.self, method_id: tp.method_id }
|
69
|
+
end
|
123
70
|
end
|
124
71
|
end
|
72
|
+
|
73
|
+
@trace.enable
|
125
74
|
end
|
data/tspec.gemspec
CHANGED
@@ -2,9 +2,11 @@
|
|
2
2
|
lib = File.expand_path('../lib', __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
4
|
|
5
|
+
require 'tspec/version'
|
6
|
+
|
5
7
|
Gem::Specification.new do |spec|
|
6
8
|
spec.name = "tspec"
|
7
|
-
spec.version =
|
9
|
+
spec.version = TSpec::VERSION
|
8
10
|
spec.authors = ["siman-man"]
|
9
11
|
spec.email = ["k128585@ie.u-ryukyu.ac.jp"]
|
10
12
|
|
@@ -25,4 +27,5 @@ Gem::Specification.new do |spec|
|
|
25
27
|
spec.add_development_dependency "bundler", "~> 1.14"
|
26
28
|
spec.add_development_dependency "rake", "~> 10.0"
|
27
29
|
spec.add_development_dependency "rspec", "~> 3.0"
|
30
|
+
spec.add_development_dependency "coderay", "~> 1.1"
|
28
31
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tspec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- siman-man
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-02-
|
11
|
+
date: 2017-02-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: coderay
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.1'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.1'
|
55
69
|
description: Add type check method
|
56
70
|
email:
|
57
71
|
- k128585@ie.u-ryukyu.ac.jp
|
@@ -74,7 +88,9 @@ files:
|
|
74
88
|
- examples/return_failed.rb
|
75
89
|
- examples/return_success.rb
|
76
90
|
- lib/tspec.rb
|
91
|
+
- lib/tspec/core.rb
|
77
92
|
- lib/tspec/type_error.rb
|
93
|
+
- lib/tspec/version.rb
|
78
94
|
- tspec.gemspec
|
79
95
|
homepage: https://github.com/siman-man/tspec
|
80
96
|
licenses:
|