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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 54913e2de72273efae7221b3dc96923c1b45e7d17020ec47bd9a417bea9a62cc
4
- data.tar.gz: 71f3cc0a28acefd20d9a4a1f3a06081a568c5b8276cb1241f34d2bb3e7f64fb8
3
+ metadata.gz: efd5822039bf577e0c4a37171d7e91abd9c7bed250c9d09c779f52faa0d84cf6
4
+ data.tar.gz: 1a88eab81b0932ab400f5aeaf08e43cb5107fc08c135e232cc444205f76fdcf3
5
5
  SHA512:
6
- metadata.gz: 06ca1f48f68eea0d10933f83ffe8ba4b296cda3c87240a5db6704ac27d9d6a12202a46b1af82ee3983ba335d4254f230a8f3d861578556ee76ea3544c551cd70
7
- data.tar.gz: 547b88a3fb93e133c69cdd2d05ce684f62cc1650afe836fe9741059d1f567f4704bf11815b3170d8f4df2a9909fcc138fc6d940b45ef5ff615853f93118a2f93
6
+ metadata.gz: a64889bcbdf6affe397a394ed3a0841a166ff376e806e050967d4676910670a5f75d774e25c2490fae410c2b5e9263f07c07597ae6c0a0c08ae226343032ed0f
7
+ data.tar.gz: 50b223fde3f5901535888d6e06f685ba128c6bda051d516fe37f8b825ce9c328bfddaff54bb19a52aa1694309be11a74732ceb0b582e00179cf939d8eba760ba
@@ -12,7 +12,7 @@ jobs:
12
12
  - uses: actions/checkout@master
13
13
  - uses: ruby/setup-ruby@v1
14
14
  with:
15
- ruby-version: 3.0
15
+ ruby-version: '3.1'
16
16
  bundler-cache: true
17
17
  - name: Test
18
18
  run: bundle exec rake test
data/Gemfile.lock CHANGED
@@ -1,16 +1,19 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- sorbet-eraser (0.1.1)
4
+ sorbet-eraser (0.2.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
- minitest (5.14.4)
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
- 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.
30
+ There are two ways to use this gem, depending on your needs.
31
31
 
32
- 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.
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
- Finally, this gem ships with a CLI that you can use to modify source files. This is useful for development of this gem itself, but could be useful for others to ensure they see what this gem actually will be doing in production. To run it, run:
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
- ```sh
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
- # T.must foo => foo
141
- # T.reveal_type foo => foo
142
- # T.unsafe foo => foo
143
- class TMustNoParensPattern < Pattern
151
+ # prop :foo, String
152
+ # const :foo, String
153
+ class PropPattern < Pattern
144
154
  def replace(segment)
145
- segment.gsub(/(T\s*\.(?:must|reveal_type|unsafe)\s*)(.+)/) do
146
- "#{blank($1)}#{$2}"
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Sorbet
4
4
  module Eraser
5
- VERSION = "0.1.1"
5
+ VERSION = "0.2.0"
6
6
  end
7
7
  end
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 = T.must(@values) - @mapping.values
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
- # include T::Props
9
- # include T::Props::Serializable
10
- # include T::Props::Constructor
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
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "t/enum"
4
+ require "t/props"
4
5
  require "t/struct"
5
6
 
6
7
  # For some constructs, it doesn't make as much sense to entirely remove them
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.1.1
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: 2021-11-18 00:00:00.000000000 Z
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.1.1/CHANGELOG.md
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.2.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.