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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a54636f321a831faea2655843bad6ce92b99fe65
4
- data.tar.gz: aab6a5ebe27dd54bbbfd6a2002cc5fcb01034189
3
+ metadata.gz: 84d333dcb760ef858b22802125d730f1b92620e3
4
+ data.tar.gz: e584d70cc0359970c93dc0b5c4953a526a20720b
5
5
  SHA512:
6
- metadata.gz: 1b5b2c4157f1656b8634add26ed21e442d65e871afd0d91e3daaf89ad31b0fd2d93c70879628c077713970b6fc987b1c369ac5fd9257cdf0f56fdc503bbf448b
7
- data.tar.gz: fc291a4ed9f314232b7082c036b6917641cfa3ef802f13623ddb0c77e629f9b8d49061368cbef9d5e5761cd496af9e01a140d6acb6278f5be8b4feb75be43aac
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
@@ -0,0 +1,3 @@
1
+ module TSpec
2
+ VERSION = '0.4.0'
3
+ 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
- def self.value_type_check(value, *types)
41
- types.any? do |type|
42
- if type.instance_of?(Array)
43
- return false unless value.instance_of?(Array)
44
-
45
- value.all? do |v|
46
- type.any? do |t|
47
- value_type_check(v, t)
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
- klass = (@before_trace[:method_id] == :singleton_method_added) ? @before_trace[:self].singleton_class : @before_trace[:self]
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
- else
80
- key = "#{tp.defined_class}::#{tp.method_id}"
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
- unless arguments.include?(name)
89
- @type_error_flag = true
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
- value = tp.binding.local_variable_get(name)
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
- unless value_type_check(value, *type)
96
- @type_error_flag = true
97
- if type.instance_of?(Array)
98
- raise ArgumentTypeError, "##{tp.method_id} '#{name}' variable should be #{type.map(&:inspect).join(' or ')}, but actual '#{value.inspect}' - #{value.class}"
99
- else
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
- end
106
- when :return
107
- if !@type_error_flag
108
- key = "#{tp.defined_class}::#{tp.method_id}"
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
- if types = @method_return_type_table[key]
111
- unless value_type_check(tp.return_value, *types)
112
- @type_error_flag = true
113
- raise ReturnValueTypeError, "`#{tp.method_id}' expected return #{types.map(&:inspect).join(' or ')}, but actual `#{tp.return_value.inspect}' - #{tp.return_value.class}"
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
- end
117
- end
63
+ end
118
64
 
119
- @type_error_flag = false
65
+ @type_error_flag = false
120
66
 
121
- if tp.defined_class != Symbol || !%i(return receive).include?(tp.method_id)
122
- @before_trace = {self: tp.self, method_id: tp.method_id}
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 = "0.3.0"
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.3.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-17 00:00:00.000000000 Z
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: