typerb 0.4.0 → 0.5.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/CHANGELOG.md +18 -0
- data/README.md +54 -58
- data/lib/typerb/exceptional.rb +5 -21
- data/lib/typerb/prism_parser.rb +67 -0
- data/lib/typerb/ruby_vm_parser.rb +57 -0
- data/lib/typerb/source_cache.rb +30 -0
- data/lib/typerb/variable_name.rb +18 -14
- data/lib/typerb/version.rb +1 -1
- data/lib/typerb.rb +23 -42
- metadata +18 -93
- data/.github/workflows/tests.yml +0 -24
- data/.gitignore +0 -15
- data/.rspec +0 -3
- data/.rubocop.yml +0 -35
- data/Gemfile +0 -8
- data/Gemfile.lock +0 -97
- data/Guardfile +0 -36
- data/Rakefile +0 -8
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/typerb.gemspec +0 -37
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 548c823e7f9d7185d069b2bb18f370bdcb8b18603e82de22b78712c251dc4a14
|
|
4
|
+
data.tar.gz: 637a184fa56802fdb93a60020e7eb633692e606726215dce533de62367edd3fe
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d1cf6e4223cae7543e133ede58d02a0016e8566501e1642afd85aa36ffea47e09ce3a0135fe81bd805461dc5dd163c546d5b65148f19bc1ebbab75694dbb05bf
|
|
7
|
+
data.tar.gz: '0486a293dea0eca23bdd4aa56f02a0a7fdd4c35d7d317a3c34683930f5642b903714c974a63a2b06533809703fecb549716e8e300b5d4248b1b84d4a7ce1ee3f'
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
- Variable name detection rewritten: the whole caller file is parsed instead of a single stripped line.
|
|
6
|
+
Prism is used on Ruby 3.3+, `RubyVM::AbstractSyntaxTree` on 3.0-3.2. Both backends behave identically.
|
|
7
|
+
- Fixed `SyntaxError` escaping from `type!` and friends when the call spanned several lines
|
|
8
|
+
(leading-dot chains, multi-line argument lists).
|
|
9
|
+
- Variable name is now reported for multi-line calls, assignments, string interpolation and chained
|
|
10
|
+
receivers (`h.fetch(:a).type!(String)`).
|
|
11
|
+
- `subset_of!` raises `ArgumentError` instead of `NoMethodError` when the receiver is not `Enumerable`.
|
|
12
|
+
- Variable names are reported correctly on lines containing multibyte characters.
|
|
13
|
+
- Minimum Ruby is 3.0.
|
|
14
|
+
- Removed Guard from development dependencies.
|
|
15
|
+
|
|
16
|
+
## 0.4.0
|
|
17
|
+
|
|
18
|
+
- Added `subset_of!`.
|
data/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
[](https://badge.fury.io/rb/typerb)
|
|
2
|
-
[](https://github.com/olegantonyan/typerb/actions/workflows/tests.yml)
|
|
3
3
|
|
|
4
4
|
# Typerb
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
Typecheck sugar for Ruby. Requires Ruby 3.0 or newer.
|
|
7
7
|
|
|
8
8
|
```ruby
|
|
9
9
|
class A
|
|
@@ -22,32 +22,33 @@ class A
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def call_with_subset(arg)
|
|
25
|
-
arg.subset_of!([
|
|
25
|
+
arg.subset_of!(%i[one two])
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
A.new.call(1)
|
|
30
|
-
A.new.call_with_respond_checks(1)
|
|
31
|
-
A.new.call_with_enum(:three)
|
|
32
|
-
A.new.call_with_subset([
|
|
29
|
+
A.new.call(1) #=> TypeError: `some_arg` should be String or Symbol, not Integer (1)
|
|
30
|
+
A.new.call_with_respond_checks(1) #=> TypeError: Integer (`some_arg`) should respond to all methods: strip
|
|
31
|
+
A.new.call_with_enum(:three) #=> TypeError: Symbol (`arg`) should be one of: [one, two], not three
|
|
32
|
+
A.new.call_with_subset(%i[one three]) #=> TypeError: Array (`arg`) should be subset of: [:one, :two], not [:one, :three]
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
This is equivalent to:
|
|
36
|
+
|
|
36
37
|
```ruby
|
|
37
38
|
class A
|
|
38
39
|
def call(some_arg)
|
|
39
|
-
raise TypeError, "`some_arg` should be String or Symbol, not #{some_arg.class}" unless
|
|
40
|
+
raise TypeError, "`some_arg` should be String or Symbol, not #{some_arg.class}" unless some_arg.is_a?(String) || some_arg.is_a?(Symbol)
|
|
40
41
|
end
|
|
41
42
|
|
|
42
43
|
def call_with_respond_checks(some_arg)
|
|
43
|
-
raise TypeError, "#{some_arg.class} should respond to all methods: strip" unless [
|
|
44
|
+
raise TypeError, "#{some_arg.class} should respond to all methods: strip" unless %i[strip].all? { |meth| some_arg.respond_to?(meth) }
|
|
44
45
|
end
|
|
45
46
|
end
|
|
46
47
|
```
|
|
47
48
|
|
|
48
|
-
But without boilerplate.
|
|
49
|
+
But without the boilerplate.
|
|
49
50
|
|
|
50
|
-
|
|
51
|
+
There is also a `not_nil!` method, similar to the Crystal language.
|
|
51
52
|
|
|
52
53
|
```ruby
|
|
53
54
|
class A
|
|
@@ -58,13 +59,15 @@ class A
|
|
|
58
59
|
end
|
|
59
60
|
end
|
|
60
61
|
|
|
61
|
-
A.new.call(nil) #=> TypeError:
|
|
62
|
+
A.new.call(nil) #=> TypeError: `some_arg` should not be nil
|
|
62
63
|
```
|
|
63
64
|
|
|
65
|
+
Every method returns `self` when the check passes, so checks can be chained or inlined into assignments.
|
|
66
|
+
|
|
64
67
|
## Why?
|
|
65
68
|
|
|
66
|
-
1. Catch
|
|
67
|
-
2. Additional documentation: you're telling other people more about interfaces.
|
|
69
|
+
1. Catch errors as early as possible (especially nils);
|
|
70
|
+
2. Additional documentation: you're telling other people more about your interfaces.
|
|
68
71
|
|
|
69
72
|
## Installation
|
|
70
73
|
|
|
@@ -82,20 +85,9 @@ Or install it yourself as:
|
|
|
82
85
|
|
|
83
86
|
$ gem install typerb
|
|
84
87
|
|
|
85
|
-
If this fails with error
|
|
86
|
-
```
|
|
87
|
-
ERROR: Error installing typerb:
|
|
88
|
-
There are no versions of typerb (>= 0) compatible with your Ruby & RubyGems
|
|
89
|
-
typerb requires Ruby version >= 2.6.0.pre.preview3. The current ruby version is 2.6.0.
|
|
90
|
-
```
|
|
91
|
-
even when you have Ruby 2.6.0-preview3 installed, then try installing it through Gemfile from git:
|
|
92
|
-
```ruby
|
|
93
|
-
gem 'typerb', github: 'olegantonyan/typerb'
|
|
94
|
-
```
|
|
95
|
-
|
|
96
88
|
## Usage
|
|
97
89
|
|
|
98
|
-
1. Add `using Typerb` to a class where you want to have type
|
|
90
|
+
1. Add `using Typerb` to a class where you want to have type checks.
|
|
99
91
|
2. Call `.type!()` on any object to assert its type.
|
|
100
92
|
3. PROFIT! No more "NoMethodError for nil" 10 methods up the stack. You'll know exactly where this nil came from.
|
|
101
93
|
|
|
@@ -112,33 +104,44 @@ class A
|
|
|
112
104
|
end
|
|
113
105
|
```
|
|
114
106
|
|
|
115
|
-
If you're unfamiliar with `using` keyword - this is refinement
|
|
107
|
+
If you're unfamiliar with the `using` keyword - this is a refinement, a kind of monkey patch with a strict
|
|
108
|
+
scope. Learn more about [refinements](https://docs.ruby-lang.org/en/master/syntax/refinements_rdoc.html).
|
|
109
|
+
|
|
110
|
+
The refinement adds `type!`, `not_nil!`, `respond_to!`, `enum!` and `subset_of!` to `BasicObject`, so
|
|
111
|
+
they can be called on any object.
|
|
112
|
+
|
|
113
|
+
`type!` raises a `TypeError` unless `self` is an instance of one of the classes passed as arguments.
|
|
114
|
+
The tricky part is getting the name of the variable it was called on, so that the error message points at
|
|
115
|
+
the exact variable instead of being an abstract `TypeError`. Typerb does that by parsing the source file
|
|
116
|
+
of the caller: with [Prism](https://github.com/ruby/prism) on Ruby 3.3+, and with `RubyVM::AbstractSyntaxTree`
|
|
117
|
+
on older versions. If neither is available, or the source cannot be read, the check still works - the message
|
|
118
|
+
just doesn't name the variable.
|
|
116
119
|
|
|
117
|
-
|
|
120
|
+
| Ruby | Parser |
|
|
121
|
+
| --------- | ---------------------------- |
|
|
122
|
+
| 3.3+ | Prism |
|
|
123
|
+
| 3.0 - 3.2 | `RubyVM::AbstractSyntaxTree` |
|
|
118
124
|
|
|
119
|
-
|
|
125
|
+
Both parsers produce the same messages, and CI runs the suite against every supported version.
|
|
120
126
|
|
|
121
127
|
## Limitations
|
|
122
128
|
|
|
123
|
-
|
|
129
|
+
The variable name is omitted (the check itself still works) in two cases.
|
|
124
130
|
|
|
125
|
-
|
|
131
|
+
1. Several checks on the same line - there is no way to tell which one raised:
|
|
126
132
|
|
|
127
|
-
1. Multi-line method call:
|
|
128
133
|
```ruby
|
|
129
134
|
class A
|
|
130
135
|
using Typerb
|
|
131
136
|
|
|
132
|
-
def
|
|
133
|
-
|
|
134
|
-
type!(String)
|
|
135
|
-
# this won't work. type!() call must be on the same line with the variable it's called on - raise error message without variable name
|
|
136
|
-
# some_arg. type!(String) is ok though
|
|
137
|
+
def initialize(arg1, arg2)
|
|
138
|
+
arg1.type!(Integer); arg2.type!(String)
|
|
137
139
|
end
|
|
138
140
|
end
|
|
139
141
|
```
|
|
140
142
|
|
|
141
|
-
2.
|
|
143
|
+
2. Code whose source file cannot be read - `eval`, a console session, or a file deleted after being loaded:
|
|
144
|
+
|
|
142
145
|
```ruby
|
|
143
146
|
[1] pry(main)> class A
|
|
144
147
|
[1] pry(main)* using Typerb
|
|
@@ -147,34 +150,26 @@ end
|
|
|
147
150
|
[1] pry(main)* end
|
|
148
151
|
[1] pry(main)* end
|
|
149
152
|
[2] pry(main)> A.new.call(1)
|
|
150
|
-
TypeError: expected Hash, got Integer
|
|
151
|
-
# here we cannot get the source code for a line containing "a.type!(Hash)", so cannot see the variable name
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
3. Multiple arguments on the same line:
|
|
155
|
-
```ruby
|
|
156
|
-
class A
|
|
157
|
-
using Typerb
|
|
158
|
-
|
|
159
|
-
def initialize(arg1, arg2)
|
|
160
|
-
arg1.type!(Integer); arg2.type!(String)
|
|
161
|
-
# no way to tell the variable - raise error message without variable name
|
|
162
|
-
# same error will be raised on Ruby < 2.6.0 because there is no RubyVM::AST
|
|
163
|
-
end
|
|
164
|
-
end
|
|
153
|
+
TypeError: expected Hash, got Integer (1)
|
|
165
154
|
```
|
|
166
155
|
|
|
167
|
-
|
|
156
|
+
Please file an issue if you know a scenario where one of these is a real problem.
|
|
168
157
|
|
|
169
158
|
## Development
|
|
170
159
|
|
|
171
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then
|
|
160
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then run `rake test` to run the tests.
|
|
161
|
+
You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
172
162
|
|
|
173
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version,
|
|
163
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version,
|
|
164
|
+
update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git
|
|
165
|
+
tag for the version, push git commits and tags, and push the `.gem` file to
|
|
166
|
+
[rubygems.org](https://rubygems.org).
|
|
174
167
|
|
|
175
168
|
## Contributing
|
|
176
169
|
|
|
177
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/olegantonyan/typerb. This project
|
|
170
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/olegantonyan/typerb. This project
|
|
171
|
+
is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the
|
|
172
|
+
[Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
|
178
173
|
|
|
179
174
|
## License
|
|
180
175
|
|
|
@@ -182,4 +177,5 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
|
182
177
|
|
|
183
178
|
## Code of Conduct
|
|
184
179
|
|
|
185
|
-
Everyone interacting in the Typerb project
|
|
180
|
+
Everyone interacting in the Typerb project's codebases, issue trackers, chat rooms and mailing lists is
|
|
181
|
+
expected to follow the [code of conduct](https://github.com/olegantonyan/typerb/blob/master/CODE_OF_CONDUCT.md).
|
data/lib/typerb/exceptional.rb
CHANGED
|
@@ -1,27 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
class Exceptional # NOTE: don't want to collide with 'Exception' class name
|
|
5
|
-
class << self
|
|
6
|
-
def klasses_text(klasses)
|
|
7
|
-
klasses.size > 1 ? klasses.map(&:name).join(' or ') : klasses.first.name
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def methods_text(methods)
|
|
11
|
-
methods.join(', ')
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def elements_text(elements)
|
|
15
|
-
'[' + elements.join(', ') + ']'
|
|
16
|
-
end
|
|
3
|
+
require 'typerb/variable_name'
|
|
17
4
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def raise_with(backtrace, exception_text)
|
|
24
|
-
exception = TypeError.new(exception_text)
|
|
5
|
+
module Typerb
|
|
6
|
+
module Exceptional # NOTE: don't want to collide with 'Exception' class name
|
|
7
|
+
def self.raise_type_error(backtrace, location, method_name)
|
|
8
|
+
exception = TypeError.new(yield(VariableName.new(location, method_name).get))
|
|
25
9
|
exception.set_backtrace(backtrace)
|
|
26
10
|
raise exception
|
|
27
11
|
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require 'prism'
|
|
5
|
+
rescue LoadError # rubocop: disable Lint/SuppressedException
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
require 'typerb/source_cache'
|
|
9
|
+
|
|
10
|
+
module Typerb
|
|
11
|
+
module PrismParser
|
|
12
|
+
RECEIVER_NODE_NAMES = %w[
|
|
13
|
+
LocalVariableReadNode
|
|
14
|
+
InstanceVariableReadNode
|
|
15
|
+
ClassVariableReadNode
|
|
16
|
+
GlobalVariableReadNode
|
|
17
|
+
ConstantReadNode
|
|
18
|
+
ConstantPathNode
|
|
19
|
+
CallNode
|
|
20
|
+
].freeze
|
|
21
|
+
|
|
22
|
+
class << self
|
|
23
|
+
def available?
|
|
24
|
+
defined?(::Prism) && ::Prism.respond_to?(:parse_file)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def receiver_sources(file, line, method_name)
|
|
28
|
+
calls(root(file), line, method_name).map { |node| receiver_source(node) }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def root(file)
|
|
34
|
+
SourceCache.read(file, :prism) { ::Prism.parse_file(file).value }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def calls(root, line, method_name)
|
|
38
|
+
found = []
|
|
39
|
+
stack = [root]
|
|
40
|
+
until stack.empty?
|
|
41
|
+
node = stack.pop
|
|
42
|
+
found << node if call?(node, line, method_name)
|
|
43
|
+
stack.concat(node.compact_child_nodes)
|
|
44
|
+
end
|
|
45
|
+
found
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def call?(node, line, method_name)
|
|
49
|
+
node.is_a?(::Prism::CallNode) &&
|
|
50
|
+
node.name.to_sym == method_name &&
|
|
51
|
+
node.message_loc&.start_line == line
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def receiver_source(node)
|
|
55
|
+
receiver = node.receiver
|
|
56
|
+
return nil unless receiver_nodes.any? { |kls| receiver.is_a?(kls) }
|
|
57
|
+
|
|
58
|
+
receiver.slice
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def receiver_nodes
|
|
62
|
+
@receiver_nodes ||= RECEIVER_NODE_NAMES.select { |name| ::Prism.const_defined?(name) }
|
|
63
|
+
.map { |name| ::Prism.const_get(name) }
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'typerb/source_cache'
|
|
4
|
+
|
|
5
|
+
module Typerb
|
|
6
|
+
module RubyVmParser
|
|
7
|
+
CALL_TYPES = %i[CALL QCALL].freeze
|
|
8
|
+
RECEIVER_TYPES = %i[LVAR DVAR IVAR CVAR GVAR CONST COLON2 COLON3 CALL QCALL VCALL FCALL ITER].freeze
|
|
9
|
+
|
|
10
|
+
class << self
|
|
11
|
+
def available?
|
|
12
|
+
defined?(::RubyVM::AbstractSyntaxTree) && ::RubyVM::AbstractSyntaxTree.respond_to?(:parse_file)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def receiver_sources(file, line, method_name)
|
|
16
|
+
calls(root(file), line, method_name).map { |node| receiver_source(file, node) }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def root(file)
|
|
22
|
+
SourceCache.read(file, :ruby_vm) { ::RubyVM::AbstractSyntaxTree.parse_file(file) }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def calls(root, line, method_name)
|
|
26
|
+
found = []
|
|
27
|
+
stack = [root]
|
|
28
|
+
until stack.empty?
|
|
29
|
+
node = stack.pop
|
|
30
|
+
found << node if call?(node, line, method_name)
|
|
31
|
+
stack.concat(node.children.grep(::RubyVM::AbstractSyntaxTree::Node))
|
|
32
|
+
end
|
|
33
|
+
found
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def call?(node, line, method_name)
|
|
37
|
+
CALL_TYPES.include?(node.type) &&
|
|
38
|
+
node.children[1] == method_name &&
|
|
39
|
+
(node.first_lineno..node.last_lineno).cover?(line)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def receiver_source(file, node)
|
|
43
|
+
receiver = node.children.first
|
|
44
|
+
return nil unless receiver.is_a?(::RubyVM::AbstractSyntaxTree::Node)
|
|
45
|
+
return nil unless RECEIVER_TYPES.include?(receiver.type)
|
|
46
|
+
return nil unless receiver.first_lineno == receiver.last_lineno
|
|
47
|
+
|
|
48
|
+
line = lines(file)[receiver.first_lineno - 1]
|
|
49
|
+
line&.byteslice(receiver.first_column...receiver.last_column) # AST columns are byte offsets
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def lines(file)
|
|
53
|
+
SourceCache.read(file, :lines) { File.readlines(file) }
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Typerb
|
|
4
|
+
module SourceCache
|
|
5
|
+
LIMIT = 32
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
def read(file, kind)
|
|
9
|
+
stat = File.stat(file)
|
|
10
|
+
key = [file, kind, stat.mtime, stat.size]
|
|
11
|
+
mutex.synchronize do
|
|
12
|
+
return store[key] if store.key?(key)
|
|
13
|
+
|
|
14
|
+
store.shift while store.size >= LIMIT
|
|
15
|
+
store[key] = yield
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def store
|
|
22
|
+
@store ||= {}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def mutex
|
|
26
|
+
@mutex ||= Mutex.new
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
data/lib/typerb/variable_name.rb
CHANGED
|
@@ -1,30 +1,34 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'typerb/prism_parser'
|
|
4
|
+
require 'typerb/ruby_vm_parser'
|
|
5
|
+
|
|
3
6
|
module Typerb
|
|
4
7
|
class VariableName
|
|
5
|
-
|
|
8
|
+
BACKENDS = [PrismParser, RubyVmParser].freeze
|
|
9
|
+
|
|
10
|
+
attr_reader :file, :line, :method_name
|
|
6
11
|
|
|
7
|
-
def initialize(
|
|
8
|
-
@file =
|
|
9
|
-
@line =
|
|
12
|
+
def initialize(location, method_name)
|
|
13
|
+
@file = location&.path
|
|
14
|
+
@line = location&.lineno
|
|
15
|
+
@method_name = method_name
|
|
10
16
|
end
|
|
11
17
|
|
|
12
18
|
def get
|
|
13
|
-
return unless
|
|
14
|
-
return unless File.exist?(file)
|
|
19
|
+
return unless backend
|
|
20
|
+
return unless file && line && File.exist?(file)
|
|
15
21
|
|
|
16
|
-
|
|
17
|
-
|
|
22
|
+
receivers = backend.receiver_sources(file, line, method_name)
|
|
23
|
+
receivers.first if receivers.size == 1
|
|
24
|
+
rescue StandardError, ScriptError
|
|
25
|
+
nil
|
|
18
26
|
end
|
|
19
27
|
|
|
20
28
|
private
|
|
21
29
|
|
|
22
|
-
def
|
|
23
|
-
|
|
24
|
-
node = RubyVM::AbstractSyntaxTree.parse(code)
|
|
25
|
-
if node.children.last.children.size == 3 && node.children.last.children[1] == caller_method # rubocop: disable Style/IfUnlessModifier, Style/GuardClause
|
|
26
|
-
node.children.last.children.first.children.first
|
|
27
|
-
end
|
|
30
|
+
def backend
|
|
31
|
+
BACKENDS.find(&:available?)
|
|
28
32
|
end
|
|
29
33
|
end
|
|
30
34
|
end
|
data/lib/typerb/version.rb
CHANGED
data/lib/typerb.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'typerb/version'
|
|
4
|
-
require 'typerb/variable_name'
|
|
5
4
|
require 'typerb/exceptional'
|
|
6
5
|
|
|
7
6
|
module Typerb
|
|
@@ -10,69 +9,51 @@ module Typerb
|
|
|
10
9
|
raise ArgumentError, 'provide at least one class' if klasses.empty?
|
|
11
10
|
return self if klasses.any? { |kls| is_a?(kls) }
|
|
12
11
|
|
|
13
|
-
klasses_text =
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"expected #{klasses_text}, got #{self.class} (#{self})"
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
Typerb::Exceptional.new.raise_with(caller, exception_text)
|
|
12
|
+
klasses_text = klasses.join(' or ')
|
|
13
|
+
Typerb::Exceptional.raise_type_error(caller, caller_locations(1, 1)[0], :type!) do |var_name|
|
|
14
|
+
var_name ? "`#{var_name}` should be #{klasses_text}, not #{self.class} (#{self})" : "expected #{klasses_text}, got #{self.class} (#{self})"
|
|
15
|
+
end
|
|
21
16
|
end
|
|
22
17
|
|
|
23
18
|
def not_nil!
|
|
24
|
-
return self unless self.nil? # rubocop: disable Style/RedundantSelf
|
|
25
|
-
|
|
26
|
-
exception_text = if (var_name = Typerb::VariableName.new(caller_locations(1, 1)).get)
|
|
27
|
-
"`#{var_name}` should not be nil"
|
|
28
|
-
else
|
|
29
|
-
'expected not nil, got nil'
|
|
30
|
-
end
|
|
19
|
+
return self unless self.nil? # rubocop: disable Style/RedundantSelf
|
|
31
20
|
|
|
32
|
-
Typerb::Exceptional.
|
|
21
|
+
Typerb::Exceptional.raise_type_error(caller, caller_locations(1, 1)[0], :not_nil!) do |var_name|
|
|
22
|
+
var_name ? "`#{var_name}` should not be nil" : 'expected not nil, got nil'
|
|
23
|
+
end
|
|
33
24
|
end
|
|
34
25
|
|
|
35
26
|
def respond_to!(*methods)
|
|
36
27
|
raise ArgumentError, 'provide at least one method' if methods.empty?
|
|
37
28
|
return self if methods.all? { |meth| respond_to?(meth) }
|
|
38
29
|
|
|
39
|
-
methods_text =
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"#{self.class} should respond to all methods: #{methods_text}"
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
Typerb::Exceptional.new.raise_with(caller, exception_text)
|
|
30
|
+
methods_text = methods.join(', ')
|
|
31
|
+
Typerb::Exceptional.raise_type_error(caller, caller_locations(1, 1)[0], :respond_to!) do |var_name|
|
|
32
|
+
var_name ? "#{self.class} (`#{var_name}`) should respond to all methods: #{methods_text}" : "#{self.class} should respond to all methods: #{methods_text}"
|
|
33
|
+
end
|
|
47
34
|
end
|
|
48
35
|
|
|
49
36
|
def enum!(*elements)
|
|
50
37
|
raise ArgumentError, 'provide at least one enum element' if elements.empty?
|
|
51
38
|
return self if elements.include?(self)
|
|
52
39
|
|
|
53
|
-
elements_text =
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"#{self.class} expected one of: #{elements_text}, got #{self}"
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
Typerb::Exceptional.new.raise_with(caller, exception_text)
|
|
40
|
+
elements_text = "[#{elements.join(', ')}]"
|
|
41
|
+
Typerb::Exceptional.raise_type_error(caller, caller_locations(1, 1)[0], :enum!) do |var_name|
|
|
42
|
+
var_name ? "#{self.class} (`#{var_name}`) should be one of: #{elements_text}, not #{self}" : "#{self.class} expected one of: #{elements_text}, got #{self}"
|
|
43
|
+
end
|
|
61
44
|
end
|
|
62
45
|
|
|
63
46
|
def subset_of!(superset)
|
|
47
|
+
raise ArgumentError, 'receiver must be Enumerable' unless is_a?(Enumerable)
|
|
64
48
|
raise ArgumentError, 'superset must be Enumerable' unless superset.is_a?(Enumerable)
|
|
65
|
-
raise ArgumentError, 'provide at least one superset element' if superset.empty?
|
|
66
|
-
return self if (self - superset).empty?
|
|
67
49
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
else
|
|
72
|
-
"#{self.class} expected subset of: #{superset_text}, got #{self}"
|
|
73
|
-
end
|
|
50
|
+
elements = superset.to_a
|
|
51
|
+
raise ArgumentError, 'provide at least one superset element' if elements.empty?
|
|
52
|
+
return self if (to_a - elements).empty?
|
|
74
53
|
|
|
75
|
-
Typerb::Exceptional.
|
|
54
|
+
Typerb::Exceptional.raise_type_error(caller, caller_locations(1, 1)[0], :subset_of!) do |var_name|
|
|
55
|
+
var_name ? "#{self.class} (`#{var_name}`) should be subset of: #{superset}, not #{self}" : "#{self.class} expected subset of: #{superset}, got #{self}"
|
|
56
|
+
end
|
|
76
57
|
end
|
|
77
58
|
end
|
|
78
59
|
end
|
metadata
CHANGED
|
@@ -1,59 +1,16 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: typerb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Oleg Antonyan
|
|
8
|
-
|
|
9
|
-
bindir: exe
|
|
8
|
+
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
|
16
|
-
requirements:
|
|
17
|
-
- - ">="
|
|
18
|
-
- !ruby/object:Gem::Version
|
|
19
|
-
version: '1.17'
|
|
20
|
-
type: :development
|
|
21
|
-
prerelease: false
|
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
-
requirements:
|
|
24
|
-
- - ">="
|
|
25
|
-
- !ruby/object:Gem::Version
|
|
26
|
-
version: '1.17'
|
|
27
|
-
- !ruby/object:Gem::Dependency
|
|
28
|
-
name: guard
|
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
|
30
|
-
requirements:
|
|
31
|
-
- - ">="
|
|
32
|
-
- !ruby/object:Gem::Version
|
|
33
|
-
version: '0'
|
|
34
|
-
type: :development
|
|
35
|
-
prerelease: false
|
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
-
requirements:
|
|
38
|
-
- - ">="
|
|
39
|
-
- !ruby/object:Gem::Version
|
|
40
|
-
version: '0'
|
|
41
|
-
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: guard-rspec
|
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
|
44
|
-
requirements:
|
|
45
|
-
- - ">="
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: '0'
|
|
48
|
-
type: :development
|
|
49
|
-
prerelease: false
|
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
-
requirements:
|
|
52
|
-
- - ">="
|
|
53
|
-
- !ruby/object:Gem::Version
|
|
54
|
-
version: '0'
|
|
55
|
-
- !ruby/object:Gem::Dependency
|
|
56
|
-
name: pry
|
|
13
|
+
name: minitest
|
|
57
14
|
requirement: !ruby/object:Gem::Requirement
|
|
58
15
|
requirements:
|
|
59
16
|
- - ">="
|
|
@@ -68,34 +25,6 @@ dependencies:
|
|
|
68
25
|
version: '0'
|
|
69
26
|
- !ruby/object:Gem::Dependency
|
|
70
27
|
name: rake
|
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
|
72
|
-
requirements:
|
|
73
|
-
- - ">="
|
|
74
|
-
- !ruby/object:Gem::Version
|
|
75
|
-
version: '10.0'
|
|
76
|
-
type: :development
|
|
77
|
-
prerelease: false
|
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
-
requirements:
|
|
80
|
-
- - ">="
|
|
81
|
-
- !ruby/object:Gem::Version
|
|
82
|
-
version: '10.0'
|
|
83
|
-
- !ruby/object:Gem::Dependency
|
|
84
|
-
name: rspec
|
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
|
86
|
-
requirements:
|
|
87
|
-
- - ">="
|
|
88
|
-
- !ruby/object:Gem::Version
|
|
89
|
-
version: '3.0'
|
|
90
|
-
type: :development
|
|
91
|
-
prerelease: false
|
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
-
requirements:
|
|
94
|
-
- - ">="
|
|
95
|
-
- !ruby/object:Gem::Version
|
|
96
|
-
version: '3.0'
|
|
97
|
-
- !ruby/object:Gem::Dependency
|
|
98
|
-
name: rubocop
|
|
99
28
|
requirement: !ruby/object:Gem::Requirement
|
|
100
29
|
requirements:
|
|
101
30
|
- - ">="
|
|
@@ -109,7 +38,7 @@ dependencies:
|
|
|
109
38
|
- !ruby/object:Gem::Version
|
|
110
39
|
version: '0'
|
|
111
40
|
- !ruby/object:Gem::Dependency
|
|
112
|
-
name:
|
|
41
|
+
name: rubocop
|
|
113
42
|
requirement: !ruby/object:Gem::Requirement
|
|
114
43
|
requirements:
|
|
115
44
|
- - ">="
|
|
@@ -122,36 +51,33 @@ dependencies:
|
|
|
122
51
|
- - ">="
|
|
123
52
|
- !ruby/object:Gem::Version
|
|
124
53
|
version: '0'
|
|
125
|
-
description:
|
|
54
|
+
description: Refinement adding type!, not_nil!, respond_to!, enum! and subset_of!
|
|
55
|
+
assertions that name the variable they failed on.
|
|
126
56
|
email:
|
|
127
57
|
- oleg.b.antonyan@gmail.com
|
|
128
58
|
executables: []
|
|
129
59
|
extensions: []
|
|
130
60
|
extra_rdoc_files: []
|
|
131
61
|
files:
|
|
132
|
-
-
|
|
133
|
-
- ".gitignore"
|
|
134
|
-
- ".rspec"
|
|
135
|
-
- ".rubocop.yml"
|
|
62
|
+
- CHANGELOG.md
|
|
136
63
|
- CODE_OF_CONDUCT.md
|
|
137
|
-
- Gemfile
|
|
138
|
-
- Gemfile.lock
|
|
139
|
-
- Guardfile
|
|
140
64
|
- LICENSE.txt
|
|
141
65
|
- README.md
|
|
142
|
-
- Rakefile
|
|
143
|
-
- bin/console
|
|
144
|
-
- bin/setup
|
|
145
66
|
- lib/typerb.rb
|
|
146
67
|
- lib/typerb/exceptional.rb
|
|
68
|
+
- lib/typerb/prism_parser.rb
|
|
69
|
+
- lib/typerb/ruby_vm_parser.rb
|
|
70
|
+
- lib/typerb/source_cache.rb
|
|
147
71
|
- lib/typerb/variable_name.rb
|
|
148
72
|
- lib/typerb/version.rb
|
|
149
|
-
- typerb.gemspec
|
|
150
73
|
homepage: https://github.com/olegantonyan/typerb
|
|
151
74
|
licenses:
|
|
152
75
|
- MIT
|
|
153
|
-
metadata:
|
|
154
|
-
|
|
76
|
+
metadata:
|
|
77
|
+
source_code_uri: https://github.com/olegantonyan/typerb
|
|
78
|
+
changelog_uri: https://github.com/olegantonyan/typerb/blob/master/CHANGELOG.md
|
|
79
|
+
bug_tracker_uri: https://github.com/olegantonyan/typerb/issues
|
|
80
|
+
rubygems_mfa_required: 'true'
|
|
155
81
|
rdoc_options: []
|
|
156
82
|
require_paths:
|
|
157
83
|
- lib
|
|
@@ -159,15 +85,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
159
85
|
requirements:
|
|
160
86
|
- - ">="
|
|
161
87
|
- !ruby/object:Gem::Version
|
|
162
|
-
version: '
|
|
88
|
+
version: '3.0'
|
|
163
89
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
164
90
|
requirements:
|
|
165
91
|
- - ">="
|
|
166
92
|
- !ruby/object:Gem::Version
|
|
167
93
|
version: '0'
|
|
168
94
|
requirements: []
|
|
169
|
-
rubygems_version:
|
|
170
|
-
signing_key:
|
|
95
|
+
rubygems_version: 4.0.16
|
|
171
96
|
specification_version: 4
|
|
172
97
|
summary: Typecheck sugar for Ruby.
|
|
173
98
|
test_files: []
|
data/.github/workflows/tests.yml
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
name: CI RSpec & Rubocop
|
|
2
|
-
|
|
3
|
-
on: [push, pull_request]
|
|
4
|
-
|
|
5
|
-
jobs:
|
|
6
|
-
build:
|
|
7
|
-
runs-on: ubuntu-latest
|
|
8
|
-
|
|
9
|
-
strategy:
|
|
10
|
-
matrix:
|
|
11
|
-
ruby-version: [2.5.3, 2.6.0, 2.7.4, 3.0.1, 3.1.0]
|
|
12
|
-
|
|
13
|
-
steps:
|
|
14
|
-
- uses: actions/checkout@v2
|
|
15
|
-
- name: Set up Ruby ${{ matrix.ruby-version }}
|
|
16
|
-
uses: ruby/setup-ruby@v1
|
|
17
|
-
with:
|
|
18
|
-
ruby-version: ${{ matrix.ruby-version }}
|
|
19
|
-
- name: Install dependencies
|
|
20
|
-
run: bundle install
|
|
21
|
-
- name: Run Rubocop
|
|
22
|
-
run: bundle exec rubocop -DP
|
|
23
|
-
- name: Run tests
|
|
24
|
-
run: bundle exec rspec
|
data/.gitignore
DELETED
data/.rspec
DELETED
data/.rubocop.yml
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
AllCops:
|
|
2
|
-
TargetRubyVersion: 3.1
|
|
3
|
-
NewCops: enable
|
|
4
|
-
SuggestExtensions: false
|
|
5
|
-
Exclude:
|
|
6
|
-
- 'bin/**/*'
|
|
7
|
-
- 'Guardfile'
|
|
8
|
-
|
|
9
|
-
inherit_mode:
|
|
10
|
-
merge:
|
|
11
|
-
- Exclude
|
|
12
|
-
|
|
13
|
-
Style/CommentedKeyword:
|
|
14
|
-
Enabled: false
|
|
15
|
-
|
|
16
|
-
Style/Documentation:
|
|
17
|
-
Enabled: false
|
|
18
|
-
|
|
19
|
-
Style/Semicolon:
|
|
20
|
-
Enabled: false
|
|
21
|
-
|
|
22
|
-
Style/StringConcatenation:
|
|
23
|
-
Enabled: false
|
|
24
|
-
|
|
25
|
-
Layout/SpaceAroundMethodCallOperator:
|
|
26
|
-
Enabled: false
|
|
27
|
-
|
|
28
|
-
Gemspec/RequiredRubyVersion:
|
|
29
|
-
Enabled: false
|
|
30
|
-
|
|
31
|
-
Layout/LineLength:
|
|
32
|
-
Max: 170
|
|
33
|
-
|
|
34
|
-
Gemspec/RequireMFA:
|
|
35
|
-
Enabled: false
|
data/Gemfile
DELETED
data/Gemfile.lock
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
PATH
|
|
2
|
-
remote: .
|
|
3
|
-
specs:
|
|
4
|
-
typerb (0.4.0)
|
|
5
|
-
|
|
6
|
-
GEM
|
|
7
|
-
remote: https://rubygems.org/
|
|
8
|
-
specs:
|
|
9
|
-
ast (2.4.2)
|
|
10
|
-
awesome_print (1.9.2)
|
|
11
|
-
coderay (1.1.3)
|
|
12
|
-
diff-lcs (1.5.0)
|
|
13
|
-
ffi (1.15.5)
|
|
14
|
-
formatador (0.3.0)
|
|
15
|
-
guard (2.18.0)
|
|
16
|
-
formatador (>= 0.2.4)
|
|
17
|
-
listen (>= 2.7, < 4.0)
|
|
18
|
-
lumberjack (>= 1.0.12, < 2.0)
|
|
19
|
-
nenv (~> 0.1)
|
|
20
|
-
notiffany (~> 0.0)
|
|
21
|
-
pry (>= 0.13.0)
|
|
22
|
-
shellany (~> 0.0)
|
|
23
|
-
thor (>= 0.18.1)
|
|
24
|
-
guard-compat (1.2.1)
|
|
25
|
-
guard-rspec (4.7.3)
|
|
26
|
-
guard (~> 2.1)
|
|
27
|
-
guard-compat (~> 1.1)
|
|
28
|
-
rspec (>= 2.99.0, < 4.0)
|
|
29
|
-
listen (3.7.1)
|
|
30
|
-
rb-fsevent (~> 0.10, >= 0.10.3)
|
|
31
|
-
rb-inotify (~> 0.9, >= 0.9.10)
|
|
32
|
-
lumberjack (1.2.8)
|
|
33
|
-
method_source (1.0.0)
|
|
34
|
-
nenv (0.3.0)
|
|
35
|
-
notiffany (0.1.3)
|
|
36
|
-
nenv (~> 0.1)
|
|
37
|
-
shellany (~> 0.0)
|
|
38
|
-
parallel (1.21.0)
|
|
39
|
-
parser (3.1.0.0)
|
|
40
|
-
ast (~> 2.4.1)
|
|
41
|
-
pry (0.14.1)
|
|
42
|
-
coderay (~> 1.1)
|
|
43
|
-
method_source (~> 1.0)
|
|
44
|
-
rainbow (3.1.1)
|
|
45
|
-
rake (13.0.6)
|
|
46
|
-
rb-fsevent (0.11.0)
|
|
47
|
-
rb-inotify (0.10.1)
|
|
48
|
-
ffi (~> 1.0)
|
|
49
|
-
regexp_parser (2.2.0)
|
|
50
|
-
rexml (3.2.5)
|
|
51
|
-
rspec (3.10.0)
|
|
52
|
-
rspec-core (~> 3.10.0)
|
|
53
|
-
rspec-expectations (~> 3.10.0)
|
|
54
|
-
rspec-mocks (~> 3.10.0)
|
|
55
|
-
rspec-core (3.10.1)
|
|
56
|
-
rspec-support (~> 3.10.0)
|
|
57
|
-
rspec-expectations (3.10.2)
|
|
58
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
|
59
|
-
rspec-support (~> 3.10.0)
|
|
60
|
-
rspec-mocks (3.10.2)
|
|
61
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
|
62
|
-
rspec-support (~> 3.10.0)
|
|
63
|
-
rspec-support (3.10.3)
|
|
64
|
-
rubocop (1.24.1)
|
|
65
|
-
parallel (~> 1.10)
|
|
66
|
-
parser (>= 3.0.0.0)
|
|
67
|
-
rainbow (>= 2.2.2, < 4.0)
|
|
68
|
-
regexp_parser (>= 1.8, < 3.0)
|
|
69
|
-
rexml
|
|
70
|
-
rubocop-ast (>= 1.15.1, < 2.0)
|
|
71
|
-
ruby-progressbar (~> 1.7)
|
|
72
|
-
unicode-display_width (>= 1.4.0, < 3.0)
|
|
73
|
-
rubocop-ast (1.15.1)
|
|
74
|
-
parser (>= 3.0.1.1)
|
|
75
|
-
ruby-progressbar (1.11.0)
|
|
76
|
-
shellany (0.0.1)
|
|
77
|
-
super_awesome_print (0.2.5)
|
|
78
|
-
awesome_print
|
|
79
|
-
thor (1.2.1)
|
|
80
|
-
unicode-display_width (2.1.0)
|
|
81
|
-
|
|
82
|
-
PLATFORMS
|
|
83
|
-
ruby
|
|
84
|
-
|
|
85
|
-
DEPENDENCIES
|
|
86
|
-
bundler (>= 1.17)
|
|
87
|
-
guard
|
|
88
|
-
guard-rspec
|
|
89
|
-
pry
|
|
90
|
-
rake (>= 10.0)
|
|
91
|
-
rspec (>= 3.0)
|
|
92
|
-
rubocop
|
|
93
|
-
super_awesome_print
|
|
94
|
-
typerb!
|
|
95
|
-
|
|
96
|
-
BUNDLED WITH
|
|
97
|
-
2.3.5
|
data/Guardfile
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# A sample Guardfile
|
|
2
|
-
# More info at https://github.com/guard/guard#readme
|
|
3
|
-
|
|
4
|
-
## Uncomment and set this to only include directories you want to watch
|
|
5
|
-
# directories %w(app lib config test spec features) \
|
|
6
|
-
# .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
|
|
7
|
-
|
|
8
|
-
## Note: if you are using the `directories` clause above and you are not
|
|
9
|
-
## watching the project directory ('.'), then you will want to move
|
|
10
|
-
## the Guardfile to a watched dir and symlink it back, e.g.
|
|
11
|
-
#
|
|
12
|
-
# $ mkdir config
|
|
13
|
-
# $ mv Guardfile config/
|
|
14
|
-
# $ ln -s config/Guardfile .
|
|
15
|
-
#
|
|
16
|
-
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"
|
|
17
|
-
guard :rspec, cmd: "bundle exec rspec" do
|
|
18
|
-
require "guard/rspec/dsl"
|
|
19
|
-
dsl = Guard::RSpec::Dsl.new(self)
|
|
20
|
-
|
|
21
|
-
# Feel free to open issues for suggestions and improvements
|
|
22
|
-
|
|
23
|
-
# RSpec files
|
|
24
|
-
rspec = dsl.rspec
|
|
25
|
-
watch(rspec.spec_helper) { rspec.spec_dir }
|
|
26
|
-
watch(rspec.spec_support) { rspec.spec_dir }
|
|
27
|
-
watch(rspec.spec_files)
|
|
28
|
-
|
|
29
|
-
# Ruby files
|
|
30
|
-
ruby = dsl.ruby
|
|
31
|
-
dsl.watch_spec_files_for(ruby.lib_files)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
# watch(rails.routes) { "#{rspec.spec_dir}/routing" }
|
|
35
|
-
# watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" }
|
|
36
|
-
end
|
data/Rakefile
DELETED
data/bin/console
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
|
|
3
|
-
require 'bundler/setup'
|
|
4
|
-
require 'typerb'
|
|
5
|
-
|
|
6
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
|
7
|
-
# with your gem easier. You can also use a different console, if you like.
|
|
8
|
-
|
|
9
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
10
|
-
require 'pry'
|
|
11
|
-
Pry.start
|
|
12
|
-
|
|
13
|
-
# require "irb"
|
|
14
|
-
# IRB.start(__FILE__)
|
data/bin/setup
DELETED
data/typerb.gemspec
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
lib = File.expand_path('lib', __dir__)
|
|
4
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
|
-
require 'typerb/version'
|
|
6
|
-
|
|
7
|
-
Gem::Specification.new do |spec|
|
|
8
|
-
spec.name = 'typerb'
|
|
9
|
-
spec.version = Typerb::VERSION
|
|
10
|
-
spec.authors = ['Oleg Antonyan']
|
|
11
|
-
spec.email = ['oleg.b.antonyan@gmail.com']
|
|
12
|
-
|
|
13
|
-
spec.summary = 'Typecheck sugar for Ruby.'
|
|
14
|
-
spec.description = 'Typecheck sugar for Ruby.'
|
|
15
|
-
spec.homepage = 'https://github.com/olegantonyan/typerb'
|
|
16
|
-
spec.license = 'MIT'
|
|
17
|
-
|
|
18
|
-
# Specify which files should be added to the gem when it is released.
|
|
19
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
20
|
-
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
21
|
-
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
22
|
-
end
|
|
23
|
-
spec.bindir = 'exe'
|
|
24
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
25
|
-
spec.require_paths = ['lib']
|
|
26
|
-
|
|
27
|
-
spec.add_development_dependency 'bundler', '>= 1.17'
|
|
28
|
-
spec.add_development_dependency 'guard'
|
|
29
|
-
spec.add_development_dependency 'guard-rspec'
|
|
30
|
-
spec.add_development_dependency 'pry'
|
|
31
|
-
spec.add_development_dependency 'rake', '>= 10.0'
|
|
32
|
-
spec.add_development_dependency 'rspec', '>= 3.0'
|
|
33
|
-
spec.add_development_dependency 'rubocop'
|
|
34
|
-
spec.add_development_dependency 'super_awesome_print'
|
|
35
|
-
|
|
36
|
-
spec.required_ruby_version = '>= 2.4'
|
|
37
|
-
end
|