sorbet-rails 0.5.5 → 0.5.5.1
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/README.md +7 -1
- data/lib/sorbet-rails.rb +2 -0
- data/lib/sorbet-rails/custom_types/boolean_string.rb +32 -0
- data/lib/sorbet-rails/custom_types/integer_string.rb +35 -0
- data/lib/sorbet-rails/model_plugins/active_record_enum.rb +5 -1
- data/lib/sorbet-rails/rails_mixins/active_record_overrides.rb +18 -4
- data/lib/sorbet-rails/tasks/rails_rbi.rake +1 -2
- data/lib/sorbet-rails/type_assert/type_assert.rb +0 -1
- data/lib/sorbet-rails/type_assert/type_assert_impl.rb +16 -16
- data/sorbet-rails.gemspec +1 -1
- data/spec/boolean_string_spec.rb +59 -0
- data/spec/generators/rails-template.rb +16 -0
- data/spec/integer_string_spec.rb +46 -0
- data/spec/rake_rails_rbi_models_spec.rb +1 -0
- data/spec/support/v4.2/Gemfile.lock +4 -4
- data/spec/support/v4.2/app/models/squib.rb +6 -0
- data/spec/support/v4.2/app/models/wizard.rb +1 -2
- data/spec/support/v4.2/db/migrate/20190620000007_add_type_to_wizard.rb +6 -0
- data/spec/support/v4.2/db/schema.rb +2 -1
- data/spec/support/v5.0/Gemfile.lock +4 -4
- data/spec/support/v5.0/app/models/squib.rb +6 -0
- data/spec/support/v5.0/app/models/wizard.rb +1 -1
- data/spec/support/v5.0/db/migrate/20190620000007_add_type_to_wizard.rb +6 -0
- data/spec/support/v5.0/db/schema.rb +4 -3
- data/spec/support/v5.1/Gemfile.lock +4 -4
- data/spec/support/v5.1/app/models/squib.rb +6 -0
- data/spec/support/v5.1/app/models/wizard.rb +1 -1
- data/spec/support/v5.1/db/migrate/20190620000007_add_type_to_wizard.rb +6 -0
- data/spec/support/v5.1/db/schema.rb +2 -1
- data/spec/support/v5.2/Gemfile.lock +4 -4
- data/spec/support/v5.2/app/models/squib.rb +6 -0
- data/spec/support/v5.2/app/models/wizard.rb +1 -1
- data/spec/support/v5.2/db/migrate/20190620000007_add_type_to_wizard.rb +6 -0
- data/spec/support/v5.2/db/schema.rb +2 -1
- data/spec/support/v6.0/Gemfile.lock +5 -5
- data/spec/support/v6.0/app/models/squib.rb +6 -0
- data/spec/support/v6.0/app/models/wizard.rb +1 -1
- data/spec/support/v6.0/db/migrate/20190620000007_add_type_to_wizard.rb +6 -0
- data/spec/support/v6.0/db/schema.rb +2 -1
- data/spec/test_data/v4.2/expected_squib.rbi +903 -0
- data/spec/test_data/v4.2/expected_wizard.rbi +9 -0
- data/spec/test_data/v4.2/expected_wizard_wo_spellbook.rbi +9 -0
- data/spec/test_data/v5.0/expected_squib.rbi +1164 -0
- data/spec/test_data/v5.0/expected_wizard.rbi +9 -0
- data/spec/test_data/v5.0/expected_wizard_wo_spellbook.rbi +9 -0
- data/spec/test_data/v5.1/expected_squib.rbi +1188 -0
- data/spec/test_data/v5.1/expected_wizard.rbi +9 -0
- data/spec/test_data/v5.1/expected_wizard_wo_spellbook.rbi +9 -0
- data/spec/test_data/v5.2/expected_squib.rbi +1236 -0
- data/spec/test_data/v5.2/expected_wizard.rbi +9 -0
- data/spec/test_data/v5.2/expected_wizard_wo_spellbook.rbi +9 -0
- data/spec/test_data/v6.0/expected_squib.rbi +1500 -0
- data/spec/test_data/v6.0/expected_wizard.rbi +9 -0
- data/spec/test_data/v6.0/expected_wizard_wo_spellbook.rbi +9 -0
- metadata +37 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f4aa01ecf2a262e021765ef53cdd6d5dbaaa93776c0d45ce991f9e70943eae84
|
4
|
+
data.tar.gz: 3957eb0b707a6615466d102eb7fafc25ab21ec35a6a7b3413629ac2924cda36d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cf331590decda2ee306cecda5bd89b1c603b5cbbfb1000f8dfe13e2ca6277d13269e5916cf9337c855be390da9a09eb0b6dd5e36867425421adeb563f1ffafe4
|
7
|
+
data.tar.gz: 61f5427e41cbee6b74311596b8aa54aa981ddf522692c151ca6db95b0cb5fc409e0ee476aab307b6baf0eb0576f38352c2e6c65be1d1d7bd7ccd28753bc3e5a8
|
data/README.md
CHANGED
@@ -87,6 +87,12 @@ For example:
|
|
87
87
|
key = params.require_typed(:key, TA[String].new)
|
88
88
|
T.reveal_type(key) # String
|
89
89
|
|
90
|
+
# nested params
|
91
|
+
nested_params = params.require_typed(:nested, TA[ActionController::Parameters].new)
|
92
|
+
T.reveal_type(nested_params) # ActionController::Parameters
|
93
|
+
key = nested_params.require_typed(:key, TA[String.new])
|
94
|
+
T.reveal_type(key) # String
|
95
|
+
|
90
96
|
# fetch_typed
|
91
97
|
key = params.fetch_typed(:key, TA[T.nilable(String)].new) # raises error if params doesn't have :key
|
92
98
|
T.reveal_type(key) # T.nilable(String)
|
@@ -96,7 +102,7 @@ T.reveal_type(key) # T.nilable(String)
|
|
96
102
|
```
|
97
103
|
The parameters are type-checked both statically and at runtime.
|
98
104
|
|
99
|
-
Note: The API `TA[...].new` may
|
105
|
+
Note: The API `TA[...].new` may seem verbose, but necessary to support this feature. Ideally, the API can be simply `require_typed(:key, Type)`. However, `sorbet` [doesn't support](http://github.com/sorbet/sorbet/issues/62) defining a method that accept a type and return an instance of the type. The library provides a wrapper `TA` (which stands for `TypeAssert`) in order to achieve the behavior. If this feature is supported by `sorbet` in the future, it will be easy to codemod to remove the `TA[...].new` part from your code.
|
100
106
|
|
101
107
|
### Routes
|
102
108
|
|
data/lib/sorbet-rails.rb
CHANGED
@@ -0,0 +1,32 @@
|
|
1
|
+
# typed: false
|
2
|
+
module BooleanStringImpl
|
3
|
+
def is_a?(type)
|
4
|
+
return super unless type == BooleanString
|
5
|
+
_is_a_boolean_string?
|
6
|
+
end
|
7
|
+
|
8
|
+
def kind_of?(type)
|
9
|
+
return super unless type == BooleanString
|
10
|
+
_is_a_boolean_string?
|
11
|
+
end
|
12
|
+
|
13
|
+
def instance_of?(type)
|
14
|
+
return super unless type == BooleanString
|
15
|
+
_is_a_boolean_string?
|
16
|
+
end
|
17
|
+
|
18
|
+
def _is_a_boolean_string?
|
19
|
+
return @cached_is_a unless @cached_is_a.nil?
|
20
|
+
@cached_is_a = (self =~ /^(true|false)$/i) == 0
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class String
|
25
|
+
include BooleanStringImpl
|
26
|
+
end
|
27
|
+
|
28
|
+
class BooleanString < String
|
29
|
+
def self.===(other)
|
30
|
+
other.is_a?(BooleanString)
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# typed: false
|
2
|
+
module IntegerStringImpl
|
3
|
+
def is_a?(type)
|
4
|
+
return super unless type == IntegerString
|
5
|
+
_is_a_integer_string?
|
6
|
+
end
|
7
|
+
|
8
|
+
def kind_of?(type)
|
9
|
+
return super unless type == IntegerString
|
10
|
+
_is_a_integer_string?
|
11
|
+
end
|
12
|
+
|
13
|
+
def instance_of?(type)
|
14
|
+
return super unless type == IntegerString
|
15
|
+
_is_a_integer_string?
|
16
|
+
end
|
17
|
+
|
18
|
+
def _is_a_integer_string?
|
19
|
+
return @cached_is_a unless @cached_is_a.nil?
|
20
|
+
Integer(self, 10)
|
21
|
+
@cached_is_a = true
|
22
|
+
rescue ArgumentError => err
|
23
|
+
@cached_is_a = false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class String
|
28
|
+
include IntegerStringImpl
|
29
|
+
end
|
30
|
+
|
31
|
+
class IntegerString < String
|
32
|
+
def self.===(other)
|
33
|
+
other.is_a?(IntegerString)
|
34
|
+
end
|
35
|
+
end
|
@@ -32,7 +32,11 @@ class SorbetRails::ModelPlugins::ActiveRecordEnum < SorbetRails::ModelPlugins::B
|
|
32
32
|
class_method: true,
|
33
33
|
)
|
34
34
|
|
35
|
-
enum_call =
|
35
|
+
enum_call = ActiveRecordOverrides.instance.get_enum_call(@model_class, enum_name.to_sym)
|
36
|
+
if enum_call.nil?
|
37
|
+
puts "Error: unable to find enum call for enum #{enum_name}, model #{self.model_class_name}"
|
38
|
+
next
|
39
|
+
end
|
36
40
|
|
37
41
|
enum_prefix = enum_call[:_prefix]
|
38
42
|
prefix =
|
@@ -11,9 +11,23 @@ class ActiveRecordOverrides
|
|
11
11
|
@enum_calls = {}
|
12
12
|
end
|
13
13
|
|
14
|
-
def store_enum_call(
|
15
|
-
|
16
|
-
@enum_calls[class_name]
|
14
|
+
def store_enum_call(klass, kwargs)
|
15
|
+
class_name = klass.name
|
16
|
+
@enum_calls[class_name] ||= {}
|
17
|
+
# modeling the logic in
|
18
|
+
# https://github.com/rails/rails/blob/master/activerecord/lib/active_record/enum.rb#L152
|
19
|
+
kwargs.each do |name, values|
|
20
|
+
next if [:_prefix, :_suffix, :_scopes].include?(name)
|
21
|
+
@enum_calls[class_name][name] = kwargs
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_enum_call(klass, enum_sym)
|
26
|
+
return nil if klass == Object
|
27
|
+
class_name = klass.name
|
28
|
+
class_enum_calls = @enum_calls[klass.name]
|
29
|
+
return class_enum_calls[enum_sym] if class_enum_calls && class_enum_calls.has_key?(enum_sym)
|
30
|
+
return get_enum_call(klass.superclass, enum_sym)
|
17
31
|
end
|
18
32
|
end
|
19
33
|
|
@@ -21,7 +35,7 @@ module ::ActiveRecord::Enum
|
|
21
35
|
alias old_enum enum
|
22
36
|
|
23
37
|
def enum(*args, **kwargs)
|
24
|
-
ActiveRecordOverrides.instance.store_enum_call(
|
38
|
+
ActiveRecordOverrides.instance.store_enum_call(self, kwargs)
|
25
39
|
old_enum(*args, **kwargs)
|
26
40
|
end
|
27
41
|
end
|
@@ -134,11 +134,10 @@ namespace :rails_rbi do
|
|
134
134
|
end
|
135
135
|
|
136
136
|
def copy_bundled_rbi(filename)
|
137
|
-
puts "
|
137
|
+
puts "Copy bundled file #{filename}"
|
138
138
|
bundled_rbi_file_path = File.join(RAILS_RBI_RAKE_DIR, "..", "..", "bundled_rbi", filename)
|
139
139
|
copy_to_path = Rails.root.join("sorbet", "rails-rbi", filename)
|
140
140
|
FileUtils.mkdir_p(File.dirname(copy_to_path))
|
141
|
-
puts "file path to copy #{bundled_rbi_file_path}"
|
142
141
|
FileUtils.cp(bundled_rbi_file_path, copy_to_path)
|
143
142
|
end
|
144
143
|
end
|
@@ -1,26 +1,26 @@
|
|
1
1
|
# typed: ignore
|
2
2
|
require 'sorbet-runtime'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
type = types.first
|
11
|
-
return Class.new do
|
12
|
-
include ITypeAssert
|
4
|
+
if !$PROGRAM_NAME.include?('sorbet')
|
5
|
+
module TypeAssertImpl
|
6
|
+
def self.included(klass)
|
7
|
+
klass.define_singleton_method(:[]) do |type|
|
8
|
+
return Class.new do
|
9
|
+
include ITypeAssert
|
13
10
|
|
14
|
-
|
11
|
+
define_method(:to_s) { "TA[#{type.to_s}]" }
|
15
12
|
|
16
|
-
|
17
|
-
|
18
|
-
|
13
|
+
define_method(:assert) do |val|
|
14
|
+
T.let(val, type)
|
15
|
+
end
|
19
16
|
|
20
|
-
|
17
|
+
define_method(:get_type) { type }
|
18
|
+
end
|
21
19
|
end
|
22
20
|
end
|
23
21
|
end
|
24
22
|
|
25
|
-
class
|
26
|
-
|
23
|
+
class TA
|
24
|
+
include TypeAssertImpl
|
25
|
+
end
|
26
|
+
end
|
data/sorbet-rails.gemspec
CHANGED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
require 'sorbet-rails/custom_types/boolean_string'
|
3
|
+
require 'sorbet-runtime'
|
4
|
+
|
5
|
+
RSpec.describe BooleanString do
|
6
|
+
shared_examples 'boolean string' do |str|
|
7
|
+
it 'acts as BooleanString perfectly' do
|
8
|
+
expect(str.is_a?(BooleanString)).to be(true)
|
9
|
+
expect(str.kind_of?(BooleanString)).to be(true)
|
10
|
+
expect(str.instance_of?(BooleanString)).to be(true)
|
11
|
+
expect(BooleanString === str).to be(true)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
include_examples 'boolean string', 'true'
|
16
|
+
include_examples 'boolean string', 'false'
|
17
|
+
include_examples 'boolean string', 'True'
|
18
|
+
include_examples 'boolean string', 'False'
|
19
|
+
|
20
|
+
shared_examples 'non boolean string' do |str|
|
21
|
+
it 'does not acts as BooleanString' do
|
22
|
+
expect(str.is_a?(BooleanString)).to be(false)
|
23
|
+
expect(str.kind_of?(BooleanString)).to be(false)
|
24
|
+
expect(str.instance_of?(BooleanString)).to be(false)
|
25
|
+
expect(BooleanString === str).to be(false)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
include_examples 'non boolean string', 'yes'
|
30
|
+
include_examples 'non boolean string', 'no'
|
31
|
+
include_examples 'non boolean string', 'alala'
|
32
|
+
|
33
|
+
context 'sorbet recognizes it at runtime' do
|
34
|
+
it 'lets boolean string pass runtime check' do
|
35
|
+
expect(T.let('true', BooleanString)).to eql('true')
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'doesnt let normal string pass runtime typecheck' do
|
39
|
+
expect {
|
40
|
+
T.let('yes', BooleanString)
|
41
|
+
}.to raise_error(TypeError)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'using with TypeAssert' do
|
46
|
+
let!(:ta) { TA[BooleanString].new }
|
47
|
+
|
48
|
+
it 'lets boolean string pass runtime typecheck' do
|
49
|
+
expect(ta.assert('true')).to eql('true')
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'doesnt let normal string pass runtime typecheck' do
|
53
|
+
expect {
|
54
|
+
ta.assert('yes')
|
55
|
+
}.to raise_error(TypeError)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
@@ -216,6 +216,14 @@ def create_models
|
|
216
216
|
end
|
217
217
|
end
|
218
218
|
RUBY
|
219
|
+
|
220
|
+
file "app/models/squib.rb", <<~RUBY
|
221
|
+
class Squib < Wizard
|
222
|
+
def is_magical
|
223
|
+
false
|
224
|
+
end
|
225
|
+
end
|
226
|
+
RUBY
|
219
227
|
end
|
220
228
|
|
221
229
|
def create_migrations
|
@@ -304,6 +312,14 @@ def create_migrations
|
|
304
312
|
end
|
305
313
|
RUBY
|
306
314
|
end
|
315
|
+
|
316
|
+
file "db/migrate/20190620000007_add_type_to_wizard.rb", <<~RUBY
|
317
|
+
class AddTypeToWizard < #{migration_superclass}
|
318
|
+
def change
|
319
|
+
add_column :wizards, :type, :string, null: false, default: 'Wizard'
|
320
|
+
end
|
321
|
+
end
|
322
|
+
RUBY
|
307
323
|
end
|
308
324
|
|
309
325
|
def create_mailers
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
require 'sorbet-rails/custom_types/integer_string'
|
3
|
+
require 'sorbet-runtime'
|
4
|
+
|
5
|
+
RSpec.describe IntegerString do
|
6
|
+
it 'lets integer string acts as IntegerString perfectly' do
|
7
|
+
expect('1'.is_a?(IntegerString)).to be(true)
|
8
|
+
expect('12'.kind_of?(IntegerString)).to be(true)
|
9
|
+
expect('123'.instance_of?(IntegerString)).to be(true)
|
10
|
+
expect(IntegerString === '1234').to be(true)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'does not let other string acts as IntegerString' do
|
14
|
+
expect('a1'.is_a?(IntegerString)).to be(false)
|
15
|
+
expect('a12'.kind_of?(IntegerString)).to be(false)
|
16
|
+
expect('a123'.instance_of?(IntegerString)).to be(false)
|
17
|
+
expect(IntegerString === 'a1234').to be(false)
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'sorbet recognizes it at runtime' do
|
21
|
+
it 'lets integer string pass runtime check' do
|
22
|
+
expect(T.let('123', IntegerString)).to eql('123')
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'doesnt let normal string pass runtime typecheck' do
|
26
|
+
expect {
|
27
|
+
T.let('a123', IntegerString)
|
28
|
+
}.to raise_error(TypeError)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'using with TypeAssert' do
|
33
|
+
let!(:ta) { TA[IntegerString].new }
|
34
|
+
|
35
|
+
it 'lets integer string pass runtime typecheck' do
|
36
|
+
expect(ta.assert('123')).to eql('123')
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'doesnt let normal string pass runtime typecheck' do
|
40
|
+
expect {
|
41
|
+
ta.assert('a123')
|
42
|
+
}.to raise_error(TypeError)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
@@ -95,10 +95,10 @@ GEM
|
|
95
95
|
thor (>= 0.18.1, < 2.0)
|
96
96
|
rainbow (3.0.0)
|
97
97
|
rake (12.3.3)
|
98
|
-
sorbet (0.4.
|
99
|
-
sorbet-static (= 0.4.
|
100
|
-
sorbet-runtime (0.4.
|
101
|
-
sorbet-static (0.4.
|
98
|
+
sorbet (0.4.4755)
|
99
|
+
sorbet-static (= 0.4.4755)
|
100
|
+
sorbet-runtime (0.4.4755)
|
101
|
+
sorbet-static (0.4.4755-x86_64-linux)
|
102
102
|
sprockets (3.7.2)
|
103
103
|
concurrent-ruby (~> 1.0)
|
104
104
|
rack (> 1, < 3)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: ignore
|
2
2
|
class Wizard < ApplicationRecord
|
3
3
|
validates :name, length: { minimum: 5 }, presence: true
|
4
4
|
|
@@ -26,5 +26,4 @@ class Wizard < ApplicationRecord
|
|
26
26
|
has_many :spell_books
|
27
27
|
|
28
28
|
scope :recent, -> { where('created_at > ?', 1.month.ago) }
|
29
|
-
|
30
29
|
end
|
@@ -12,7 +12,7 @@
|
|
12
12
|
#
|
13
13
|
# It's strongly recommended that you check this file into your version control system.
|
14
14
|
|
15
|
-
ActiveRecord::Schema.define(version:
|
15
|
+
ActiveRecord::Schema.define(version: 20190620000007) do
|
16
16
|
|
17
17
|
create_table "spell_books", force: :cascade do |t|
|
18
18
|
t.string "name"
|
@@ -42,6 +42,7 @@ ActiveRecord::Schema.define(version: 20190620000005) do
|
|
42
42
|
t.datetime "created_at"
|
43
43
|
t.datetime "updated_at"
|
44
44
|
t.string "broom"
|
45
|
+
t.string "type", default: "Wizard", null: false
|
45
46
|
end
|
46
47
|
|
47
48
|
end
|