rigortype 0.1.9 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/rigor/analysis/baseline.rb +51 -15
  4. data/lib/rigor/analysis/runner.rb +67 -9
  5. data/lib/rigor/analysis/worker_session.rb +13 -4
  6. data/lib/rigor/cache/rbs_descriptor.rb +21 -2
  7. data/lib/rigor/cache/rbs_environment.rb +2 -1
  8. data/lib/rigor/cli/annotate_command.rb +57 -7
  9. data/lib/rigor/cli/baseline_command.rb +4 -3
  10. data/lib/rigor/cli/coverage_command.rb +126 -0
  11. data/lib/rigor/cli/coverage_renderer.rb +162 -0
  12. data/lib/rigor/cli/coverage_report.rb +75 -0
  13. data/lib/rigor/cli/mcp_command.rb +70 -0
  14. data/lib/rigor/cli.rb +88 -5
  15. data/lib/rigor/environment/rbs_loader.rb +46 -5
  16. data/lib/rigor/environment/reporters.rb +3 -2
  17. data/lib/rigor/environment.rb +159 -4
  18. data/lib/rigor/inference/def_return_typer.rb +98 -0
  19. data/lib/rigor/inference/expression_typer.rb +143 -12
  20. data/lib/rigor/inference/method_dispatcher/constant_folding.rb +5 -0
  21. data/lib/rigor/inference/method_dispatcher/literal_string_folding.rb +62 -15
  22. data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +115 -7
  23. data/lib/rigor/inference/precision_scanner.rb +131 -0
  24. data/lib/rigor/inference/statement_evaluator.rb +26 -2
  25. data/lib/rigor/mcp/loop.rb +43 -0
  26. data/lib/rigor/mcp/server.rb +263 -0
  27. data/lib/rigor/mcp.rb +16 -0
  28. data/lib/rigor/plugin/base.rb +28 -5
  29. data/lib/rigor/plugin/manifest.rb +33 -5
  30. data/lib/rigor/plugin/registry.rb +21 -0
  31. data/lib/rigor/plugin/source_rbs_synthesis_reporter.rb +51 -0
  32. data/lib/rigor/sig_gen/generator.rb +150 -75
  33. data/lib/rigor/type/combinator.rb +57 -0
  34. data/lib/rigor/version.rb +1 -1
  35. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/analyzer.rb +190 -0
  36. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +189 -0
  37. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +81 -0
  38. data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +142 -0
  39. data/plugins/rigor-actioncable/lib/rigor-actioncable.rb +3 -0
  40. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/analyzer.rb +178 -0
  41. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +310 -0
  42. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_index.rb +76 -0
  43. data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +177 -0
  44. data/plugins/rigor-actionmailer/lib/rigor-actionmailer.rb +3 -0
  45. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +589 -0
  46. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_discoverer.rb +150 -0
  47. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/controller_index.rb +123 -0
  48. data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +247 -0
  49. data/plugins/rigor-actionpack/lib/rigor-actionpack.rb +3 -0
  50. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/analyzer.rb +114 -0
  51. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_discoverer.rb +177 -0
  52. data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +65 -0
  53. data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +117 -0
  54. data/plugins/rigor-activejob/lib/rigor-activejob.rb +3 -0
  55. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/analyzer.rb +273 -0
  56. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/inflector.rb +114 -0
  57. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +561 -0
  58. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_index.rb +194 -0
  59. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_parser.rb +240 -0
  60. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/schema_table.rb +94 -0
  61. data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +514 -0
  62. data/plugins/rigor-activerecord/lib/rigor-activerecord.rb +8 -0
  63. data/plugins/rigor-activerecord/sig/active_record/relation.rbs +182 -0
  64. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +78 -0
  65. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_discoverer.rb +162 -0
  66. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/attachment_index.rb +43 -0
  67. data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +170 -0
  68. data/plugins/rigor-activestorage/lib/rigor-activestorage.rb +8 -0
  69. data/plugins/rigor-activesupport-core-ext/lib/rigor/plugin/activesupport_core_ext.rb +34 -0
  70. data/plugins/rigor-activesupport-core-ext/lib/rigor-activesupport-core-ext.rb +20 -0
  71. data/plugins/rigor-activesupport-core-ext/sig/active_support/core_ext.rbs +463 -0
  72. data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +108 -0
  73. data/plugins/rigor-devise/lib/rigor-devise.rb +8 -0
  74. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema/schema_scanner.rb +285 -0
  75. data/plugins/rigor-dry-schema/lib/rigor/plugin/dry_schema.rb +124 -0
  76. data/plugins/rigor-dry-schema/lib/rigor-dry-schema.rb +8 -0
  77. data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +116 -0
  78. data/plugins/rigor-dry-struct/lib/rigor-dry-struct.rb +8 -0
  79. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types/alias_scanner.rb +341 -0
  80. data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +120 -0
  81. data/plugins/rigor-dry-types/lib/rigor-dry-types.rb +8 -0
  82. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation/contract_scanner.rb +120 -0
  83. data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +85 -0
  84. data/plugins/rigor-dry-validation/lib/rigor-dry-validation.rb +7 -0
  85. data/plugins/rigor-dry-validation/sig/dry_validation.rbs +25 -0
  86. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +177 -0
  87. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +242 -0
  88. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +56 -0
  89. data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +174 -0
  90. data/plugins/rigor-factorybot/lib/rigor-factorybot.rb +3 -0
  91. data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +409 -0
  92. data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +114 -0
  93. data/plugins/rigor-graphql/lib/rigor-graphql.rb +8 -0
  94. data/plugins/rigor-hanami/lib/rigor/plugin/hanami/action_checker.rb +124 -0
  95. data/plugins/rigor-hanami/lib/rigor/plugin/hanami.rb +111 -0
  96. data/plugins/rigor-hanami/lib/rigor-hanami.rb +3 -0
  97. data/plugins/rigor-hanami/sig/hanami_action.rbs +78 -0
  98. data/plugins/rigor-minitest/lib/rigor/plugin/minitest/assertion_analyzer.rb +302 -0
  99. data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +72 -0
  100. data/plugins/rigor-minitest/lib/rigor-minitest.rb +3 -0
  101. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/analyzer.rb +194 -0
  102. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_discoverer.rb +140 -0
  103. data/plugins/rigor-pundit/lib/rigor/plugin/pundit/policy_index.rb +65 -0
  104. data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +130 -0
  105. data/plugins/rigor-pundit/lib/rigor-pundit.rb +3 -0
  106. data/plugins/rigor-rails/lib/rigor-rails.rb +31 -0
  107. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +277 -0
  108. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_index.rb +108 -0
  109. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +138 -0
  110. data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +167 -0
  111. data/plugins/rigor-rails-i18n/lib/rigor-rails-i18n.rb +3 -0
  112. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/analyzer.rb +161 -0
  113. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/helper_table.rb +103 -0
  114. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +490 -0
  115. data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +158 -0
  116. data/plugins/rigor-rails-routes/lib/rigor-rails-routes.rb +3 -0
  117. data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +163 -0
  118. data/plugins/rigor-rbs-inline/lib/rigor-rbs-inline.rb +24 -0
  119. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/analyzer.rb +110 -0
  120. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +200 -0
  121. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +170 -0
  122. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +233 -0
  123. data/plugins/rigor-rspec/lib/rigor/plugin/rspec/scope_walker.rb +190 -0
  124. data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +188 -0
  125. data/plugins/rigor-rspec/lib/rigor-rspec.rb +3 -0
  126. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/have_http_status_analyzer.rb +128 -0
  127. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails/http_status_codes.rb +60 -0
  128. data/plugins/rigor-rspec-rails/lib/rigor/plugin/rspec_rails.rb +75 -0
  129. data/plugins/rigor-rspec-rails/lib/rigor-rspec-rails.rb +3 -0
  130. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +266 -0
  131. data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +113 -0
  132. data/plugins/rigor-shoulda-matchers/lib/rigor-shoulda-matchers.rb +3 -0
  133. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/analyzer.rb +152 -0
  134. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_discoverer.rb +190 -0
  135. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +61 -0
  136. data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +124 -0
  137. data/plugins/rigor-sidekiq/lib/rigor-sidekiq.rb +3 -0
  138. data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +85 -0
  139. data/plugins/rigor-sinatra/lib/rigor-sinatra.rb +8 -0
  140. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/absurd_recognizer.rb +108 -0
  141. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +250 -0
  142. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog.rb +95 -0
  143. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/catalog_walker.rb +226 -0
  144. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/method_signature.rb +28 -0
  145. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +154 -0
  146. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +100 -0
  147. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +323 -0
  148. data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +660 -0
  149. data/plugins/rigor-sorbet/lib/rigor-sorbet.rb +3 -0
  150. data/plugins/rigor-statesman/lib/rigor/plugin/statesman.rb +209 -0
  151. data/plugins/rigor-statesman/lib/rigor-statesman.rb +8 -0
  152. data/plugins/rigor-typescript-utility-types/lib/rigor/plugin/typescript_utility_types.rb +163 -0
  153. data/plugins/rigor-typescript-utility-types/lib/rigor-typescript-utility-types.rb +9 -0
  154. data/sig/rigor/analysis/baseline.rbs +39 -0
  155. data/sig/rigor/environment.rbs +3 -2
  156. data/sig/rigor/type.rbs +4 -0
  157. data/sig/rigor.rbs +2 -0
  158. metadata +180 -1
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rigor-activesupport-core-ext — a community-maintained RBS bundle
4
+ # for the most-frequently-flagged ActiveSupport core extensions.
5
+ #
6
+ # Per ADR-25 this gem is a pure RBS-bundle *plugin*: it ships a
7
+ # `sig/` directory and a trivial `Rigor::Plugin::Base` subclass
8
+ # whose manifest declares `signature_paths: ["sig"]`. Activate it
9
+ # by listing the gem under `.rigor.yml`'s `plugins:` — Rigor
10
+ # resolves and loads the bundled `sig/` automatically, with no
11
+ # hand-written path:
12
+ #
13
+ # # .rigor.yml
14
+ # plugins:
15
+ # - rigor-activesupport-core-ext
16
+ #
17
+ # Coverage scope and rationale: see `README.md` in this directory
18
+ # and `docs/notes/20260515-real-world-rails-survey.md` for the
19
+ # survey that established the selector ranking.
20
+ require_relative "rigor/plugin/activesupport_core_ext"
@@ -0,0 +1,463 @@
1
+ # ActiveSupport core_ext — community-maintained RBS bundle.
2
+ #
3
+ # Scope: the most-frequently-flagged ActiveSupport extension methods
4
+ # from a four-project Rails survey (Redmine, Discourse, Mastodon,
5
+ # GitLab FOSS — see `docs/notes/20260515-real-world-rails-survey.md`).
6
+ # Roughly the top ~40 selectors by `call.undefined-method` frequency,
7
+ # which together account for the bulk of the Rails-extension long
8
+ # tail.
9
+ #
10
+ # This file is opt-in: enable by adding the gem's `sig/` directory
11
+ # to `.rigor.yml`'s `signature_paths:`:
12
+ #
13
+ # # .rigor.yml
14
+ # signature_paths:
15
+ # - sig
16
+ # - path/to/rigor-activesupport-core-ext/sig
17
+ #
18
+ # Not exhaustive. ActiveSupport ships many more extensions across
19
+ # Hash / Array / String / Object / Date / Time / Integer / Module
20
+ # than this file covers; PRs welcome to extend.
21
+
22
+ # ---------------------------------------------------------------
23
+ # Object — universal predicates and helpers
24
+ # ---------------------------------------------------------------
25
+
26
+ class Object
27
+ # @return [bool]
28
+ def blank?: () -> bool
29
+
30
+ # @return [bool]
31
+ def present?: () -> bool
32
+
33
+ # @return [self?]
34
+ def presence: () -> self?
35
+
36
+ # `active_support/core_ext/object/json` monkey-patches
37
+ # `as_json` onto Object (default returns the object's
38
+ # `instance_values` hash) plus per-class overrides on
39
+ # NilClass / TrueClass / FalseClass / Numeric / String /
40
+ # Symbol / Time / Date / Array / Hash / Range / Regexp /
41
+ # Struct / Enumerable. Most call-site code uses the
42
+ # universal `obj.as_json` shape so a single Object row
43
+ # restores type coverage at the Mastodon-derived
44
+ # `as_json` call sites (`fan_out_on_write_service.rb`,
45
+ # `push_update_worker.rb`, `media_component_helper.rb`).
46
+ # `options` is the standard `only:` / `except:` / `methods:`
47
+ # / `include:` hash — left as untyped because the surface
48
+ # is large and per-call-site precision is rarely useful.
49
+ def as_json: (?untyped options) -> untyped
50
+
51
+ # `try` swallows NoMethodError; declared return is `untyped`
52
+ # because the called method's signature is not known to RBS
53
+ # at this call site.
54
+ def try: (Symbol | String) -> untyped
55
+ | (Symbol | String, *untyped) -> untyped
56
+ | (Symbol | String, *untyped) { (?) -> untyped } -> untyped
57
+
58
+ def try!: (Symbol | String) -> untyped
59
+ | (Symbol | String, *untyped) -> untyped
60
+ | (Symbol | String, *untyped) { (?) -> untyped } -> untyped
61
+
62
+ # `acts_like?(:string)` / `acts_like?(:date)` / `acts_like?(:time)`
63
+ # is ActiveSupport's "duck-typing helper" predicate.
64
+ def acts_like?: (Symbol | String) -> bool
65
+ end
66
+
67
+ # `nil.blank?` / `nil.present?` / `nil.try` are the most frequent
68
+ # call sites the survey turned up.
69
+ class NilClass
70
+ def blank?: () -> true
71
+ def present?: () -> false
72
+ def presence: () -> nil
73
+ def try: (*untyped) -> nil
74
+ def try!: (*untyped) -> nil
75
+ end
76
+
77
+ class TrueClass
78
+ def blank?: () -> false
79
+ def present?: () -> true
80
+ end
81
+
82
+ class FalseClass
83
+ def blank?: () -> true
84
+ def present?: () -> false
85
+ end
86
+
87
+ # ---------------------------------------------------------------
88
+ # String — ActiveSupport `core_ext/string/*`
89
+ # ---------------------------------------------------------------
90
+
91
+ class String
92
+ # `core_ext/string/inflections`
93
+ def underscore: () -> String
94
+ def camelize: (?Symbol upper_case_first) -> String
95
+ def camelcase: (?Symbol upper_case_first) -> String
96
+ def classify: () -> String
97
+ def constantize: () -> untyped
98
+ def safe_constantize: () -> untyped
99
+ def demodulize: () -> String
100
+ def deconstantize: () -> String
101
+ def titleize: () -> String
102
+ def parameterize: (?separator: String, ?preserve_case: bool, ?locale: Symbol?) -> String
103
+ def tableize: () -> String
104
+ def foreign_key: (?bool separate_class_name_and_id_with_underscore) -> String
105
+ def pluralize: (?Integer count, ?Symbol locale) -> String
106
+ def singularize: (?Symbol locale) -> String
107
+ def humanize: (?capitalize: bool, ?keep_id_suffix: bool) -> String
108
+
109
+ # `core_ext/string/filters`
110
+ def squish: () -> String
111
+ def squish!: () -> String
112
+ def truncate: (Integer truncate_at, ?omission: String, ?separator: String | Regexp | nil) -> String
113
+ def truncate_bytes: (Integer truncate_at, ?omission: String) -> String
114
+ def truncate_words: (Integer words_count, ?omission: String, ?separator: String | Regexp | nil) -> String
115
+
116
+ # `core_ext/string/output_safety` — `html_safe` returns an
117
+ # `ActiveSupport::SafeBuffer` (a String subclass). The closest
118
+ # RBS-canonical carrier is plain `String`; precision lost is the
119
+ # `html_safe?` predicate value.
120
+ def html_safe: () -> String
121
+ def html_safe?: () -> bool
122
+
123
+ # `core_ext/string/indent`
124
+ def indent: (Integer amount, ?String indent_string, ?bool indent_empty_lines) -> String
125
+ def indent!: (Integer amount, ?String indent_string, ?bool indent_empty_lines) -> String?
126
+
127
+ # `core_ext/string/starts_ends_with` — also covered by core
128
+ # `start_with?` / `end_with?`; these are the ActiveSupport aliases.
129
+ def starts_with?: (*String prefixes) -> bool
130
+ def ends_with?: (*String suffixes) -> bool
131
+
132
+ # `core_ext/string/conversions` — `to_time` / `to_date` /
133
+ # `to_datetime` return Time / Date / DateTime.
134
+ def to_time: (?Symbol form) -> Time?
135
+ def to_date: () -> Date?
136
+ def to_datetime: () -> DateTime
137
+ def to_hours: () -> Float
138
+
139
+ # `core_ext/string/access`
140
+ def at: (Integer | Range[Integer] | Regexp position) -> String?
141
+ def from: (Integer position) -> String
142
+ def to: (Integer position) -> String
143
+ def first: (?Integer limit) -> String
144
+ def last: (?Integer limit) -> String
145
+
146
+ # `core_ext/string/strip` — leading-blank strip with shared margin.
147
+ def strip_heredoc: () -> String
148
+
149
+ # `core_ext/string/multibyte`
150
+ def mb_chars: () -> untyped
151
+
152
+ # `core_ext/string/inquiry`
153
+ def inquiry: () -> untyped # ActiveSupport::StringInquirer
154
+ end
155
+
156
+ # ---------------------------------------------------------------
157
+ # Integer — `core_ext/integer/*`
158
+ # ---------------------------------------------------------------
159
+
160
+ class Integer
161
+ # `core_ext/numeric/time` — Duration multipliers.
162
+ def second: () -> untyped # ActiveSupport::Duration
163
+ def seconds: () -> untyped
164
+ def minute: () -> untyped
165
+ def minutes: () -> untyped
166
+ def hour: () -> untyped
167
+ def hours: () -> untyped
168
+ def day: () -> untyped
169
+ def days: () -> untyped
170
+ def week: () -> untyped
171
+ def weeks: () -> untyped
172
+ def fortnight: () -> untyped
173
+ def fortnights: () -> untyped
174
+ def month: () -> untyped
175
+ def months: () -> untyped
176
+ def year: () -> untyped
177
+ def years: () -> untyped
178
+
179
+ # `core_ext/numeric/bytes`
180
+ def byte: () -> Integer
181
+ def bytes: () -> Integer
182
+ def kilobyte: () -> Integer
183
+ def kilobytes: () -> Integer
184
+ def megabyte: () -> Integer
185
+ def megabytes: () -> Integer
186
+ def gigabyte: () -> Integer
187
+ def gigabytes: () -> Integer
188
+ def terabyte: () -> Integer
189
+ def terabytes: () -> Integer
190
+ def petabyte: () -> Integer
191
+ def petabytes: () -> Integer
192
+ def exabyte: () -> Integer
193
+ def exabytes: () -> Integer
194
+
195
+ # `core_ext/integer/multiple`
196
+ def multiple_of?: (Integer) -> bool
197
+
198
+ # `core_ext/integer/inflections`
199
+ def ordinal: () -> String
200
+ def ordinalize: () -> String
201
+ end
202
+
203
+ # ---------------------------------------------------------------
204
+ # Float — same Duration / Bytes pattern as Integer
205
+ # ---------------------------------------------------------------
206
+
207
+ class Float
208
+ def second: () -> untyped
209
+ def seconds: () -> untyped
210
+ def minute: () -> untyped
211
+ def minutes: () -> untyped
212
+ def hour: () -> untyped
213
+ def hours: () -> untyped
214
+ def day: () -> untyped
215
+ def days: () -> untyped
216
+ def week: () -> untyped
217
+ def weeks: () -> untyped
218
+ def month: () -> untyped
219
+ def months: () -> untyped
220
+ def year: () -> untyped
221
+ def years: () -> untyped
222
+
223
+ def byte: () -> Float
224
+ def bytes: () -> Float
225
+ def kilobyte: () -> Float
226
+ def kilobytes: () -> Float
227
+ def megabyte: () -> Float
228
+ def megabytes: () -> Float
229
+ def gigabyte: () -> Float
230
+ def gigabytes: () -> Float
231
+ end
232
+
233
+ # ---------------------------------------------------------------
234
+ # Time — singleton + instance extensions
235
+ # ---------------------------------------------------------------
236
+
237
+ class Time
238
+ def self.current: () -> Time
239
+ def self.zone: () -> untyped # ActiveSupport::TimeZone | nil
240
+ def self.zone=: (String | Symbol | untyped) -> untyped
241
+
242
+ # `core_ext/time/conversions` — also `Time.parse` from stdlib
243
+ # `time`, which is already in core RBS.
244
+ def self.httpdate: (String) -> Time
245
+ def self.iso8601: (String) -> Time
246
+
247
+ # `core_ext/time/calculations`
248
+ def yesterday: () -> Time
249
+ def tomorrow: () -> Time
250
+ def beginning_of_day: () -> Time
251
+ def end_of_day: () -> Time
252
+ def beginning_of_hour: () -> Time
253
+ def end_of_hour: () -> Time
254
+ def beginning_of_minute: () -> Time
255
+ def end_of_minute: () -> Time
256
+ def beginning_of_week: (?Symbol start_day) -> Time
257
+ def end_of_week: (?Symbol start_day) -> Time
258
+ def beginning_of_month: () -> Time
259
+ def end_of_month: () -> Time
260
+ def beginning_of_year: () -> Time
261
+ def end_of_year: () -> Time
262
+ def ago: (Numeric seconds) -> Time
263
+ def since: (Numeric seconds) -> Time
264
+ def in: (Numeric seconds) -> Time
265
+ def change: (**untyped) -> Time
266
+ def at_beginning_of_day: () -> Time
267
+ def at_end_of_day: () -> Time
268
+ def at_beginning_of_week: () -> Time
269
+ def at_end_of_week: () -> Time
270
+ def at_midnight: () -> Time
271
+ def at_noon: () -> Time
272
+ def midday: () -> Time
273
+ def midnight: () -> Time
274
+ def noon: () -> Time
275
+ def utc?: () -> bool
276
+ def acts_like_time?: () -> true
277
+ end
278
+
279
+ # ---------------------------------------------------------------
280
+ # Date — `core_ext/date/*`
281
+ # ---------------------------------------------------------------
282
+
283
+ class Date
284
+ def self.current: () -> Date
285
+ def self.yesterday: () -> Date
286
+ def self.tomorrow: () -> Date
287
+ def self.beginning_of_week: () -> Date
288
+ def self.end_of_week: () -> Date
289
+ def self.beginning_of_month: () -> Date
290
+ def self.end_of_month: () -> Date
291
+ def self.beginning_of_year: () -> Date
292
+ def self.end_of_year: () -> Date
293
+
294
+ def yesterday: () -> Date
295
+ def tomorrow: () -> Date
296
+ def beginning_of_week: (?Symbol start_day) -> Date
297
+ def end_of_week: (?Symbol start_day) -> Date
298
+ def beginning_of_month: () -> Date
299
+ def end_of_month: () -> Date
300
+ def beginning_of_year: () -> Date
301
+ def end_of_year: () -> Date
302
+ def ago: (Numeric seconds) -> Time
303
+ def since: (Numeric seconds) -> Time
304
+ def acts_like_date?: () -> true
305
+ end
306
+
307
+ # ---------------------------------------------------------------
308
+ # Array — singleton wrapper + instance extensions
309
+ # ---------------------------------------------------------------
310
+
311
+ class Array[unchecked out Elem]
312
+ # `Array.wrap(x)` is the dominant Rails idiom: `nil → []`,
313
+ # `Array x → x`, `else → [x]`.
314
+ def self.wrap: (untyped) -> Array[untyped]
315
+
316
+ # `core_ext/array/access`
317
+ def from: (Integer position) -> Array[Elem]
318
+ def to: (Integer position) -> Array[Elem]
319
+ def second: () -> Elem?
320
+ def third: () -> Elem?
321
+ def fourth: () -> Elem?
322
+ def fifth: () -> Elem?
323
+ def forty_two: () -> Elem?
324
+
325
+ # `core_ext/array/grouping`
326
+ def in_groups_of: (Integer number, ?untyped fill_with) -> Array[Array[Elem]]
327
+ | (Integer number, ?untyped fill_with) { (Array[Elem]) -> void } -> Array[Elem]
328
+ def in_groups: (Integer number, ?untyped fill_with) -> Array[Array[Elem]]
329
+ | (Integer number, ?untyped fill_with) { (Array[Elem]) -> void } -> Array[Elem]
330
+ def split: (?untyped value) -> Array[Array[Elem]]
331
+ | () { (Elem) -> bool } -> Array[Array[Elem]]
332
+
333
+ # `core_ext/array/conversions`
334
+ def to_sentence: (?two_words_connector: String, ?last_word_connector: String, ?words_connector: String, ?locale: Symbol?) -> String
335
+ def to_formatted_s: (?Symbol format) -> String
336
+ def to_fs: (?Symbol format) -> String
337
+ def to_xml: (**untyped) -> String
338
+
339
+ # `core_ext/array/inquiry`
340
+ def inquiry: () -> untyped # ActiveSupport::ArrayInquirer
341
+
342
+ # `core_ext/array/extract`
343
+ def extract!: () { (Elem) -> bool } -> Array[Elem]
344
+
345
+ # `core_ext/object/blank` / `core_ext/enumerable` — both
346
+ # ActiveSupport additions, shipped on Array specifically.
347
+ def compact_blank: () -> Array[Elem]
348
+ def compact_blank!: () -> self
349
+ def exclude?: (Elem) -> bool
350
+ end
351
+
352
+ # ---------------------------------------------------------------
353
+ # Enumerable — `core_ext/enumerable`
354
+ # ---------------------------------------------------------------
355
+
356
+ module Enumerable[unchecked out Elem]
357
+ def index_by: () { (Elem) -> untyped } -> Hash[untyped, Elem]
358
+ def index_with: (?untyped default) { (Elem) -> untyped } -> Hash[Elem, untyped]
359
+ | (untyped default) -> Hash[Elem, untyped]
360
+ def exclude?: (Elem) -> bool
361
+ def including: (*Elem) -> Array[Elem]
362
+ def excluding: (*Elem) -> Array[Elem]
363
+ def without: (*Elem) -> Array[Elem]
364
+ def pluck: (Symbol | String) -> Array[untyped]
365
+ | (*Symbol | String) -> Array[Array[untyped]]
366
+ def pick: (Symbol | String) -> untyped
367
+ | (*Symbol | String) -> Array[untyped]?
368
+ def maximum: (Symbol | String) -> untyped
369
+ def minimum: (Symbol | String) -> untyped
370
+ def sole: () -> Elem
371
+ def compact_blank: () -> Array[Elem]
372
+ end
373
+
374
+ # ---------------------------------------------------------------
375
+ # Hash — `core_ext/hash/*`
376
+ # ---------------------------------------------------------------
377
+
378
+ class Hash[unchecked out K, unchecked out V]
379
+ # `core_ext/hash/keys`
380
+ def symbolize_keys: () -> Hash[Symbol, V]
381
+ def symbolize_keys!: () -> self
382
+ def deep_symbolize_keys: () -> Hash[untyped, untyped]
383
+ def deep_symbolize_keys!: () -> self
384
+ def stringify_keys: () -> Hash[String, V]
385
+ def stringify_keys!: () -> self
386
+ def deep_stringify_keys: () -> Hash[untyped, untyped]
387
+ def deep_stringify_keys!: () -> self
388
+ def assert_valid_keys: (*K | Array[K]) -> self
389
+ def deep_transform_keys: () { (K) -> K } -> Hash[K, untyped]
390
+ def deep_transform_keys!: () { (K) -> K } -> self
391
+ def deep_transform_values: () { (V) -> untyped } -> Hash[K, untyped]
392
+ def deep_transform_values!: () { (V) -> untyped } -> self
393
+
394
+ # `core_ext/hash/deep_dup`
395
+ def deep_dup: () -> Hash[K, V]
396
+
397
+ # `core_ext/hash/deep_merge`
398
+ def deep_merge: (Hash[K, V]) -> Hash[K, V]
399
+ | (Hash[K, V]) { (K, V, V) -> V } -> Hash[K, V]
400
+ def deep_merge!: (Hash[K, V]) -> self
401
+ | (Hash[K, V]) { (K, V, V) -> V } -> self
402
+
403
+ # `core_ext/hash/except` — `Hash#except` is in core RBS as of
404
+ # Ruby 3.0+; `except!` is ActiveSupport-only.
405
+ def except!: (*K) -> self
406
+
407
+ # `core_ext/hash/conversions`
408
+ def to_query: (?String namespace) -> String
409
+ def to_param: (?String namespace) -> String
410
+ def to_xml: (**untyped) -> String
411
+
412
+ # `core_ext/hash/indifferent_access`
413
+ def with_indifferent_access: () -> untyped
414
+
415
+ # `core_ext/hash/conversions`
416
+ def self.from_xml: (String, ?Symbol disallowed_types) -> Hash[String, untyped]
417
+ def self.from_trusted_xml: (String) -> Hash[String, untyped]
418
+
419
+ # `core_ext/object/blank` / `core_ext/hash`
420
+ def compact_blank: () -> Hash[K, V]
421
+ def compact_blank!: () -> self
422
+
423
+ # `core_ext/hash/reverse_merge`
424
+ def reverse_merge: (Hash[K, V]) -> Hash[K, V]
425
+ def reverse_merge!: (Hash[K, V]) -> self
426
+
427
+ # `core_ext/hash/slice` — `Hash#except` is in core RBS (Ruby 3.0+);
428
+ # `Hash#slice` is in core RBS (Ruby 2.5+); the bang variants are
429
+ # ActiveSupport-only.
430
+ def slice!: (*K) -> Hash[K, V]
431
+ end
432
+
433
+ # ---------------------------------------------------------------
434
+ # DateTime — `core_ext/date_time/*`
435
+ # ---------------------------------------------------------------
436
+
437
+ class DateTime
438
+ def utc: () -> Time
439
+ def to_time: () -> Time
440
+ def in_time_zone: (?String | Symbol zone) -> untyped
441
+ def yesterday: () -> DateTime
442
+ def tomorrow: () -> DateTime
443
+ def ago: (Numeric seconds) -> DateTime
444
+ def since: (Numeric seconds) -> DateTime
445
+ def beginning_of_day: () -> DateTime
446
+ def end_of_day: () -> DateTime
447
+ def beginning_of_hour: () -> DateTime
448
+ def end_of_hour: () -> DateTime
449
+ def beginning_of_minute: () -> DateTime
450
+ def end_of_minute: () -> DateTime
451
+ def acts_like_time?: () -> true
452
+ end
453
+
454
+ # ---------------------------------------------------------------
455
+ # String — `core_ext/string/exclude` (an ActiveSupport addition)
456
+ # ---------------------------------------------------------------
457
+
458
+ # (Re-opens String to add `#exclude?`. Listed here rather than in the
459
+ # main String block above because it's a less-common method whose
460
+ # coverage was added after a second-round survey.)
461
+ class String
462
+ def exclude?: (String) -> bool
463
+ end
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rigor/plugin"
4
+
5
+ module Rigor
6
+ module Plugin
7
+ # ADR-16 Tier B worked plugin: recognises Devise's model-side
8
+ # `devise :strategy_a, :strategy_b` DSL on `ActiveRecord::Base`
9
+ # subclasses and explodes each strategy's module RBS into the
10
+ # calling class.
11
+ #
12
+ # Per the per-library survey (§ Devise), Devise's `devise(*modules)`
13
+ # at `lib/devise/models.rb:79-112` is a **table-driven include
14
+ # sequence**: each Symbol argument resolves through
15
+ # `Devise::Models.const_get(m.to_s.classify)` to a concrete
16
+ # `Devise::Models::*` module, which is then mixed into the
17
+ # calling class. The substrate replays the same shape
18
+ # statically — the manifest's `modules_by_symbol:` mirrors
19
+ # Devise's `lib/devise/modules.rb` and the pre-pass scanner
20
+ # synthesises one SyntheticMethod per (calling class, included
21
+ # module instance method) pair into the SyntheticMethodIndex.
22
+ #
23
+ # ## Reach
24
+ #
25
+ # The full set of strategies Devise registers via
26
+ # `Devise.add_module` at gem load (per `lib/devise/modules.rb`):
27
+ #
28
+ # - `:database_authenticatable` — password + email auth core
29
+ # - `:recoverable` — password-reset flow
30
+ # - `:rememberable` — persistent cookies
31
+ # - `:registerable` — sign-up + account-edit flow
32
+ # - `:trackable` — sign-in count / last-IP / last-at
33
+ # - `:validatable` — email + password validations
34
+ # - `:confirmable` — email confirmation flow
35
+ # - `:lockable` — account-lock-after-failed-attempts
36
+ # - `:timeoutable` — automatic logout after idle
37
+ # - `:omniauthable` — OmniAuth providers
38
+ # - `:authenticatable` — always-included base module
39
+ #
40
+ # The substrate's pre-pass scanner consults each module's RBS
41
+ # via `Environment::RbsLoader#instance_definition` to enumerate
42
+ # method names. A user project providing real Devise via
43
+ # Bundler will see methods like `valid_password?`,
44
+ # `send_reset_password_instructions`, etc. resolve through the
45
+ # synthetic-method tier without `call.undefined-method`.
46
+ #
47
+ # ## Floor / ceiling per ADR-16 WD13
48
+ #
49
+ # Slice 3 ships at the **floor**: synthesised method names
50
+ # emit and the dispatcher's `try_synthetic_method` tier
51
+ # returns `Type::Combinator.untyped` (Dynamic[T]) for every
52
+ # match. Per the slice-3 design judgment (1) the precision
53
+ # promotion — looking up the module's authored RBS return
54
+ # type at dispatch time — is **slice-6 ceiling work** and is
55
+ # NOT a delivery commitment of slice 3c. The `origin_module:`
56
+ # provenance field is recorded so the ceiling slice can
57
+ # promote without rescanning.
58
+ #
59
+ # ## Scope (slice 3c minimum)
60
+ #
61
+ # - Recognises model-side `devise :a, :b` on any AR::Base
62
+ # subclass; trait symbol set mirrors `lib/devise/modules.rb`.
63
+ # - `Devise::Models::Authenticatable` is always_included
64
+ # (matches Devise's `with_options model: true`).
65
+ # - Unknown trait symbols silently skipped (per slice-3
66
+ # design judgment (2)). User initializers that call
67
+ # `Devise.add_module :my_strategy, ...` are NOT seen — that
68
+ # path requires a separate scanner for `config/initializers/`
69
+ # and is deferred.
70
+ # - Controller-side helpers (`current_user`, `authenticate_user!`,
71
+ # etc.) are Tier C work, NOT Tier B; deferred to a future
72
+ # slice that consumes ADR-9 fact-store entries from a
73
+ # `rigor-rails-routes`-style walker.
74
+ # - Per-strategy `ClassMethods` extend (Devise's `extend
75
+ # Mod::ClassMethods` pattern) is NOT yet wired — slice 3
76
+ # covers instance methods only per WD13 floor.
77
+ class Devise < Rigor::Plugin::Base
78
+ manifest(
79
+ id: "devise",
80
+ version: "0.1.0",
81
+ description: "Recognises Devise's `devise :strategy` DSL via ADR-16 Tier B.",
82
+ trait_registries: [
83
+ Rigor::Plugin::Macro::TraitRegistry.new(
84
+ receiver_constraint: "ActiveRecord::Base",
85
+ method_name: :devise,
86
+ symbol_arg_position: :rest,
87
+ modules_by_symbol: {
88
+ database_authenticatable: "Devise::Models::DatabaseAuthenticatable",
89
+ recoverable: "Devise::Models::Recoverable",
90
+ rememberable: "Devise::Models::Rememberable",
91
+ registerable: "Devise::Models::Registerable",
92
+ trackable: "Devise::Models::Trackable",
93
+ validatable: "Devise::Models::Validatable",
94
+ confirmable: "Devise::Models::Confirmable",
95
+ lockable: "Devise::Models::Lockable",
96
+ timeoutable: "Devise::Models::Timeoutable",
97
+ omniauthable: "Devise::Models::Omniauthable",
98
+ authenticatable: "Devise::Models::Authenticatable"
99
+ },
100
+ always_included: ["Devise::Models::Authenticatable"]
101
+ )
102
+ ]
103
+ )
104
+ end
105
+
106
+ Rigor::Plugin.register(Devise)
107
+ end
108
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Gem entry point. Required by Rigor's plugin loader when
4
+ # `.rigor.yml` lists `rigor-devise` under `plugins:`. The loader
5
+ # expects this `require` to side-effect a call to
6
+ # `Rigor::Plugin.register`, which the body of
7
+ # `lib/rigor/plugin/devise.rb` performs at load time.
8
+ require_relative "rigor/plugin/devise"