sorbet-rails 0.5.3 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CONTRIBUTING.md +2 -2
  4. data/README.md +22 -1
  5. data/lib/sorbet-rails/actionmailer.rbi +7 -0
  6. data/lib/sorbet-rails/activerecord.rbi +3 -0
  7. data/lib/sorbet-rails/config.rb +5 -0
  8. data/lib/sorbet-rails/helper_rbi_formatter.rb +3 -0
  9. data/lib/sorbet-rails/mailer_rbi_formatter.rb +38 -0
  10. data/lib/sorbet-rails/model_plugins/active_record_assoc.rb +26 -6
  11. data/lib/sorbet-rails/model_plugins/active_record_attribute.rb +4 -5
  12. data/lib/sorbet-rails/model_plugins/active_record_enum.rb +37 -3
  13. data/lib/sorbet-rails/model_plugins/active_record_named_scope.rb +2 -0
  14. data/lib/sorbet-rails/model_plugins/active_record_overrides.rb +27 -0
  15. data/lib/sorbet-rails/model_plugins/active_storage_methods.rb +60 -0
  16. data/lib/sorbet-rails/model_plugins/plugins.rb +4 -0
  17. data/lib/sorbet-rails/sorbet_utils.rb +68 -0
  18. data/lib/sorbet-rails/tasks/rails_rbi.rake +20 -0
  19. data/lib/sorbet-rails/utils.rb +5 -0
  20. data/sorbet-rails.gemspec +1 -1
  21. data/spec/generators/rails-template.rb +145 -16
  22. data/spec/generators/sorbet_test_cases.rb +11 -0
  23. data/spec/helper_rbi_formatter_spec.rb +19 -0
  24. data/spec/mailer_rbi_formatter_spec.rb +13 -0
  25. data/spec/model_rbi_formatter_spec.rb +12 -2
  26. data/spec/rails_helper.rb +20 -0
  27. data/spec/rake_rails_rbi_mailers_spec.rb +21 -0
  28. data/spec/rake_rails_rbi_models_spec.rb +1 -21
  29. data/spec/sorbet_spec.rb +13 -0
  30. data/spec/sorbet_utils_spec.rb +126 -0
  31. data/spec/support/v4.2/Gemfile.lock +5 -5
  32. data/spec/support/v4.2/app/mailers/application_mailer.rb +3 -0
  33. data/spec/support/v4.2/app/mailers/daily_prophet_mailer.rb +9 -0
  34. data/spec/support/v4.2/app/mailers/hogwarts_acceptance_mailer.rb +13 -0
  35. data/spec/support/v4.2/app/models/wizard.rb +14 -0
  36. data/spec/support/v4.2/db/migrate/20190620000005_add_broom_to_wizard.rb +6 -0
  37. data/spec/support/v4.2/db/schema.rb +2 -1
  38. data/spec/support/v4.2/sorbet_test_cases.rb +11 -0
  39. data/spec/support/v5.0/Gemfile.lock +6 -6
  40. data/spec/support/v5.0/app/mailers/daily_prophet_mailer.rb +9 -0
  41. data/spec/support/v5.0/app/mailers/hogwarts_acceptance_mailer.rb +13 -0
  42. data/spec/support/v5.0/app/models/wizard.rb +33 -0
  43. data/spec/support/v5.0/config/initializers/new_framework_defaults.rb +1 -1
  44. data/spec/support/v5.0/db/migrate/20190620000001_create_wizards.rb +1 -1
  45. data/spec/support/v5.0/db/migrate/20190620000002_create_wands.rb +1 -1
  46. data/spec/support/v5.0/db/migrate/20190620000003_create_spell_books.rb +1 -1
  47. data/spec/support/v5.0/db/migrate/20190620000004_add_more_column_types_to_wands.rb +1 -1
  48. data/spec/support/v5.0/db/migrate/20190620000005_add_broom_to_wizard.rb +6 -0
  49. data/spec/support/v5.0/db/migrate/20190620000006_add_more_enums_to_wizard.rb +9 -0
  50. data/spec/support/v5.0/db/schema.rb +9 -4
  51. data/spec/support/v5.0/sorbet_test_cases.rb +11 -0
  52. data/spec/support/v5.1/Gemfile.lock +6 -6
  53. data/spec/support/v5.1/app/mailers/daily_prophet_mailer.rb +9 -0
  54. data/spec/support/v5.1/app/mailers/hogwarts_acceptance_mailer.rb +13 -0
  55. data/spec/support/v5.1/app/models/wizard.rb +33 -0
  56. data/spec/support/v5.1/db/migrate/20190620000001_create_wizards.rb +1 -1
  57. data/spec/support/v5.1/db/migrate/20190620000002_create_wands.rb +1 -1
  58. data/spec/support/v5.1/db/migrate/20190620000003_create_spell_books.rb +1 -1
  59. data/spec/support/v5.1/db/migrate/20190620000004_add_more_column_types_to_wands.rb +1 -1
  60. data/spec/support/v5.1/db/migrate/20190620000005_add_broom_to_wizard.rb +6 -0
  61. data/spec/support/v5.1/db/migrate/20190620000006_add_more_enums_to_wizard.rb +9 -0
  62. data/spec/support/v5.1/db/schema.rb +7 -2
  63. data/spec/support/v5.1/sorbet_test_cases.rb +11 -0
  64. data/spec/support/v5.2/Gemfile.lock +6 -6
  65. data/spec/support/v5.2/app/mailers/daily_prophet_mailer.rb +9 -0
  66. data/spec/support/v5.2/app/mailers/hogwarts_acceptance_mailer.rb +13 -0
  67. data/spec/support/v5.2/app/models/wizard.rb +34 -0
  68. data/spec/support/v5.2/db/migrate/20190620000005_add_broom_to_wizard.rb +6 -0
  69. data/spec/support/v5.2/db/migrate/20190620000006_add_more_enums_to_wizard.rb +9 -0
  70. data/spec/support/v5.2/db/schema.rb +7 -2
  71. data/spec/support/v5.2/sorbet_test_cases.rb +11 -0
  72. data/spec/support/v6.0/Gemfile.lock +6 -6
  73. data/spec/support/v6.0/app/mailers/daily_prophet_mailer.rb +9 -0
  74. data/spec/support/v6.0/app/mailers/hogwarts_acceptance_mailer.rb +13 -0
  75. data/spec/support/v6.0/app/models/wizard.rb +35 -1
  76. data/spec/support/v6.0/db/migrate/20190620000005_add_broom_to_wizard.rb +6 -0
  77. data/spec/support/v6.0/db/migrate/20190620000006_add_more_enums_to_wizard.rb +9 -0
  78. data/spec/support/v6.0/db/schema.rb +6 -1
  79. data/spec/support/v6.0/sorbet_test_cases.rb +11 -0
  80. data/spec/test_data/v4.2/expected_application_mailer.rbi +5 -0
  81. data/spec/test_data/v4.2/expected_daily_prophet_mailer.rbi +7 -0
  82. data/spec/test_data/v4.2/expected_helpers_with_application_and_devise_helpers.rbi +29 -0
  83. data/spec/test_data/v4.2/expected_hogwarts_acceptance_mailer.rbi +10 -0
  84. data/spec/test_data/v4.2/expected_srb_tc_output.txt +57 -1
  85. data/spec/test_data/v4.2/expected_wizard.rbi +69 -0
  86. data/spec/test_data/v4.2/expected_wizard_wo_spellbook.rbi +69 -0
  87. data/spec/test_data/v5.0/expected_application_mailer.rbi +5 -0
  88. data/spec/test_data/v5.0/expected_daily_prophet_mailer.rbi +7 -0
  89. data/spec/test_data/v5.0/expected_helpers_with_application_and_devise_helpers.rbi +29 -0
  90. data/spec/test_data/v5.0/expected_hogwarts_acceptance_mailer.rbi +10 -0
  91. data/spec/test_data/v5.0/expected_spell_book.rbi +2 -2
  92. data/spec/test_data/v5.0/expected_wizard.rbi +294 -0
  93. data/spec/test_data/v5.0/expected_wizard_wo_spellbook.rbi +294 -0
  94. data/spec/test_data/v5.1/expected_application_mailer.rbi +5 -0
  95. data/spec/test_data/v5.1/expected_daily_prophet_mailer.rbi +7 -0
  96. data/spec/test_data/v5.1/expected_helpers_with_application_and_devise_helpers.rbi +29 -0
  97. data/spec/test_data/v5.1/expected_hogwarts_acceptance_mailer.rbi +10 -0
  98. data/spec/test_data/v5.1/expected_spell_book.rbi +2 -2
  99. data/spec/test_data/v5.1/expected_wizard.rbi +294 -0
  100. data/spec/test_data/v5.1/expected_wizard_wo_spellbook.rbi +294 -0
  101. data/spec/test_data/v5.2-no-sorbet/expected_helpers_with_application_and_devise_helpers.rbi +29 -0
  102. data/spec/test_data/v5.2/expected_application_mailer.rbi +5 -0
  103. data/spec/test_data/v5.2/expected_attachment.rbi +4 -4
  104. data/spec/test_data/v5.2/expected_daily_prophet_mailer.rbi +7 -0
  105. data/spec/test_data/v5.2/expected_helpers_with_application_and_devise_helpers.rbi +29 -0
  106. data/spec/test_data/v5.2/expected_hogwarts_acceptance_mailer.rbi +10 -0
  107. data/spec/test_data/v5.2/expected_spell_book.rbi +2 -2
  108. data/spec/test_data/v5.2/expected_wizard.rbi +342 -0
  109. data/spec/test_data/v5.2/expected_wizard_wo_spellbook.rbi +342 -0
  110. data/spec/test_data/v6.0/expected_application_mailer.rbi +5 -0
  111. data/spec/test_data/v6.0/expected_attachment.rbi +4 -4
  112. data/spec/test_data/v6.0/expected_blob.rbi +28 -22
  113. data/spec/test_data/v6.0/expected_daily_prophet_mailer.rbi +7 -0
  114. data/spec/test_data/v6.0/expected_helpers_with_application_and_devise_helpers.rbi +29 -0
  115. data/spec/test_data/v6.0/expected_hogwarts_acceptance_mailer.rbi +10 -0
  116. data/spec/test_data/v6.0/expected_spell_book.rbi +2 -2
  117. data/spec/test_data/v6.0/expected_wizard.rbi +526 -16
  118. data/spec/test_data/v6.0/expected_wizard_wo_spellbook.rbi +526 -16
  119. metadata +94 -1
@@ -1,6 +1,7 @@
1
1
  require("sorbet-rails/model_rbi_formatter")
2
2
  require("sorbet-rails/routes_rbi_formatter")
3
3
  require("sorbet-rails/helper_rbi_formatter")
4
+ require("sorbet-rails/mailer_rbi_formatter")
4
5
  require("sorbet-rails/utils")
5
6
 
6
7
  namespace :rails_rbi do
@@ -9,6 +10,7 @@ namespace :rails_rbi do
9
10
  Rake::Task['rails_rbi:routes'].invoke
10
11
  Rake::Task['rails_rbi:models'].invoke
11
12
  Rake::Task['rails_rbi:helpers'].invoke
13
+ Rake::Task['rails_rbi:mailers'].invoke
12
14
  end
13
15
 
14
16
  desc "Generate rbis for rails routes"
@@ -64,6 +66,24 @@ namespace :rails_rbi do
64
66
  end
65
67
  end
66
68
 
69
+ desc "Generate rbis for rails mailers"
70
+ task :mailers, [:root_dir] => :environment do |t, args|
71
+ SorbetRails::Utils.rails_eager_load_all!
72
+ all_mailers = ActionMailer::Base.descendants
73
+
74
+ all_mailers.each do |mailer_class|
75
+ file_path = Rails.root.join(
76
+ "sorbet",
77
+ "rails-rbi",
78
+ "mailers",
79
+ "#{mailer_class.name.underscore}.rbi",
80
+ )
81
+ formatter = SorbetRails::MailerRbiFormatter.new(mailer_class)
82
+ FileUtils.mkdir_p(File.dirname(file_path))
83
+ File.write(file_path, formatter.generate_rbi)
84
+ end
85
+ end
86
+
67
87
  def generate_rbis_for_models(model_classes, available_classes)
68
88
  available_class_names = Set.new(available_classes.map { |c| c.name })
69
89
  formatted = model_classes.map do |model_class|
@@ -12,4 +12,9 @@ module SorbetRails::Utils
12
12
  # But this is not applied to Rails.application.eager_load! method
13
13
  Zeitwerk::Loader.eager_load_all if defined?(Zeitwerk)
14
14
  end
15
+
16
+ sig { params(method_name: String).returns(T::Boolean) }
17
+ def self.valid_method_name?(method_name)
18
+ !!method_name.match(/^[A-z][A-z0-9_]*[!?=]?$/)
19
+ end
15
20
  end
data/sorbet-rails.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = %q{sorbet-rails}
3
- s.version = "0.5.3"
3
+ s.version = "0.5.4"
4
4
  s.date = %q{2019-04-18}
5
5
  s.summary = %q{Set of tools to make Sorbet work with Rails seamlessly.}
6
6
  s.authors = ["Chan Zuckerberg Initiative"]
@@ -114,23 +114,95 @@ def create_models
114
114
  end
115
115
  RUBY
116
116
 
117
- file "app/models/wizard.rb", <<~RUBY
118
- class Wizard < ApplicationRecord
119
- validates :name, length: { minimum: 5 }, presence: true
120
-
121
- enum house: {
122
- Gryffindor: 0,
123
- Hufflepuff: 1,
124
- Ravenclaw: 2,
125
- Slytherin: 3,
126
- }
127
-
128
- has_one :wand
129
- has_many :spell_books
117
+ # A nasty hack to add has_one_attached and has_many_attached to the models/wizard.rb file.
118
+ attachments = nil
119
+ if ['5.2', '6.0'].include?(ENV["RAILS_VERSION"])
120
+ attachments = "has_one_attached :school_photo\n has_many_attached :hats"
121
+ end
130
122
 
131
- scope :recent, -> { where('created_at > ?', 1.month.ago) }
132
- end
133
- RUBY
123
+ if ENV["RAILS_VERSION"] == "4.2"
124
+ file "app/models/wizard.rb", <<~RUBY
125
+ class Wizard < ApplicationRecord
126
+ validates :name, length: { minimum: 5 }, presence: true
127
+
128
+ enum house: {
129
+ Gryffindor: 0,
130
+ Hufflepuff: 1,
131
+ Ravenclaw: 2,
132
+ Slytherin: 3,
133
+ }
134
+
135
+ enum professor: {
136
+ "Severus Snape": 0,
137
+ "Minerva McGonagall": 1,
138
+ "Pomona Sprout": 2,
139
+ "Filius Flitwick": 3,
140
+ "Hagrid": 4,
141
+ }
142
+
143
+ enum broom: {
144
+ nimbus: 'nimbus',
145
+ firebolt: 'firebolt',
146
+ }
147
+
148
+ has_one :wand
149
+ has_many :spell_books
150
+
151
+ scope :recent, -> { where('created_at > ?', 1.month.ago) }
152
+ end
153
+ RUBY
154
+ else
155
+ file "app/models/wizard.rb", <<~RUBY
156
+ class Wizard < ApplicationRecord
157
+ validates :name, length: { minimum: 5 }, presence: true
158
+
159
+ enum house: {
160
+ Gryffindor: 0,
161
+ Hufflepuff: 1,
162
+ Ravenclaw: 2,
163
+ Slytherin: 3,
164
+ }
165
+
166
+ enum professor: {
167
+ "Severus Snape": 0,
168
+ "Minerva McGonagall": 1,
169
+ "Pomona Sprout": 2,
170
+ "Filius Flitwick": 3,
171
+ "Hagrid": 4,
172
+ }
173
+
174
+ enum broom: {
175
+ nimbus: 'nimbus',
176
+ firebolt: 'firebolt',
177
+ }, _prefix: true
178
+
179
+ enum quidditch_position: {
180
+ keeper: 0,
181
+ seeker: 1,
182
+ beater: 2,
183
+ chaser: 3,
184
+ }, _prefix: :quidditch
185
+
186
+ enum hair_color: {
187
+ brown: 0,
188
+ black: 1,
189
+ blonde: 2,
190
+ }, _suffix: :hair
191
+
192
+ enum eye_color: {
193
+ brown: 0,
194
+ green: 1,
195
+ blue: 2,
196
+ }, _prefix: :color, _suffix: :eyes
197
+
198
+ has_one :wand
199
+ has_many :spell_books
200
+
201
+ scope :recent, -> { where('created_at > ?', 1.month.ago) }
202
+ #{attachments}
203
+ end
204
+ RUBY
205
+ end
134
206
 
135
207
  file "app/models/concerns/mythical.rb", <<~RUBY
136
208
  require 'active_support/concern'
@@ -211,6 +283,62 @@ def create_migrations
211
283
  end
212
284
  end
213
285
  RUBY
286
+
287
+ file "db/migrate/20190620000005_add_broom_to_wizard.rb", <<~RUBY
288
+ class AddBroomToWizard < #{migration_superclass}
289
+ def change
290
+ add_column :wizards, :broom, :string
291
+ end
292
+ end
293
+ RUBY
294
+
295
+ if ENV["RAILS_VERSION"] != "4.2"
296
+ file "db/migrate/20190620000006_add_more_enums_to_wizard.rb", <<~RUBY
297
+ class AddMoreEnumsToWizard < #{migration_superclass}
298
+ def change
299
+ add_column :wizards, :quidditch_position, :integer
300
+ add_column :wizards, :hair_color, :integer
301
+ add_column :wizards, :eye_color, :integer
302
+ add_column :wizards, :hair_length, :integer
303
+ end
304
+ end
305
+ RUBY
306
+ end
307
+ end
308
+
309
+ def create_mailers
310
+ if ENV['RAILS_VERSION'] == '4.2'
311
+ file "app/mailers/application_mailer.rb", <<~RUBY
312
+ class ApplicationMailer < ActionMailer::Base
313
+ end
314
+ RUBY
315
+ end
316
+
317
+ file "app/mailers/hogwarts_acceptance_mailer.rb", <<~RUBY
318
+ class HogwartsAcceptanceMailer < ApplicationMailer
319
+ extend T::Sig
320
+
321
+ sig { params(student: Wizard).void }
322
+ def notify(student)
323
+ # TODO: mail acceptance letter to student
324
+ end
325
+
326
+ def notify_retry(student)
327
+ # TODO: send more owls!!
328
+ end
329
+ end
330
+ RUBY
331
+
332
+ file "app/mailers/daily_prophet_mailer.rb", <<~RUBY
333
+ class DailyProphetMailer < ApplicationMailer
334
+ extend T::Sig
335
+
336
+ sig { params(wizards: T::Array[Wizard], hotnews_only: T::Boolean).void }
337
+ def notify_subscribers(wizards:, hotnews_only:)
338
+ # TODO: mail the latest news to wizards!
339
+ end
340
+ end
341
+ RUBY
214
342
  end
215
343
 
216
344
  def add_sorbet_test_files
@@ -254,6 +382,7 @@ after_bundle do
254
382
  create_helpers
255
383
  create_models
256
384
  create_migrations
385
+ create_mailers
257
386
  add_sorbet_test_files
258
387
 
259
388
  bundle_version = ENV["RAILS_VERSION"] == "4.2" ? "_1.17.3_" : ""
@@ -153,9 +153,20 @@ T.assert_type!(wizard.notes, T.nilable(String))
153
153
  T.assert_type!(wizard.Gryffindor?, T::Boolean)
154
154
  T.assert_type!(wizard.house, T.nilable(String))
155
155
  T.assert_type!(Wizard.houses, T::Hash[T.any(String, Symbol), Integer])
156
+ T.assert_type!(Wizard.brooms, T::Hash[T.any(String, Symbol), String])
156
157
  T.assert_type!(spell_book.biology?, T::Boolean)
157
158
  T.assert_type!(spell_book.book_type, String)
158
159
  T.assert_type!(SpellBook.book_types, T::Hash[T.any(String, Symbol), Integer])
159
160
 
160
161
  # Mythical plugin
161
162
  T.assert_type!(Wand.mythicals, T::Array[Wand])
163
+
164
+ T.assert_type!(HogwartsAcceptanceMailer.notify(wizard), ActionMailer::MessageDelivery)
165
+
166
+ if ENV["RAILS_VERSION"] != "4.2"
167
+ T.assert_type!(wizard.broom_nimbus?, T::Boolean)
168
+ T.assert_type!(wizard.color_brown_eyes?, T::Boolean)
169
+ T.assert_type!(wizard.quidditch_keeper?, T::Boolean)
170
+ T.assert_type!(wizard.brown_hair?, T::Boolean)
171
+ end
172
+
@@ -2,6 +2,12 @@ require 'rails_helper'
2
2
  require 'sorbet-rails/helper_rbi_formatter'
3
3
 
4
4
  RSpec.describe SorbetRails::HelperRbiFormatter do
5
+ after(:each) do
6
+ # reset config after each test
7
+ SorbetRails.configure do |config|
8
+ config.extra_helper_includes = []
9
+ end
10
+ end
5
11
 
6
12
  it 'returns the expected rbi for a given array of helpers' do
7
13
  formatter = SorbetRails::HelperRbiFormatter.new([ApplicationHelper, BarHelper, BazHelper, FooHelper])
@@ -10,4 +16,17 @@ RSpec.describe SorbetRails::HelperRbiFormatter do
10
16
  'expected_helpers.rbi'
11
17
  )
12
18
  end
19
+
20
+ it 'returns the expected rbi for a given array of helpers, with additional includes added where appropriate' do
21
+ SorbetRails.configure do |config|
22
+ config.extra_helper_includes = ['ApplicationHelper', 'DeviseHelper']
23
+ end
24
+
25
+ # ApplicationHelper won't get included in itself, but will in others. DeviseHelper will be included in all helpers.
26
+ formatter = SorbetRails::HelperRbiFormatter.new([ApplicationHelper, BarHelper, BazHelper, FooHelper])
27
+ expect_match_file(
28
+ formatter.generate_rbi,
29
+ 'expected_helpers_with_application_and_devise_helpers.rbi'
30
+ )
31
+ end
13
32
  end
@@ -0,0 +1,13 @@
1
+ require 'rails_helper'
2
+ require 'sorbet-rails/mailer_rbi_formatter'
3
+
4
+ RSpec.describe SorbetRails::MailerRbiFormatter do
5
+
6
+ it 'returns the expected rbi for a given array of helpers' do
7
+ formatter = SorbetRails::MailerRbiFormatter.new(HogwartsAcceptanceMailer)
8
+ expect_match_file(
9
+ formatter.generate_rbi,
10
+ 'expected_hogwarts_acceptance_mailer.rbi'
11
+ )
12
+ end
13
+ end
@@ -12,7 +12,12 @@ RSpec.describe SorbetRails::ModelRbiFormatter do
12
12
  end
13
13
 
14
14
  it 'generates correct rbi file for Wizard' do
15
- formatter = SorbetRails::ModelRbiFormatter.new(Wizard, Set.new(['Wizard', 'Wand', 'SpellBook']))
15
+ class_set = Set.new(['Wizard', 'Wand', 'SpellBook'])
16
+ if ['5.2', '6.0'].include?(ENV['RAILS_VERSION'])
17
+ class_set << 'ActiveStorage::Attachment'
18
+ class_set << 'ActiveStorage::Blob'
19
+ end
20
+ formatter = SorbetRails::ModelRbiFormatter.new(Wizard, class_set)
16
21
  expect_match_file(
17
22
  formatter.generate_rbi,
18
23
  'expected_wizard.rbi',
@@ -29,7 +34,12 @@ RSpec.describe SorbetRails::ModelRbiFormatter do
29
34
 
30
35
  context 'there is a hidden model' do
31
36
  it 'fallbacks to use ActiveRecord::Relation' do
32
- formatter = SorbetRails::ModelRbiFormatter.new(Wizard, Set.new(['Wizard', 'Wand']))
37
+ class_set = Set.new(['Wizard', 'Wand'])
38
+ if ['5.2', '6.0'].include?(ENV['RAILS_VERSION'])
39
+ class_set << 'ActiveStorage::Attachment'
40
+ class_set << 'ActiveStorage::Blob'
41
+ end
42
+ formatter = SorbetRails::ModelRbiFormatter.new(Wizard, class_set)
33
43
  expect_match_file(
34
44
  formatter.generate_rbi,
35
45
  'expected_wizard_wo_spellbook.rbi',
data/spec/rails_helper.rb CHANGED
@@ -68,3 +68,23 @@ def expect_match_file(content, file_path)
68
68
  expected_value = File.read(relative_path)
69
69
  expect(content).to eql(expected_value)
70
70
  end
71
+
72
+ def expect_files(base_dir:, files:)
73
+ rbi_files = Dir[File.join(base_dir, "*.rbi")]
74
+
75
+ # smoke test
76
+ expect(rbi_files.size).to eql(files.size)
77
+
78
+ # check we generate correct content
79
+ generated_files = rbi_files.map do |file_path|
80
+ pathname = Pathname.new(file_path)
81
+ generated = File.read(file_path)
82
+ # expect there is the same file in test_data folder
83
+ path_from_dir = pathname.relative_path_from(base_dir).to_path
84
+ expect_match_file(generated, "expected_#{path_from_dir}")
85
+ path_from_dir
86
+ end
87
+
88
+ # double check we generate correct files
89
+ expect(generated_files.sort).to eql(files)
90
+ end
@@ -0,0 +1,21 @@
1
+ require 'rails_helper'
2
+
3
+ RSpec.describe 'rake rails_rbi:mailers', type: :task do
4
+ let!(:generated_dir_path) { Rails.root.join("sorbet", "rails-rbi", "mailers") }
5
+
6
+ it "preloads the Rails environment" do
7
+ expect(task.prerequisites).to include("environment")
8
+ end
9
+
10
+ it "generates mailers rbi correctly" do
11
+ task.invoke
12
+ expect_files(
13
+ base_dir: generated_dir_path,
14
+ files: [
15
+ "application_mailer.rbi",
16
+ "daily_prophet_mailer.rbi",
17
+ "hogwarts_acceptance_mailer.rbi",
18
+ ],
19
+ )
20
+ end
21
+ end
@@ -49,7 +49,7 @@ RSpec.describe 'rake rails_rbi:models', type: :task do
49
49
  end
50
50
 
51
51
  it 'generates more than 1 selected models correctly' do
52
- task.invoke("Wizard","SpellBook")
52
+ task.invoke("Wizard", "SpellBook")
53
53
  expect_files(
54
54
  base_dir: generated_dir_path,
55
55
  files: [
@@ -58,24 +58,4 @@ RSpec.describe 'rake rails_rbi:models', type: :task do
58
58
  ]
59
59
  )
60
60
  end
61
-
62
- def expect_files(base_dir:, files:)
63
- rbi_files = Dir[File.join(base_dir, "*.rbi")]
64
-
65
- # smoke test
66
- expect(rbi_files.size).to eql(files.size)
67
-
68
- # check we generate correct content
69
- generated_files = rbi_files.map do |file_path|
70
- pathname = Pathname.new(file_path)
71
- generated = File.read(file_path)
72
- # expect there is the same file in test_data folder
73
- path_from_dir = pathname.relative_path_from(base_dir).to_path
74
- expect_match_file(generated, "expected_#{path_from_dir}")
75
- path_from_dir
76
- end
77
-
78
- # double check we generate correct files
79
- expect(generated_files.sort).to eql(files)
80
- end
81
61
  end
data/spec/sorbet_spec.rb CHANGED
@@ -30,6 +30,19 @@ RSpec.describe 'sorbet' do
30
30
  chdir: Rails.root.to_path,
31
31
  )
32
32
 
33
+ stdout, stderr, status = Open3.capture3(
34
+ 'bundle', 'exec', 'srb', 'tc',
35
+ chdir: Rails.root.to_path,
36
+ )
37
+
38
+ if status != 0
39
+ puts "================================="
40
+ puts "stdout #{stdout}"
41
+ puts "stderr #{stderr}"
42
+ puts "srb-init doesn't produce clean build: #{status}. This will affect sorbet tests."
43
+ puts "================================="
44
+ end
45
+
33
46
  # run sorbet-rails rake tasks
34
47
  Rake::Task['rails_rbi:all'].invoke
35
48
 
@@ -0,0 +1,126 @@
1
+ require 'rails_helper'
2
+ require 'sorbet-runtime'
3
+ require 'sorbet-rails/sorbet_utils'
4
+
5
+ module SorbetUtilsExampleModule
6
+ end
7
+
8
+ class SorbetUtilsExampleClass
9
+ extend T::Sig
10
+
11
+ sig { void }
12
+ def no_param_method; end
13
+
14
+ sig { params(p1: String, p2: T.class_of(String)).void }
15
+ def method_req_args(p1, p2); end
16
+
17
+ sig { params(p1: T.any(String, Integer), p2: T.nilable(String)).void }
18
+ def method_req_kwarg(p1:, p2:); end
19
+
20
+ sig { params(p1: SorbetUtilsExampleModule, p2: T.nilable(T.any(String, Integer))).void }
21
+ def method_mixed(p1, p2:); end
22
+
23
+ sig { params(p1: T::Hash[String, T.untyped], p2: Integer).void }
24
+ def method_with_rest(p1, *p2); end
25
+
26
+ sig { params(p1: T::Array[String], p2: T::Set[Integer], p3: Integer).void }
27
+ def method_with_keyrest(p1, p2:, **p3); end
28
+
29
+ sig { params(p1: String, p2: T.proc.params(p3: T.class_of(String)).void).void }
30
+ def method_with_block(*p1, &p2); end
31
+
32
+ sig { params(p1: T.proc.params(p3: T.class_of(String)).returns(T.nilable(String))).void }
33
+ def method_with_block_return(&p1); end
34
+
35
+ def method_without_sig(p1, p2, *p3, p4:, **p5, &p6); end
36
+ end
37
+
38
+ RSpec.describe SorbetRails::SorbetUtils do
39
+ Parameter = ::Parlour::RbiGenerator::Parameter
40
+
41
+ it 'works with no_param_method' do
42
+ method_def = SorbetUtilsExampleClass.instance_method(:no_param_method)
43
+ parameters = SorbetRails::SorbetUtils.parameters_from_method_def(method_def)
44
+ expect(parameters).to match_array([])
45
+ end
46
+
47
+ it 'works with method_req_args' do
48
+ method_def = SorbetUtilsExampleClass.instance_method(:method_req_args)
49
+ parameters = SorbetRails::SorbetUtils.parameters_from_method_def(method_def)
50
+ expect(parameters).to match_array([
51
+ Parameter.new('p1', type: 'String'),
52
+ Parameter.new('p2', type: 'T.class_of(String)'),
53
+ ])
54
+ end
55
+
56
+ it 'works with method_req_kwarg' do
57
+ method_def = SorbetUtilsExampleClass.instance_method(:method_req_kwarg)
58
+ parameters = SorbetRails::SorbetUtils.parameters_from_method_def(method_def)
59
+ expect(parameters).to match_array([
60
+ Parameter.new('p1', type: 'T.any(Integer, String)'), # sorbet re-order the types
61
+ Parameter.new('p2', type: 'T.nilable(String)'),
62
+ ])
63
+ end
64
+
65
+ it 'works with method_mixed' do
66
+ method_def = SorbetUtilsExampleClass.instance_method(:method_mixed)
67
+ parameters = SorbetRails::SorbetUtils.parameters_from_method_def(method_def)
68
+ expect(parameters).to match_array([
69
+ Parameter.new('p1', type: 'SorbetUtilsExampleModule'),
70
+ Parameter.new('p2', type: 'T.nilable(T.any(Integer, String))'),
71
+ ])
72
+ end
73
+
74
+ it 'works with method_with_rest' do
75
+ method_def = SorbetUtilsExampleClass.instance_method(:method_with_rest)
76
+ parameters = SorbetRails::SorbetUtils.parameters_from_method_def(method_def)
77
+ expect(parameters).to match_array([
78
+ Parameter.new('p1', type: 'T::Hash[String, T.untyped]'),
79
+ Parameter.new('*p2', type: 'Integer'),
80
+ ])
81
+ end
82
+
83
+ it 'works with method_with_keyrest' do
84
+ method_def = SorbetUtilsExampleClass.instance_method(:method_with_keyrest)
85
+ parameters = SorbetRails::SorbetUtils.parameters_from_method_def(method_def)
86
+ expect(parameters).to match_array([
87
+ Parameter.new('p1', type: 'T::Array[String]'),
88
+ Parameter.new('p2', type: 'T::Set[Integer]'),
89
+ Parameter.new('**p3', type: 'Integer'),
90
+ ])
91
+ end
92
+
93
+ it 'works with method_with_block' do
94
+ method_def = SorbetUtilsExampleClass.instance_method(:method_with_block)
95
+ parameters = SorbetRails::SorbetUtils.parameters_from_method_def(method_def)
96
+ expect(parameters).to match_array([
97
+ Parameter.new('*p1', type: 'String'),
98
+ Parameter.new('&p2', type: 'T.proc.params(p3: T.class_of(String)).void'),
99
+ ])
100
+ end
101
+
102
+ it 'works with method_with_block_return' do
103
+ method_def = SorbetUtilsExampleClass.instance_method(:method_with_block_return)
104
+ parameters = SorbetRails::SorbetUtils.parameters_from_method_def(method_def)
105
+ expect(parameters).to match_array([
106
+ Parameter.new(
107
+ '&p1',
108
+ type: 'T.proc.params(p3: T.class_of(String)).returns(T.nilable(String))',
109
+ ),
110
+ ])
111
+ end
112
+
113
+ it 'works with method_without_sig' do
114
+ method_def = SorbetUtilsExampleClass.instance_method(:method_without_sig)
115
+ parameters = SorbetRails::SorbetUtils.parameters_from_method_def(method_def)
116
+ # method_without_sig(p1, p2, *p3, p4:, **p5, &p6); end
117
+ expect(parameters).to match_array([
118
+ Parameter.new('p1', type: 'T.untyped'),
119
+ Parameter.new('p2', type: 'T.untyped'),
120
+ Parameter.new('*p3', type: 'T.untyped'),
121
+ Parameter.new('p4', type: 'T.untyped'),
122
+ Parameter.new('**p5', type: 'T.untyped'),
123
+ Parameter.new('&p6', type: 'T.untyped'),
124
+ ])
125
+ end
126
+ end