sorbet-rails 0.6.5.1 → 0.7.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (199) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +1 -2
  3. data/.gitignore +2 -1
  4. data/.travis.yml +1 -1
  5. data/README.md +66 -13
  6. data/Rakefile +3 -3
  7. data/lib/bundled_rbi/customizabel_rbi_formatter.rbi +29 -0
  8. data/lib/bundled_rbi/pluck_to_tstruct.rbi +2 -1
  9. data/lib/bundled_rbi/typed_enum.rbi +7 -0
  10. data/lib/sorbet-rails.rb +1 -3
  11. data/lib/sorbet-rails/active_record_rbi_formatter.rb +6 -6
  12. data/lib/sorbet-rails/config.rb +12 -0
  13. data/lib/sorbet-rails/dependent_gem_rbis/activerecord.rbi +3 -0
  14. data/lib/sorbet-rails/dependent_gem_rbis/parlour.rbi +1896 -0
  15. data/lib/sorbet-rails/deprecation.rb +1 -0
  16. data/lib/sorbet-rails/gem_plugins/attr_json_plugin.rb +137 -0
  17. data/lib/sorbet-rails/gem_plugins/flag_shih_tzu_plugin.rb +201 -0
  18. data/lib/sorbet-rails/gem_plugins/kaminari_plugin.rb +28 -0
  19. data/lib/sorbet-rails/gem_plugins/shrine_plugin.rb +1 -1
  20. data/lib/sorbet-rails/helper_rbi_formatter.rb +1 -1
  21. data/lib/sorbet-rails/job_rbi_formatter.rb +75 -62
  22. data/lib/sorbet-rails/mailer_rbi_formatter.rb +40 -27
  23. data/lib/sorbet-rails/model_column_utils.rb +129 -0
  24. data/lib/sorbet-rails/model_plugins/active_record_assoc.rb +40 -1
  25. data/lib/sorbet-rails/model_plugins/active_record_attribute.rb +12 -112
  26. data/lib/sorbet-rails/model_plugins/active_record_serialized_attribute.rb +68 -0
  27. data/lib/sorbet-rails/model_plugins/base.rb +6 -8
  28. data/lib/sorbet-rails/model_plugins/plugins.rb +9 -0
  29. data/lib/sorbet-rails/model_rbi_formatter.rb +3 -3
  30. data/lib/sorbet-rails/model_utils.rb +5 -2
  31. data/lib/sorbet-rails/rails_mixins/generated_url_helpers.rb +2 -3
  32. data/lib/sorbet-rails/rails_mixins/pluck_to_tstruct.rb +54 -7
  33. data/lib/sorbet-rails/railtie.rb +0 -2
  34. data/lib/sorbet-rails/routes_rbi_formatter.rb +6 -2
  35. data/lib/sorbet-rails/sorbet_utils.rb +152 -150
  36. data/lib/sorbet-rails/tasks/rails_rbi.rake +9 -8
  37. data/sorbet-rails.gemspec +2 -2
  38. data/spec/bin/run_spec.sh +1 -1
  39. data/spec/generators/rails-template.rb +16 -0
  40. data/spec/generators/sorbet_test_cases.rb +28 -56
  41. data/spec/job_rbi_formatter_spec.rb +1 -1
  42. data/spec/pluck_to_tstruct_spec.rb +115 -16
  43. data/spec/rails_helper.rb +3 -7
  44. data/spec/rake_rails_rbi_jobs_spec.rb +20 -0
  45. data/spec/rake_rails_rbi_mailers_spec.rb +21 -0
  46. data/spec/sorbet_spec.rb +5 -5
  47. data/spec/support/v5.0/Gemfile +1 -1
  48. data/spec/support/v5.0/Gemfile.lock +23 -19
  49. data/spec/support/v5.0/app/controllers/application_controller.rb +1 -1
  50. data/spec/support/v5.0/app/models/headmaster.rb +1 -1
  51. data/spec/support/v5.0/app/models/potion.rb +1 -1
  52. data/spec/support/v5.0/app/models/robe.rb +1 -1
  53. data/spec/support/v5.0/app/models/school.rb +1 -1
  54. data/spec/support/v5.0/app/models/spell.rb +1 -1
  55. data/spec/support/v5.0/app/models/subject.rb +1 -1
  56. data/spec/support/v5.0/app/models/wizard.rb +5 -0
  57. data/spec/support/v5.0/config/environments/development.rb +1 -1
  58. data/spec/support/v5.0/config/environments/production.rb +1 -1
  59. data/spec/support/v5.0/config/environments/test.rb +1 -1
  60. data/spec/support/v5.0/config/routes.rb +1 -1
  61. data/spec/support/v5.0/db/migrate/20190620000015_add_serialized_to_wizards.rb +9 -0
  62. data/spec/support/v5.0/db/schema.rb +8 -4
  63. data/spec/support/v5.0/sorbet_test_cases.rb +28 -56
  64. data/spec/support/v5.1/Gemfile.lock +21 -17
  65. data/spec/support/v5.1/app/controllers/application_controller.rb +1 -1
  66. data/spec/support/v5.1/app/models/headmaster.rb +1 -1
  67. data/spec/support/v5.1/app/models/school.rb +1 -1
  68. data/spec/support/v5.1/app/models/wizard.rb +5 -0
  69. data/spec/support/v5.1/config/environments/production.rb +1 -1
  70. data/spec/support/v5.1/config/routes.rb +1 -1
  71. data/spec/support/v5.1/db/migrate/20190620000015_add_serialized_to_wizards.rb +9 -0
  72. data/spec/support/v5.1/db/schema.rb +5 -1
  73. data/spec/support/v5.1/sorbet_test_cases.rb +28 -56
  74. data/spec/support/v5.2/Gemfile +1 -1
  75. data/spec/support/v5.2/Gemfile.lock +23 -19
  76. data/spec/support/v5.2/app/models/headmaster.rb +1 -1
  77. data/spec/support/v5.2/app/models/school.rb +1 -1
  78. data/spec/support/v5.2/app/models/wizard.rb +5 -0
  79. data/spec/support/v5.2/config/environments/development.rb +1 -1
  80. data/spec/support/v5.2/config/environments/production.rb +1 -1
  81. data/spec/support/v5.2/config/environments/test.rb +1 -1
  82. data/spec/support/v5.2/config/routes.rb +1 -1
  83. data/spec/support/v5.2/db/migrate/20190620000015_add_serialized_to_wizards.rb +9 -0
  84. data/spec/support/v5.2/db/schema.rb +5 -1
  85. data/spec/support/v5.2/sorbet_test_cases.rb +28 -56
  86. data/spec/support/v6.0/.gitignore +6 -0
  87. data/spec/support/v6.0/Gemfile +3 -3
  88. data/spec/support/v6.0/Gemfile.lock +89 -84
  89. data/spec/support/v6.0/app/models/wizard.rb +5 -0
  90. data/spec/support/v6.0/bin/bundle +22 -13
  91. data/spec/support/v6.0/config/environments/development.rb +1 -1
  92. data/spec/support/v6.0/config/environments/production.rb +1 -1
  93. data/spec/support/v6.0/config/environments/test.rb +2 -2
  94. data/spec/support/v6.0/config/routes.rb +1 -1
  95. data/spec/support/v6.0/db/migrate/20190620000015_add_serialized_to_wizards.rb +9 -0
  96. data/spec/support/v6.0/db/schema.rb +5 -1
  97. data/spec/support/v6.0/sorbet_test_cases.rb +28 -56
  98. data/spec/support/v6.0/tmp/pids/.keep +0 -0
  99. data/spec/test_data/v5.0/expected_active_record_relation.rbi +0 -1
  100. data/spec/test_data/v5.0/expected_application_job.rbi +2 -2
  101. data/spec/test_data/v5.0/expected_award_house_point_hourglasses.rbi +2 -2
  102. data/spec/test_data/v5.0/expected_custom_application_job.rbi +21 -0
  103. data/spec/test_data/v5.0/expected_custom_application_mailer.rbi +6 -0
  104. data/spec/test_data/v5.0/expected_custom_award_house_point_hourglasses.rbi +21 -0
  105. data/spec/test_data/v5.0/expected_custom_daily_prophet_mailer.rbi +8 -0
  106. data/spec/test_data/v5.0/expected_custom_hogwarts_acceptance_mailer.rbi +21 -0
  107. data/spec/test_data/v5.0/expected_headmaster.rbi +24 -0
  108. data/spec/test_data/v5.0/expected_potion.rbi +12 -0
  109. data/spec/test_data/v5.0/expected_robe.rbi +12 -0
  110. data/spec/test_data/v5.0/expected_routes.rbi +4 -0
  111. data/spec/test_data/v5.0/expected_school.rbi +12 -0
  112. data/spec/test_data/v5.0/expected_spell/habtm_spell_books.rbi +24 -0
  113. data/spec/test_data/v5.0/expected_spell_book.rbi +21 -9
  114. data/spec/test_data/v5.0/expected_spell_book/habtm_spells.rbi +24 -0
  115. data/spec/test_data/v5.0/expected_squib.rbi +63 -0
  116. data/spec/test_data/v5.0/expected_subject/habtm_wizards.rbi +24 -0
  117. data/spec/test_data/v5.0/expected_wand.rbi +22 -10
  118. data/spec/test_data/v5.0/expected_wizard.rbi +121 -58
  119. data/spec/test_data/v5.0/expected_wizard/habtm_subjects.rbi +24 -0
  120. data/spec/test_data/v5.0/expected_wizard_wo_spellbook.rbi +121 -58
  121. data/spec/test_data/v5.1/expected_active_record_relation.rbi +0 -1
  122. data/spec/test_data/v5.1/expected_application_job.rbi +2 -2
  123. data/spec/test_data/v5.1/expected_award_house_point_hourglasses.rbi +2 -2
  124. data/spec/test_data/v5.1/expected_custom_application_job.rbi +21 -0
  125. data/spec/test_data/v5.1/expected_custom_application_mailer.rbi +6 -0
  126. data/spec/test_data/v5.1/expected_custom_award_house_point_hourglasses.rbi +21 -0
  127. data/spec/test_data/v5.1/expected_custom_daily_prophet_mailer.rbi +8 -0
  128. data/spec/test_data/v5.1/expected_custom_hogwarts_acceptance_mailer.rbi +21 -0
  129. data/spec/test_data/v5.1/expected_headmaster.rbi +24 -0
  130. data/spec/test_data/v5.1/expected_potion.rbi +12 -0
  131. data/spec/test_data/v5.1/expected_robe.rbi +12 -0
  132. data/spec/test_data/v5.1/expected_routes.rbi +4 -0
  133. data/spec/test_data/v5.1/expected_school.rbi +12 -0
  134. data/spec/test_data/v5.1/expected_spell/habtm_spell_books.rbi +24 -0
  135. data/spec/test_data/v5.1/expected_spell_book.rbi +21 -9
  136. data/spec/test_data/v5.1/expected_spell_book/habtm_spells.rbi +24 -0
  137. data/spec/test_data/v5.1/expected_squib.rbi +63 -0
  138. data/spec/test_data/v5.1/expected_subject/habtm_wizards.rbi +24 -0
  139. data/spec/test_data/v5.1/expected_wand.rbi +22 -10
  140. data/spec/test_data/v5.1/expected_wizard.rbi +121 -58
  141. data/spec/test_data/v5.1/expected_wizard/habtm_subjects.rbi +24 -0
  142. data/spec/test_data/v5.1/expected_wizard_wo_spellbook.rbi +121 -58
  143. data/spec/test_data/v5.2/expected_active_record_relation.rbi +0 -1
  144. data/spec/test_data/v5.2/expected_application_job.rbi +2 -2
  145. data/spec/test_data/v5.2/expected_attachment.rbi +24 -0
  146. data/spec/test_data/v5.2/expected_award_house_point_hourglasses.rbi +2 -2
  147. data/spec/test_data/v5.2/expected_blob.rbi +25 -1
  148. data/spec/test_data/v5.2/expected_custom_application_job.rbi +21 -0
  149. data/spec/test_data/v5.2/expected_custom_application_mailer.rbi +6 -0
  150. data/spec/test_data/v5.2/expected_custom_award_house_point_hourglasses.rbi +21 -0
  151. data/spec/test_data/v5.2/expected_custom_daily_prophet_mailer.rbi +8 -0
  152. data/spec/test_data/v5.2/expected_custom_hogwarts_acceptance_mailer.rbi +21 -0
  153. data/spec/test_data/v5.2/expected_headmaster.rbi +24 -0
  154. data/spec/test_data/v5.2/expected_potion.rbi +12 -0
  155. data/spec/test_data/v5.2/expected_robe.rbi +12 -0
  156. data/spec/test_data/v5.2/expected_routes.rbi +4 -0
  157. data/spec/test_data/v5.2/expected_school.rbi +12 -0
  158. data/spec/test_data/v5.2/expected_spell/habtm_spell_books.rbi +24 -0
  159. data/spec/test_data/v5.2/expected_spell_book.rbi +21 -9
  160. data/spec/test_data/v5.2/expected_spell_book/habtm_spells.rbi +24 -0
  161. data/spec/test_data/v5.2/expected_squib.rbi +89 -2
  162. data/spec/test_data/v5.2/expected_subject/habtm_wizards.rbi +24 -0
  163. data/spec/test_data/v5.2/expected_wand.rbi +22 -10
  164. data/spec/test_data/v5.2/expected_wizard.rbi +147 -60
  165. data/spec/test_data/v5.2/expected_wizard/habtm_subjects.rbi +24 -0
  166. data/spec/test_data/v5.2/expected_wizard_wo_spellbook.rbi +147 -60
  167. data/spec/test_data/v6.0/expected_active_record_relation.rbi +0 -1
  168. data/spec/test_data/v6.0/expected_application_job.rbi +2 -2
  169. data/spec/test_data/v6.0/expected_attachment.rbi +24 -0
  170. data/spec/test_data/v6.0/expected_award_house_point_hourglasses.rbi +2 -2
  171. data/spec/test_data/v6.0/expected_blob.rbi +25 -1
  172. data/spec/test_data/v6.0/expected_custom_application_job.rbi +21 -0
  173. data/spec/test_data/v6.0/expected_custom_application_mailer.rbi +6 -0
  174. data/spec/test_data/v6.0/expected_custom_award_house_point_hourglasses.rbi +21 -0
  175. data/spec/test_data/v6.0/expected_custom_daily_prophet_mailer.rbi +8 -0
  176. data/spec/test_data/v6.0/expected_custom_hogwarts_acceptance_mailer.rbi +21 -0
  177. data/spec/test_data/v6.0/expected_headmaster.rbi +24 -0
  178. data/spec/test_data/v6.0/expected_potion.rbi +12 -0
  179. data/spec/test_data/v6.0/expected_robe.rbi +12 -0
  180. data/spec/test_data/v6.0/expected_routes.rbi +4 -0
  181. data/spec/test_data/v6.0/expected_school.rbi +12 -0
  182. data/spec/test_data/v6.0/expected_spell/habtm_spell_books.rbi +24 -0
  183. data/spec/test_data/v6.0/expected_spell_book.rbi +21 -9
  184. data/spec/test_data/v6.0/expected_spell_book/habtm_spells.rbi +24 -0
  185. data/spec/test_data/v6.0/expected_squib.rbi +89 -2
  186. data/spec/test_data/v6.0/expected_subject/habtm_wizards.rbi +24 -0
  187. data/spec/test_data/v6.0/expected_wand.rbi +22 -10
  188. data/spec/test_data/v6.0/expected_wizard.rbi +147 -60
  189. data/spec/test_data/v6.0/expected_wizard/habtm_subjects.rbi +24 -0
  190. data/spec/test_data/v6.0/expected_wizard_wo_spellbook.rbi +147 -60
  191. data/spec/tstruct_comparable.rb +13 -0
  192. metadata +64 -15
  193. data/lib/bundled_rbi/parameters.rbi +0 -28
  194. data/lib/sorbet-rails/custom_types/boolean_string.rb +0 -42
  195. data/lib/sorbet-rails/custom_types/integer_string.rb +0 -45
  196. data/lib/sorbet-rails/rails_mixins/custom_params_methods.rb +0 -57
  197. data/spec/boolean_string_spec.rb +0 -59
  198. data/spec/custom_params_methods_spec.rb +0 -138
  199. data/spec/integer_string_spec.rb +0 -46
@@ -30,14 +30,12 @@ module SorbetRails::ModelPlugins
30
30
  @available_classes = T.let(available_classes, T::Set[String])
31
31
  end
32
32
 
33
- sig { params(attribute: T.any(String, Symbol)).returns(T::Boolean) }
34
- def attribute_has_unconditional_presence_validation?(attribute)
35
- model_class.validators_on(attribute).any? do |validator|
36
- validator.is_a?(ActiveModel::Validations::PresenceValidator) &&
37
- !validator.options.key?(:if) &&
38
- !validator.options.key?(:unless) &&
39
- !validator.options.key?(:on)
40
- end
33
+ sig { params(column_name: String).returns(T.nilable(Class)) }
34
+ def serialization_coder_for_column(column_name)
35
+ column_type = @model_class.type_for_attribute(column_name)
36
+ return unless column_type.is_a?(ActiveRecord::Type::Serialized)
37
+
38
+ column_type.coder.try(:object_class) || Object
41
39
  end
42
40
  end
43
41
  end
@@ -6,6 +6,7 @@ require('sorbet-rails/model_plugins/active_relation_where_not')
6
6
  require('sorbet-rails/model_plugins/active_record_named_scope')
7
7
  require('sorbet-rails/model_plugins/active_record_attribute')
8
8
  require('sorbet-rails/model_plugins/active_record_assoc')
9
+ require('sorbet-rails/model_plugins/active_record_serialized_attribute')
9
10
  require('sorbet-rails/model_plugins/custom_finder_methods')
10
11
  require('sorbet-rails/model_plugins/enumerable_collections')
11
12
  require('sorbet-rails/model_plugins/active_storage_methods')
@@ -47,6 +48,8 @@ module SorbetRails::ModelPlugins
47
48
  ActiveRecordQuerying
48
49
  when :active_relation_where_not
49
50
  ActiveRelationWhereNot
51
+ when :active_record_serialized_attribute
52
+ ActiveRecordSerializedAttribute
50
53
  when :active_record_attribute
51
54
  ActiveRecordAttribute
52
55
  when :active_record_assoc
@@ -78,6 +81,12 @@ module SorbetRails::ModelPlugins
78
81
  when :paperclip
79
82
  require('sorbet-rails/gem_plugins/paperclip_plugin')
80
83
  PaperclipPlugin
84
+ when :attr_json
85
+ require('sorbet-rails/gem_plugins/attr_json_plugin')
86
+ AttrJsonPlugin
87
+ when :flag_shih_tzu
88
+ require('sorbet-rails/gem_plugins/flag_shih_tzu_plugin')
89
+ FlagShihTzuPlugin
81
90
  else
82
91
  raise UnrecognizedPluginName.new(
83
92
  "Unrecognized plugin with name: #{plugin_name}. Please check available plugins in the
@@ -42,7 +42,7 @@ class SorbetRails::ModelRbiFormatter
42
42
  end
43
43
 
44
44
  generator = Parlour::RbiGenerator.new(break_params: 3)
45
- run_plugins(plugin_instances, generator, allow_failure: true)
45
+ run_plugins(plugin_instances, generator)
46
46
  # Generate the base after the plugins because when ConflictResolver merge the modules,
47
47
  # it'll put the modules at the last position merged. Putting the base stuff
48
48
  # last will keep the order consistent and minimize changes when new plugins are added.
@@ -115,11 +115,11 @@ class SorbetRails::ModelRbiFormatter
115
115
  params(
116
116
  plugins: T::Array[Parlour::Plugin],
117
117
  generator: Parlour::RbiGenerator,
118
- allow_failure: T::Boolean,
119
118
  ).
120
119
  void
121
120
  }
122
- def run_plugins(plugins, generator, allow_failure: true)
121
+ def run_plugins(plugins, generator)
122
+ allow_failure = ENV["SBR_DEBUG_MODE"] == "true"
123
123
  plugins.each do |plugin|
124
124
  begin
125
125
  generator.current_plugin = plugin
@@ -1,14 +1,17 @@
1
1
  # typed: strict
2
+ require('sorbet-rails/model_column_utils')
2
3
  module SorbetRails::ModelUtils
3
4
  extend T::Sig
4
5
  extend T::Helpers
6
+ include SorbetRails::ModelColumnUtils
5
7
 
6
8
  abstract!
7
9
 
8
10
  # if we're a HABTM class then model_class is an anonymous class (see the rails link below) and
9
11
  # i'm not sure how to explain that to sorbet other than T.class_of(Class).
10
- sig { abstract.returns(T.any(T.class_of(ActiveRecord::Base), T.class_of(Class))) }
11
- def model_class; end
12
+ # This is also defined in ModelColumnUtils
13
+ # sig { abstract.returns(T.any(T.class_of(ActiveRecord::Base), T.class_of(Class))) }
14
+ # def model_class; end
12
15
 
13
16
  sig { returns(T::Boolean) }
14
17
  def habtm_class?
@@ -11,6 +11,5 @@
11
11
  # + include GeneratedUrlHelpers
12
12
  # end
13
13
  #
14
- module GeneratedUrlHelpers
15
- include Rails.application.routes.url_helpers
16
- end
14
+ # Reference: https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/routing/route_set.rb
15
+ GeneratedUrlHelpers = Rails.application.routes.url_helpers
@@ -3,31 +3,78 @@ require 'sorbet-runtime'
3
3
 
4
4
  module SorbetRails::PluckToTStruct
5
5
  extend T::Sig
6
+
7
+ NILCLASS_STRING = "NilClass".freeze
8
+
6
9
  sig {
7
10
  type_parameters(:U).
8
11
  params(
9
12
  ta_struct: ITypeAssert[T.type_parameter(:U)],
13
+ associations: T::Hash[Symbol, String],
10
14
  ).
11
15
  returns(T::Array[T.type_parameter(:U)])
12
16
  }
13
- def pluck_to_tstruct(ta_struct)
17
+ def pluck_to_tstruct(ta_struct, associations: {})
14
18
  tstruct = ta_struct.get_type
15
-
19
+
16
20
  if !(tstruct < T::Struct)
17
21
  raise UnexpectedType.new("pluck_to_tstruct expects a tstruct subclass, given #{tstruct}")
18
22
  end
19
23
 
20
- keys = tstruct.props.keys
24
+ tstruct_props = tstruct.props
25
+ tstruct_keys = tstruct_props.keys
26
+ associations_keys = associations.keys
27
+ invalid_keys = associations_keys - tstruct_keys
28
+
29
+ if invalid_keys.any?
30
+ raise UnexpectedAssociations.new("Argument 'associations' contains keys that don't exist in #{tstruct}: #{invalid_keys.join(", ")}")
31
+ end
32
+
33
+ pluck_keys = (tstruct_keys - associations_keys) + associations.values
21
34
 
22
35
  # loosely based on pluck_to_hash gem
23
36
  # https://github.com/girishso/pluck_to_hash/blob/master/lib/pluck_to_hash.rb
24
- keys_one = keys.size == 1
25
- pluck(*keys).map do |row|
37
+ keys_one = pluck_keys.size == 1
38
+ pluck(*pluck_keys).map do |row|
26
39
  row = [row] if keys_one
27
- value = Hash[keys.zip(row)]
40
+ value = Hash[map_nil_values_to_default(tstruct_props, tstruct_keys.zip(row))]
28
41
  tstruct.new(value)
29
42
  end
30
43
  end
31
-
44
+
45
+ sig {params(type_object: T::Types::Base).returns(T::Boolean)}
46
+ private def nilable?(type_object)
47
+ return false unless type_object.is_a?(T::Types::Union)
48
+
49
+ type_object.types.any? { |type| type.name == NILCLASS_STRING }
50
+ end
51
+
52
+ sig {
53
+ params(
54
+ tstruct_props: T::Hash[Symbol, T.untyped],
55
+ zipped_rows: T::Array[[Symbol, T.untyped]]
56
+ )
57
+ .returns(T::Array[[Symbol, T.untyped]])
58
+ }
59
+ private def map_nil_values_to_default(tstruct_props, zipped_rows)
60
+ # we use the default value defined on a prop if
61
+ # 1. the plucked value is nil
62
+ # 2. the prop has a default
63
+ # 3. the prop's type isn't nilable
64
+
65
+ zipped_rows.map do |key, value|
66
+ next [key, value] unless value.nil?
67
+
68
+ default = tstruct_props.dig(key, :default)
69
+ next [key, value] unless default
70
+
71
+ type_object = tstruct_props.dig(key, :type_object)
72
+ next [key, value] if nilable?(type_object)
73
+
74
+ [key, default]
75
+ end
76
+ end
77
+
32
78
  class UnexpectedType < StandardError; end
79
+ class UnexpectedAssociations < StandardError; end
33
80
  end
@@ -50,9 +50,7 @@ class SorbetRails::Railtie < Rails::Railtie
50
50
  end
51
51
 
52
52
  ActiveSupport.on_load(:action_controller) do
53
- require "sorbet-rails/rails_mixins/custom_params_methods"
54
53
  require "sorbet-rails/rails_mixins/generated_url_helpers"
55
- ActionController::Parameters.include SorbetRails::CustomParamsMethods
56
54
  end
57
55
 
58
56
  SorbetRails.register_configured_plugins
@@ -19,7 +19,7 @@ class SorbetRails::RoutesRbiFormatter
19
19
 
20
20
  sig { params(routes: T.untyped).void }
21
21
  def header(routes)
22
- @parlour.root.add_comments([
22
+ @parlour.root.add_comment([
23
23
  'This is an autogenerated file for Rails routes.',
24
24
  'Please run bundle exec rake rails_rbi:routes to regenerate.'
25
25
  ])
@@ -28,6 +28,10 @@ class SorbetRails::RoutesRbiFormatter
28
28
  klass.create_include('GeneratedUrlHelpers')
29
29
  end
30
30
 
31
+ @parlour.root.create_class('ActionController::API') do |klass|
32
+ klass.create_include('GeneratedUrlHelpers')
33
+ end
34
+
31
35
  @parlour.root.create_module('ActionView::Helpers') do |mod|
32
36
  mod.create_include('GeneratedUrlHelpers')
33
37
  end
@@ -39,7 +43,7 @@ class SorbetRails::RoutesRbiFormatter
39
43
 
40
44
  sig { params(routes: T.untyped, filter: T.untyped).void }
41
45
  def no_routes(routes = nil, filter = nil)
42
- @parlour.root.add_comments([
46
+ @parlour.root.add_comment([
43
47
  'You do not have any routes defined!',
44
48
  'Please add some routes in config/routes.rb.',
45
49
  'For more information about routes, see the Rails guide: https://guides.rubyonrails.org/routing.html.'
@@ -5,168 +5,170 @@ require('sorbet-runtime')
5
5
  require('method_source')
6
6
  require('parser/current')
7
7
 
8
- module SorbetRails::SorbetUtils
9
- extend T::Sig
10
- include Kernel
11
-
12
- class ParsedParamDef < T::Struct
13
- const :name, Symbol
14
- const :kind, Symbol
15
- const :type_str, String
16
- prop :default, T.nilable(String), default: nil
17
- prop :prefix, T.nilable(String)
18
- prop :suffix, T.nilable(String)
19
- end
20
-
21
- sig { params(method_def: UnboundMethod).returns(T::Array[Parlour::RbiGenerator::Parameter]) }
22
- def self.parameters_from_method_def(method_def)
23
- signature = T::Private::Methods.signature_for_method(method_def)
24
- method_def = signature.nil? ? method_def : signature.method
25
-
26
- parameters_with_type = signature.nil? ?
27
- method_def.parameters.map { |p|
28
- ParsedParamDef.new(
29
- name: p.size == 1 ? :_ : p[1], # give param without name default name _
30
- kind: p[0], # append untyped as type of each param
31
- type_str: 'T.untyped',
32
- )
33
- } :
34
- get_ordered_parameters_with_type(signature)
35
-
36
- # add prefix & suffix
37
- parameters_with_type.each do |param_def|
38
- param_def.prefix =
39
- case param_def.kind
40
- when :rest; '*'
41
- when :keyrest; '**'
42
- when :block; '&'
43
- # being comprehensive
44
- when :req, :opt; ''
45
- when :key, :keyreq; ''
46
- else nil
47
- end
48
-
49
- param_def.suffix =
50
- case param_def.kind
51
- when :key, :keyreq; ':'
52
- else nil
53
- end
8
+ module SorbetRails
9
+ module SorbetUtils
10
+ extend T::Sig
11
+ include Kernel
12
+
13
+ class ParsedParamDef < T::Struct
14
+ const :name, Symbol
15
+ const :kind, Symbol
16
+ const :type_str, String
17
+ prop :default, T.nilable(String), default: nil
18
+ prop :prefix, T.nilable(String)
19
+ prop :suffix, T.nilable(String)
54
20
  end
55
21
 
56
- extract_default_value_for_params!(
57
- parameters_with_type,
58
- method_def,
59
- )
60
-
61
- parameters_with_type.map do |param_def|
62
- ::Parlour::RbiGenerator::Parameter.new(
63
- "#{param_def.prefix}#{param_def.name}#{param_def.suffix}",
64
- type: param_def.type_str,
65
- default: param_def.default,
22
+ sig { params(method_def: UnboundMethod).returns(T::Array[Parlour::RbiGenerator::Parameter]) }
23
+ def self.parameters_from_method_def(method_def)
24
+ signature = T::Private::Methods.signature_for_method(method_def)
25
+ method_def = signature.nil? ? method_def : signature.method
26
+
27
+ parameters_with_type = signature.nil? ?
28
+ method_def.parameters.map { |p|
29
+ ParsedParamDef.new(
30
+ name: p.size == 1 ? :_ : p[1], # give param without name default name _
31
+ kind: p[0], # append untyped as type of each param
32
+ type_str: 'T.untyped',
33
+ )
34
+ } :
35
+ get_ordered_parameters_with_type(signature)
36
+
37
+ # add prefix & suffix
38
+ parameters_with_type.each do |param_def|
39
+ param_def.prefix =
40
+ case param_def.kind
41
+ when :rest; '*'
42
+ when :keyrest; '**'
43
+ when :block; '&'
44
+ # being comprehensive
45
+ when :req, :opt; ''
46
+ when :key, :keyreq; ''
47
+ else nil
48
+ end
49
+
50
+ param_def.suffix =
51
+ case param_def.kind
52
+ when :key, :keyreq; ':'
53
+ else nil
54
+ end
55
+ end
56
+
57
+ extract_default_value_for_params!(
58
+ parameters_with_type,
59
+ method_def,
66
60
  )
67
- end
68
- end
69
61
 
70
- sig {
71
- params(signature: T::Private::Methods::Signature).
72
- returns(T::Array[ParsedParamDef])
73
- }
74
- def self.get_ordered_parameters_with_type(signature)
75
- # extract original method param from signature
76
- # https://github.com/sorbet/sorbet/blob/master/gems/sorbet-runtime/lib/types/private/methods/signature.rb#L5-L8
77
- params = T.let([], T::Array[ParsedParamDef])
78
- signature.arg_types.each do |arg_type|
79
- # could be :opt, but doesn't matter
80
- params << ParsedParamDef.new(
81
- name: arg_type[0],
82
- kind: :req,
83
- type_str: arg_type[1].to_s,
84
- )
85
- end
86
- signature.kwarg_types.each do |kwarg_name, kwarg_type|
87
- # could be :key, but doesn't matter
88
- params << ParsedParamDef.new(
89
- name: kwarg_name,
90
- kind: :keyreq,
91
- type_str: kwarg_type.to_s,
92
- )
93
- end
94
- if signature.has_rest
95
- params << ParsedParamDef.new(
96
- name: signature.rest_name,
97
- kind: :rest,
98
- type_str: signature.rest_type.to_s,
99
- )
100
- end
101
- if signature.has_keyrest
102
- params << ParsedParamDef.new(
103
- name: signature.keyrest_name,
104
- kind: :keyrest,
105
- type_str: signature.keyrest_type.to_s,
106
- )
107
- end
108
- if !signature.block_name.nil?
109
- # special case `.void` in a proc
110
- # see https://github.com/sorbet/sorbet/blob/master/gems/sorbet-runtime/lib/types/types/proc.rb#L10
111
- block_param_type = signature.block_type.to_s
112
- block_param_type = block_param_type.gsub('returns(<VOID>)', 'void')
113
- params << ParsedParamDef.new(
114
- name: signature.block_name,
115
- kind: :block,
116
- type_str: block_param_type,
117
- )
62
+ parameters_with_type.map do |param_def|
63
+ ::Parlour::RbiGenerator::Parameter.new(
64
+ "#{param_def.prefix}#{param_def.name}#{param_def.suffix}",
65
+ type: param_def.type_str,
66
+ default: param_def.default,
67
+ )
68
+ end
118
69
  end
119
- params
120
- end
121
70
 
122
- sig {
123
- params(
124
- parsed_params: T::Array[ParsedParamDef],
125
- method_def: UnboundMethod,
126
- ).void
127
- }
128
- def self.extract_default_value_for_params!(parsed_params, method_def)
129
- source = method_def.source
130
- parsed_ast = Parser::CurrentRuby.parse(source)
131
- if parsed_ast.type != :def
132
- # could be a method added at runtime? ignore it
133
- puts "Warning: unable to parse the source of #{method_def.name}"
134
- return
71
+ sig {
72
+ params(signature: T::Private::Methods::Signature).
73
+ returns(T::Array[ParsedParamDef])
74
+ }
75
+ def self.get_ordered_parameters_with_type(signature)
76
+ # extract original method param from signature
77
+ # https://github.com/sorbet/sorbet/blob/master/gems/sorbet-runtime/lib/types/private/methods/signature.rb#L5-L8
78
+ params = T.let([], T::Array[ParsedParamDef])
79
+ signature.arg_types.each do |arg_type|
80
+ # could be :opt, but doesn't matter
81
+ params << ParsedParamDef.new(
82
+ name: arg_type[0],
83
+ kind: :req,
84
+ type_str: arg_type[1].to_s,
85
+ )
86
+ end
87
+ signature.kwarg_types.each do |kwarg_name, kwarg_type|
88
+ # could be :key, but doesn't matter
89
+ params << ParsedParamDef.new(
90
+ name: kwarg_name,
91
+ kind: :keyreq,
92
+ type_str: kwarg_type.to_s,
93
+ )
94
+ end
95
+ if signature.has_rest
96
+ params << ParsedParamDef.new(
97
+ name: signature.rest_name,
98
+ kind: :rest,
99
+ type_str: signature.rest_type.to_s,
100
+ )
101
+ end
102
+ if signature.has_keyrest
103
+ params << ParsedParamDef.new(
104
+ name: signature.keyrest_name,
105
+ kind: :keyrest,
106
+ type_str: signature.keyrest_type.to_s,
107
+ )
108
+ end
109
+ if !signature.block_name.nil?
110
+ # special case `.void` in a proc
111
+ # see https://github.com/sorbet/sorbet/blob/master/gems/sorbet-runtime/lib/types/types/proc.rb#L10
112
+ block_param_type = signature.block_type.to_s
113
+ block_param_type = block_param_type.gsub('returns(<VOID>)', 'void')
114
+ params << ParsedParamDef.new(
115
+ name: signature.block_name,
116
+ kind: :block,
117
+ type_str: block_param_type,
118
+ )
119
+ end
120
+ params
135
121
  end
136
122
 
137
- args = parsed_ast.children[1]
138
- if args.type != :args
139
- puts "Warning: unable to parse the source of #{method_def.name}"
140
- return
123
+ sig {
124
+ params(
125
+ parsed_params: T::Array[ParsedParamDef],
126
+ method_def: UnboundMethod,
127
+ ).void
128
+ }
129
+ def self.extract_default_value_for_params!(parsed_params, method_def)
130
+ source = method_def.source
131
+ parsed_ast = Parser::CurrentRuby.parse(source)
132
+ if parsed_ast.type != :def
133
+ # could be a method added at runtime? ignore it
134
+ puts "Warning: unable to parse the source of #{method_def.name}"
135
+ return
136
+ end
137
+
138
+ args = parsed_ast.children[1]
139
+ if args.type != :args
140
+ puts "Warning: unable to parse the source of #{method_def.name}"
141
+ return
142
+ end
143
+
144
+ parsed_params_map = Hash[parsed_params.map {|p| [p.name, p]}]
145
+ args.children.each do |arg|
146
+ arg_name = arg.children[0]
147
+ default = arg.children[1] ? node_to_s(arg.children[1]) : nil
148
+
149
+ next if arg_name.blank?
150
+
151
+ param_def = parsed_params_map[arg_name]
152
+
153
+ raise UnexpectedParam.new(
154
+ "Unexpected param #{arg_name} when parsing #{method_def.name}"
155
+ ) unless param_def.present?
156
+
157
+ param_def.default = default
158
+ end
141
159
  end
142
160
 
143
- parsed_params_map = Hash[parsed_params.map {|p| [p.name, p]}]
144
- args.children.each do |arg|
145
- arg_name = arg.children[0]
146
- default = arg.children[1] ? node_to_s(arg.children[1]) : nil
147
-
148
- next if arg_name.blank?
149
-
150
- param_def = parsed_params_map[arg_name]
151
-
152
- raise UnexpectedParam.new(
153
- "Unexpected param #{arg_name} when parsing #{method_def.name}"
154
- ) unless param_def.present?
161
+ # Given an AST node, returns the source code from which it was constructed.
162
+ # If the given AST node is nil, this returns nil.
163
+ # Taken from https://github.com/AaronC81/parlour/blob/master/lib/parlour/type_parser.rb#L506
164
+ sig { params(node: T.nilable(Parser::AST::Node)).returns(T.nilable(String)) }
165
+ def self.node_to_s(node)
166
+ return nil unless node
155
167
 
156
- param_def.default = default
168
+ exp = node.loc.expression
169
+ exp.source_buffer.source[exp.begin_pos...exp.end_pos]
157
170
  end
158
- end
159
-
160
- # Given an AST node, returns the source code from which it was constructed.
161
- # If the given AST node is nil, this returns nil.
162
- # Taken from https://github.com/AaronC81/parlour/blob/master/lib/parlour/type_parser.rb#L506
163
- sig { params(node: T.nilable(Parser::AST::Node)).returns(T.nilable(String)) }
164
- def self.node_to_s(node)
165
- return nil unless node
166
171
 
167
- exp = node.loc.expression
168
- exp.source_buffer.source[exp.begin_pos...exp.end_pos]
172
+ class UnexpectedParam < StandardError; end
169
173
  end
170
-
171
- class UnexpectedParam < StandardError; end
172
174
  end