sorbet-rails 0.6.3 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitattributes +1 -2
- data/.gitignore +2 -1
- data/.travis.yml +3 -1
- data/README.md +54 -7
- data/Rakefile +3 -3
- data/lib/bundled_rbi/customizabel_rbi_formatter.rbi +29 -0
- data/lib/bundled_rbi/pluck_to_tstruct.rbi +2 -1
- data/lib/bundled_rbi/typed_enum.rbi +7 -0
- data/lib/sorbet-rails.rb +1 -3
- data/lib/sorbet-rails/active_record_rbi_formatter.rb +8 -2
- data/lib/sorbet-rails/config.rb +11 -0
- data/lib/sorbet-rails/deprecation.rb +1 -0
- data/lib/sorbet-rails/gem_plugins/active_flag_plugin.rb +0 -1
- data/lib/sorbet-rails/gem_plugins/kaminari_plugin.rb +8 -0
- data/lib/sorbet-rails/gem_plugins/paperclip_plugin.rb +0 -1
- data/lib/sorbet-rails/job_rbi_formatter.rb +73 -62
- data/lib/sorbet-rails/mailer_rbi_formatter.rb +40 -27
- data/lib/sorbet-rails/model_column_utils.rb +129 -0
- data/lib/sorbet-rails/model_plugins/active_record_assoc.rb +37 -3
- data/lib/sorbet-rails/model_plugins/active_record_attribute.rb +0 -103
- data/lib/sorbet-rails/model_plugins/active_record_enum.rb +0 -1
- data/lib/sorbet-rails/model_plugins/active_record_named_scope.rb +15 -6
- data/lib/sorbet-rails/model_plugins/active_record_querying.rb +34 -3
- data/lib/sorbet-rails/model_plugins/active_storage_methods.rb +1 -1
- data/lib/sorbet-rails/model_plugins/base.rb +0 -9
- data/lib/sorbet-rails/model_rbi_formatter.rb +4 -8
- data/lib/sorbet-rails/model_utils.rb +73 -32
- data/lib/sorbet-rails/rails_mixins/generated_url_helpers.rb +2 -3
- data/lib/sorbet-rails/rails_mixins/pluck_to_tstruct.rb +17 -7
- data/lib/sorbet-rails/railtie.rb +0 -2
- data/lib/sorbet-rails/sorbet_utils.rb +152 -150
- data/lib/sorbet-rails/tasks/rails_rbi.rake +18 -26
- data/sorbet-rails.gemspec +1 -1
- data/spec/generators/rails-template.rb +3 -6
- data/spec/generators/sorbet_test_cases.rb +66 -88
- data/spec/job_rbi_formatter_spec.rb +1 -1
- data/spec/pluck_to_tstruct_spec.rb +74 -1
- data/spec/rails_helper.rb +2 -2
- data/spec/rake_rails_rbi_jobs_spec.rb +20 -0
- data/spec/rake_rails_rbi_mailers_spec.rb +21 -0
- data/spec/support/v5.0/Gemfile +1 -1
- data/spec/support/v5.0/Gemfile.lock +9 -9
- data/spec/support/v5.0/app/models/spell_book.rb +2 -0
- data/spec/support/v5.0/app/models/wizard.rb +1 -1
- data/spec/support/v5.0/sorbet_test_cases.rb +66 -88
- data/spec/support/v5.1/Gemfile.lock +7 -7
- data/spec/support/v5.1/app/models/spell_book.rb +2 -0
- data/spec/support/v5.1/app/models/wizard.rb +1 -1
- data/spec/support/v5.1/sorbet_test_cases.rb +66 -88
- data/spec/support/v5.2/Gemfile +1 -1
- data/spec/support/v5.2/Gemfile.lock +9 -9
- data/spec/support/v5.2/app/models/spell_book.rb +2 -0
- data/spec/support/v5.2/app/models/wizard.rb +1 -1
- data/spec/support/v5.2/sorbet_test_cases.rb +66 -88
- data/spec/support/v6.0/.gitignore +6 -0
- data/spec/support/v6.0/Gemfile +3 -3
- data/spec/support/v6.0/Gemfile.lock +77 -76
- data/spec/support/v6.0/app/models/spell_book.rb +2 -0
- data/spec/support/v6.0/app/models/wizard.rb +1 -1
- data/spec/support/v6.0/bin/bundle +22 -13
- data/spec/support/v6.0/config/environments/test.rb +1 -1
- data/spec/support/v6.0/sorbet_test_cases.rb +66 -88
- data/spec/support/v6.0/tmp/pids/.keep +0 -0
- data/spec/test_data/v5.0/expected_active_record_base.rbi +2 -2
- data/spec/test_data/v5.0/expected_active_record_relation.rbi +2 -2
- data/spec/test_data/v5.0/expected_custom_application_job.rbi +21 -0
- data/spec/test_data/v5.0/expected_custom_application_mailer.rbi +6 -0
- data/spec/test_data/v5.0/expected_custom_award_house_point_hourglasses.rbi +21 -0
- data/spec/test_data/v5.0/expected_custom_daily_prophet_mailer.rbi +8 -0
- data/spec/test_data/v5.0/expected_custom_hogwarts_acceptance_mailer.rbi +21 -0
- data/spec/test_data/v5.0/expected_headmaster.rbi +60 -190
- data/spec/test_data/v5.0/expected_internal_metadata.rbi +42 -188
- data/spec/test_data/v5.0/expected_potion.rbi +51 -188
- data/spec/test_data/v5.0/expected_robe.rbi +51 -190
- data/spec/test_data/v5.0/expected_schema_migration.rbi +42 -188
- data/spec/test_data/v5.0/expected_school.rbi +51 -190
- data/spec/test_data/v5.0/expected_spell.rbi +42 -190
- data/spec/test_data/v5.0/expected_spell/habtm_spell_books.rbi +60 -190
- data/spec/test_data/v5.0/expected_spell_book.rbi +86 -215
- data/spec/test_data/v5.0/expected_spell_book/habtm_spells.rbi +60 -190
- data/spec/test_data/v5.0/expected_squib.rbi +131 -263
- data/spec/test_data/v5.0/expected_subject.rbi +42 -190
- data/spec/test_data/v5.0/expected_subject/habtm_wizards.rbi +60 -190
- data/spec/test_data/v5.0/expected_wand.rbi +84 -225
- data/spec/test_data/v5.0/expected_wizard.rbi +131 -263
- data/spec/test_data/v5.0/expected_wizard/habtm_subjects.rbi +60 -190
- data/spec/test_data/v5.0/expected_wizard_wo_spellbook.rbi +131 -263
- data/spec/test_data/v5.1/expected_active_record_base.rbi +2 -2
- data/spec/test_data/v5.1/expected_active_record_relation.rbi +2 -2
- data/spec/test_data/v5.1/expected_custom_application_job.rbi +21 -0
- data/spec/test_data/v5.1/expected_custom_application_mailer.rbi +6 -0
- data/spec/test_data/v5.1/expected_custom_award_house_point_hourglasses.rbi +21 -0
- data/spec/test_data/v5.1/expected_custom_daily_prophet_mailer.rbi +8 -0
- data/spec/test_data/v5.1/expected_custom_hogwarts_acceptance_mailer.rbi +21 -0
- data/spec/test_data/v5.1/expected_headmaster.rbi +60 -196
- data/spec/test_data/v5.1/expected_internal_metadata.rbi +42 -194
- data/spec/test_data/v5.1/expected_potion.rbi +51 -194
- data/spec/test_data/v5.1/expected_robe.rbi +51 -196
- data/spec/test_data/v5.1/expected_schema_migration.rbi +42 -194
- data/spec/test_data/v5.1/expected_school.rbi +51 -196
- data/spec/test_data/v5.1/expected_spell.rbi +42 -196
- data/spec/test_data/v5.1/expected_spell/habtm_spell_books.rbi +60 -196
- data/spec/test_data/v5.1/expected_spell_book.rbi +86 -221
- data/spec/test_data/v5.1/expected_spell_book/habtm_spells.rbi +60 -196
- data/spec/test_data/v5.1/expected_squib.rbi +132 -270
- data/spec/test_data/v5.1/expected_subject.rbi +42 -196
- data/spec/test_data/v5.1/expected_subject/habtm_wizards.rbi +60 -196
- data/spec/test_data/v5.1/expected_wand.rbi +84 -231
- data/spec/test_data/v5.1/expected_wizard.rbi +132 -270
- data/spec/test_data/v5.1/expected_wizard/habtm_subjects.rbi +60 -196
- data/spec/test_data/v5.1/expected_wizard_wo_spellbook.rbi +132 -270
- data/spec/test_data/v5.2/expected_active_record_base.rbi +2 -2
- data/spec/test_data/v5.2/expected_active_record_relation.rbi +2 -2
- data/spec/test_data/v5.2/expected_attachment.rbi +60 -194
- data/spec/test_data/v5.2/expected_blob.rbi +80 -214
- data/spec/test_data/v5.2/expected_custom_application_job.rbi +21 -0
- data/spec/test_data/v5.2/expected_custom_application_mailer.rbi +6 -0
- data/spec/test_data/v5.2/expected_custom_award_house_point_hourglasses.rbi +21 -0
- data/spec/test_data/v5.2/expected_custom_daily_prophet_mailer.rbi +8 -0
- data/spec/test_data/v5.2/expected_custom_hogwarts_acceptance_mailer.rbi +21 -0
- data/spec/test_data/v5.2/expected_headmaster.rbi +60 -196
- data/spec/test_data/v5.2/expected_internal_metadata.rbi +42 -194
- data/spec/test_data/v5.2/expected_potion.rbi +51 -194
- data/spec/test_data/v5.2/expected_robe.rbi +51 -196
- data/spec/test_data/v5.2/expected_schema_migration.rbi +42 -194
- data/spec/test_data/v5.2/expected_school.rbi +51 -196
- data/spec/test_data/v5.2/expected_spell.rbi +42 -196
- data/spec/test_data/v5.2/expected_spell/habtm_spell_books.rbi +60 -196
- data/spec/test_data/v5.2/expected_spell_book.rbi +86 -221
- data/spec/test_data/v5.2/expected_spell_book/habtm_spells.rbi +60 -196
- data/spec/test_data/v5.2/expected_squib.rbi +156 -276
- data/spec/test_data/v5.2/expected_subject.rbi +42 -196
- data/spec/test_data/v5.2/expected_subject/habtm_wizards.rbi +60 -196
- data/spec/test_data/v5.2/expected_wand.rbi +84 -231
- data/spec/test_data/v5.2/expected_wizard.rbi +156 -276
- data/spec/test_data/v5.2/expected_wizard/habtm_subjects.rbi +60 -196
- data/spec/test_data/v5.2/expected_wizard_wo_spellbook.rbi +156 -276
- data/spec/test_data/v6.0/expected_active_record_base.rbi +2 -2
- data/spec/test_data/v6.0/expected_active_record_relation.rbi +10 -10
- data/spec/test_data/v6.0/expected_attachment.rbi +60 -218
- data/spec/test_data/v6.0/expected_blob.rbi +80 -238
- data/spec/test_data/v6.0/expected_custom_application_job.rbi +21 -0
- data/spec/test_data/v6.0/expected_custom_application_mailer.rbi +6 -0
- data/spec/test_data/v6.0/expected_custom_award_house_point_hourglasses.rbi +21 -0
- data/spec/test_data/v6.0/expected_custom_daily_prophet_mailer.rbi +8 -0
- data/spec/test_data/v6.0/expected_custom_hogwarts_acceptance_mailer.rbi +21 -0
- data/spec/test_data/v6.0/expected_headmaster.rbi +60 -220
- data/spec/test_data/v6.0/expected_internal_metadata.rbi +42 -218
- data/spec/test_data/v6.0/expected_potion.rbi +51 -218
- data/spec/test_data/v6.0/expected_robe.rbi +51 -220
- data/spec/test_data/v6.0/expected_routes.rbi +14 -7
- data/spec/test_data/v6.0/expected_schema_migration.rbi +42 -218
- data/spec/test_data/v6.0/expected_school.rbi +51 -220
- data/spec/test_data/v6.0/expected_spell.rbi +42 -220
- data/spec/test_data/v6.0/expected_spell/habtm_spell_books.rbi +60 -220
- data/spec/test_data/v6.0/expected_spell_book.rbi +98 -257
- data/spec/test_data/v6.0/expected_spell_book/habtm_spells.rbi +60 -220
- data/spec/test_data/v6.0/expected_squib.rbi +182 -326
- data/spec/test_data/v6.0/expected_subject.rbi +42 -220
- data/spec/test_data/v6.0/expected_subject/habtm_wizards.rbi +60 -220
- data/spec/test_data/v6.0/expected_wand.rbi +100 -271
- data/spec/test_data/v6.0/expected_wizard.rbi +182 -326
- data/spec/test_data/v6.0/expected_wizard/habtm_subjects.rbi +60 -220
- data/spec/test_data/v6.0/expected_wizard_wo_spellbook.rbi +182 -326
- metadata +46 -11
- data/lib/bundled_rbi/parameters.rbi +0 -28
- data/lib/sorbet-rails/custom_types/boolean_string.rb +0 -42
- data/lib/sorbet-rails/custom_types/integer_string.rb +0 -45
- data/lib/sorbet-rails/rails_mixins/custom_params_methods.rb +0 -57
- data/spec/boolean_string_spec.rb +0 -59
- data/spec/custom_params_methods_spec.rb +0 -138
- data/spec/integer_string_spec.rb +0 -46
@@ -35,19 +35,19 @@ namespace :rails_rbi do
|
|
35
35
|
File.write(file_path, inspector.format(SorbetRails::RoutesRbiFormatter.new))
|
36
36
|
end
|
37
37
|
|
38
|
-
desc "Copy custom rbis for
|
38
|
+
desc "Copy custom rbis for typed_params, pluck_to_struct, etc."
|
39
39
|
task custom: :environment do
|
40
40
|
copy_bundled_rbi('type_assert.rbi')
|
41
|
-
copy_bundled_rbi('parameters.rbi')
|
42
41
|
copy_bundled_rbi('pluck_to_tstruct.rbi')
|
43
42
|
copy_bundled_rbi('typed_params.rbi')
|
43
|
+
copy_bundled_rbi('typed_enum.rbi')
|
44
44
|
|
45
45
|
# These files were previously bundled_rbi but are now generated so this
|
46
46
|
# is needed for backwards compatibility with anyone using `rails_rbi:custom`
|
47
47
|
Rake::Task['rails_rbi:active_record'].invoke
|
48
48
|
end
|
49
49
|
|
50
|
-
desc "Generate rbis for rails
|
50
|
+
desc "Generate rbis for rails active_record base"
|
51
51
|
task :active_record, [:root_dir] => :environment do |t, args|
|
52
52
|
formatter = SorbetRails::ActiveRecordRbiFormatter.new
|
53
53
|
FileUtils.mkdir_p(Rails.root.join("sorbet", "rails-rbi"))
|
@@ -77,11 +77,19 @@ namespace :rails_rbi do
|
|
77
77
|
models_to_generate = models_to_generate - blacklisted_models
|
78
78
|
end
|
79
79
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
80
|
+
available_class_names = Set.new(all_models.map { |c| c.name })
|
81
|
+
models_to_generate.each do |model_class|
|
82
|
+
model_class_name = model_class.to_s
|
83
|
+
begin
|
84
|
+
formatter = SorbetRails::ModelRbiFormatter.new(model_class, available_class_names)
|
85
|
+
file_path = Rails.root.join("sorbet", "rails-rbi", "models", "#{model_class_name.underscore}.rbi")
|
86
|
+
FileUtils.mkdir_p(File.dirname(file_path))
|
87
|
+
File.write(file_path, formatter.generate_rbi)
|
88
|
+
rescue StandardError, NotImplementedError => ex
|
89
|
+
puts "---"
|
90
|
+
puts "Error when handling model #{model_class_name}: #{ex}"
|
91
|
+
nil
|
92
|
+
end
|
85
93
|
end
|
86
94
|
end
|
87
95
|
|
@@ -121,7 +129,7 @@ namespace :rails_rbi do
|
|
121
129
|
"mailers",
|
122
130
|
"#{mailer_class.name.underscore}.rbi",
|
123
131
|
)
|
124
|
-
formatter = SorbetRails
|
132
|
+
formatter = ::SorbetRails.config.mailer_generator_class.new(mailer_class)
|
125
133
|
FileUtils.mkdir_p(File.dirname(file_path))
|
126
134
|
File.write(file_path, formatter.generate_rbi)
|
127
135
|
end
|
@@ -139,28 +147,12 @@ namespace :rails_rbi do
|
|
139
147
|
"jobs",
|
140
148
|
"#{job_class.name.underscore}.rbi",
|
141
149
|
)
|
142
|
-
formatter = SorbetRails
|
150
|
+
formatter = ::SorbetRails.config.job_generator_class.new(job_class)
|
143
151
|
FileUtils.mkdir_p(File.dirname(file_path))
|
144
152
|
File.write(file_path, formatter.generate_rbi)
|
145
153
|
end
|
146
154
|
end
|
147
155
|
|
148
|
-
def generate_rbis_for_models(model_classes, available_classes)
|
149
|
-
available_class_names = Set.new(available_classes.map { |c| c.name })
|
150
|
-
formatted = model_classes.map do |model_class|
|
151
|
-
model_class_name = model_class.to_s
|
152
|
-
begin
|
153
|
-
formatter = SorbetRails::ModelRbiFormatter.new(model_class, available_class_names)
|
154
|
-
[model_class_name, formatter.generate_rbi]
|
155
|
-
rescue StandardError, NotImplementedError => ex
|
156
|
-
puts "---"
|
157
|
-
puts "Error when handling model #{model_class_name}: #{ex}"
|
158
|
-
nil
|
159
|
-
end
|
160
|
-
end
|
161
|
-
Hash[formatted.compact] # remove models with errors
|
162
|
-
end
|
163
|
-
|
164
156
|
def blacklisted_models
|
165
157
|
blacklisted_models = []
|
166
158
|
blacklisted_models << ApplicationRecord if defined?(ApplicationRecord)
|
data/sorbet-rails.gemspec
CHANGED
@@ -91,6 +91,8 @@ def create_models
|
|
91
91
|
biology: 1,
|
92
92
|
dark_art: 999,
|
93
93
|
}
|
94
|
+
|
95
|
+
scope :recent, -> { where('created_at > ?', 1.month.ago) }
|
94
96
|
end
|
95
97
|
RUBY
|
96
98
|
|
@@ -133,7 +135,7 @@ def create_models
|
|
133
135
|
class Wizard < ApplicationRecord
|
134
136
|
validates :name, length: { minimum: 5 }, presence: true
|
135
137
|
# simulate conditional validation
|
136
|
-
validates :parent_email, presence: true, if:
|
138
|
+
validates :parent_email, presence: true, if: :Slytherin?
|
137
139
|
|
138
140
|
typed_enum house: {
|
139
141
|
Gryffindor: 0,
|
@@ -478,11 +480,6 @@ def create_jobs
|
|
478
480
|
end
|
479
481
|
|
480
482
|
def add_sorbet_test_files
|
481
|
-
file "typed-override.yaml", <<~YAML
|
482
|
-
true:
|
483
|
-
- ./sorbet_test_cases.rb
|
484
|
-
YAML
|
485
|
-
|
486
483
|
copy_file "./sorbet_test_cases.rb", "sorbet_test_cases.rb"
|
487
484
|
end
|
488
485
|
|
@@ -29,26 +29,6 @@ T.assert_type!(T.must(wizard.wand).wizard, Wizard)
|
|
29
29
|
T.assert_type!(wizard.spell_books, SpellBook::ActiveRecord_Associations_CollectionProxy)
|
30
30
|
T.assert_type!(wizard.spell_book_ids, T::Array[Integer])
|
31
31
|
|
32
|
-
# -- model relation
|
33
|
-
# default
|
34
|
-
T.assert_type!(Wizard.all, Wizard::ActiveRecord_Relation)
|
35
|
-
|
36
|
-
# custom scope
|
37
|
-
T.assert_type!(Wizard.recent, Wizard::ActiveRecord_Relation)
|
38
|
-
|
39
|
-
# enum scope
|
40
|
-
T.assert_type!(Wizard.Gryffindor, Wizard::ActiveRecord_Relation)
|
41
|
-
|
42
|
-
# ActiveRecord Querying
|
43
|
-
T.assert_type!(Wizard.Gryffindor.recent, Wizard::ActiveRecord_Relation)
|
44
|
-
T.assert_type!(Wizard.Gryffindor.recent.unscoped, Wizard::ActiveRecord_Relation)
|
45
|
-
T.assert_type!(Wizard.where(id: 1), Wizard::ActiveRecord_Relation)
|
46
|
-
T.assert_type!(Wizard.where(id: 1).recent, Wizard::ActiveRecord_Relation)
|
47
|
-
T.assert_type!(Wizard.where.not(id: 1), Wizard::ActiveRecord_Relation)
|
48
|
-
T.assert_type!(Wizard.preload(:spell_books), Wizard::ActiveRecord_Relation)
|
49
|
-
T.assert_type!(Wizard.eager_load(:spell_books), Wizard::ActiveRecord_Relation)
|
50
|
-
T.assert_type!(Wizard.order(:id), Wizard::ActiveRecord_Relation)
|
51
|
-
|
52
32
|
# Finder methods -- Model
|
53
33
|
T.assert_type!(Wizard.exists?(name: 'Test'), T::Boolean)
|
54
34
|
T.assert_type!(Wizard.find(wizard.id), Wizard)
|
@@ -83,6 +63,8 @@ Wizard.find_each { |w| T.assert_type!(w, Wizard) }
|
|
83
63
|
T.assert_type!(Wizard.find_each, T::Enumerator[Wizard])
|
84
64
|
Wizard.find_in_batches { |w| T.assert_type!(w, T::Array[Wizard]) }
|
85
65
|
T.assert_type!(Wizard.find_in_batches, T::Enumerator[T::Array[Wizard]])
|
66
|
+
Wizard.in_batches { |w| T.assert_type!(w, Wizard::ActiveRecord_Relation) }
|
67
|
+
T.assert_type!(Wizard.in_batches, ActiveRecord::Batches::BatchEnumerator)
|
86
68
|
# T.assert_type!(Wizard.destroy_all, T::Array[Wizard]) # Ignored until we add support
|
87
69
|
T.assert_type!(Wizard.any?, T::Boolean)
|
88
70
|
T.assert_type!(Wizard.many?, T::Boolean)
|
@@ -90,6 +72,18 @@ T.assert_type!(Wizard.none?, T::Boolean)
|
|
90
72
|
T.assert_type!(Wizard.one?, T::Boolean)
|
91
73
|
# T.assert_type!(Wizard.update_all(name: 'Harry Potter'), Integer) # Ignored until we add support
|
92
74
|
# T.assert_type!(Wizard.delete_all, Integer) # Ignored until we add support
|
75
|
+
# Query methods
|
76
|
+
T.assert_type!(Wizard.all, Wizard::ActiveRecord_Relation)
|
77
|
+
T.assert_type!(Wizard.recent, Wizard::ActiveRecord_Relation) # Named scope
|
78
|
+
T.assert_type!(Wizard.Gryffindor, Wizard::ActiveRecord_Relation) # Enum scope
|
79
|
+
T.assert_type!(Wizard.Gryffindor.recent, Wizard::ActiveRecord_Relation)
|
80
|
+
T.assert_type!(Wizard.Gryffindor.recent.unscoped, Wizard::ActiveRecord_Relation)
|
81
|
+
T.assert_type!(Wizard.where(id: 1), Wizard::ActiveRecord_Relation)
|
82
|
+
T.assert_type!(Wizard.where(id: 1).recent, Wizard::ActiveRecord_Relation)
|
83
|
+
T.assert_type!(Wizard.where.not(id: 1), Wizard::ActiveRecord_Relation)
|
84
|
+
T.assert_type!(Wizard.preload(:spell_books), Wizard::ActiveRecord_Relation)
|
85
|
+
T.assert_type!(Wizard.eager_load(:spell_books), Wizard::ActiveRecord_Relation)
|
86
|
+
T.assert_type!(Wizard.order(:id), Wizard::ActiveRecord_Relation)
|
93
87
|
|
94
88
|
# Finder methods -- ActiveRecord::Relation
|
95
89
|
T.assert_type!(Wizard.all.exists?(name: 'Harry Potter'), T::Boolean)
|
@@ -126,6 +120,8 @@ Wizard.all.find_each { |w| T.assert_type!(w, Wizard) }
|
|
126
120
|
T.assert_type!(Wizard.all.find_each, T::Enumerator[Wizard])
|
127
121
|
Wizard.all.find_in_batches { |w| T.assert_type!(w, T::Array[Wizard]) }
|
128
122
|
T.assert_type!(Wizard.all.find_in_batches, T::Enumerator[T::Array[Wizard]])
|
123
|
+
Wizard.all.in_batches { |w| T.assert_type!(w, Wizard::ActiveRecord_Relation) }
|
124
|
+
T.assert_type!(Wizard.all.in_batches, T::Enumerable[Wizard::ActiveRecord_Relation])
|
129
125
|
# T.assert_type!(Wizard.all.destroy_all, T::Array[Wizard]) # Ignored until we add support
|
130
126
|
T.assert_type!(Wizard.all.any?, T::Boolean)
|
131
127
|
T.assert_type!(Wizard.all.many?, T::Boolean)
|
@@ -133,6 +129,18 @@ T.assert_type!(Wizard.all.none?, T::Boolean)
|
|
133
129
|
T.assert_type!(Wizard.all.one?, T::Boolean)
|
134
130
|
# T.assert_type!(Wizard.all.update_all(name: 'Harry Potter'), Integer) # Ignored until we add support
|
135
131
|
# T.assert_type!(Wizard.all.delete_all, Integer) # Ignored until we add support
|
132
|
+
# Query methods
|
133
|
+
T.assert_type!(Wizard.all.all, Wizard::ActiveRecord_Relation)
|
134
|
+
T.assert_type!(Wizard.all.recent, Wizard::ActiveRecord_Relation) # Named scope
|
135
|
+
T.assert_type!(Wizard.all.Gryffindor, Wizard::ActiveRecord_Relation) # Enum scope
|
136
|
+
T.assert_type!(Wizard.all.Gryffindor.recent, Wizard::ActiveRecord_Relation)
|
137
|
+
T.assert_type!(Wizard.all.Gryffindor.recent.unscoped, Wizard::ActiveRecord_Relation)
|
138
|
+
T.assert_type!(Wizard.all.where(id: 1), Wizard::ActiveRecord_Relation)
|
139
|
+
T.assert_type!(Wizard.all.where(id: 1).recent, Wizard::ActiveRecord_Relation)
|
140
|
+
T.assert_type!(Wizard.all.where.not(id: 1), Wizard::ActiveRecord_Relation)
|
141
|
+
T.assert_type!(Wizard.all.preload(:spell_books), Wizard::ActiveRecord_Relation)
|
142
|
+
T.assert_type!(Wizard.all.eager_load(:spell_books), Wizard::ActiveRecord_Relation)
|
143
|
+
T.assert_type!(Wizard.all.order(:id), Wizard::ActiveRecord_Relation)
|
136
144
|
# Enumerable methods
|
137
145
|
Wizard.all.each { |w| T.assert_type!(w, Wizard) }
|
138
146
|
Wizard.all.map { |w| T.assert_type!(w, Wizard) }
|
@@ -172,10 +180,12 @@ T.assert_type!(spell_books.create!(name: 'Fantastic Beasts') { |s| T.assert_type
|
|
172
180
|
T.assert_type!(spell_books.first_or_create(name: 'Fantastic Beasts') { |s| T.assert_type!(s, SpellBook) }, SpellBook)
|
173
181
|
T.assert_type!(spell_books.first_or_create!(name: 'Fantastic Beasts') { |s| T.assert_type!(s, SpellBook) }, SpellBook)
|
174
182
|
T.assert_type!(spell_books.first_or_initialize { |s| T.assert_type!(s, SpellBook) }, SpellBook)
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
183
|
+
spell_books.find_each { |s| T.assert_type!(s, SpellBook) }
|
184
|
+
T.assert_type!(spell_books.find_each, T::Enumerator[SpellBook])
|
185
|
+
spell_books.find_in_batches { |s| T.assert_type!(s, T::Array[SpellBook]) }
|
186
|
+
T.assert_type!(spell_books.find_in_batches, T::Enumerator[T::Array[SpellBook]])
|
187
|
+
spell_books.in_batches { |s| T.assert_type!(s, SpellBook::ActiveRecord_AssociationRelation) }
|
188
|
+
T.assert_type!(spell_books.in_batches, T::Enumerable[SpellBook::ActiveRecord_AssociationRelation])
|
179
189
|
# T.assert_type!(spell_books.destroy_all, T::Array[SpellBook]) # Ignored until we add support
|
180
190
|
T.assert_type!(spell_books.any?, T::Boolean)
|
181
191
|
T.assert_type!(spell_books.many?, T::Boolean)
|
@@ -183,13 +193,18 @@ T.assert_type!(spell_books.none?, T::Boolean)
|
|
183
193
|
T.assert_type!(spell_books.one?, T::Boolean)
|
184
194
|
# T.assert_type!(spell_books.update_all(name: 'Fantastic Beasts'), Integer) # Ignored until we add support
|
185
195
|
# T.assert_type!(spell_books.delete_all, Integer) # Ignored until we add support
|
186
|
-
#
|
196
|
+
# Query methods
|
197
|
+
T.assert_type!(spell_books.all, SpellBook::ActiveRecord_AssociationRelation)
|
198
|
+
T.assert_type!(spell_books.recent, SpellBook::ActiveRecord_AssociationRelation) # Named scope
|
199
|
+
T.assert_type!(spell_books.unclassified, SpellBook::ActiveRecord_AssociationRelation) # Enum scope
|
200
|
+
T.assert_type!(spell_books.unclassified.recent, SpellBook::ActiveRecord_AssociationRelation)
|
201
|
+
T.assert_type!(spell_books.unclassified.recent.unscoped, SpellBook::ActiveRecord_Relation) # Turns back into relation
|
187
202
|
T.assert_type!(spell_books.where(id: 1), SpellBook::ActiveRecord_AssociationRelation)
|
203
|
+
T.assert_type!(spell_books.where(id: 1).recent, SpellBook::ActiveRecord_AssociationRelation)
|
204
|
+
T.assert_type!(spell_books.where.not(id: 1), SpellBook::ActiveRecord_AssociationRelation)
|
188
205
|
T.assert_type!(spell_books.preload(:wizard), SpellBook::ActiveRecord_AssociationRelation)
|
189
206
|
T.assert_type!(spell_books.eager_load(:wizard), SpellBook::ActiveRecord_AssociationRelation)
|
190
207
|
T.assert_type!(spell_books.order(:id), SpellBook::ActiveRecord_AssociationRelation)
|
191
|
-
T.assert_type!(spell_books.where.not(id: 1), SpellBook::ActiveRecord_AssociationRelation)
|
192
|
-
T.assert_type!(spell_books.biology, SpellBook::ActiveRecord_AssociationRelation)
|
193
208
|
# Enumerable methods
|
194
209
|
spell_books.each { |s| T.assert_type!(s, SpellBook) }
|
195
210
|
spell_books.map { |s| T.assert_type!(s, SpellBook) }
|
@@ -237,10 +252,12 @@ T.assert_type!(spell_books_query.create!(name: 'Fantastic Beasts') { |s| T.asser
|
|
237
252
|
T.assert_type!(spell_books_query.first_or_create(name: 'Fantastic Beasts') { |s| T.assert_type!(s, SpellBook) }, SpellBook) # Ignored until we add support
|
238
253
|
T.assert_type!(spell_books_query.first_or_create!(name: 'Fantastic Beasts') { |s| T.assert_type!(s, SpellBook) }, SpellBook) # Ignored until we add support
|
239
254
|
T.assert_type!(spell_books_query.first_or_initialize { |s| T.assert_type!(s, SpellBook) }, SpellBook) # Ignored until we add support
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
255
|
+
spell_books_query.find_each { |s| T.assert_type!(s, SpellBook) }
|
256
|
+
T.assert_type!(spell_books_query.find_each, T::Enumerator[SpellBook])
|
257
|
+
spell_books_query.find_in_batches { |s| T.assert_type!(s, T::Array[SpellBook]) }
|
258
|
+
T.assert_type!(spell_books_query.find_in_batches, T::Enumerator[T::Array[SpellBook]])
|
259
|
+
spell_books_query.in_batches { |s| T.assert_type!(s, SpellBook::ActiveRecord_AssociationRelation) }
|
260
|
+
T.assert_type!(spell_books_query.in_batches, T::Enumerable[SpellBook::ActiveRecord_AssociationRelation])
|
244
261
|
# T.assert_type!(spell_books_query.destroy_all, T::Array[SpellBook]) # Ignored until we add support
|
245
262
|
T.assert_type!(spell_books_query.any?, T::Boolean)
|
246
263
|
T.assert_type!(spell_books_query.many?, T::Boolean)
|
@@ -248,12 +265,18 @@ T.assert_type!(spell_books_query.none?, T::Boolean)
|
|
248
265
|
T.assert_type!(spell_books_query.one?, T::Boolean)
|
249
266
|
# T.assert_type!(spell_books_query.update_all(name: 'Fantastic Beasts'), Integer) # Ignored until we add support
|
250
267
|
# T.assert_type!(spell_books_query.delete_all, Integer) # Ignored until we add support
|
251
|
-
# Query
|
268
|
+
# Query methods
|
269
|
+
T.assert_type!(spell_books_query.all, SpellBook::ActiveRecord_AssociationRelation)
|
270
|
+
T.assert_type!(spell_books_query.recent, SpellBook::ActiveRecord_AssociationRelation) # Named scope
|
271
|
+
T.assert_type!(spell_books_query.unclassified, SpellBook::ActiveRecord_AssociationRelation) # Enum scope
|
272
|
+
T.assert_type!(spell_books_query.unclassified.recent, SpellBook::ActiveRecord_AssociationRelation)
|
273
|
+
T.assert_type!(spell_books_query.unclassified.recent.unscoped, SpellBook::ActiveRecord_Relation) # Turns back into relation
|
274
|
+
T.assert_type!(spell_books_query.where(id: 1), SpellBook::ActiveRecord_AssociationRelation)
|
275
|
+
T.assert_type!(spell_books_query.where(id: 1).recent, SpellBook::ActiveRecord_AssociationRelation)
|
276
|
+
T.assert_type!(spell_books_query.where.not(id: 1), SpellBook::ActiveRecord_AssociationRelation)
|
252
277
|
T.assert_type!(spell_books_query.preload(:wizard), SpellBook::ActiveRecord_AssociationRelation)
|
253
278
|
T.assert_type!(spell_books_query.eager_load(:wizard), SpellBook::ActiveRecord_AssociationRelation)
|
254
279
|
T.assert_type!(spell_books_query.order(:id), SpellBook::ActiveRecord_AssociationRelation)
|
255
|
-
T.assert_type!(spell_books_query.where.not(id: 1), SpellBook::ActiveRecord_AssociationRelation)
|
256
|
-
T.assert_type!(spell_books_query.biology, SpellBook::ActiveRecord_AssociationRelation)
|
257
280
|
# Enumerable methods
|
258
281
|
spell_books_query.each { |s| T.assert_type!(s, SpellBook) }
|
259
282
|
spell_books_query.map { |s| T.assert_type!(s, SpellBook) }
|
@@ -338,44 +361,6 @@ params = ActionController::Parameters.new({
|
|
338
361
|
typed_params = TypedParams[MyActionParams].new.extract!(params)
|
339
362
|
T.assert_type!(typed_params, MyActionParams)
|
340
363
|
|
341
|
-
# -- require_typed
|
342
|
-
T.assert_type!(
|
343
|
-
params.require_typed(:age, TA[Integer].new),
|
344
|
-
Integer,
|
345
|
-
)
|
346
|
-
T.assert_type!(
|
347
|
-
params.require_typed(:name, TA[String].new),
|
348
|
-
String,
|
349
|
-
)
|
350
|
-
info = params.require_typed(:info, TA[ActionController::Parameters].new)
|
351
|
-
T.assert_type!(info, ActionController::Parameters)
|
352
|
-
T.assert_type!(
|
353
|
-
info.require_typed(:friends, TA[T::Array[String]].new),
|
354
|
-
T::Array[String],
|
355
|
-
)
|
356
|
-
# -- fetch_typed
|
357
|
-
T.assert_type!(
|
358
|
-
params.fetch_typed(:age, TA[Integer].new),
|
359
|
-
Integer,
|
360
|
-
)
|
361
|
-
T.assert_type!(
|
362
|
-
params.fetch_typed(:name, TA[String].new),
|
363
|
-
String,
|
364
|
-
)
|
365
|
-
T.assert_type!(
|
366
|
-
params.fetch_typed(:nonexistence, TA[String].new, ''),
|
367
|
-
String,
|
368
|
-
)
|
369
|
-
T.assert_type!(
|
370
|
-
params.fetch_typed(:nonexistence, TA[T.nilable(String)].new, nil),
|
371
|
-
T.nilable(String),
|
372
|
-
)
|
373
|
-
T.assert_type!(
|
374
|
-
params.fetch_typed(:nonexistence, TA[T::Array[Integer]].new, []),
|
375
|
-
T::Array[Integer],
|
376
|
-
)
|
377
|
-
|
378
|
-
|
379
364
|
# -- pluck to tstruct
|
380
365
|
class WizardStruct < T::Struct
|
381
366
|
const :name, String
|
@@ -392,23 +377,16 @@ Wizard.all.pluck_to_tstruct(TA[WizardStruct].new).each do |row|
|
|
392
377
|
T.assert_type!(row, WizardStruct)
|
393
378
|
end
|
394
379
|
|
395
|
-
|
396
380
|
# -- GeneratedUrlHelpers
|
397
381
|
class TestHelper
|
398
382
|
include GeneratedUrlHelpers
|
399
|
-
|
400
|
-
# need to implement this for the url
|
401
|
-
def default_url_options
|
402
|
-
{
|
403
|
-
protocol: 'http',
|
404
|
-
host: 'localhost',
|
405
|
-
port: 3000,
|
406
|
-
}
|
407
|
-
end
|
408
|
-
|
409
|
-
def test_url_helper
|
410
|
-
T.assert_type!(test_index_path, String)
|
411
|
-
T.assert_type!(test_index_url, String)
|
412
|
-
end
|
413
383
|
end
|
414
|
-
|
384
|
+
|
385
|
+
T.assert_type!(TestHelper.new.test_index_path, String)
|
386
|
+
# need to set this config for _url methods
|
387
|
+
Rails.application.routes.default_url_options = {
|
388
|
+
protocol: 'http',
|
389
|
+
host: 'localhost',
|
390
|
+
port: 3000,
|
391
|
+
}
|
392
|
+
T.assert_type!(TestHelper.new.test_index_url, String)
|
@@ -9,6 +9,18 @@ RSpec.describe SorbetRails::PluckToTStruct do
|
|
9
9
|
)
|
10
10
|
end
|
11
11
|
|
12
|
+
let!(:harrys_wand) do
|
13
|
+
Wand.create!(
|
14
|
+
wizard: harry,
|
15
|
+
core_type: :phoenix_feather,
|
16
|
+
wood_type: "Holly",
|
17
|
+
chosen_at_date: Date.parse('2019-09-01'),
|
18
|
+
chosen_at_time: Time.parse('2019-09-01T09:00:00Z'),
|
19
|
+
broken: true,
|
20
|
+
broken_at: Time.parse('2019-09-05T15:30:00Z'),
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
12
24
|
let!(:hermione) do
|
13
25
|
Wizard.create!(
|
14
26
|
name: 'Hermione Granger',
|
@@ -16,6 +28,18 @@ RSpec.describe SorbetRails::PluckToTStruct do
|
|
16
28
|
)
|
17
29
|
end
|
18
30
|
|
31
|
+
let!(:hermiones_wand) do
|
32
|
+
Wand.create!(
|
33
|
+
wizard: hermione,
|
34
|
+
core_type: :phoenix_feather,
|
35
|
+
wood_type: "Vine",
|
36
|
+
chosen_at_date: Date.parse('2019-09-01'),
|
37
|
+
chosen_at_time: Time.parse('2019-09-01T09:00:00Z'),
|
38
|
+
broken: true,
|
39
|
+
broken_at: Time.parse('2019-09-05T15:30:00Z'),
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
19
43
|
class WizardName < T::Struct
|
20
44
|
const :name, String
|
21
45
|
|
@@ -43,6 +67,21 @@ RSpec.describe SorbetRails::PluckToTStruct do
|
|
43
67
|
end
|
44
68
|
end
|
45
69
|
|
70
|
+
class WizardWithWandT < T::Struct
|
71
|
+
const :name, String
|
72
|
+
const :house, String
|
73
|
+
const :wand_wood_type, String
|
74
|
+
|
75
|
+
def ==(other)
|
76
|
+
return false unless other.is_a?(self.class)
|
77
|
+
name == other.name && house == other.house
|
78
|
+
end
|
79
|
+
|
80
|
+
def eql?(other)
|
81
|
+
self == other
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
46
85
|
shared_examples 'pluck_to_tstruct' do |struct_type, expected_values|
|
47
86
|
it 'plucks correctly from ActiveRecord model' do
|
48
87
|
plucked = Wizard.pluck_to_tstruct(TA[struct_type].new)
|
@@ -55,6 +94,18 @@ RSpec.describe SorbetRails::PluckToTStruct do
|
|
55
94
|
end
|
56
95
|
end
|
57
96
|
|
97
|
+
shared_examples 'pluck_to_tstruct with associations' do |struct_type, associations, expected_values|
|
98
|
+
it 'plucks correctly from ActiveRecord model with associations' do
|
99
|
+
plucked = Wizard.joins(:wand).pluck_to_tstruct(TA[struct_type].new, associations: associations)
|
100
|
+
expect(plucked).to match_array(expected_values)
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'plucks correctly from ActiveRecord relation with associations' do
|
104
|
+
plucked = Wizard.all.joins(:wand).pluck_to_tstruct(TA[struct_type].new, associations: associations)
|
105
|
+
expect(plucked).to match_array(expected_values)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
58
109
|
context 'pluck 1 attribute' do
|
59
110
|
it_should_behave_like 'pluck_to_tstruct', WizardName, [
|
60
111
|
WizardName.new(name: "Harry Potter"),
|
@@ -72,8 +123,30 @@ RSpec.describe SorbetRails::PluckToTStruct do
|
|
72
123
|
context 'given a wrong type' do
|
73
124
|
it 'should raise error' do
|
74
125
|
expect {
|
75
|
-
|
126
|
+
Wizard.pluck_to_tstruct(TA[String].new)
|
76
127
|
}.to raise_error(SorbetRails::PluckToTStruct::UnexpectedType)
|
77
128
|
end
|
78
129
|
end
|
130
|
+
|
131
|
+
context "given associations mappings that don't exist in T::Struct" do
|
132
|
+
it 'should raise error' do
|
133
|
+
expect {
|
134
|
+
Wizard.pluck_to_tstruct(
|
135
|
+
TA[WizardWithWandT].new,
|
136
|
+
associations: { wood_type: "wands.wood_type" }
|
137
|
+
)
|
138
|
+
}.to raise_error(SorbetRails::PluckToTStruct::UnexpectedAssociations)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
context 'pluck with associations' do
|
143
|
+
associations = { wand_wood_type: "wands.wood_type" }
|
144
|
+
|
145
|
+
expected = [
|
146
|
+
WizardWithWandT.new(name: "Harry Potter", house: "Gryffindor", wand_wood_type: "Holly"),
|
147
|
+
WizardWithWandT.new(name: "Hermione Granger", house: "Gryffindor", wand_wood_type: "Vine"),
|
148
|
+
]
|
149
|
+
|
150
|
+
it_should_behave_like 'pluck_to_tstruct with associations', WizardWithWandT, associations, expected
|
151
|
+
end
|
79
152
|
end
|