typerb 0.1.6 → 0.3.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: 1ce9f42a58f215a179f850016e0f647a4e16b4f23b3021d7cd17a43960857439
4
- data.tar.gz: 9bb4dc2cb1f2396c7ccfd773d27a2d85e0d11f3a66ef54537e9c809594795d5a
3
+ metadata.gz: 0d36da771caf7376c9ade13b380b77ae20cd50ac03172c86e6d54f3c54af5414
4
+ data.tar.gz: 1bb04d2199a1384049168302010c8a8d0e5114301e26108167d8d54810f4c79a
5
5
  SHA512:
6
- metadata.gz: a35899b9f7f7cc045725241d6eef0d312470016e2945edea86c67696c67f3ba69bbc52743fd51cb0253b6b7bc4f3be30ac9567784269a6f382d46484b9bc2476
7
- data.tar.gz: 58c8a8839809ff2c87801388cca35dca300a565616645a01d9bd59fdea6e6986c74763250093400aaca8d2b91a47cfad8b24b0060b24ae0215a3bdb846617568
6
+ metadata.gz: 79fa49f81f729c9e760afe1cd4abf5e2d259b1ed48a711ff64732a1411219c2d6c6fd2154dc5386e3b12d4f7cd607d4f612e06908938802ae6890c2cfd0029be
7
+ data.tar.gz: 5f405c7242a53f27925df6981944cc31f57b7bad60077aa36c9f7c08fd6a6a428998caca93ddf3f124829c5b40254f840bcbdf183d20ce19b41caf7b2438ebfa
data/.gitignore CHANGED
@@ -6,7 +6,10 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ /vendor/
9
10
  *.gem
10
11
 
11
12
  # rspec failure tracking
12
13
  .rspec_status
14
+
15
+ .ruby-version
data/.rubocop.yml CHANGED
@@ -2,6 +2,7 @@ AllCops:
2
2
  TargetRubyVersion: 2.4
3
3
  Exclude:
4
4
  - 'bin/**/*'
5
+ - 'Guardfile'
5
6
 
6
7
  inherit_mode:
7
8
  merge:
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.6.0-preview3
1
+ 2.6.6
data/.travis.yml CHANGED
@@ -7,7 +7,8 @@ before_install: gem install bundler
7
7
  rvm:
8
8
  - 2.4.0
9
9
  - 2.5.3
10
- # - 2.6.0-preview3 travis does not support it yet
10
+ - 2.6.0
11
+ - 2.6.1
11
12
 
12
13
  script:
13
14
  - bundle exec rubocop -DP
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- typerb (0.1.6)
4
+ typerb (0.3.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -10,48 +10,79 @@ GEM
10
10
  awesome_print (1.8.0)
11
11
  coderay (1.1.2)
12
12
  diff-lcs (1.3)
13
- jaro_winkler (1.5.1)
14
- method_source (0.9.1)
15
- parallel (1.12.1)
16
- parser (2.5.3.0)
13
+ ffi (1.11.1)
14
+ formatador (0.2.5)
15
+ guard (2.15.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.9.12)
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
+ jaro_winkler (1.5.3)
30
+ listen (3.1.5)
31
+ rb-fsevent (~> 0.9, >= 0.9.4)
32
+ rb-inotify (~> 0.9, >= 0.9.7)
33
+ ruby_dep (~> 1.2)
34
+ lumberjack (1.0.13)
35
+ method_source (0.9.2)
36
+ nenv (0.3.0)
37
+ notiffany (0.1.1)
38
+ nenv (~> 0.1)
39
+ shellany (~> 0.0)
40
+ parallel (1.17.0)
41
+ parser (2.6.3.0)
17
42
  ast (~> 2.4.0)
18
- powerpack (0.1.2)
19
- pry (0.12.0)
43
+ pry (0.12.2)
20
44
  coderay (~> 1.1.0)
21
45
  method_source (~> 0.9.0)
22
46
  rainbow (3.0.0)
23
- rake (10.5.0)
47
+ rake (12.3.2)
48
+ rb-fsevent (0.10.3)
49
+ rb-inotify (0.10.0)
50
+ ffi (~> 1.0)
24
51
  rspec (3.8.0)
25
52
  rspec-core (~> 3.8.0)
26
53
  rspec-expectations (~> 3.8.0)
27
54
  rspec-mocks (~> 3.8.0)
28
- rspec-core (3.8.0)
55
+ rspec-core (3.8.1)
29
56
  rspec-support (~> 3.8.0)
30
- rspec-expectations (3.8.2)
57
+ rspec-expectations (3.8.4)
31
58
  diff-lcs (>= 1.2.0, < 2.0)
32
59
  rspec-support (~> 3.8.0)
33
- rspec-mocks (3.8.0)
60
+ rspec-mocks (3.8.1)
34
61
  diff-lcs (>= 1.2.0, < 2.0)
35
62
  rspec-support (~> 3.8.0)
36
- rspec-support (3.8.0)
37
- rubocop (0.60.0)
63
+ rspec-support (3.8.2)
64
+ rubocop (0.72.0)
38
65
  jaro_winkler (~> 1.5.1)
39
66
  parallel (~> 1.10)
40
- parser (>= 2.5, != 2.5.1.1)
41
- powerpack (~> 0.1)
67
+ parser (>= 2.6)
42
68
  rainbow (>= 2.2.2, < 4.0)
43
69
  ruby-progressbar (~> 1.7)
44
- unicode-display_width (~> 1.4.0)
45
- ruby-progressbar (1.10.0)
70
+ unicode-display_width (>= 1.4.0, < 1.7)
71
+ ruby-progressbar (1.10.1)
72
+ ruby_dep (1.5.0)
73
+ shellany (0.0.1)
46
74
  super_awesome_print (0.2.5)
47
75
  awesome_print
48
- unicode-display_width (1.4.0)
76
+ thor (0.20.3)
77
+ unicode-display_width (1.6.0)
49
78
 
50
79
  PLATFORMS
51
80
  ruby
52
81
 
53
82
  DEPENDENCIES
54
83
  bundler (>= 1.17)
84
+ guard
85
+ guard-rspec
55
86
  pry
56
87
  rake (>= 10.0)
57
88
  rspec (>= 3.0)
@@ -60,4 +91,4 @@ DEPENDENCIES
60
91
  typerb!
61
92
 
62
93
  BUNDLED WITH
63
- 1.17.1
94
+ 1.17.2
data/Guardfile ADDED
@@ -0,0 +1,36 @@
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/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  # Typerb
5
5
 
6
- Proof of concept type-checking library for Ruby 2.6.
6
+ Proof of concept type-checking library for Ruby 2.6. Works with previous versions too with some limitation (see below).
7
7
 
8
8
  ```ruby
9
9
  class A
@@ -12,9 +12,19 @@ class A
12
12
  def call(some_arg)
13
13
  some_arg.type!(String, Symbol)
14
14
  end
15
+
16
+ def call_with_respond_checks(some_arg)
17
+ some_arg.respond_to!(:strip)
18
+ end
19
+
20
+ def call_with_enum(arg)
21
+ arg.enum!(:one, :two)
22
+ end
15
23
  end
16
24
 
17
25
  A.new.call(1) #=> TypeError: `some_arg` should be String or Symbol, not Integer
26
+ A.new.call_with_respond_checks(1) #=> TypeError: 'Integer should respond to all methods: strip'
27
+ A.new.call_with_enum(:three) #=> TypeError: 'Symbol (`arg`) should be one of: [one, two], not three'
18
28
  ```
19
29
 
20
30
  This is equivalent to:
@@ -23,6 +33,10 @@ class A
23
33
  def call(some_arg)
24
34
  raise TypeError, "`some_arg` should be String or Symbol, not #{some_arg.class}" unless [String, Symbol].include?(some_arg.class)
25
35
  end
36
+
37
+ def call_with_respond_checks(some_arg)
38
+ raise TypeError, "#{some_arg.class} should respond to all methods: strip" unless [:strip].all{|meth| some_arg.respond_to?(meth)}
39
+ end
26
40
  end
27
41
  ```
28
42
 
@@ -42,6 +56,11 @@ end
42
56
  A.new.call(nil) #=> TypeError: expected not nil, but got nil
43
57
  ```
44
58
 
59
+ ## Why?
60
+
61
+ 1. Catch error as early as possible (especially nils);
62
+ 2. Additional documentation: you're telling other people more about interfaces.
63
+
45
64
  ## Installation
46
65
 
47
66
  Add this line to your application's Gemfile:
@@ -90,13 +109,13 @@ end
90
109
 
91
110
  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).
92
111
 
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).
112
+ This refinement adds `type!()` and `not_nil!` methods to `Object` class so you can call it on almost much any object (except those inherited from `BasicObject`, but these are rare).
94
113
 
95
114
  The method will raise an exception if `self` is not an instance of one of the classes passed as arguments. The tricky part, however, is to get the variable name on which it's called. You need this to get a nice error message telling you exactly which variable has wrong type, not just an abstract `TypeError`. That's why we need Ruby 2.6 with its new `RubyVM::AST` (https://ruby-doc.org/core-2.6.0.preview3/RubyVM/AST.html).
96
115
 
97
116
  ## Limitations
98
117
 
99
- It requires Ruby 2.6.0-preview3. Relies on `RubyVM::AST` which may change in release version. So, expect breaking changes in Ruby.
118
+ Full functionality Ruby 2.6.0-preview3. Relies on `RubyVM::AST` which may change in release version. So, expect breaking changes in Ruby. Previous versions also supported, but without variable name in exception message.
100
119
 
101
120
  Known limitations:
102
121
 
@@ -107,8 +126,9 @@ class A
107
126
 
108
127
  def call(some_arg)
109
128
  some_arg.
110
- type!(String) # 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
111
- # some_arg. type!(String) is ok though
129
+ type!(String)
130
+ # 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
131
+ # some_arg. type!(String) is ok though
112
132
  end
113
133
  end
114
134
  ```
@@ -119,10 +139,11 @@ end
119
139
  [1] pry(main)* using Typerb
120
140
  [1] pry(main)* def call(a)
121
141
  [1] pry(main)* a.type!(Hash)
122
- [1] pry(main)* end
123
- [1] pry(main)* end
142
+ [1] pry(main)* end
143
+ [1] pry(main)* end
124
144
  [2] pry(main)> A.new.call(1)
125
- TypeError: expected Hash, got Integer # here we cannot get the source code for a line containing "a.type!(Hash)", so cannot see the variable name
145
+ TypeError: expected Hash, got Integer
146
+ # here we cannot get the source code for a line containing "a.type!(Hash)", so cannot see the variable name
126
147
  ```
127
148
 
128
149
  3. Multiple arguments on the same line:
@@ -131,7 +152,9 @@ class A
131
152
  using Typerb
132
153
 
133
154
  def initialize(arg1, arg2)
134
- arg1.type!(Integer); arg2.type!(String) # no way to tell the variable - raise error message without variable name
155
+ arg1.type!(Integer); arg2.type!(String)
156
+ # no way to tell the variable - raise error message without variable name
157
+ # same error will be raised on Ruby < 2.6.0 because there is no RubyVM::AST
135
158
  end
136
159
  end
137
160
  ```
data/lib/typerb.rb CHANGED
@@ -5,7 +5,7 @@ require 'typerb/variable_name'
5
5
  require 'typerb/exceptional'
6
6
 
7
7
  module Typerb
8
- refine Object do
8
+ refine BasicObject do
9
9
  def type!(*klasses)
10
10
  raise ArgumentError, 'provide at least one class' if klasses.empty?
11
11
  return self if klasses.any? { |kls| is_a?(kls) }
@@ -31,5 +31,33 @@ module Typerb
31
31
 
32
32
  Typerb::Exceptional.new.raise_with(caller, exception_text)
33
33
  end
34
+
35
+ def respond_to!(*methods)
36
+ raise ArgumentError, 'provide at least one method' if methods.empty?
37
+ return self if methods.all? { |meth| respond_to?(meth) }
38
+
39
+ methods_text = Typerb::Exceptional.methods_text(methods)
40
+ exception_text = if (var_name = Typerb::VariableName.new(caller_locations(1, 1)).get)
41
+ "#{self.class} (`#{var_name}`) should respond to all methods: #{methods_text}"
42
+ else
43
+ "#{self.class} should respond to all methods: #{methods_text}"
44
+ end
45
+
46
+ Typerb::Exceptional.new.raise_with(caller, exception_text)
47
+ end
48
+
49
+ def enum!(*elements)
50
+ raise ArgumentError, 'provide at least one enum element' if elements.empty?
51
+ return self if elements.include?(self)
52
+
53
+ elements_text = Typerb::Exceptional.elements_text(elements)
54
+ exception_text = if (var_name = Typerb::VariableName.new(caller_locations(1, 1)).get)
55
+ "#{self.class} (`#{var_name}`) should be one of: #{elements_text}, not #{self}"
56
+ else
57
+ "#{self.class} expected one of: #{elements_text}, got #{self}"
58
+ end
59
+
60
+ Typerb::Exceptional.new.raise_with(caller, exception_text)
61
+ end
34
62
  end
35
63
  end
@@ -6,6 +6,14 @@ module Typerb
6
6
  def klasses_text(klasses)
7
7
  klasses.size > 1 ? klasses.map(&:name).join(' or ') : klasses.first.name
8
8
  end
9
+
10
+ def methods_text(methods)
11
+ methods.join(', ')
12
+ end
13
+
14
+ def elements_text(elements)
15
+ '[' + elements.join(', ') + ']'
16
+ end
9
17
  end
10
18
 
11
19
  def raise_with(backtrace, exception_text)
@@ -10,7 +10,7 @@ module Typerb
10
10
  end
11
11
 
12
12
  def get
13
- return if RUBY_VERSION < '2.6.0'
13
+ return unless defined?(RubyVM::AbstractSyntaxTree)
14
14
  return unless File.exist?(file)
15
15
 
16
16
  caller_method = caller_locations(1, 1)[0].label.to_sym
@@ -21,7 +21,7 @@ module Typerb
21
21
 
22
22
  def from_ast(caller_method) # rubocop: disable Metrics/AbcSize not worth fixing
23
23
  code = File.read(file).lines[line - 1].strip
24
- node = RubyVM::AST.parse(code)
24
+ node = RubyVM::AbstractSyntaxTree.parse(code)
25
25
  if node.children.last.children.size == 3 && node.children.last.children[1] == caller_method # rubocop: disable Style/IfUnlessModifier, Style/GuardClause
26
26
  node.children.last.children.first.children.first
27
27
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Typerb
4
- VERSION = '0.1.6'
4
+ VERSION = '0.3.0'
5
5
  end
data/typerb.gemspec CHANGED
@@ -25,6 +25,8 @@ Gem::Specification.new do |spec|
25
25
  spec.require_paths = ['lib']
26
26
 
27
27
  spec.add_development_dependency 'bundler', '>= 1.17'
28
+ spec.add_development_dependency 'guard'
29
+ spec.add_development_dependency 'guard-rspec'
28
30
  spec.add_development_dependency 'pry'
29
31
  spec.add_development_dependency 'rake', '>= 10.0'
30
32
  spec.add_development_dependency 'rspec', '>= 3.0'
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.6
4
+ version: 0.3.0
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-13 00:00:00.000000000 Z
11
+ date: 2021-06-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -24,6 +24,34 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
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'
27
55
  - !ruby/object:Gem::Dependency
28
56
  name: pry
29
57
  requirement: !ruby/object:Gem::Requirement
@@ -109,6 +137,7 @@ files:
109
137
  - CODE_OF_CONDUCT.md
110
138
  - Gemfile
111
139
  - Gemfile.lock
140
+ - Guardfile
112
141
  - LICENSE.txt
113
142
  - README.md
114
143
  - Rakefile
@@ -138,8 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
138
167
  - !ruby/object:Gem::Version
139
168
  version: '0'
140
169
  requirements: []
141
- rubyforge_project:
142
- rubygems_version: 3.0.0.beta2
170
+ rubygems_version: 3.0.3
143
171
  signing_key:
144
172
  specification_version: 4
145
173
  summary: Typecheck sugar for Ruby.