sorbet-rails 0.6.2 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (187) hide show
  1. checksums.yaml +4 -4
  2. data/lib/sorbet-rails/active_record_rbi_formatter.rb +295 -0
  3. data/lib/sorbet-rails/config.rb +0 -1
  4. data/lib/sorbet-rails/custom_types/boolean_string.rb +10 -0
  5. data/lib/sorbet-rails/custom_types/integer_string.rb +10 -0
  6. data/lib/sorbet-rails/dependent_gem_rbis/activerecord.rbi +11 -0
  7. data/lib/sorbet-rails/deprecation.rb +5 -0
  8. data/lib/sorbet-rails/model_plugins/active_record_assoc.rb +27 -6
  9. data/lib/sorbet-rails/model_plugins/active_record_attribute.rb +20 -8
  10. data/lib/sorbet-rails/model_plugins/base.rb +9 -0
  11. data/lib/sorbet-rails/model_plugins/enumerable_collections.rb +0 -50
  12. data/lib/sorbet-rails/model_plugins/plugins.rb +0 -3
  13. data/lib/sorbet-rails/model_rbi_formatter.rb +2 -2
  14. data/lib/sorbet-rails/model_utils.rb +15 -6
  15. data/lib/sorbet-rails/rails_mixins/custom_params_methods.rb +11 -0
  16. data/lib/sorbet-rails/tasks/rails_rbi.rake +20 -4
  17. data/sorbet-rails.gemspec +2 -2
  18. data/spec/active_record_rbi_formatter_spec.rb +24 -0
  19. data/spec/generators/rails-template.rb +88 -1
  20. data/spec/generators/sorbet_test_cases.rb +146 -42
  21. data/spec/model_rbi_formatter_spec.rb +1 -1
  22. data/spec/rails_helper.rb +14 -1
  23. data/spec/rake_rails_rbi_active_record_spec.rb +21 -0
  24. data/spec/rake_rails_rbi_models_spec.rb +7 -0
  25. data/spec/sorbet_spec.rb +12 -1
  26. data/spec/support/v5.0/Gemfile.lock +8 -8
  27. data/spec/support/v5.0/app/models/headmaster.rb +8 -0
  28. data/spec/support/v5.0/app/models/school.rb +2 -0
  29. data/spec/support/v5.0/app/models/spell.rb +5 -0
  30. data/spec/support/v5.0/app/models/spell_book.rb +3 -0
  31. data/spec/support/v5.0/app/models/subject.rb +5 -0
  32. data/spec/support/v5.0/app/models/wizard.rb +6 -1
  33. data/spec/support/v5.0/db/migrate/20190620000010_add_subject.rb +8 -0
  34. data/spec/support/v5.0/db/migrate/20190620000011_add_subjects_wizards.rb +8 -0
  35. data/spec/support/v5.0/db/migrate/20190620000012_add_spell.rb +8 -0
  36. data/spec/support/v5.0/db/migrate/20190620000013_add_spells_spell_books.rb +8 -0
  37. data/spec/support/v5.0/db/migrate/20190620000014_create_headmasters.rb +9 -0
  38. data/spec/support/v5.0/db/schema.rb +28 -1
  39. data/spec/support/v5.0/lib/mythical_rbi_plugin.rb +1 -1
  40. data/spec/support/v5.0/sorbet_test_cases.rb +146 -42
  41. data/spec/support/v5.1/Gemfile.lock +8 -8
  42. data/spec/support/v5.1/app/models/headmaster.rb +8 -0
  43. data/spec/support/v5.1/app/models/school.rb +2 -0
  44. data/spec/support/v5.1/app/models/spell.rb +5 -0
  45. data/spec/support/v5.1/app/models/spell_book.rb +3 -0
  46. data/spec/support/v5.1/app/models/subject.rb +5 -0
  47. data/spec/support/v5.1/app/models/wizard.rb +6 -1
  48. data/spec/support/v5.1/db/migrate/20190620000010_add_subject.rb +8 -0
  49. data/spec/support/v5.1/db/migrate/20190620000011_add_subjects_wizards.rb +8 -0
  50. data/spec/support/v5.1/db/migrate/20190620000012_add_spell.rb +8 -0
  51. data/spec/support/v5.1/db/migrate/20190620000013_add_spells_spell_books.rb +8 -0
  52. data/spec/support/v5.1/db/migrate/20190620000014_create_headmasters.rb +9 -0
  53. data/spec/support/v5.1/db/schema.rb +28 -1
  54. data/spec/support/v5.1/lib/mythical_rbi_plugin.rb +1 -1
  55. data/spec/support/v5.1/sorbet_test_cases.rb +146 -42
  56. data/spec/support/v5.2/Gemfile +1 -1
  57. data/spec/support/v5.2/Gemfile.lock +9 -9
  58. data/spec/support/v5.2/app/models/headmaster.rb +8 -0
  59. data/spec/support/v5.2/app/models/school.rb +2 -0
  60. data/spec/support/v5.2/app/models/spell.rb +5 -0
  61. data/spec/support/v5.2/app/models/spell_book.rb +3 -0
  62. data/spec/support/v5.2/app/models/subject.rb +5 -0
  63. data/spec/support/v5.2/app/models/wizard.rb +5 -0
  64. data/spec/support/v5.2/config/puma.rb +3 -0
  65. data/spec/support/v5.2/db/migrate/20190620000010_add_subject.rb +8 -0
  66. data/spec/support/v5.2/db/migrate/20190620000011_add_subjects_wizards.rb +8 -0
  67. data/spec/support/v5.2/db/migrate/20190620000012_add_spell.rb +8 -0
  68. data/spec/support/v5.2/db/migrate/20190620000013_add_spells_spell_books.rb +8 -0
  69. data/spec/support/v5.2/db/migrate/20190620000014_create_headmasters.rb +9 -0
  70. data/spec/support/v5.2/db/schema.rb +28 -1
  71. data/spec/support/v5.2/lib/mythical_rbi_plugin.rb +1 -1
  72. data/spec/support/v5.2/sorbet_test_cases.rb +146 -42
  73. data/spec/support/v6.0/Gemfile.lock +8 -8
  74. data/spec/support/v6.0/app/models/headmaster.rb +8 -0
  75. data/spec/support/v6.0/app/models/school.rb +2 -0
  76. data/spec/support/v6.0/app/models/spell.rb +5 -0
  77. data/spec/support/v6.0/app/models/spell_book.rb +3 -0
  78. data/spec/support/v6.0/app/models/subject.rb +5 -0
  79. data/spec/support/v6.0/app/models/wizard.rb +6 -1
  80. data/spec/support/v6.0/db/migrate/20190620000010_add_subject.rb +8 -0
  81. data/spec/support/v6.0/db/migrate/20190620000011_add_subjects_wizards.rb +8 -0
  82. data/spec/support/v6.0/db/migrate/20190620000012_add_spell.rb +8 -0
  83. data/spec/support/v6.0/db/migrate/20190620000013_add_spells_spell_books.rb +8 -0
  84. data/spec/support/v6.0/db/migrate/20190620000014_create_headmasters.rb +9 -0
  85. data/spec/support/v6.0/db/schema.rb +28 -1
  86. data/spec/support/v6.0/lib/mythical_rbi_plugin.rb +1 -1
  87. data/spec/support/v6.0/sorbet_test_cases.rb +146 -42
  88. data/spec/test_data/v5.0/expected_active_record_base.rbi +113 -0
  89. data/spec/test_data/v5.0/expected_active_record_relation.rbi +199 -0
  90. data/spec/test_data/v5.0/expected_habtm_subjects.rbi +660 -0
  91. data/spec/test_data/v5.0/expected_habtm_wizards.rbi +660 -0
  92. data/spec/test_data/v5.0/expected_headmaster.rbi +452 -0
  93. data/spec/test_data/v5.0/expected_internal_metadata.rbi +0 -217
  94. data/spec/test_data/v5.0/expected_potion.rbi +0 -217
  95. data/spec/test_data/v5.0/expected_robe.rbi +0 -217
  96. data/spec/test_data/v5.0/expected_schema_migration.rbi +0 -217
  97. data/spec/test_data/v5.0/expected_school.rbi +11 -217
  98. data/spec/test_data/v5.0/expected_spell.rbi +440 -0
  99. data/spec/test_data/v5.0/expected_spell/habtm_spell_books.rbi +443 -0
  100. data/spec/test_data/v5.0/expected_spell_book.rbi +14 -222
  101. data/spec/test_data/v5.0/expected_spell_book/habtm_spell_books.rbi +637 -0
  102. data/spec/test_data/v5.0/expected_spell_book/habtm_spells.rbi +443 -0
  103. data/spec/test_data/v5.0/expected_squib.rbi +14 -219
  104. data/spec/test_data/v5.0/expected_subject.rbi +440 -0
  105. data/spec/test_data/v5.0/expected_subject/habtm_wizards.rbi +443 -0
  106. data/spec/test_data/v5.0/expected_wand.rbi +4 -221
  107. data/spec/test_data/v5.0/expected_wizard.rbi +36 -240
  108. data/spec/test_data/v5.0/expected_wizard/habtm_subjects.rbi +443 -0
  109. data/spec/test_data/v5.0/expected_wizard_wo_spellbook.rbi +30 -240
  110. data/spec/test_data/v5.1/expected_active_record_base.rbi +113 -0
  111. data/spec/test_data/v5.1/expected_active_record_relation.rbi +178 -0
  112. data/spec/test_data/v5.1/expected_habtm_subjects.rbi +672 -0
  113. data/spec/test_data/v5.1/expected_habtm_wizards.rbi +672 -0
  114. data/spec/test_data/v5.1/expected_headmaster.rbi +464 -0
  115. data/spec/test_data/v5.1/expected_internal_metadata.rbi +0 -217
  116. data/spec/test_data/v5.1/expected_potion.rbi +0 -217
  117. data/spec/test_data/v5.1/expected_robe.rbi +0 -217
  118. data/spec/test_data/v5.1/expected_schema_migration.rbi +0 -217
  119. data/spec/test_data/v5.1/expected_school.rbi +11 -217
  120. data/spec/test_data/v5.1/expected_spell.rbi +452 -0
  121. data/spec/test_data/v5.1/expected_spell/habtm_spell_books.rbi +455 -0
  122. data/spec/test_data/v5.1/expected_spell_book.rbi +14 -222
  123. data/spec/test_data/v5.1/expected_spell_book/habtm_spell_books.rbi +649 -0
  124. data/spec/test_data/v5.1/expected_spell_book/habtm_spells.rbi +455 -0
  125. data/spec/test_data/v5.1/expected_squib.rbi +14 -219
  126. data/spec/test_data/v5.1/expected_subject.rbi +452 -0
  127. data/spec/test_data/v5.1/expected_subject/habtm_wizards.rbi +455 -0
  128. data/spec/test_data/v5.1/expected_wand.rbi +4 -221
  129. data/spec/test_data/v5.1/expected_wizard.rbi +36 -240
  130. data/spec/test_data/v5.1/expected_wizard/habtm_subjects.rbi +455 -0
  131. data/spec/test_data/v5.1/expected_wizard_wo_spellbook.rbi +30 -240
  132. data/spec/test_data/v5.2/expected_active_record_base.rbi +113 -0
  133. data/spec/test_data/v5.2/expected_active_record_relation.rbi +175 -0
  134. data/spec/test_data/v5.2/expected_attachment.rbi +0 -217
  135. data/spec/test_data/v5.2/expected_blob.rbi +3 -217
  136. data/spec/test_data/v5.2/expected_habtm_subjects.rbi +672 -0
  137. data/spec/test_data/v5.2/expected_habtm_wizards.rbi +672 -0
  138. data/spec/test_data/v5.2/expected_headmaster.rbi +464 -0
  139. data/spec/test_data/v5.2/expected_internal_metadata.rbi +0 -217
  140. data/spec/test_data/v5.2/expected_potion.rbi +0 -217
  141. data/spec/test_data/v5.2/expected_robe.rbi +0 -217
  142. data/spec/test_data/v5.2/expected_schema_migration.rbi +0 -217
  143. data/spec/test_data/v5.2/expected_school.rbi +11 -217
  144. data/spec/test_data/v5.2/expected_spell.rbi +452 -0
  145. data/spec/test_data/v5.2/expected_spell/habtm_spell_books.rbi +455 -0
  146. data/spec/test_data/v5.2/expected_spell_book.rbi +14 -222
  147. data/spec/test_data/v5.2/expected_spell_book/habtm_spell_books.rbi +649 -0
  148. data/spec/test_data/v5.2/expected_spell_book/habtm_spells.rbi +455 -0
  149. data/spec/test_data/v5.2/expected_squib.rbi +20 -219
  150. data/spec/test_data/v5.2/expected_subject.rbi +452 -0
  151. data/spec/test_data/v5.2/expected_subject/habtm_wizards.rbi +455 -0
  152. data/spec/test_data/v5.2/expected_wand.rbi +4 -221
  153. data/spec/test_data/v5.2/expected_wizard.rbi +42 -240
  154. data/spec/test_data/v5.2/expected_wizard/habtm_subjects.rbi +455 -0
  155. data/spec/test_data/v5.2/expected_wizard_wo_spellbook.rbi +36 -240
  156. data/spec/test_data/v6.0/expected_active_record_base.rbi +113 -0
  157. data/spec/test_data/v6.0/expected_active_record_relation.rbi +175 -0
  158. data/spec/test_data/v6.0/expected_attachment.rbi +0 -217
  159. data/spec/test_data/v6.0/expected_blob.rbi +3 -217
  160. data/spec/test_data/v6.0/expected_habtm_subjects.rbi +720 -0
  161. data/spec/test_data/v6.0/expected_habtm_wizards.rbi +720 -0
  162. data/spec/test_data/v6.0/expected_headmaster.rbi +512 -0
  163. data/spec/test_data/v6.0/expected_internal_metadata.rbi +0 -217
  164. data/spec/test_data/v6.0/expected_potion.rbi +0 -217
  165. data/spec/test_data/v6.0/expected_robe.rbi +0 -217
  166. data/spec/test_data/v6.0/expected_schema_migration.rbi +0 -217
  167. data/spec/test_data/v6.0/expected_school.rbi +11 -217
  168. data/spec/test_data/v6.0/expected_spell.rbi +500 -0
  169. data/spec/test_data/v6.0/expected_spell/habtm_spell_books.rbi +503 -0
  170. data/spec/test_data/v6.0/expected_spell_book.rbi +14 -222
  171. data/spec/test_data/v6.0/expected_spell_book/habtm_spell_books.rbi +697 -0
  172. data/spec/test_data/v6.0/expected_spell_book/habtm_spells.rbi +503 -0
  173. data/spec/test_data/v6.0/expected_squib.rbi +20 -219
  174. data/spec/test_data/v6.0/expected_subject.rbi +500 -0
  175. data/spec/test_data/v6.0/expected_subject/habtm_wizards.rbi +503 -0
  176. data/spec/test_data/v6.0/expected_wand.rbi +4 -221
  177. data/spec/test_data/v6.0/expected_wizard.rbi +42 -240
  178. data/spec/test_data/v6.0/expected_wizard/habtm_subjects.rbi +503 -0
  179. data/spec/test_data/v6.0/expected_wizard_wo_spellbook.rbi +36 -240
  180. metadata +169 -14
  181. data/lib/bundled_rbi/active_record_base.rbi +0 -83
  182. data/lib/bundled_rbi/active_record_relation.rbi +0 -122
  183. data/lib/sorbet-rails/model_plugins/active_record_finder_methods.rb +0 -131
  184. data/spec/support/v5.0/typed-override.yaml +0 -2
  185. data/spec/support/v5.1/typed-override.yaml +0 -2
  186. data/spec/support/v5.2/typed-override.yaml +0 -2
  187. data/spec/support/v6.0/typed-override.yaml +0 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a7e0b556a5c7db33a8fa24c494fa70265802a360eb842545b6124d54ade20e47
4
- data.tar.gz: 5f26a6773d2769e1dfc523f4202b179ded8deaebb65e05ade61291f03153f164
3
+ metadata.gz: 6ade7fadcb2cc5890f8cd5716247f14d7d9b0324029fb13de4f8b902e205a516
4
+ data.tar.gz: 766dfdd66acea268fe696345ae871bf4b88cd67e2b845dffeb9f218d9a38586d
5
5
  SHA512:
6
- metadata.gz: cc61969e6954e727113e9e74ba48925c15ee07b940fbe5d3a711d62270f90ba06ce9c36373420a3cf02c9a52bda3256ef63d9bea6bf2bcd580dfa592faba068c
7
- data.tar.gz: 8c0bb6e747de6ac12c77b326b01d941f3f24fdd6444fca05d573ff26c20a2d0d679bbcd713adb37f66919781446137d3f0fdd21bb39b36241c6e1d6a98ac1de6
6
+ metadata.gz: 91ce6f173680ed297198333b0780c4f4257a7b95d56f33d6f464f67f122c37a6483b87f00392792219ce51724ad5379fea9b7cbf91fc100d439396f828f210be
7
+ data.tar.gz: ba26ebc1b34724a1eee5cc667add0b843c3ef77994606938e31b2f3fc0a0a88d8a0b7ad39f65194350045166c1eb130cd94951f54c763d82a7efd19dd65ed7a0
@@ -0,0 +1,295 @@
1
+ # typed: strict
2
+ require('parlour')
3
+
4
+ class SorbetRails::ActiveRecordRbiFormatter
5
+ extend T::Sig
6
+
7
+ Parameter = ::Parlour::RbiGenerator::Parameter
8
+
9
+ sig {returns(String)}
10
+ def generate_active_record_base_rbi
11
+ puts "-- Generate sigs for ActiveRecord::Base --"
12
+
13
+ parlour = T.let(Parlour::RbiGenerator.new, Parlour::RbiGenerator)
14
+
15
+ parlour.root.add_comments([
16
+ 'This is an autogenerated file for Rails\' ActiveRecord.',
17
+ 'Please rerun bundle exec rake rails_rbi:active_record to regenerate.'
18
+ ])
19
+
20
+ parlour.root.create_class('ActiveRecord::Base') do |class_rbi|
21
+ create_elem_specific_query_methods(class_rbi, type: 'T.attached_class', class_method: true)
22
+ create_general_query_methods(class_rbi, class_method: true)
23
+ end
24
+
25
+ parlour.rbi
26
+ end
27
+
28
+ sig {returns(String)}
29
+ def generate_active_record_relation_rbi
30
+ puts "-- Generate sigs for ActiveRecord::Relation --"
31
+
32
+ parlour = T.let(Parlour::RbiGenerator.new, Parlour::RbiGenerator)
33
+
34
+ parlour.root.add_comments([
35
+ 'This is an autogenerated file for Rails\' ActiveRecord.',
36
+ 'Please rerun bundle exec rake rails_rbi:active_record to regenerate.'
37
+ ])
38
+
39
+ parlour.root.create_class('ActiveRecord::Relation') do |class_rbi|
40
+ class_rbi.create_include("Enumerable")
41
+ class_rbi.create_constant(
42
+ "Elem",
43
+ value: "type_member(fixed: T.untyped)",
44
+ )
45
+
46
+ create_elem_specific_query_methods(class_rbi, type: 'Elem', class_method: false)
47
+ create_general_query_methods(class_rbi, class_method: false)
48
+
49
+ # Many methods that exist on the relation classes also exist on the model class
50
+ # by delegating to `:all` (e.g. `Model.any?` is really `Model.all.any?`). These
51
+ # methods (e.g. each, empty?) only exist on the relation classes.
52
+ class_rbi.create_method(
53
+ "each",
54
+ parameters: [
55
+ Parameter.new("&block", type: "T.proc.params(e: Elem).void")
56
+ ],
57
+ return_type: "T::Array[Elem]",
58
+ implementation: true,
59
+ )
60
+ class_rbi.create_method(
61
+ "flatten",
62
+ parameters: [ Parameter.new("level", type: "T.nilable(Integer)") ],
63
+ return_type: "T::Array[Elem]",
64
+ )
65
+ class_rbi.create_method("to_a", return_type: "T::Array[Elem]")
66
+ class_rbi.create_method(
67
+ "map",
68
+ type_parameters: [:U],
69
+ parameters: [ Parameter.new("&blk", type: "T.proc.params(arg0: Elem).returns(T.type_parameter(:U))") ],
70
+ return_type: "T::Array[T.type_parameter(:U)]",
71
+ )
72
+ class_rbi.create_method('empty?', return_type: "T::Boolean")
73
+ end
74
+
75
+ parlour.root.create_class("ActiveRecord::AssociationRelation", superclass: "ActiveRecord::Relation") do |class_rbi|
76
+ class_rbi.create_constant(
77
+ "Elem",
78
+ value: "type_member(fixed: T.untyped)",
79
+ )
80
+
81
+ # Ideally we shouldn't need to define these since this class inherits from
82
+ # ActiveRecord::Relation but the activerecord.rbi that sorbet generates
83
+ # defines some methods which sorbet finds instead of the methods inherited
84
+ # by ActiveRecord::Relation. Some of these methods have different arity or
85
+ # parameters than the ones defined by `create_elem_specific_query_methods` so
86
+ # we need to match the signatures in that conflicting rbi.
87
+ build_methods = %w(new build create create!)
88
+ build_methods.each do |build_method|
89
+ class_rbi.create_method(
90
+ build_method,
91
+ parameters: [
92
+ Parameter.new("*args", type: "T.untyped"),
93
+ Parameter.new(
94
+ "&block",
95
+ type: "T.nilable(T.proc.params(object: Elem).void)",
96
+ ),
97
+ ],
98
+ return_type: "Elem",
99
+ )
100
+ end
101
+ end
102
+
103
+ parlour.root.create_class("ActiveRecord::Associations::CollectionProxy", superclass: "ActiveRecord::Relation") do |class_rbi|
104
+ class_rbi.create_constant(
105
+ "Elem",
106
+ value: "type_member(fixed: T.untyped)",
107
+ )
108
+
109
+ # This _should_ work which would let us remove it from the enumerable_collections
110
+ # plugin but sorbet has a bug with T.any and generics.
111
+ # See: https://github.com/sorbet/sorbet/issues/2938
112
+ # push_methods = %w(<< append push concat)
113
+ # push_methods.each do |push_method|
114
+ # class_rbi.create_method(
115
+ # push_method,
116
+ # parameters: [
117
+ # Parameter.new("*records", type: "T.any(Elem, T::Array[Elem])"),
118
+ # ],
119
+ # return_type: "T.self_type",
120
+ # )
121
+ # end
122
+
123
+ # Ideally we shouldn't need to define these since this class inherits from
124
+ # ActiveRecord::Relation but the activerecord.rbi that sorbet generates
125
+ # defines some methods which sorbet finds instead of the methods inherited
126
+ # by ActiveRecord::Relation. Some of these methods have different arity or
127
+ # parameters than the ones defined by `create_elem_specific_query_methods` so
128
+ # we need to match the signatures in that conflicting rbi.
129
+ build_methods = %w(new build create create!)
130
+ build_methods.each do |build_method|
131
+ class_rbi.create_method(
132
+ build_method,
133
+ parameters: [
134
+ Parameter.new("attributes", type: "T.untyped", default: 'nil'),
135
+ Parameter.new(
136
+ "&block",
137
+ type: "T.nilable(T.proc.params(object: Elem).void)",
138
+ ),
139
+ ],
140
+ return_type: "Elem",
141
+ )
142
+ end
143
+
144
+ class_rbi.create_method(
145
+ "find",
146
+ parameters: [Parameter.new("*args", type: "T.untyped")],
147
+ return_type: "Elem",
148
+ )
149
+
150
+ if Rails.version =~ /^5\.0/
151
+ item_methods = %w(first second third third_to_last second_to_last last)
152
+ item_methods.each do |item_method|
153
+ class_rbi.create_method(
154
+ item_method,
155
+ parameters: [Parameter.new("*args", type: "T.untyped")],
156
+ return_type: "T.nilable(Elem)",
157
+ )
158
+ end
159
+
160
+ boolean_methods = %w(any? many?)
161
+ boolean_methods.each do |boolean_method|
162
+ class_rbi.create_method(boolean_method, return_type: "T::Boolean")
163
+ end
164
+ else
165
+ class_rbi.create_method(
166
+ "last",
167
+ parameters: [Parameter.new("limit", type: "T.untyped", default: "nil")],
168
+ return_type: "T.nilable(Elem)",
169
+ )
170
+ end
171
+
172
+ if Rails.version =~ /^5\.(0|1)/
173
+ class_rbi.create_method("to_a", return_type: "T::Array[Elem]")
174
+ end
175
+
176
+ class_rbi.create_method('empty?', return_type: "T::Boolean")
177
+ end
178
+
179
+ parlour.rbi
180
+ end
181
+
182
+ sig {
183
+ params(
184
+ class_rbi: Parlour::RbiGenerator::Namespace,
185
+ type: String,
186
+ class_method: T::Boolean,
187
+ ).void
188
+ }
189
+ def create_elem_specific_query_methods(class_rbi, type:, class_method:)
190
+ finder_methods = %w(find find_by find_by!)
191
+ finder_methods.each do |finder_method|
192
+ class_rbi.create_method(
193
+ finder_method,
194
+ parameters: [ Parameter.new("*args", type: "T.untyped") ],
195
+ return_type: (finder_method == 'find' || finder_method.ends_with?('!')) ? type : "T.nilable(#{type})",
196
+ class_method: class_method,
197
+ )
198
+ end
199
+
200
+ first_or_something_by_methods = %w(find_or_initialize_by find_or_create_by find_or_create_by!)
201
+ first_or_something_by_methods.each do |first_or_something_by_method|
202
+ class_rbi.create_method(
203
+ first_or_something_by_method,
204
+ parameters: [
205
+ Parameter.new("attributes", type: "T.untyped"),
206
+ Parameter.new(
207
+ "&block",
208
+ type: "T.nilable(T.proc.params(object: #{type}).void)",
209
+ ),
210
+ ],
211
+ return_type: type,
212
+ class_method: class_method
213
+ )
214
+ end
215
+
216
+ item_methods = %w(first first! second second! third third! third_to_last third_to_last! second_to_last second_to_last! last last!)
217
+ item_methods.each do |item_method|
218
+ class_rbi.create_method(
219
+ item_method,
220
+ return_type: item_method.ends_with?('!') ? type : "T.nilable(#{type})",
221
+ class_method: class_method,
222
+ )
223
+ end
224
+
225
+ build_methods = %w(create create! new build first_or_create first_or_create! first_or_initialize)
226
+ build_methods.each do |build_method|
227
+ # `build` method doesn't exist on the model, only on the relations
228
+ next if build_method == 'build' && class_method
229
+
230
+ # This needs to match the generated method signature in activerecord.rbi and
231
+ # in Rails 5.0 and 5.1 the param is a splat.
232
+ if Rails.version =~ /^5\.(0|1)/ && %w(new build create create!).include?(build_method)
233
+ param = Parameter.new("*args", type: "T.untyped")
234
+ else
235
+ param = Parameter.new("attributes", type: "T.untyped", default: 'nil')
236
+ end
237
+
238
+ class_rbi.create_method(
239
+ build_method,
240
+ parameters: [
241
+ param,
242
+ Parameter.new(
243
+ "&block",
244
+ type: "T.nilable(T.proc.params(object: #{type}).void)",
245
+ ),
246
+ ],
247
+ return_type: type,
248
+ class_method: class_method,
249
+ )
250
+ end
251
+
252
+ batch_methods = %w(find_each find_in_batches)
253
+ batch_methods.each do |batch_method|
254
+ inner_type = batch_method == 'find_each' ? type : "T::Array[#{type}]"
255
+
256
+ class_rbi.create_method(
257
+ batch_method,
258
+ parameters: [
259
+ Parameter.new("start:", type: "T.nilable(Integer)", default: "nil"),
260
+ Parameter.new("finish:", type: "T.nilable(Integer)", default: "nil"),
261
+ Parameter.new("batch_size:", type: "T.nilable(Integer)", default: "1000"),
262
+ Parameter.new("error_on_ignore:", type: "T.nilable(T::Boolean)", default: "nil"),
263
+ Parameter.new("&block", type: "T.nilable(T.proc.params(e: #{inner_type}).void)"),
264
+ ],
265
+ return_type: "T::Enumerator[#{inner_type}]",
266
+ class_method: class_method,
267
+ override: true,
268
+ )
269
+ end
270
+ end
271
+
272
+ sig {
273
+ params(
274
+ class_rbi: Parlour::RbiGenerator::Namespace,
275
+ class_method: T::Boolean,
276
+ ).void
277
+ }
278
+ def create_general_query_methods(class_rbi, class_method:)
279
+ class_rbi.create_method(
280
+ "exists?",
281
+ parameters: [ Parameter.new("conditions", type: "T.untyped", default: "nil") ],
282
+ return_type: "T::Boolean",
283
+ class_method: class_method,
284
+ )
285
+
286
+ boolean_methods = %w(any? many? none? one?)
287
+ boolean_methods.each do |boolean_method|
288
+ class_rbi.create_method(
289
+ boolean_method,
290
+ return_type: "T::Boolean",
291
+ class_method: class_method,
292
+ )
293
+ end
294
+ end
295
+ end
@@ -47,7 +47,6 @@ module SorbetRails
47
47
  :active_relation_where_not,
48
48
  :active_record_attribute,
49
49
  :active_record_assoc,
50
- :active_record_finder_methods,
51
50
  :custom_finder_methods,
52
51
  :enumerable_collections,
53
52
  ]
@@ -1,4 +1,6 @@
1
1
  # typed: false
2
+ require('sorbet-rails/deprecation.rb')
3
+
2
4
  module BooleanStringImpl
3
5
  def is_a?(type)
4
6
  return super unless type == BooleanString
@@ -17,6 +19,10 @@ module BooleanStringImpl
17
19
 
18
20
  def _is_a_boolean_string?
19
21
  return @cached_is_a unless @cached_is_a.nil?
22
+ SorbetRails::TypeAssertDeprecation.deprecation_warning(
23
+ :BooleanString,
24
+ 'Use TypedParam with T::Boolean type instead.'
25
+ )
20
26
  @cached_is_a = (self =~ /^(true|false)$/i) == 0
21
27
  end
22
28
  end
@@ -27,6 +33,10 @@ end
27
33
 
28
34
  class BooleanString < String
29
35
  def self.===(other)
36
+ SorbetRails::TypeAssertDeprecation.deprecation_warning(
37
+ :BooleanString,
38
+ 'Use TypedParam with T::Boolean type instead.'
39
+ )
30
40
  other.is_a?(BooleanString)
31
41
  end
32
42
  end
@@ -1,4 +1,6 @@
1
1
  # typed: false
2
+ require('sorbet-rails/deprecation.rb')
3
+
2
4
  module IntegerStringImpl
3
5
  def is_a?(type)
4
6
  return super unless type == IntegerString
@@ -17,6 +19,10 @@ module IntegerStringImpl
17
19
 
18
20
  def _is_a_integer_string?
19
21
  return @cached_is_a unless @cached_is_a.nil?
22
+ SorbetRails::TypeAssertDeprecation.deprecation_warning(
23
+ :IntegerString,
24
+ 'Use TypedParams with Integer type instead.'
25
+ )
20
26
  Integer(self, 10)
21
27
  @cached_is_a = true
22
28
  rescue ArgumentError => err
@@ -30,6 +36,10 @@ end
30
36
 
31
37
  class IntegerString < String
32
38
  def self.===(other)
39
+ SorbetRails::TypeAssertDeprecation.deprecation_warning(
40
+ :IntegerString,
41
+ 'Use TypedParams with Integer type instead.'
42
+ )
33
43
  other.is_a?(IntegerString)
34
44
  end
35
45
  end
@@ -28,3 +28,14 @@ end
28
28
  class ActiveRecord::AttributeMethods::TimeZoneConversion::TimeZoneConverter;
29
29
  def klass; end
30
30
  end
31
+
32
+ class ActiveModel::Validations::PresenceValidator
33
+ sig { returns(T::Hash[T.untyped, T.untyped]) }
34
+ attr_reader :options
35
+ end
36
+
37
+ module ActiveModel::Validations
38
+ module ClassMethods
39
+ def validators_on(*attributes); end
40
+ end
41
+ end
@@ -0,0 +1,5 @@
1
+ require 'active_support/deprecation'
2
+
3
+ module SorbetRails
4
+ TypeAssertDeprecation = ActiveSupport::Deprecation.new('0.7', 'SorbetRails')
5
+ end
@@ -35,7 +35,7 @@ class SorbetRails::ModelPlugins::ActiveRecordAssoc < SorbetRails::ModelPlugins::
35
35
  def populate_single_assoc_getter_setter(assoc_module_rbi, assoc_name, reflection)
36
36
  # TODO allow people to specify the possible values of polymorphic associations
37
37
  assoc_class = assoc_should_be_untyped?(reflection) ? "T.untyped" : "::#{reflection.klass.name}"
38
- assoc_type = belongs_to_and_required?(reflection) ? assoc_class : "T.nilable(#{assoc_class})"
38
+ assoc_type = (belongs_to_and_required?(reflection) || has_one_and_required?(reflection)) ? assoc_class : "T.nilable(#{assoc_class})"
39
39
 
40
40
  assoc_module_rbi.create_method(
41
41
  assoc_name.to_s,
@@ -56,6 +56,9 @@ class SorbetRails::ModelPlugins::ActiveRecordAssoc < SorbetRails::ModelPlugins::
56
56
  # optional (via `optional` or `!required` or `!belongs_to_required_by_default`)
57
57
  return false if !reflection.belongs_to?
58
58
 
59
+ column_def = @columns_hash[reflection.foreign_key.to_s]
60
+ db_required_config = column_def.present? && !column_def.null
61
+
59
62
  rails_required_config =
60
63
  if reflection.options.key?(:required)
61
64
  !!reflection.options[:required]
@@ -65,22 +68,34 @@ class SorbetRails::ModelPlugins::ActiveRecordAssoc < SorbetRails::ModelPlugins::
65
68
  !!reflection.active_record.belongs_to_required_by_default
66
69
  end
67
70
 
68
- column_def = @columns_hash[reflection.foreign_key.to_s]
69
- db_required_config = column_def.present? && !column_def.null
71
+ # We check for validations on both the column name (e.g. wizard_id) and
72
+ # association name (e.g. wizard).
73
+ rails_required_config ||= [column_def&.name, reflection.name].compact.any? { |n| attribute_has_unconditional_presence_validation?(n) }
70
74
 
71
75
  if rails_required_config && !db_required_config
72
76
  puts "Warning: belongs_to association #{reflection.name} is required at the application
73
77
  level but **nullable** at the DB level.\n Add a constraint at the DB level
74
78
  (using `null: false` and foreign key constraint) to ensure it is enforced.".squish!
75
79
  elsif !rails_required_config && db_required_config
76
- puts "Note: belongs_to association #{reflection.name} is specified as not-null at the
77
- DB level but **optional** at the application level.\n Add a constraint at the app level
78
- (using `optional: false`) as a validation hint to Rails.".squish!
80
+ if habtm_class?
81
+ puts "Note: belongs_to association #{reflection.name} is specified as not-null at the
82
+ DB level but will always be **optional** at the application level since it's part of a
83
+ has_and_belongs_to_many association.\n To resolve move to a 'has_many through:' association.".squish!
84
+ else
85
+ puts "Note: belongs_to association #{reflection.name} is specified as not-null at the
86
+ DB level but **optional** at the application level.\n Add a constraint at the app level
87
+ (using `optional: false`) as a validation hint to Rails.".squish!
88
+ end
79
89
  end
80
90
 
81
91
  rails_required_config || db_required_config
82
92
  end
83
93
 
94
+ sig { params(reflection: T.untyped).returns(T::Boolean) }
95
+ private def has_one_and_required?(reflection)
96
+ !!(reflection.has_one? && attribute_has_unconditional_presence_validation?(reflection.name))
97
+ end
98
+
84
99
  sig do
85
100
  params(
86
101
  assoc_module_rbi: T.untyped,
@@ -99,6 +114,12 @@ class SorbetRails::ModelPlugins::ActiveRecordAssoc < SorbetRails::ModelPlugins::
99
114
  assoc_name.to_s,
100
115
  return_type: relation_class,
101
116
  )
117
+ unless assoc_should_be_untyped?(reflection)
118
+ assoc_module_rbi.create_method(
119
+ "#{assoc_name.singularize}_ids",
120
+ return_type: "T::Array[Integer]",
121
+ )
122
+ end
102
123
  assoc_module_rbi.create_method(
103
124
  "#{assoc_name}=",
104
125
  parameters: [