sorbet-eraser 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +1 -1
- data/Gemfile.lock +5 -2
- data/README.md +16 -6
- data/lib/sorbet/eraser/patterns.rb +28 -6
- data/lib/sorbet/eraser/version.rb +1 -1
- data/lib/t/enum.rb +1 -1
- data/lib/t/props.rb +59 -0
- data/lib/t/struct.rb +44 -4
- data/lib/t.rb +1 -0
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: efd5822039bf577e0c4a37171d7e91abd9c7bed250c9d09c779f52faa0d84cf6
|
4
|
+
data.tar.gz: 1a88eab81b0932ab400f5aeaf08e43cb5107fc08c135e232cc444205f76fdcf3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a64889bcbdf6affe397a394ed3a0841a166ff376e806e050967d4676910670a5f75d774e25c2490fae410c2b5e9263f07c07597ae6c0a0c08ae226343032ed0f
|
7
|
+
data.tar.gz: 50b223fde3f5901535888d6e06f685ba128c6bda051d516fe37f8b825ce9c328bfddaff54bb19a52aa1694309be11a74732ceb0b582e00179cf939d8eba760ba
|
data/.github/workflows/main.yml
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,16 +1,19 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
sorbet-eraser (0.
|
4
|
+
sorbet-eraser (0.2.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
-
minitest (5.
|
9
|
+
minitest (5.18.1)
|
10
10
|
rake (13.0.6)
|
11
11
|
|
12
12
|
PLATFORMS
|
13
|
+
arm64-darwin-22
|
13
14
|
x86_64-darwin-19
|
15
|
+
x86_64-darwin-21
|
16
|
+
x86_64-linux
|
14
17
|
|
15
18
|
DEPENDENCIES
|
16
19
|
bundler
|
data/README.md
CHANGED
@@ -27,17 +27,17 @@ Or install it yourself as:
|
|
27
27
|
|
28
28
|
## Usage
|
29
29
|
|
30
|
-
|
30
|
+
There are two ways to use this gem, depending on your needs.
|
31
31
|
|
32
|
-
|
32
|
+
The first is that you can hook into the Ruby compilation process and do just-in-time erasure. To do this — before any code is loaded that would require a `sorbet-runtime` construct — call `require "sorbet/eraser/autoload"`. This will hook into the autoload process to erase all `sorbet-runtime` code before it gets passed to Ruby to parse. This eliminates the need for a build step, but slows down your parse/boot time.
|
33
33
|
|
34
|
-
|
34
|
+
The second is that you can preprocess your Ruby files using either the CLI or the Ruby API. With the CLI, you would run:
|
35
35
|
|
36
|
-
```
|
36
|
+
```bash
|
37
37
|
bundle exec sorbet-eraser '**/*.rb'
|
38
38
|
```
|
39
39
|
|
40
|
-
It accepts any number of filepaths/patterns on the command line and will modify the source files with their erased contents.
|
40
|
+
It accepts any number of filepaths/patterns on the command line and will modify the source files with their erased contents. Alternatively, you can programmatically use this gem through the `Sorbet::Eraser.erase(source)` API, where `source` is a string that represents valid Ruby code. Ruby code without the listed constructs will be returned.
|
41
41
|
|
42
42
|
### Status
|
43
43
|
|
@@ -50,7 +50,12 @@ Below is a table of the status of each `sorbet-runtime` construct and its curren
|
|
50
50
|
| `mixes_in_class_methods(*)`, `requires_ancestor(*)` | ✅ | Shimmed |
|
51
51
|
| `type_member(*)`, `type_template(*)` | ✅ | Shimmed |
|
52
52
|
| `class Foo < T::Enum` | ✅ | Shimmed |
|
53
|
+
| `class Foo < T::InexactStruct` | 🛠 | Shimmed |
|
53
54
|
| `class Foo < T::Struct` | 🛠 | Shimmed |
|
55
|
+
| `class Foo < T::ImmutableStruct` | 🛠 | Shimmed |
|
56
|
+
| `include T::Props` | 🛠 | Shimmed |
|
57
|
+
| `include T::Props::Serializable` | 🛠 | Shimmed |
|
58
|
+
| `include T::Props::Constructor` | 🛠 | Shimmed |
|
54
59
|
| `sig` | ✅ | Removed |
|
55
60
|
| `T.absurd(foo)` | ✅ | Shimmed |
|
56
61
|
| `T.assert_type!(foo, bar)` | ✅ | `foo` |
|
@@ -62,7 +67,12 @@ Below is a table of the status of each `sorbet-runtime` construct and its curren
|
|
62
67
|
| `T.type_alias { foo }` | ✅ | Shimmed |
|
63
68
|
| `T.unsafe(foo)` | ✅ | `foo` |
|
64
69
|
|
65
|
-
In the above table
|
70
|
+
In the above table, for `Status`:
|
71
|
+
|
72
|
+
* ✅ means that we are confident this is replaced 1:1.
|
73
|
+
* 🛠 means there may be APIs that are not entirely supported. If you run into something that is missing, please open an issue.
|
74
|
+
|
75
|
+
In the above table, for `Replacement`:
|
66
76
|
|
67
77
|
* `Shimmed` means that this gem provides a replacement module that will simply do nothing when its respective methods are called. We do this in order to maintain the same interface in the case that someone is doing runtime reflection. Also because anything that is shimmed will not be called that much/will not be in a hot path so performance is not really a consideration for those cases.
|
68
78
|
* `Removed` means that the construct is removed entirely from the source.
|
@@ -89,6 +89,17 @@ module Sorbet
|
|
89
89
|
end
|
90
90
|
end
|
91
91
|
|
92
|
+
# T.must foo => foo
|
93
|
+
# T.reveal_type foo => foo
|
94
|
+
# T.unsafe foo => foo
|
95
|
+
class TMustNoParensPattern < Pattern
|
96
|
+
def replace(segment)
|
97
|
+
segment.gsub(/(T\s*\.(?:must|reveal_type|unsafe)\s*)(.+)/) do
|
98
|
+
"#{blank($1)}#{$2}"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
92
103
|
def on_method_add_arg(call, arg_paren)
|
93
104
|
# T.must(foo)
|
94
105
|
# T.reveal_type(foo)
|
@@ -137,17 +148,28 @@ module Sorbet
|
|
137
148
|
super
|
138
149
|
end
|
139
150
|
|
140
|
-
#
|
141
|
-
#
|
142
|
-
|
143
|
-
class TMustNoParensPattern < Pattern
|
151
|
+
# prop :foo, String
|
152
|
+
# const :foo, String
|
153
|
+
class PropPattern < Pattern
|
144
154
|
def replace(segment)
|
145
|
-
segment.gsub(/(
|
146
|
-
"#{blank($
|
155
|
+
segment.gsub(/((?:prop|const)\s+:.+),(\s*)([^\n;,]+)(.*)/m) do
|
156
|
+
"#{$1} #{$2}#{blank($3)}#{$4}"
|
147
157
|
end
|
148
158
|
end
|
149
159
|
end
|
150
160
|
|
161
|
+
def on_command(ident, args_add_block)
|
162
|
+
if ident.match?(/<@ident (?:const|prop)>/)
|
163
|
+
# prop :foo, String
|
164
|
+
# const :foo, String
|
165
|
+
if args_add_block.match?(/<args_add_block <args <symbol_literal <symbol <@ident .+>>> <.+> false>/)
|
166
|
+
patterns << PropPattern.new(ident.range.begin...args_add_block.range.end)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
super
|
171
|
+
end
|
172
|
+
|
151
173
|
def on_command_call(var_ref, period, ident, args_add_block)
|
152
174
|
if var_ref.match?("<var_ref <@const T>>") && period.match?("<@period .>")
|
153
175
|
# T.must foo
|
data/lib/t/enum.rb
CHANGED
@@ -226,7 +226,7 @@ module T
|
|
226
226
|
@values.freeze
|
227
227
|
@mapping.freeze
|
228
228
|
|
229
|
-
orphaned_instances =
|
229
|
+
orphaned_instances = @values - @mapping.values
|
230
230
|
if !orphaned_instances.empty?
|
231
231
|
raise "Enum values must be assigned to constants: #{orphaned_instances.map {|v| v.instance_variable_get('@serialized_val')}}"
|
232
232
|
end
|
data/lib/t/props.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module T
|
4
|
+
# Here is the place we don't match up to sorbet because we simply don't
|
5
|
+
# implement as much as they do in the runtime. If there are pieces of this
|
6
|
+
# that folks would like implemented I'd be happy to include them. This is here
|
7
|
+
# just to get a baseline for folks using T::Struct with basic const/prop
|
8
|
+
# calls.
|
9
|
+
module Props
|
10
|
+
def self.included(base)
|
11
|
+
base.extend(ClassMethods)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Here we're implementing a very basic version of the prop/const methods
|
15
|
+
# that are in sorbet-runtime. These are only here to allow consumers to call
|
16
|
+
# them and not raise errors and for bookkeeping.
|
17
|
+
module ClassMethods
|
18
|
+
def props
|
19
|
+
@props ||= []
|
20
|
+
end
|
21
|
+
|
22
|
+
def prop(name, rules = {})
|
23
|
+
create_prop(name)
|
24
|
+
attr_accessor name
|
25
|
+
end
|
26
|
+
|
27
|
+
def const(name, rules = {})
|
28
|
+
create_prop(name)
|
29
|
+
attr_reader name
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def create_prop(name)
|
35
|
+
props << name
|
36
|
+
props.sort!
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Here we're going to check against the props that have been defined on the
|
41
|
+
# class level and set appropriate values.
|
42
|
+
def initialize(hash = {})
|
43
|
+
if self.class.props == hash.keys.sort
|
44
|
+
hash.each { |key, value| instance_variable_set("@#{key}", value) }
|
45
|
+
else
|
46
|
+
raise ArgumentError, "Expected keys #{self.class.props} but got #{hash.keys.sort}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# This module is entirely empty because we haven't implemented anything from
|
51
|
+
# sorbet-runtime here.
|
52
|
+
module Serializable
|
53
|
+
end
|
54
|
+
|
55
|
+
# This is empty for the same reason.
|
56
|
+
module Constructor
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/t/struct.rb
CHANGED
@@ -1,12 +1,52 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module T
|
4
|
+
# This is the actual parent class of T::Struct. It's here to match up the
|
5
|
+
# inheritance chain in case someone is doing reflection.
|
6
|
+
class InexactStruct
|
7
|
+
include Props
|
8
|
+
include Props::Serializable
|
9
|
+
include Props::Constructor
|
10
|
+
end
|
11
|
+
|
4
12
|
# This is a shim for the T::Struct class because since you're actually
|
5
13
|
# inheriting from the class there's not really a way to remove it from the
|
6
14
|
# source.
|
7
|
-
class Struct
|
8
|
-
|
9
|
-
|
10
|
-
|
15
|
+
class Struct < InexactStruct
|
16
|
+
def self.inherited(child)
|
17
|
+
super(child)
|
18
|
+
|
19
|
+
child.define_singleton_method(:inherited) do |grandchild|
|
20
|
+
super(grandchild)
|
21
|
+
raise "#{grandchild.name} is a subclass of T::Struct and cannot be subclassed"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class ImmutableStruct < InexactStruct
|
27
|
+
def self.inherited(child)
|
28
|
+
super(child)
|
29
|
+
|
30
|
+
child.define_singleton_method(:inherited) do |grandchild|
|
31
|
+
super(grandchild)
|
32
|
+
raise "#{grandchild.name} is a subclass of T::ImmutableStruct and cannot be subclassed"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(hash = {})
|
37
|
+
super
|
38
|
+
freeze
|
39
|
+
end
|
40
|
+
|
41
|
+
# Matches the signature in Props, but raises since this is an immutable struct and only const is allowed
|
42
|
+
def self.prop(name, rules = {})
|
43
|
+
return super if rules[:immutable]
|
44
|
+
|
45
|
+
raise "Cannot use `prop` in #{self.name} because it is an immutable struct. Use `const` instead"
|
46
|
+
end
|
47
|
+
|
48
|
+
def with(changed_props)
|
49
|
+
raise "Cannot use `with` in #{self.class.name} because it is an immutable struct"
|
50
|
+
end
|
11
51
|
end
|
12
52
|
end
|
data/lib/t.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sorbet-eraser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Newton
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-06-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -80,6 +80,7 @@ files:
|
|
80
80
|
- lib/sorbet/eraser/version.rb
|
81
81
|
- lib/t.rb
|
82
82
|
- lib/t/enum.rb
|
83
|
+
- lib/t/props.rb
|
83
84
|
- lib/t/struct.rb
|
84
85
|
- sorbet-eraser.gemspec
|
85
86
|
homepage: https://github.com/kddnewton/sorbet-eraser
|
@@ -87,7 +88,7 @@ licenses:
|
|
87
88
|
- MIT
|
88
89
|
metadata:
|
89
90
|
bug_tracker_uri: https://github.com/kddnewton/sorbet-eraser/issues
|
90
|
-
changelog_uri: https://github.com/kddnewton/sorbet-eraser/blob/v0.
|
91
|
+
changelog_uri: https://github.com/kddnewton/sorbet-eraser/blob/v0.2.0/CHANGELOG.md
|
91
92
|
source_code_uri: https://github.com/kddnewton/sorbet-eraser
|
92
93
|
rubygems_mfa_required: 'true'
|
93
94
|
post_install_message:
|
@@ -105,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
105
106
|
- !ruby/object:Gem::Version
|
106
107
|
version: '0'
|
107
108
|
requirements: []
|
108
|
-
rubygems_version: 3.
|
109
|
+
rubygems_version: 3.4.1
|
109
110
|
signing_key:
|
110
111
|
specification_version: 4
|
111
112
|
summary: Erase all traces of sorbet-runtime code.
|