typerb 0.1.3 → 0.1.4
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/.rubocop.yml +15 -0
- data/.travis.yml +5 -3
- data/Gemfile +3 -1
- data/Gemfile.lock +19 -1
- data/README.md +31 -1
- data/Rakefile +3 -1
- data/lib/typerb/version.rb +1 -1
- data/lib/typerb.rb +40 -15
- data/typerb.gemspec +7 -4
- metadata +19 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 19f90a93082e4acdb25f5750c9de2880bf0c613cb414800a7ce8beb275d2ee54
|
|
4
|
+
data.tar.gz: 10e47702b997c0f4b54b416c7f77c800ee50343b391a0f41b33ff6bd4b379dc2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4c3a3d1dbc81a742d08c3e68413a70a7aa8858e473bf46a78dcdec452e862421bf85b02354ad70d203600ea1031ac85dbfc08758ea1bc79145ce03542bc92b6c
|
|
7
|
+
data.tar.gz: 55ffd3b8d1ed4fc33141a9345baaf3f7380336bfa7a4135630e24009607d7d3554b8d5892a887676a24a38a70ca7acfa351293b31f8d3f77e13ba31558cdcf5a
|
data/.rubocop.yml
ADDED
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
source 'https://rubygems.org'
|
|
2
4
|
|
|
3
|
-
git_source(:github) { |repo_name|
|
|
5
|
+
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
|
|
4
6
|
|
|
5
7
|
# Specify your gem's dependencies in typerb.gemspec
|
|
6
8
|
gemspec
|
data/Gemfile.lock
CHANGED
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
typerb (0.1.
|
|
4
|
+
typerb (0.1.3)
|
|
5
5
|
|
|
6
6
|
GEM
|
|
7
7
|
remote: https://rubygems.org/
|
|
8
8
|
specs:
|
|
9
|
+
ast (2.4.0)
|
|
9
10
|
awesome_print (1.8.0)
|
|
10
11
|
coderay (1.1.2)
|
|
11
12
|
diff-lcs (1.3)
|
|
13
|
+
jaro_winkler (1.5.1)
|
|
12
14
|
method_source (0.9.1)
|
|
15
|
+
parallel (1.12.1)
|
|
16
|
+
parser (2.5.3.0)
|
|
17
|
+
ast (~> 2.4.0)
|
|
18
|
+
powerpack (0.1.2)
|
|
13
19
|
pry (0.12.0)
|
|
14
20
|
coderay (~> 1.1.0)
|
|
15
21
|
method_source (~> 0.9.0)
|
|
22
|
+
rainbow (3.0.0)
|
|
16
23
|
rake (10.5.0)
|
|
17
24
|
rspec (3.8.0)
|
|
18
25
|
rspec-core (~> 3.8.0)
|
|
@@ -27,8 +34,18 @@ GEM
|
|
|
27
34
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
28
35
|
rspec-support (~> 3.8.0)
|
|
29
36
|
rspec-support (3.8.0)
|
|
37
|
+
rubocop (0.60.0)
|
|
38
|
+
jaro_winkler (~> 1.5.1)
|
|
39
|
+
parallel (~> 1.10)
|
|
40
|
+
parser (>= 2.5, != 2.5.1.1)
|
|
41
|
+
powerpack (~> 0.1)
|
|
42
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
43
|
+
ruby-progressbar (~> 1.7)
|
|
44
|
+
unicode-display_width (~> 1.4.0)
|
|
45
|
+
ruby-progressbar (1.10.0)
|
|
30
46
|
super_awesome_print (0.2.5)
|
|
31
47
|
awesome_print
|
|
48
|
+
unicode-display_width (1.4.0)
|
|
32
49
|
|
|
33
50
|
PLATFORMS
|
|
34
51
|
ruby
|
|
@@ -38,6 +55,7 @@ DEPENDENCIES
|
|
|
38
55
|
pry
|
|
39
56
|
rake (>= 10.0)
|
|
40
57
|
rspec (>= 3.0)
|
|
58
|
+
rubocop
|
|
41
59
|
super_awesome_print
|
|
42
60
|
typerb!
|
|
43
61
|
|
data/README.md
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
[](https://badge.fury.io/rb/typerb)
|
|
2
|
+
[](https://travis-ci.org/olegantonyan/typerb)
|
|
3
|
+
|
|
1
4
|
# Typerb
|
|
2
5
|
|
|
3
6
|
Proof of concept type-checking library for Ruby 2.6.
|
|
@@ -11,7 +14,7 @@ class A
|
|
|
11
14
|
end
|
|
12
15
|
end
|
|
13
16
|
|
|
14
|
-
A.new.call(1) #=> TypeError:
|
|
17
|
+
A.new.call(1) #=> TypeError: `some_arg` should be String or Symbol, not Integer
|
|
15
18
|
```
|
|
16
19
|
|
|
17
20
|
This is equivalent to:
|
|
@@ -25,6 +28,20 @@ end
|
|
|
25
28
|
|
|
26
29
|
But without boilerplate.
|
|
27
30
|
|
|
31
|
+
It also has `not_nil!` method, similar to Crystal language.
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
class A
|
|
35
|
+
using Typerb
|
|
36
|
+
|
|
37
|
+
def call(some_arg)
|
|
38
|
+
some_arg.not_nil!
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
A.new.call(nil) #=> TypeError: expected not nil, but got nil
|
|
43
|
+
```
|
|
44
|
+
|
|
28
45
|
## Installation
|
|
29
46
|
|
|
30
47
|
Add this line to your application's Gemfile:
|
|
@@ -58,6 +75,19 @@ gem 'typerb', github: 'olegantonyan/typerb'
|
|
|
58
75
|
2. Call `.type!()` on any object to assert its type.
|
|
59
76
|
3. PROFIT! No more "NoMethodError for nil" 10 methods up the stack. You'll know exactly where this nil came from.
|
|
60
77
|
|
|
78
|
+
```ruby
|
|
79
|
+
class A
|
|
80
|
+
using Typerb
|
|
81
|
+
|
|
82
|
+
attr_reader :param, :another_param
|
|
83
|
+
|
|
84
|
+
def initialize(param, another_param)
|
|
85
|
+
@param = param.type!(String)
|
|
86
|
+
@another_param = another_param.not_nil!
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
```
|
|
90
|
+
|
|
61
91
|
If you're unfamiliar with `using` keyword - this is refinement - a relatively new feature in Ruby (since 2.0). It's kind of monkey-patch, but with strict scope. Learn more about [refinements](https://ruby-doc.org/core-2.5.3/doc/syntax/refinements_rdoc.html).
|
|
62
92
|
|
|
63
93
|
This refinement adds `type!()` method to `Object` class so you can call it on almost much any object (except those inherited from `BasicObject`, but these are rare).
|
data/Rakefile
CHANGED
data/lib/typerb/version.rb
CHANGED
data/lib/typerb.rb
CHANGED
|
@@ -6,27 +6,52 @@ module Typerb
|
|
|
6
6
|
refine Object do
|
|
7
7
|
def type!(*klasses)
|
|
8
8
|
raise ArgumentError, 'provide at least one class' if klasses.empty?
|
|
9
|
-
return if klasses.any? { |kls| is_a?(kls) }
|
|
9
|
+
return self if klasses.any? { |kls| is_a?(kls) }
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
exception_text = if (var_name = VariableNameUtil.get(caller_locations(1, 1)))
|
|
12
|
+
"`#{var_name}` should be #{VariableNameUtil.klasses_text(klasses)}, not #{self.class} (#{self})"
|
|
13
|
+
else
|
|
14
|
+
"expected #{VariableNameUtil.klasses_text(klasses)}, got #{self.class} (#{self})"
|
|
15
|
+
end
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
exception = TypeError.new(exception_text)
|
|
18
|
+
exception.set_backtrace(caller)
|
|
19
|
+
raise exception
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def not_nil!
|
|
23
|
+
return self unless self.nil? # rubocop: disable Style/RedundantSelf
|
|
24
|
+
|
|
25
|
+
exception_text = if (var_name = VariableNameUtil.get(caller_locations(1, 1)))
|
|
26
|
+
"`#{var_name}` should not be nil"
|
|
27
|
+
else
|
|
28
|
+
'expected not nil, but got nil'
|
|
29
|
+
end
|
|
15
30
|
|
|
16
|
-
where = caller_locations(1, 1)[0]
|
|
17
|
-
file = where.path
|
|
18
|
-
line = where.lineno
|
|
19
|
-
if File.exist?(file)
|
|
20
|
-
code = File.read(file).lines[line - 1].strip
|
|
21
|
-
node = RubyVM::AST.parse(code)
|
|
22
|
-
if node.children.last.children.size == 3 && node.children.last.children[1] == :type!
|
|
23
|
-
var_name = node.children.last.children.first.children.first
|
|
24
|
-
exception_text = "`#{var_name}` should be #{kls_text}, not #{self.class} (#{to_s})"
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
31
|
exception = TypeError.new(exception_text)
|
|
28
32
|
exception.set_backtrace(caller)
|
|
29
33
|
raise exception
|
|
30
34
|
end
|
|
31
35
|
end
|
|
36
|
+
|
|
37
|
+
module VariableNameUtil
|
|
38
|
+
module_function
|
|
39
|
+
|
|
40
|
+
def get(c_loc) # rubocop: disable Metrics/AbcSize screw this
|
|
41
|
+
where = c_loc[0]
|
|
42
|
+
file = where.path
|
|
43
|
+
line = where.lineno
|
|
44
|
+
return unless File.exist?(file)
|
|
45
|
+
|
|
46
|
+
code = File.read(file).lines[line - 1].strip
|
|
47
|
+
node = RubyVM::AST.parse(code)
|
|
48
|
+
if node.children.last.children.size == 3 && %i[type! not_nil!].include?(node.children.last.children[1]) # rubocop: disable Style/IfUnlessModifier, Style/GuardClause
|
|
49
|
+
node.children.last.children.first.children.first
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def klasses_text(klasses)
|
|
54
|
+
klasses.size > 1 ? klasses.map(&:name).join(' or ') : klasses.first.name
|
|
55
|
+
end
|
|
56
|
+
end
|
|
32
57
|
end
|
data/typerb.gemspec
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
|
2
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
5
|
require 'typerb/version'
|
|
4
6
|
|
|
@@ -8,8 +10,8 @@ Gem::Specification.new do |spec|
|
|
|
8
10
|
spec.authors = ['Oleg Antonyan']
|
|
9
11
|
spec.email = ['oleg.b.antonyan@gmail.com']
|
|
10
12
|
|
|
11
|
-
spec.summary =
|
|
12
|
-
spec.description =
|
|
13
|
+
spec.summary = 'Typecheck sugar for Ruby.'
|
|
14
|
+
spec.description = 'Typecheck sugar for Ruby.'
|
|
13
15
|
spec.homepage = 'https://github.com/olegantonyan/typerb'
|
|
14
16
|
spec.license = 'MIT'
|
|
15
17
|
|
|
@@ -23,10 +25,11 @@ Gem::Specification.new do |spec|
|
|
|
23
25
|
spec.require_paths = ['lib']
|
|
24
26
|
|
|
25
27
|
spec.add_development_dependency 'bundler', '>= 1.17'
|
|
28
|
+
spec.add_development_dependency 'pry'
|
|
26
29
|
spec.add_development_dependency 'rake', '>= 10.0'
|
|
27
30
|
spec.add_development_dependency 'rspec', '>= 3.0'
|
|
31
|
+
spec.add_development_dependency 'rubocop'
|
|
28
32
|
spec.add_development_dependency 'super_awesome_print'
|
|
29
|
-
spec.add_development_dependency 'pry'
|
|
30
33
|
|
|
31
34
|
spec.required_ruby_version = '>= 2.6.0-preview3'
|
|
32
35
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: typerb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Oleg Antonyan
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2018-11-
|
|
11
|
+
date: 2018-11-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -24,6 +24,20 @@ dependencies:
|
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '1.17'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: pry
|
|
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'
|
|
27
41
|
- !ruby/object:Gem::Dependency
|
|
28
42
|
name: rake
|
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -53,7 +67,7 @@ dependencies:
|
|
|
53
67
|
- !ruby/object:Gem::Version
|
|
54
68
|
version: '3.0'
|
|
55
69
|
- !ruby/object:Gem::Dependency
|
|
56
|
-
name:
|
|
70
|
+
name: rubocop
|
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
|
58
72
|
requirements:
|
|
59
73
|
- - ">="
|
|
@@ -67,7 +81,7 @@ dependencies:
|
|
|
67
81
|
- !ruby/object:Gem::Version
|
|
68
82
|
version: '0'
|
|
69
83
|
- !ruby/object:Gem::Dependency
|
|
70
|
-
name:
|
|
84
|
+
name: super_awesome_print
|
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
|
72
86
|
requirements:
|
|
73
87
|
- - ">="
|
|
@@ -89,6 +103,7 @@ extra_rdoc_files: []
|
|
|
89
103
|
files:
|
|
90
104
|
- ".gitignore"
|
|
91
105
|
- ".rspec"
|
|
106
|
+
- ".rubocop.yml"
|
|
92
107
|
- ".ruby-version"
|
|
93
108
|
- ".travis.yml"
|
|
94
109
|
- CODE_OF_CONDUCT.md
|