sorbet-rails 0.5.9.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +4 -0
  3. data/.travis.yml +3 -3
  4. data/README.md +129 -52
  5. data/lib/bundled_rbi/active_record_base.rbi +83 -0
  6. data/lib/bundled_rbi/active_record_relation.rbi +43 -0
  7. data/lib/bundled_rbi/pluck_to_tstruct.rbi +0 -1
  8. data/lib/bundled_rbi/typed_params.rbi +9 -0
  9. data/lib/sorbet-rails.rb +1 -0
  10. data/lib/sorbet-rails/config.rb +0 -1
  11. data/lib/sorbet-rails/gem_plugins/elastic_search_plugin.rb +1 -1
  12. data/lib/sorbet-rails/gem_plugins/friendly_id_plugin.rb +1 -1
  13. data/lib/sorbet-rails/model_plugins/active_record_assoc.rb +3 -3
  14. data/lib/sorbet-rails/model_plugins/active_record_attribute.rb +85 -17
  15. data/lib/sorbet-rails/model_plugins/active_record_enum.rb +0 -2
  16. data/lib/sorbet-rails/model_plugins/active_record_finder_methods.rb +49 -12
  17. data/lib/sorbet-rails/model_plugins/active_record_querying.rb +1 -1
  18. data/lib/sorbet-rails/model_plugins/active_storage_methods.rb +1 -1
  19. data/lib/sorbet-rails/model_plugins/plugins.rb +0 -3
  20. data/lib/sorbet-rails/rails_mixins/active_record_overrides.rb +156 -5
  21. data/lib/sorbet-rails/rails_mixins/generated_url_helpers.rb +16 -0
  22. data/lib/sorbet-rails/railtie.rb +2 -1
  23. data/lib/sorbet-rails/tasks/rails_rbi.rake +2 -0
  24. data/lib/sorbet-rails/type_assert/actionpack.rbi +4 -0
  25. data/lib/sorbet-rails/type_assert/type_assert.rb +1 -1
  26. data/lib/sorbet-rails/typed_params.rb +22 -0
  27. data/lib/sorbet-rails/utils.rb +5 -0
  28. data/sorbet-rails.gemspec +5 -3
  29. data/spec/generators/rails-template.rb +7 -3
  30. data/spec/generators/sorbet_test_cases.rb +46 -1
  31. data/spec/sorbet_spec.rb +3 -1
  32. data/spec/support/v5.0/Gemfile.lock +36 -23
  33. data/spec/support/v5.0/app/models/wizard.rb +6 -3
  34. data/spec/support/v5.0/db/migrate/20190620000001_create_wizards.rb +1 -0
  35. data/spec/support/v5.0/db/schema.rb +1 -0
  36. data/spec/support/v5.0/sorbet_test_cases.rb +46 -1
  37. data/spec/support/v5.1/Gemfile.lock +37 -24
  38. data/spec/support/v5.1/app/models/wizard.rb +6 -3
  39. data/spec/support/v5.1/db/migrate/20190620000001_create_wizards.rb +1 -0
  40. data/spec/support/v5.1/db/schema.rb +1 -0
  41. data/spec/support/v5.1/sorbet_test_cases.rb +46 -1
  42. data/spec/support/v5.2/Gemfile.lock +75 -62
  43. data/spec/support/v5.2/app/models/wizard.rb +6 -3
  44. data/spec/support/v5.2/db/migrate/20190620000001_create_wizards.rb +1 -0
  45. data/spec/support/v5.2/db/schema.rb +1 -0
  46. data/spec/support/v5.2/sorbet_test_cases.rb +46 -1
  47. data/spec/support/v6.0/Gemfile.lock +89 -76
  48. data/spec/support/v6.0/app/models/wizard.rb +6 -3
  49. data/spec/support/v6.0/db/migrate/20190620000001_create_wizards.rb +1 -0
  50. data/spec/support/v6.0/db/schema.rb +1 -0
  51. data/spec/support/v6.0/sorbet_test_cases.rb +46 -1
  52. data/spec/test_data/v5.0/expected_internal_metadata.rbi +33 -54
  53. data/spec/test_data/v5.0/expected_potion.rbi +33 -54
  54. data/spec/test_data/v5.0/expected_robe.rbi +33 -54
  55. data/spec/test_data/v5.0/expected_schema_migration.rbi +33 -54
  56. data/spec/test_data/v5.0/expected_school.rbi +33 -54
  57. data/spec/test_data/v5.0/expected_spell_book.rbi +46 -52
  58. data/spec/test_data/v5.0/expected_squib.rbi +42 -54
  59. data/spec/test_data/v5.0/expected_wand.rbi +47 -52
  60. data/spec/test_data/v5.0/expected_wizard.rbi +119 -56
  61. data/spec/test_data/v5.0/expected_wizard_wo_spellbook.rbi +119 -56
  62. data/spec/test_data/v5.1/expected_internal_metadata.rbi +33 -54
  63. data/spec/test_data/v5.1/expected_potion.rbi +33 -54
  64. data/spec/test_data/v5.1/expected_robe.rbi +33 -54
  65. data/spec/test_data/v5.1/expected_schema_migration.rbi +33 -54
  66. data/spec/test_data/v5.1/expected_school.rbi +33 -54
  67. data/spec/test_data/v5.1/expected_spell_book.rbi +46 -52
  68. data/spec/test_data/v5.1/expected_squib.rbi +42 -54
  69. data/spec/test_data/v5.1/expected_wand.rbi +47 -52
  70. data/spec/test_data/v5.1/expected_wizard.rbi +119 -56
  71. data/spec/test_data/v5.1/expected_wizard_wo_spellbook.rbi +119 -56
  72. data/spec/test_data/v5.2/expected_attachment.rbi +33 -54
  73. data/spec/test_data/v5.2/expected_blob.rbi +33 -54
  74. data/spec/test_data/v5.2/expected_internal_metadata.rbi +33 -54
  75. data/spec/test_data/v5.2/expected_potion.rbi +33 -54
  76. data/spec/test_data/v5.2/expected_robe.rbi +33 -54
  77. data/spec/test_data/v5.2/expected_schema_migration.rbi +33 -54
  78. data/spec/test_data/v5.2/expected_school.rbi +33 -54
  79. data/spec/test_data/v5.2/expected_spell_book.rbi +46 -52
  80. data/spec/test_data/v5.2/expected_squib.rbi +42 -54
  81. data/spec/test_data/v5.2/expected_wand.rbi +47 -52
  82. data/spec/test_data/v5.2/expected_wizard.rbi +119 -56
  83. data/spec/test_data/v5.2/expected_wizard_wo_spellbook.rbi +119 -56
  84. data/spec/test_data/v6.0/expected_attachment.rbi +33 -54
  85. data/spec/test_data/v6.0/expected_blob.rbi +33 -54
  86. data/spec/test_data/v6.0/expected_internal_metadata.rbi +33 -54
  87. data/spec/test_data/v6.0/expected_potion.rbi +33 -54
  88. data/spec/test_data/v6.0/expected_robe.rbi +33 -54
  89. data/spec/test_data/v6.0/expected_schema_migration.rbi +33 -54
  90. data/spec/test_data/v6.0/expected_school.rbi +33 -54
  91. data/spec/test_data/v6.0/expected_spell_book.rbi +46 -52
  92. data/spec/test_data/v6.0/expected_squib.rbi +42 -54
  93. data/spec/test_data/v6.0/expected_wand.rbi +47 -52
  94. data/spec/test_data/v6.0/expected_wizard.rbi +119 -56
  95. data/spec/test_data/v6.0/expected_wizard_wo_spellbook.rbi +119 -56
  96. data/spec/typed_enum_spec.rb +55 -0
  97. data/spec/typed_params_spec.rb +91 -0
  98. metadata +41 -4
  99. data/lib/sorbet-rails/model_plugins/active_record_factory_methods.rb +0 -28
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d1f0f0abf650a5deb1d7a365fc7feff0d07965c6aba43cdd5f8fc856351c9a54
4
- data.tar.gz: 134c9924721309eda4b8a50c80bb97380e1d6628f7d4fbb6fb9447d7ec836b63
3
+ metadata.gz: 58c588bf2eb9eb09d2c4322d54436840452bb5a026e6ca4132df8e651aa26968
4
+ data.tar.gz: 11b54b70f6c86f5d97152fc5a7c9f0b48b021b8be03472fcb83014557353dc4c
5
5
  SHA512:
6
- metadata.gz: 1170b14c1c3b0d1f3751f88b3d5b85e48b61569432eb9941fd569acf4089e1f466ebcd27c4cb41815adc5b6203cd40759276b1f6210efc435f859a220f8b86af
7
- data.tar.gz: 4fd1da4f8639130b3a281fd68196f7228eed591d0e0b8fb173e332e06899ab32363a7d2d5d14b69741f7c931a9320758b83bbb42875ae1ece4119e89ac33c223
6
+ metadata.gz: 6b88d51c0fae405a9249974746b250ad6ad2ce8ca09a5620c385105bffd0617dd2fbf5584a07a35abb35104c8242a1954156be9f98ddc456554803ad3b2a92ac
7
+ data.tar.gz: 98c2836dcbbb20798d0b57579eb7c3a07175bf8e5c58ed062cb652d2c7826bf62c1586cc4a9a5337702e88151862c8caf4cda81a21765f583a3c85caefc3d659
@@ -0,0 +1,4 @@
1
+ lib/sorbet/* linguist-generated
2
+ spec/test_data/* linguist-generated
3
+ spec/support/* linguist-generated
4
+
@@ -8,15 +8,15 @@ env:
8
8
  global:
9
9
  secure: S3rpBSDgDQjhA11/80dZ4RUlBP3XLJWOgvcl1eySFdXqHCZGS5p8Fa97yEvZHWiPInUTNhdvyQ5LHlgzu1CCZ9+MOykpEgYo+1yIueriv9Ia5PUNFbwhRp2eBkluSUMPNPOxf15mUlgAGdScnssRL9zftYel29dw4yg+BQnnkLOqpnLO7wHTjouEmp7M8Y/1j0ScIZ0tZL1ovDq70Lst+TJKHm4jdBbwoNmILIwNBgnzZT1LdAuaXVHEmwLvy0GLvClewuSmToJ4jyjuJorCjyMhyoBLrutSuN4KaJcSlP/LVtH4ZCFkFDayDMiVcSyUE3asSnL3yxGL0K99lgwNk4D58WWic7N+0UKnGoiMQOHC9EIsQKaxfXMOpO+Cq5wYhgmFzPGF4iL6ZBNCehcc+ToBojprmJw/0Ev71OS2YGvu5SZpYco5AvJeR73IUA2K0rjUEeY3gXm60aIac09kQuuminN3hUkJ894YoA35B8IEU0uW2uL5n/vX2Y8SD6RV8csbKlhpJmphYYWQQOYjHuFoW4MVnnrFHq6YIUDQpi6/We3I4xH3kvfjIpSemrYOmfF4PPXCarWA7ryI9ytR7BloQ0NQH7hqp1hbKN86LAy8zanW08xNOrxI7VDdbtV1k6mf+wiV1W2Vo54HYEDIw0+jwmW/uKWatTyRo5AUsMc=
10
10
  rvm:
11
- - 2.3
12
11
  - 2.4
13
12
  - 2.5
14
13
  - 2.6
14
+ - 2.7
15
15
  - ruby-head
16
16
  matrix:
17
17
  include:
18
18
  - rvm: 2.5
19
- env: RAILS_VERSION=5.2 SORBET_VERSION=0.4.4929
19
+ env: RAILS_VERSION=5.2 SORBET_VERSION=0.5.5400
20
20
  exclude:
21
21
  - rvm: 2.3
22
22
  env: RAILS_VERSION=6.0
@@ -28,7 +28,7 @@ before_install:
28
28
  - gem install bundler -v 2.0.1 --no-doc
29
29
  - gem install bundler -v 1.17.3 --no-doc
30
30
  script:
31
- - (cd lib && bundle exec srb tc)
31
+ - bundle exec srb tc
32
32
  - "./spec/bin/run_spec.sh"
33
33
  deploy:
34
34
  provider: rubygems
data/README.md CHANGED
@@ -7,9 +7,9 @@ A set of tools to make the [Sorbet](https://sorbet.org) typechecker work with Ru
7
7
 
8
8
  This gem adds a few Rake tasks to generate Ruby Interface (RBI) files for dynamic methods generated by Rails. It also includes signatures for related Rails classes. The RBI files are added to a `sorbet/rails-rbi/` folder.
9
9
 
10
- `sorbet-rails` supports Rails 4.2 or later.
10
+ `sorbet-rails` supports Rails 5+ or later.
11
11
 
12
- **Notice**: From v0.6, we'll stop supporting Rails 4.2. Fixes targeting Rails 4.2 specifically can be merged to v0.5.x branch.
12
+ **Notice**: we no longer supports Rails 4.2. [Version 0.5.6](https://github.com/chanzuckerberg/sorbet-rails/releases/tag/v0.5.6) is the last version supporting Rails 4.2.
13
13
 
14
14
  ## Initial Setup
15
15
 
@@ -72,44 +72,120 @@ It is possible to add custom RBI generation logic for your custom module or gems
72
72
  We also add following methods to make type-checking more easily:
73
73
  - [`find_n`, `first_n`, `last_n`](https://github.com/chanzuckerberg/sorbet-rails#find-first-and-last)
74
74
  - [`pluck_to_tstruct`](#pluck_to_tstruct-instead-of-pluck)
75
+ - [`typed_enum`](#enums)
75
76
 
76
- ### Controllers
77
- ```sh
78
- rake rails_rbi:custom
77
+ #### `pluck_to_tstruct` instead of `pluck`
78
+
79
+ The [`pluck` method](https://apidock.com/rails/ActiveRecord/Calculations/pluck) in Rails is a performant way to query value without instantiating ActiveRecord objects. However, it doesn't have any type information: it doesn't have type information (or name) of the attribute plucked. Sorbet-rails provides `pluck_to_tstruct` method as a replacement that returns an array of `T::Struct` instead. The attributes plucked is based on props defined in the `T::Struct`
80
+
81
+ ```ruby
82
+ # -- API
83
+ Arel.pluck_to_tstruct(TA[ <TStructSubClass> ].new)
84
+
85
+ # -- example
86
+ class WizardStruct < T::Struct
87
+ const :name, String
88
+ const :house, T.nilable(String)
89
+ end
90
+
91
+ Wizard.pluck_to_tstruct(TA[WizardStruct].new) # T::Array[WizardStruct]
92
+ Wizard.all.pluck_to_tstruct(TA[WizardStruct].new) # T::Array[WizardStruct]
93
+ ```
94
+
95
+ This method is based on [pluck_to_hash](https://github.com/girishso/pluck_to_hash) gem.
96
+
97
+ #### Enums
98
+
99
+ If you use [Rails `enum`](https://guides.rubyonrails.org/active_record_querying.html#enums), `sorbet-rails` will generate a corresponding `T::Enum`. It will also include getters, setters, and scope methods in the rbi file it generates.
100
+
101
+ ie.
102
+
103
+ ```ruby
104
+ enum house: {
105
+ Gryffindor: 0,
106
+ Hufflepuff: 1,
107
+ Ravenclaw: 2,
108
+ Slytherin: 3,
109
+ }
110
+ ```
111
+
112
+ Will generate this enum:
113
+
114
+ ```ruby
115
+ class Wizard::House < T::Enum
116
+ enums do
117
+ Gryffindor = new('Gryffindor')
118
+ Hufflepuff = new('Hufflepuff')
119
+ Ravenclaw = new('Ravenclaw')
120
+ Slytherin = new('Slytherin')
121
+ end
122
+ end
123
+ ```
124
+
125
+ And these methods:
126
+
127
+ ```ruby
128
+ sig { returns(T.nilable(String)) }
129
+ def house; end
130
+
131
+ sig { params(value: T.nilable(T.any(Integer, String, Symbol))).void }
132
+ def house=(value); end
133
+
134
+ sig { returns(T.nilable(Wizard::House)) }
135
+ def typed_house; end
136
+
137
+ sig { params(value: T.nilable(Wizard::House)).void }
138
+ def typed_house=(value); end
79
139
  ```
80
140
 
81
- `sorbet-rails` adds methods to extract typed parameters from `params`, namely `require_typed` and `fetch_typed`. They are direct replacement of `require` and `fetch` that return typed object. They have the same API their counterpart, with an addition of the parameter's type, which can be any type understood by `sorbet`
141
+ Alternatively, you can replace your call to `enum` with `typed_enum`. This will hide the Rails methods (`house`) from Sorbet static-check (they are still usable in un-checked files).
82
142
 
83
- This is the conversion in essence:
84
143
  ```ruby
85
- params.require(:key) -> params.require_typed(:key, TA[Type].new)
86
- params.fetch(:key) -> params.fetch_typed(:key, TA[Type].new)
87
- params.fetch(:key, default_value) -> params.fetch_typed(:key, TA[Type].new, default_value)
88
- params[:key] -> params.fetch_typed(:key, TA[T.nilable(Type)].new, nil)
144
+ typed_enum house: {
145
+ Gryffindor: 0,
146
+ Hufflepuff: 1,
147
+ Ravenclaw: 2,
148
+ Slytherin: 3,
149
+ }
89
150
  ```
90
151
 
91
- For example:
152
+ Generates only typed enum setter & getter:
153
+
92
154
  ```ruby
93
- # require_typed
94
- key = params.require_typed(:key, TA[String].new)
95
- T.reveal_type(key) # String
96
-
97
- # nested params
98
- nested_params = params.require_typed(:nested, TA[ActionController::Parameters].new)
99
- T.reveal_type(nested_params) # ActionController::Parameters
100
- key = nested_params.require_typed(:key, TA[String.new])
101
- T.reveal_type(key) # String
102
-
103
- # fetch_typed
104
- key = params.fetch_typed(:key, TA[T.nilable(String)].new) # raises error if params doesn't have :key
105
- T.reveal_type(key) # T.nilable(String)
106
-
107
- key = params.fetch_typed(:key, TA[T.nilable(String)].new, nil) # returns nil when key doesn't have :key
108
- T.reveal_type(key) # T.nilable(String)
155
+ sig { returns(T.nilable(Wizard::House)) }
156
+ def typed_house; end
157
+
158
+ sig { params(value: T.nilable(Wizard::House)).void }
159
+ def typed_house=(value); end
160
+ ```
161
+
162
+ ### Controllers
163
+ ```sh
164
+ ❯ rake rails_rbi:custom
109
165
  ```
110
- The parameters are type-checked both statically and at runtime.
111
166
 
112
- Note: The API `TA[...].new` may seem verbose, but necessary to support this feature. Ideally, the API can be simply `require_typed(:key, Type)`. However, `sorbet` [doesn't support](http://github.com/sorbet/sorbet/issues/62) defining a method that accept a type and return an instance of the type. The library provides a wrapper `TA` (which stands for `TypeAssert`) in order to achieve the behavior. If this feature is supported by `sorbet` in the future, it will be easy to codemod to remove the `TA[...].new` part from your code.
167
+ `sorbet-rails` adds `TypedParams` to extact typed controller parameters.
168
+
169
+ TypedParams takes a parameter definition, which is a subclass of `T::Struct` and coerces the `params` object into an instance of that subclass using [sorbet-coerce](https://github.com/chanzuckerberg/sorbet-coerce):
170
+ ```ruby
171
+ class MyCoolController < ApplicationController
172
+ class MyActionParams < T::Struct
173
+ const :id, T.nilable(Integer)
174
+ const :show, T.nilable(T::Boolean)
175
+ const :wands, T::Array[Integer]
176
+ end
177
+ sig { void }
178
+ def my_action
179
+ typed_params = TypedParams[MyActionParams].new.extract!(params)
180
+ # T.reveal_type(typed_params) => MyActionParams
181
+ # T.reveal_type(typed_params.show) => T.nilable(T::Boolean)
182
+ end
183
+ end
184
+ ```
185
+ If it fails to coerce the params into the right type, an `ActionController::BadRequest` exception will be raised with the coercion context for debugging.
186
+
187
+ Note: The API `TypedParams[...].new.extract!` may seem verbose, but necessary to support this feature. Ideally, the API can be simply `TypedParams.extract!(...)`. However, `sorbet` [doesn't support](http://github.com/sorbet/sorbet/issues/62) defining a method that accept a type and return an instance of the type. If this feature is supported by `sorbet` in the future, it will be easy to codemod to be `TypedParams.extract(...)!` part from your code.
188
+ Note: [`require_typed` and `fetch_typed`](https://github.com/chanzuckerberg/sorbet-rails/blob/v0.5.9.1/README.md) are deprecated in favor of `TypedParams`.
113
189
 
114
190
  ### Routes
115
191
 
@@ -157,8 +233,11 @@ specific environment group (eg. `development` only).
157
233
 
158
234
  - Relation class: Making the relations available at runtime (they are normally private constants, the gem makes them public)
159
235
  - Examples: `User::ActiveRecord_Relation`, `User::ActiveRecord_AssociationRelation`
160
- - Model: `find_n`, `first_n`, `last_n` are the methods we added. `pluck_to_tstruct` as well.
161
- - Controller: adding `fetch_typed` and `require_typed`
236
+ - Model:
237
+ - `find_n`, `first_n`, `last_n`
238
+ - `pluck_to_tstruct`
239
+ - `typed_enum`
240
+ - Controller: using `TypedParams`
162
241
 
163
242
  In addition to `require`ing `sorbet-rails`, you must also run
164
243
  `rake rails_rbi:custom`, which will produce the RBI for these runtime features.
@@ -188,6 +267,22 @@ class ModelName
188
267
  end
189
268
  ```
190
269
 
270
+ ### Replace `Rails.application.routes.url_helpers`
271
+
272
+ When using url helpers like _url or _path methods outside of a controller,
273
+ we usually have to add `include Rails.application.routes.url_helpers` in the class.
274
+ However, Sorbet does not allow code that
275
+ [includes dynamic module](https://sorbet.org/docs/error-reference#4002).
276
+ Sorbet Rails provides a drop-in replacement module for the dynamic url_helpers module, called `GeneratedUrlHelpers`.
277
+ Following code change should resolve the sorbet type-check error:
278
+
279
+ ```ruby
280
+ class MyClass
281
+ - include Rails.application.routes.url_helpers
282
+ + include GeneratedUrlHelpers
283
+ end
284
+ ```
285
+
191
286
  ### `find`, `first` and `last`
192
287
 
193
288
  These 3 methods can either return a single nilable record or an array of records. Sorbet does not allow us to define multiple signatures for a function ([except stdlib](https://github.com/chanzuckerberg/sorbet-rails/issues/18)). It doesn't support defining one function signature that has varying returning value depending on the input parameter type. We opt to define the most commonly used signature for these methods, and monkey-patch new functions for the secondary use case.
@@ -204,7 +299,9 @@ Following are the list of attribute dynamic methods and their static counterpart
204
299
  - `find_by_<attributes>` -> `find_by(<attributes>)`
205
300
  - `find_by_<attributes>!` -> `find_by!(<attributes>)`
206
301
  - `<attribute>_changed?` -> `attribute_changed?(<attribute>)`
302
+ - `<attribute>_was` -> `attribute_was(<attribute>)`
207
303
  - `saved_change_to_<attribute>?` -> `saved_change_to_attribute?(<attribute>)`
304
+ - `<attribute>_before_type_cast` -> `read_attribute_before_type_cast(<attribute>)`
208
305
 
209
306
  ### `after_commit` and other callbacks
210
307
 
@@ -259,26 +356,6 @@ Model.unscoped.scoping do … end
259
356
 
260
357
  The [`select` method](https://apidock.com/rails/v4.0.2/ActiveRecord/QueryMethods/select) in Rails has two modes: it can be given a list of symbols, in which case rails will only return the given columns from the database, or it can be given a block, in which case it acts like [`Enumerable.select`](https://ruby-doc.org/core-2.6.4/Enumerable.html) and returns an array. We have chosen to support the first use case. If you want to pass a block to `select`, you can simply call `to_a` before you do. Note that this would be done within the `select` call anyway, so the performance penalty will be minimal.
261
358
 
262
- ### `pluck_to_tstruct` instead of `pluck`
263
-
264
- The [`pluck` method](https://apidock.com/rails/ActiveRecord/Calculations/pluck) in Rails is a performant way to query value without instantiating ActiveRecord objects. However, it doesn't have any type information: it doesn't have type information (or name) of the attribute plucked. Sorbet-rails provides `pluck_to_tstruct` method as a replacement that does the same thing, but creates `T::Struct` object instead, and returns an array of `T::Struct`. The attributes plucked is based on props defined in the `T::Struct`
265
-
266
- ```ruby
267
- # -- API
268
- Arel.pluck_to_tstruct(TA[ <TStructSubClass> ].new)
269
-
270
- # -- example
271
- class WizardStruct < T::Struct
272
- const :name, String
273
- const :house, T.nilable(String)
274
- end
275
-
276
- Wizard.pluck_to_tstruct(TA[WizardStruct].new) # T::Array[WizardStruct]
277
- Wizard.all.pluck_to_tstruct(TA[WizardStruct].new) # T::Array[WizardStruct]
278
- ```
279
-
280
- This method is based on [pluck_to_hash](https://github.com/girishso/pluck_to_hash) gem.
281
-
282
359
  ## Extending Model Generation Task with Custom Plugins
283
360
 
284
361
  `sorbet-rails` support a customizable plugin system that you can use to generate additional RBI for each model. This will be useful to generate RBI for methods dynamically added by gems or private concerns. If you write plugins for public gems, please feel free to contribute it to this repo.
@@ -0,0 +1,83 @@
1
+ # typed: strong
2
+ class ActiveRecord::Base
3
+ sig { params(args: T.untyped).returns(T.attached_class) }
4
+ def self.find(*args); end
5
+
6
+ sig { params(args: T.untyped).returns(T.nilable(T.attached_class)) }
7
+ def self.find_by(*args); end
8
+
9
+ sig { params(args: T.untyped).returns(T.attached_class) }
10
+ def self.find_by!(*args); end
11
+
12
+ sig {
13
+ params(
14
+ attributes: T.untyped,
15
+ block: T.nilable(T.proc.params(object: T.attached_class).void),
16
+ ).
17
+ returns(T.attached_class)
18
+ }
19
+ def self.find_or_initialize_by(attributes, &block); end
20
+
21
+ sig {
22
+ params(
23
+ attributes: T.untyped,
24
+ block: T.nilable(T.proc.params(object: T.attached_class).void),
25
+ ).
26
+ returns(T.attached_class)
27
+ }
28
+ def self.find_or_create_by(attributes, &block); end
29
+
30
+ sig {
31
+ params(
32
+ attributes: T.untyped,
33
+ block: T.nilable(T.proc.params(object: T.attached_class).void),
34
+ ).
35
+ returns(T.attached_class)
36
+ }
37
+ def self.find_or_create_by!(attributes, &block); end
38
+
39
+ sig { returns(T.nilable(T.attached_class)) }
40
+ def self.first; end
41
+
42
+ sig { returns(T.attached_class) }
43
+ def self.first!; end
44
+
45
+ sig { returns(T.nilable(T.attached_class)) }
46
+ def self.second; end
47
+
48
+ sig { returns(T.attached_class) }
49
+ def self.second!; end
50
+
51
+ sig { returns(T.nilable(T.attached_class)) }
52
+ def self.third; end
53
+
54
+ sig { returns(T.attached_class) }
55
+ def self.third!; end
56
+
57
+ sig { returns(T.nilable(T.attached_class)) }
58
+ def self.third_to_last; end
59
+
60
+ sig { returns(T.attached_class) }
61
+ def self.third_to_last!; end
62
+
63
+ sig { returns(T.nilable(T.attached_class)) }
64
+ def self.second_to_last; end
65
+
66
+ sig { returns(T.attached_class) }
67
+ def self.second_to_last!; end
68
+
69
+ sig { returns(T.nilable(T.attached_class)) }
70
+ def self.last; end
71
+
72
+ sig { returns(T.attached_class) }
73
+ def self.last!; end
74
+
75
+ sig { params(attributes: T.untyped, block: T.untyped).returns(T.attached_class) }
76
+ def self.create(attributes = nil, &block); end
77
+
78
+ sig { params(attributes: T.untyped, block: T.untyped).returns(T.attached_class) }
79
+ def self.create!(attributes = nil, &block); end
80
+
81
+ sig { params(attributes: T.untyped, block: T.untyped).returns(T.attached_class) }
82
+ def self.new(attributes = nil, &block); end
83
+ end
@@ -1,5 +1,7 @@
1
1
  # typed: strong
2
2
  class ActiveRecord::Relation
3
+ Elem = type_member(fixed: T.untyped)
4
+
3
5
  sig { params(args: T.untyped).returns(Elem) }
4
6
  def find(*args); end
5
7
 
@@ -9,6 +11,33 @@ class ActiveRecord::Relation
9
11
  sig { params(args: T.untyped).returns(Elem) }
10
12
  def find_by!(*args); end
11
13
 
14
+ sig {
15
+ params(
16
+ attributes: T.untyped,
17
+ block: T.nilable(T.proc.params(object: Elem).void)
18
+ ).
19
+ returns(Elem)
20
+ }
21
+ def find_or_initialize_by(attributes, &block); end
22
+
23
+ sig {
24
+ params(
25
+ attributes: T.untyped,
26
+ block: T.nilable(T.proc.params(object: Elem).void)
27
+ ).
28
+ returns(Elem)
29
+ }
30
+ def find_or_create_by(attributes, &block); end
31
+
32
+ sig {
33
+ params(
34
+ attributes: T.untyped,
35
+ block: T.nilable(T.proc.params(object: Elem).void)
36
+ ).
37
+ returns(Elem)
38
+ }
39
+ def find_or_create_by!(attributes, &block); end
40
+
12
41
  sig { returns(T.nilable(Elem)) }
13
42
  def first; end
14
43
 
@@ -45,6 +74,17 @@ class ActiveRecord::Relation
45
74
  sig { returns(Elem) }
46
75
  def last!; end
47
76
 
77
+ sig do
78
+ override.params(
79
+ start: T.nilable(Integer),
80
+ finish: T.nilable(Integer),
81
+ batch_size: T.nilable(Integer),
82
+ error_on_ignore: T.nilable(T::Boolean),
83
+ block: T.nilable(T.proc.params(e: Elem).void) # block is optional, eg. you can do Klass.find_each.map
84
+ ).returns(T::Array[Elem])
85
+ end
86
+ def find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil, &block); end
87
+
48
88
  sig { override.params(block: T.proc.params(e: Elem).void).returns(T::Array[Elem]) }
49
89
  def each(&block); end
50
90
 
@@ -68,6 +108,9 @@ class ActiveRecord::Relation
68
108
  sig { returns(T::Boolean) }
69
109
  def any?; end
70
110
 
111
+ sig { returns(T::Boolean) }
112
+ def empty?; end
113
+
71
114
  sig { returns(T::Boolean) }
72
115
  def many?; end
73
116
 
@@ -17,6 +17,5 @@ class ActiveRecord::Base
17
17
  end
18
18
 
19
19
  class ActiveRecord::Relation
20
- Elem = type_member
21
20
  include SorbetRails::PluckToTStruct
22
21
  end
@@ -0,0 +1,9 @@
1
+ # typed: true
2
+ module TypedParams
3
+ extend T::Generic
4
+
5
+ Elem = type_member
6
+
7
+ sig { params(args: ActionController::Parameters, raise_coercion_error: T.nilable(T::Boolean)).returns(Elem) }
8
+ def extract!(args, raise_coercion_error: nil); end
9
+ end