rigortype 0.1.18 → 0.2.0
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.
- checksums.yaml +4 -4
- data/README.md +159 -224
- data/lib/rigor/analysis/check_rules/always_truthy_condition_collector.rb +9 -3
- data/lib/rigor/analysis/check_rules/dead_assignment_collector.rb +25 -0
- data/lib/rigor/analysis/check_rules/ivar_write_collector.rb +32 -23
- data/lib/rigor/analysis/check_rules/main_pass_collector.rb +54 -0
- data/lib/rigor/analysis/check_rules/rule_walk.rb +151 -23
- data/lib/rigor/analysis/check_rules/self_closedness_scanner.rb +24 -15
- data/lib/rigor/analysis/check_rules/unreachable_clause_collector.rb +9 -3
- data/lib/rigor/analysis/check_rules.rb +756 -132
- data/lib/rigor/analysis/dependency_source_inference/index.rb +4 -7
- data/lib/rigor/analysis/dependency_source_inference/walker.rb +2 -18
- data/lib/rigor/analysis/dependency_source_inference.rb +3 -12
- data/lib/rigor/analysis/diagnostic.rb +8 -0
- data/lib/rigor/analysis/fact_store.rb +5 -4
- data/lib/rigor/analysis/rule_catalog.rb +153 -6
- data/lib/rigor/analysis/runner/diagnostic_aggregator.rb +19 -18
- data/lib/rigor/analysis/runner/project_pre_passes.rb +13 -9
- data/lib/rigor/analysis/runner.rb +75 -27
- data/lib/rigor/analysis/self_call_resolution_recorder.rb +3 -4
- data/lib/rigor/analysis/worker_session.rb +31 -25
- data/lib/rigor/bleeding_edge.rb +123 -0
- data/lib/rigor/builtins/predefined_constant_refinements.rb +151 -0
- data/lib/rigor/cache/descriptor.rb +86 -8
- data/lib/rigor/cache/rbs_descriptor.rb +2 -1
- data/lib/rigor/cache/store.rb +5 -3
- data/lib/rigor/cli/annotate_command.rb +122 -16
- data/lib/rigor/cli/baseline_command.rb +4 -3
- data/lib/rigor/cli/check_command.rb +118 -16
- data/lib/rigor/cli/coverage_command.rb +148 -16
- data/lib/rigor/cli/coverage_scan.rb +57 -0
- data/lib/rigor/cli/explain_command.rb +2 -0
- data/lib/rigor/cli/lsp_command.rb +3 -7
- data/lib/rigor/cli/mutation_protection_renderer.rb +63 -0
- data/lib/rigor/cli/mutation_protection_report.rb +73 -0
- data/lib/rigor/cli/options.rb +9 -0
- data/lib/rigor/cli/plugins_command.rb +4 -5
- data/lib/rigor/cli/plugins_renderer.rb +0 -2
- data/lib/rigor/cli/protection_renderer.rb +63 -0
- data/lib/rigor/cli/protection_report.rb +68 -0
- data/lib/rigor/cli/show_bleedingedge_command.rb +114 -0
- data/lib/rigor/cli/sig_gen_command.rb +2 -1
- data/lib/rigor/cli/trace_command.rb +2 -1
- data/lib/rigor/cli/triage_command.rb +8 -4
- data/lib/rigor/cli/triage_renderer.rb +15 -1
- data/lib/rigor/cli/type_of_command.rb +1 -1
- data/lib/rigor/cli/type_scan_command.rb +2 -1
- data/lib/rigor/cli.rb +12 -3
- data/lib/rigor/configuration/dependencies.rb +2 -4
- data/lib/rigor/configuration/severity_profile.rb +13 -1
- data/lib/rigor/configuration.rb +100 -6
- data/lib/rigor/environment/bundle_sig_discovery.rb +61 -13
- data/lib/rigor/environment/class_registry.rb +4 -3
- data/lib/rigor/environment/constant_type_cache_holder.rb +43 -0
- data/lib/rigor/environment/lockfile_resolver.rb +1 -1
- data/lib/rigor/environment/rbs_collection_discovery.rb +1 -2
- data/lib/rigor/environment/rbs_coverage_report.rb +2 -1
- data/lib/rigor/environment/rbs_loader.rb +74 -5
- data/lib/rigor/environment.rb +17 -7
- data/lib/rigor/flow_contribution/fact.rb +1 -1
- data/lib/rigor/flow_contribution.rb +3 -5
- data/lib/rigor/inference/acceptance.rb +17 -9
- data/lib/rigor/inference/block_parameter_binder.rb +2 -3
- data/lib/rigor/inference/body_fixpoint.rb +89 -0
- data/lib/rigor/inference/budget_trace.rb +29 -2
- data/lib/rigor/inference/builtins/comparable_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/enumerable_catalog.rb +2 -2
- data/lib/rigor/inference/builtins/method_catalog.rb +19 -0
- data/lib/rigor/inference/builtins/string_catalog.rb +9 -1
- data/lib/rigor/inference/expression_typer.rb +1072 -71
- data/lib/rigor/inference/hkt_body.rb +8 -11
- data/lib/rigor/inference/hkt_body_parser.rb +10 -12
- data/lib/rigor/inference/hkt_registry.rb +10 -11
- data/lib/rigor/inference/macro_block_self_type.rb +2 -2
- data/lib/rigor/inference/method_dispatcher/array_to_h_folding.rb +60 -0
- data/lib/rigor/inference/method_dispatcher/call_context.rb +1 -4
- data/lib/rigor/inference/method_dispatcher/constant_folding.rb +210 -35
- data/lib/rigor/inference/method_dispatcher/data_folding.rb +9 -73
- data/lib/rigor/inference/method_dispatcher/file_folding.rb +6 -7
- data/lib/rigor/inference/method_dispatcher/iterator_dispatch.rb +10 -16
- data/lib/rigor/inference/method_dispatcher/kernel_dispatch.rb +25 -13
- data/lib/rigor/inference/method_dispatcher/member_shape_projection.rb +93 -0
- data/lib/rigor/inference/method_dispatcher/overload_selector.rb +1 -3
- data/lib/rigor/inference/method_dispatcher/rbs_dispatch.rb +24 -22
- data/lib/rigor/inference/method_dispatcher/reduce_folding.rb +281 -0
- data/lib/rigor/inference/method_dispatcher/regexp_folding.rb +71 -0
- data/lib/rigor/inference/method_dispatcher/shape_dispatch.rb +237 -24
- data/lib/rigor/inference/method_dispatcher/struct_folding.rb +303 -0
- data/lib/rigor/inference/method_dispatcher.rb +112 -49
- data/lib/rigor/inference/method_parameter_binder.rb +56 -2
- data/lib/rigor/inference/multi_target_binder.rb +46 -3
- data/lib/rigor/inference/mutation_widening.rb +147 -11
- data/lib/rigor/inference/narrowing.rb +284 -53
- data/lib/rigor/inference/parameter_inference_collector.rb +367 -0
- data/lib/rigor/inference/project_patched_methods.rb +4 -7
- data/lib/rigor/inference/project_patched_scanner.rb +2 -13
- data/lib/rigor/inference/protection_scanner.rb +86 -0
- data/lib/rigor/inference/scope_indexer.rb +821 -76
- data/lib/rigor/inference/statement_evaluator.rb +1179 -102
- data/lib/rigor/inference/struct_fold_safety.rb +181 -0
- data/lib/rigor/inference/synthetic_method.rb +7 -7
- data/lib/rigor/inference/synthetic_method_scanner.rb +1 -1
- data/lib/rigor/language_server/completion_provider.rb +6 -12
- data/lib/rigor/language_server/diagnostic_publisher.rb +4 -4
- data/lib/rigor/language_server/document_symbol_provider.rb +3 -3
- data/lib/rigor/language_server/hover_provider.rb +2 -3
- data/lib/rigor/language_server/hover_renderer.rb +2 -11
- data/lib/rigor/language_server/server.rb +9 -17
- data/lib/rigor/language_server.rb +4 -5
- data/lib/rigor/plugin/base.rb +245 -87
- data/lib/rigor/plugin/macro/block_as_method.rb +25 -25
- data/lib/rigor/plugin/macro/heredoc_template.rb +4 -7
- data/lib/rigor/plugin/macro/nested_class_template.rb +9 -7
- data/lib/rigor/plugin/macro/trait_registry.rb +3 -6
- data/lib/rigor/plugin/macro.rb +6 -8
- data/lib/rigor/plugin/manifest.rb +49 -90
- data/lib/rigor/plugin/node_rule_walk.rb +59 -14
- data/lib/rigor/plugin/registry.rb +18 -18
- data/lib/rigor/plugin/type_node_resolver.rb +6 -8
- data/lib/rigor/protection/mutation_scanner.rb +120 -0
- data/lib/rigor/protection/mutator.rb +246 -0
- data/lib/rigor/rbs_extended.rb +24 -36
- data/lib/rigor/reflection.rb +4 -7
- data/lib/rigor/scope/discovery_index.rb +16 -2
- data/lib/rigor/scope.rb +185 -16
- data/lib/rigor/sig_gen/generator.rb +8 -0
- data/lib/rigor/sig_gen/observed_call.rb +3 -3
- data/lib/rigor/sig_gen/writer.rb +40 -2
- data/lib/rigor/source/constant_path.rb +62 -0
- data/lib/rigor/source.rb +1 -0
- data/lib/rigor/triage/catalogue.rb +4 -19
- data/lib/rigor/triage.rb +69 -1
- data/lib/rigor/type/bound_method.rb +2 -11
- data/lib/rigor/type/combinator.rb +45 -3
- data/lib/rigor/type/constant.rb +2 -11
- data/lib/rigor/type/data_class.rb +2 -11
- data/lib/rigor/type/data_instance.rb +2 -11
- data/lib/rigor/type/hash_shape.rb +2 -11
- data/lib/rigor/type/integer_range.rb +2 -11
- data/lib/rigor/type/intersection.rb +2 -11
- data/lib/rigor/type/nominal.rb +2 -11
- data/lib/rigor/type/plain_lattice.rb +37 -0
- data/lib/rigor/type/refined.rb +72 -13
- data/lib/rigor/type/singleton.rb +2 -11
- data/lib/rigor/type/struct_class.rb +75 -0
- data/lib/rigor/type/struct_instance.rb +93 -0
- data/lib/rigor/type/tuple.rb +5 -15
- data/lib/rigor/type.rb +2 -0
- data/lib/rigor/version.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_discoverer.rb +1 -1
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable/channel_index.rb +3 -3
- data/plugins/rigor-actioncable/lib/rigor/plugin/actioncable.rb +16 -32
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer/mailer_discoverer.rb +5 -13
- data/plugins/rigor-actionmailer/lib/rigor/plugin/actionmailer.rb +13 -32
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack/analyzer.rb +11 -17
- data/plugins/rigor-actionpack/lib/rigor/plugin/actionpack.rb +34 -100
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob/job_index.rb +3 -2
- data/plugins/rigor-activejob/lib/rigor/plugin/activejob.rb +13 -30
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord/model_discoverer.rb +4 -4
- data/plugins/rigor-activerecord/lib/rigor/plugin/activerecord.rb +26 -27
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage/analyzer.rb +5 -7
- data/plugins/rigor-activestorage/lib/rigor/plugin/activestorage.rb +9 -8
- data/plugins/rigor-devise/lib/rigor/plugin/devise.rb +9 -11
- data/plugins/rigor-dry-struct/lib/rigor/plugin/dry_struct.rb +8 -9
- data/plugins/rigor-dry-types/lib/rigor/plugin/dry_types.rb +13 -12
- data/plugins/rigor-dry-validation/lib/rigor/plugin/dry_validation.rb +3 -4
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/analyzer.rb +8 -8
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_discoverer.rb +9 -11
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot/factory_index.rb +7 -8
- data/plugins/rigor-factorybot/lib/rigor/plugin/factorybot.rb +18 -49
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql/type_scanner.rb +12 -13
- data/plugins/rigor-graphql/lib/rigor/plugin/graphql.rb +15 -23
- data/plugins/rigor-mangrove/lib/rigor/plugin/mangrove.rb +4 -4
- data/plugins/rigor-minitest/lib/rigor/plugin/minitest.rb +3 -3
- data/plugins/rigor-pundit/lib/rigor/plugin/pundit.rb +10 -21
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/analyzer.rb +2 -4
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n/locale_loader.rb +27 -11
- data/plugins/rigor-rails-i18n/lib/rigor/plugin/rails_i18n.rb +22 -35
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/devise_routes.rb +4 -6
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes/routes_parser.rb +12 -18
- data/plugins/rigor-rails-routes/lib/rigor/plugin/rails_routes.rb +16 -23
- data/plugins/rigor-rbs-inline/lib/rigor/plugin/rbs_inline.rb +0 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_scope_index.rb +3 -4
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/let_type_resolver.rb +1 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec/matcher_analyzer.rb +1 -1
- data/plugins/rigor-rspec/lib/rigor/plugin/rspec.rb +21 -27
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers/analyzer.rb +0 -1
- data/plugins/rigor-shoulda-matchers/lib/rigor/plugin/shoulda_matchers.rb +3 -23
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq/worker_index.rb +5 -4
- data/plugins/rigor-sidekiq/lib/rigor/plugin/sidekiq.rb +8 -21
- data/plugins/rigor-sinatra/lib/rigor/plugin/sinatra.rb +1 -1
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/assertion_recognizer.rb +2 -3
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/method_signature.rb +7 -11
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sig_parser.rb +4 -5
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/sigil_detector.rb +6 -9
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet/type_translator.rb +5 -15
- data/plugins/rigor-sorbet/lib/rigor/plugin/sorbet.rb +52 -40
- data/sig/rigor/analysis/fact_store.rbs +3 -0
- data/sig/rigor/inference/builtins/method_catalog.rbs +1 -1
- data/sig/rigor/plugin/base.rbs +5 -2
- data/sig/rigor/plugin/manifest.rbs +1 -2
- data/sig/rigor/scope.rbs +18 -1
- data/sig/rigor/type.rbs +37 -1
- data/sig/rigor.rbs +1 -1
- data/skills/rigor-baseline-reduce/references/01-classify.md +27 -0
- data/skills/rigor-plugin-author/SKILL.md +6 -4
- data/skills/rigor-plugin-author/references/02-walker-and-types.md +22 -17
- data/skills/rigor-project-init/references/03-baseline-and-bugs.md +18 -1
- metadata +25 -2
- data/lib/rigor/plugin/macro/external_file.rb +0 -143
|
@@ -27,10 +27,12 @@ module Rigor
|
|
|
27
27
|
# whose direct superclass is in `model_base_classes`, and
|
|
28
28
|
# composes them with the schema table into a {ModelIndex}.
|
|
29
29
|
#
|
|
30
|
-
# Both producers ride `Plugin::Base#cache_for
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
30
|
+
# Both producers ride `Plugin::Base#cache_for` (ADR-60 WD3
|
|
31
|
+
# record-and-validate): each producer's in-block boundary reads
|
|
32
|
+
# are captured into its dependency descriptor after the block
|
|
33
|
+
# runs, and `model_index`'s `watch:` covers model-file additions,
|
|
34
|
+
# so editing `db/schema.rb`, editing any model, or adding a new
|
|
35
|
+
# model file invalidates exactly the right cache entry.
|
|
34
36
|
#
|
|
35
37
|
# The per-file `#diagnostics_for_file` hook delegates to
|
|
36
38
|
# {Analyzer}, which walks Prism and emits diagnostics for
|
|
@@ -73,10 +75,9 @@ module Rigor
|
|
|
73
75
|
"model_base_classes" => { kind: :array, default: %w[ApplicationRecord ActiveRecord::Base] }
|
|
74
76
|
},
|
|
75
77
|
produces: [:model_index],
|
|
76
|
-
# ADR-25 — the bundled `ActiveRecord::Relation` RBS
|
|
77
|
-
#
|
|
78
|
-
#
|
|
79
|
-
# against.
|
|
78
|
+
# ADR-25 — the bundled `ActiveRecord::Relation` RBS that
|
|
79
|
+
# relation-typed call sites (`has_many` accessors,
|
|
80
|
+
# `Model.where`, scopes) dispatch against.
|
|
80
81
|
signature_paths: ["sig"],
|
|
81
82
|
# ADR-26 — `ActiveRecord::Relation` is an "open" receiver:
|
|
82
83
|
# it delegates an unbounded set of user-defined scopes /
|
|
@@ -87,7 +88,7 @@ module Rigor
|
|
|
87
88
|
)
|
|
88
89
|
|
|
89
90
|
# The class the bundled `sig/active_record/relation.rbs`
|
|
90
|
-
# describes; `
|
|
91
|
+
# describes; `dynamic_return` contributes
|
|
91
92
|
# `ActiveRecord::Relation[Model]` for relation-returning
|
|
92
93
|
# call sites (`has_many` accessors, `Model.where`, scopes).
|
|
93
94
|
RELATION_CLASS_NAME = "ActiveRecord::Relation"
|
|
@@ -101,8 +102,11 @@ module Rigor
|
|
|
101
102
|
end
|
|
102
103
|
|
|
103
104
|
# Cached: model index. Walks every model file, then composes
|
|
104
|
-
# the rows with the cached schema table.
|
|
105
|
-
|
|
105
|
+
# the rows with the cached schema table. `watch:` (ADR-60 WD3)
|
|
106
|
+
# covers model-file additions; the discoverer's in-block reads
|
|
107
|
+
# are captured into the record-and-validate dependency
|
|
108
|
+
# descriptor after the block runs.
|
|
109
|
+
producer :model_index, watch: -> { [[@model_search_paths, "**/*.rb"]] } do |_params|
|
|
106
110
|
rows = ModelDiscoverer.new(
|
|
107
111
|
io_boundary: io_boundary,
|
|
108
112
|
search_paths: @model_search_paths,
|
|
@@ -256,9 +260,8 @@ module Rigor
|
|
|
256
260
|
names
|
|
257
261
|
end
|
|
258
262
|
|
|
259
|
-
#
|
|
260
|
-
#
|
|
261
|
-
# `dynamic_return` contract expects.
|
|
263
|
+
# Resolution body for `dynamic_return` — same four-path
|
|
264
|
+
# order, returning the bare type the contract expects.
|
|
262
265
|
def contribution_return_type(call_node, scope)
|
|
263
266
|
return nil unless call_node.is_a?(Prism::CallNode)
|
|
264
267
|
|
|
@@ -571,16 +574,11 @@ module Rigor
|
|
|
571
574
|
table = schema_table_or_nil
|
|
572
575
|
return nil if table.nil?
|
|
573
576
|
|
|
574
|
-
#
|
|
575
|
-
#
|
|
576
|
-
# descriptor
|
|
577
|
-
#
|
|
578
|
-
|
|
579
|
-
io_boundary: io_boundary,
|
|
580
|
-
search_paths: @model_search_paths,
|
|
581
|
-
base_classes: @model_base_classes
|
|
582
|
-
).discover
|
|
583
|
-
|
|
577
|
+
# ADR-60 WD3 record-and-validate: the producer's own in-block
|
|
578
|
+
# `ModelDiscoverer` reads are captured into the dependency
|
|
579
|
+
# descriptor after the block runs, and the producer's `watch:`
|
|
580
|
+
# covers model-file additions — so no priming walk is needed
|
|
581
|
+
# (it used to run the discover twice).
|
|
584
582
|
@model_index = cache_for(:model_index, params: {}).call
|
|
585
583
|
rescue StandardError => e
|
|
586
584
|
@load_errors << "model index build failed: #{e.class}: #{e.message}"
|
|
@@ -599,9 +597,10 @@ module Rigor
|
|
|
599
597
|
return nil if @schema_load_attempted
|
|
600
598
|
|
|
601
599
|
@schema_load_attempted = true
|
|
602
|
-
#
|
|
603
|
-
#
|
|
604
|
-
|
|
600
|
+
# ADR-60 WD3 record-and-validate: the producer reads
|
|
601
|
+
# `@schema_file` in-block, and that read is captured into the
|
|
602
|
+
# dependency descriptor after the block runs — so no priming
|
|
603
|
+
# read is needed here.
|
|
605
604
|
@schema_table = cache_for(:schema_table, params: {}).call
|
|
606
605
|
rescue Plugin::AccessDeniedError => e
|
|
607
606
|
@load_errors << "rigor-activerecord: #{e.message}"
|
|
@@ -11,13 +11,11 @@ module Rigor
|
|
|
11
11
|
# plugin recognised so users can verify the model →
|
|
12
12
|
# attachment mapping the plugin sees.
|
|
13
13
|
#
|
|
14
|
-
# No `:error` diagnostics
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
# rely on. A future slice can add `unknown-attachment`
|
|
20
|
-
# similar to `rigor-activerecord`'s `unknown-column`.
|
|
14
|
+
# No `:error` diagnostics here — the `dynamic_return`
|
|
15
|
+
# return-type narrowing carries the type-checking value.
|
|
16
|
+
# An `unknown-attachment` rule (similar to
|
|
17
|
+
# `rigor-activerecord`'s `unknown-column`) is deferred:
|
|
18
|
+
# it requires a coupled receiver-class narrowing pass.
|
|
21
19
|
class Analyzer
|
|
22
20
|
attr_reader :diagnostics
|
|
23
21
|
|
|
@@ -54,8 +54,11 @@ module Rigor
|
|
|
54
54
|
)
|
|
55
55
|
|
|
56
56
|
# Cached: attachment index. Walks every `.rb` file under
|
|
57
|
-
# `model_search_paths` for `has_*_attached` macros.
|
|
58
|
-
|
|
57
|
+
# `model_search_paths` for `has_*_attached` macros. `watch:`
|
|
58
|
+
# (ADR-60 WD3) covers model-file additions; the discoverer's
|
|
59
|
+
# in-block reads are captured into the record-and-validate
|
|
60
|
+
# dependency descriptor after the block runs.
|
|
61
|
+
producer :attachment_index, watch: -> { [[@model_search_paths, "**/*.rb"]] } do |_params|
|
|
59
62
|
rows = AttachmentDiscoverer.new(
|
|
60
63
|
io_boundary: io_boundary,
|
|
61
64
|
search_paths: @model_search_paths
|
|
@@ -126,12 +129,10 @@ module Rigor
|
|
|
126
129
|
def attachment_index
|
|
127
130
|
return @attachment_index if @attachment_index
|
|
128
131
|
|
|
129
|
-
#
|
|
130
|
-
#
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
search_paths: @model_search_paths
|
|
134
|
-
).discover
|
|
132
|
+
# ADR-60 WD3 record-and-validate: the producer's in-block
|
|
133
|
+
# `AttachmentDiscoverer` reads are captured into the
|
|
134
|
+
# dependency descriptor after the block runs, and the
|
|
135
|
+
# producer's `watch:` covers model-file additions.
|
|
135
136
|
@attachment_index = cache_for(:attachment_index, params: {}).call
|
|
136
137
|
rescue Plugin::AccessDeniedError => e
|
|
137
138
|
@load_errors << "rigor-activestorage: #{e.message}"
|
|
@@ -44,19 +44,17 @@ module Rigor
|
|
|
44
44
|
# `send_reset_password_instructions`, etc. resolve through the
|
|
45
45
|
# synthetic-method tier without `call.undefined-method`.
|
|
46
46
|
#
|
|
47
|
-
# ##
|
|
47
|
+
# ## Precision tier
|
|
48
48
|
#
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
# provenance field is recorded so the ceiling slice can
|
|
57
|
-
# promote without rescanning.
|
|
49
|
+
# The scanner records `origin_module:` in each synthetic
|
|
50
|
+
# method's provenance. The dispatcher's slice-6a TierB path
|
|
51
|
+
# (`promote_via_origin_module`) redispatches on
|
|
52
|
+
# `Nominal[origin_module]` via `RbsDispatch`, so Devise's
|
|
53
|
+
# authored RBS return types win: `valid_password?` returns
|
|
54
|
+
# `bool`, not `Dynamic[T]`. Unknown return types degrade
|
|
55
|
+
# gracefully to `Dynamic[T]`.
|
|
58
56
|
#
|
|
59
|
-
# ## Scope
|
|
57
|
+
# ## Scope
|
|
60
58
|
#
|
|
61
59
|
# - Recognises model-side `devise :a, :b` on any AR::Base
|
|
62
60
|
# subclass; trait symbol set mirrors `lib/devise/modules.rb`.
|
|
@@ -35,16 +35,15 @@ module Rigor
|
|
|
35
35
|
# other files then dispatch through the synthetic record rather
|
|
36
36
|
# than falling through to `call.undefined-method`.
|
|
37
37
|
#
|
|
38
|
-
# ##
|
|
38
|
+
# ## Precision model (ADR-16 WD13 + ADR-18)
|
|
39
39
|
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
# return shape; slice 6 unlocks precision without re-authoring.
|
|
40
|
+
# The synthetic reader's return type is resolved via ADR-18's
|
|
41
|
+
# `returns_from_arg:` fact lookup: the call-site's second argument
|
|
42
|
+
# (`Types::String` etc.) is looked up through the `:dry_type_aliases`
|
|
43
|
+
# fact published by `rigor-dry-types`, yielding `Nominal[String]`
|
|
44
|
+
# for common cases. When the lookup misses (e.g. inline method-chain
|
|
45
|
+
# argument whose chain-head isn't currently extracted), the row
|
|
46
|
+
# falls back to `Dynamic[Top]` silently.
|
|
48
47
|
#
|
|
49
48
|
# ## Scope (slice 2c minimum)
|
|
50
49
|
#
|
|
@@ -22,9 +22,8 @@ module Rigor
|
|
|
22
22
|
# Other dry-rb adapter plugins consume this fact:
|
|
23
23
|
#
|
|
24
24
|
# - `rigor-dry-struct` reads it so `attribute :city, Types::String`
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
# resolver chain).
|
|
25
|
+
# promotes `address.city` from `Dynamic[Top]` to `Nominal[String]`
|
|
26
|
+
# via ADR-18's `returns_from_arg:` fact lookup.
|
|
28
27
|
# - `rigor-dry-validation` / `rigor-dry-schema` read it for
|
|
29
28
|
# per-key type recognition in `schema { … }` / `params { … }`
|
|
30
29
|
# blocks (separate plugin slice).
|
|
@@ -43,17 +42,19 @@ module Rigor
|
|
|
43
42
|
# "<UnderlyingClass>" }` so consumers can match on the
|
|
44
43
|
# qualified constant name they see in source.
|
|
45
44
|
#
|
|
46
|
-
#
|
|
45
|
+
# Implemented beyond the floor:
|
|
47
46
|
#
|
|
48
|
-
# -
|
|
47
|
+
# - Nested-namespace aliases (`Types::Coercible::Integer`,
|
|
49
48
|
# `Types::Strict::Symbol`, `Types::Params::Bool`,
|
|
50
|
-
# `Types::JSON::Date`) —
|
|
51
|
-
#
|
|
52
|
-
# -
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
#
|
|
49
|
+
# `Types::JSON::Date`) — the four coercion categories each
|
|
50
|
+
# map to the same underlying class as their canonical shortcut.
|
|
51
|
+
# - User-authored compositions (`Email = Types::String.constrained(...)`,
|
|
52
|
+
# transitive resolution through composition chains) — the alias
|
|
53
|
+
# surface extends beyond the 15 canonical names.
|
|
54
|
+
#
|
|
55
|
+
# Deferred:
|
|
56
|
+
#
|
|
57
|
+
# - `dry-types.unknown-alias` / `dry-types.alias-shadow`
|
|
57
58
|
# diagnostics when downstream code references a name that
|
|
58
59
|
# wasn't published.
|
|
59
60
|
#
|
|
@@ -21,10 +21,9 @@ module Rigor
|
|
|
21
21
|
# cross-plugin fact.
|
|
22
22
|
# - Ships an RBS overlay (`sig/dry_validation.rbs`) typing
|
|
23
23
|
# `Dry::Validation::Contract#call` (returns Result) and
|
|
24
|
-
# `Dry::Validation::Result#{success?, failure?, to_h}`.
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
# for the wiring step.
|
|
24
|
+
# `Dry::Validation::Result#{success?, failure?, to_h}`.
|
|
25
|
+
# The manifest's `signature_paths: ["sig"]` auto-contributes
|
|
26
|
+
# the overlay (ADR-25) — no project-side wiring needed.
|
|
28
27
|
#
|
|
29
28
|
# Slice 2 (deferred, per design note):
|
|
30
29
|
#
|
|
@@ -86,14 +86,14 @@ module Rigor
|
|
|
86
86
|
# a `Prism::SymbolNode` is treated as a literal
|
|
87
87
|
# attribute reference.
|
|
88
88
|
#
|
|
89
|
-
#
|
|
90
|
-
#
|
|
91
|
-
#
|
|
92
|
-
#
|
|
93
|
-
#
|
|
94
|
-
#
|
|
95
|
-
#
|
|
96
|
-
#
|
|
89
|
+
# When `model_index` (the cross-plugin `:model_index`
|
|
90
|
+
# fact published by rigor-activerecord) is present,
|
|
91
|
+
# the effective accepted key set is the UNION of the
|
|
92
|
+
# factory's declared attributes plus the corresponding
|
|
93
|
+
# model's columns. FactoryBot's runtime accepts any AR
|
|
94
|
+
# attribute regardless of whether the factory declared
|
|
95
|
+
# it, so the cross-check broadens the acceptance
|
|
96
|
+
# accordingly.
|
|
97
97
|
def unknown_attribute_violations(call_node, entry, model_index)
|
|
98
98
|
accepted_keys, suggestion_dictionary = effective_keys(entry, model_index)
|
|
99
99
|
attr_spell_checker = DidYouMean::SpellChecker.new(dictionary: suggestion_dictionary)
|
|
@@ -23,8 +23,8 @@ module Rigor
|
|
|
23
23
|
# - `factory :users, aliases: [:author] do ... end` — alias form
|
|
24
24
|
#
|
|
25
25
|
# Inside a factory block, attribute declarations come in
|
|
26
|
-
# several shapes.
|
|
27
|
-
#
|
|
26
|
+
# several shapes. Only literal-name forms are recognised
|
|
27
|
+
# (Symbol arg / String arg):
|
|
28
28
|
#
|
|
29
29
|
# - `name { "Alice" }` — implicit attribute via
|
|
30
30
|
# `method_missing` with a block (FactoryBot's modern
|
|
@@ -116,8 +116,8 @@ module Rigor
|
|
|
116
116
|
Rigor::Source::Literals.symbol_or_string_name(call_node.arguments&.arguments&.first)
|
|
117
117
|
end
|
|
118
118
|
|
|
119
|
-
#
|
|
120
|
-
#
|
|
119
|
+
# Resolves the model class name for the factory.
|
|
120
|
+
# Three sources, in priority order:
|
|
121
121
|
#
|
|
122
122
|
# 1. Explicit `class: <Const>` keyword arg —
|
|
123
123
|
# ConstantReadNode / ConstantPathNode value.
|
|
@@ -188,11 +188,10 @@ module Rigor
|
|
|
188
188
|
attributes
|
|
189
189
|
end
|
|
190
190
|
|
|
191
|
-
# Walks the block body collecting attribute names.
|
|
192
|
-
#
|
|
193
|
-
#
|
|
194
|
-
#
|
|
195
|
-
# (traits ship in a follow-up).
|
|
191
|
+
# Walks the block body collecting attribute names. Only
|
|
192
|
+
# top-level statements are examined — attributes inside
|
|
193
|
+
# `trait :admin do ... end` or other nested blocks are
|
|
194
|
+
# not collected (traits deferred to a follow-up).
|
|
196
195
|
def collect_attributes_from(node, accumulator)
|
|
197
196
|
return unless node.is_a?(Prism::Node)
|
|
198
197
|
|
|
@@ -206,8 +205,7 @@ module Rigor
|
|
|
206
205
|
def record_attribute(node, accumulator)
|
|
207
206
|
return unless node.is_a?(Prism::CallNode) && node.receiver.nil?
|
|
208
207
|
# Skip association / sequence / trait / framework
|
|
209
|
-
# methods —
|
|
210
|
-
# declarations.
|
|
208
|
+
# methods — only plain attribute declarations are recorded.
|
|
211
209
|
return if SKIPPED_METHODS.include?(node.name)
|
|
212
210
|
|
|
213
211
|
name = if node.name == :add_attribute
|
|
@@ -4,15 +4,14 @@ module Rigor
|
|
|
4
4
|
module Plugin
|
|
5
5
|
class Factorybot < Rigor::Plugin::Base
|
|
6
6
|
# Per-run frozen index of discovered FactoryBot factories
|
|
7
|
-
# and the attribute keys each declares.
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
7
|
+
# and the attribute keys each declares. Indexes only
|
|
8
|
+
# **literal symbol/string** factory names + **literal
|
|
9
|
+
# symbol** attribute names; sequences, parent/child
|
|
10
|
+
# relationships, traits, and dynamically-named factories
|
|
11
|
+
# are deferred to follow-up slices.
|
|
12
12
|
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
# builds. Resolved from:
|
|
13
|
+
# Each entry carries a `model_class` — the inferred or
|
|
14
|
+
# explicit class the factory builds. Resolved from:
|
|
16
15
|
#
|
|
17
16
|
# 1. An explicit `factory :user, class: User do`
|
|
18
17
|
# keyword option (ConstantReadNode / ConstantPathNode
|
|
@@ -13,12 +13,10 @@ module Rigor
|
|
|
13
13
|
# attributes_for / *_list family against a per-run index
|
|
14
14
|
# built from `factory_search_paths`.
|
|
15
15
|
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
# `rigor-activerecord` `:model_index` ADR-9 fact, after
|
|
21
|
-
# `rigor-activerecord` adds the matching publish hook.
|
|
16
|
+
# Recognises factory NAMES + literal ATTRIBUTE KEYS in
|
|
17
|
+
# the call's keyword hash. The AR column cross-check uses
|
|
18
|
+
# the `rigor-activerecord` `:model_index` ADR-9 fact when
|
|
19
|
+
# that plugin is loaded (optional `consumes:`).
|
|
22
20
|
# Traits, sequences, parent / child factories, and dynamic
|
|
23
21
|
# factory names are deferred to follow-up slices.
|
|
24
22
|
#
|
|
@@ -54,9 +52,9 @@ module Rigor
|
|
|
54
52
|
# `.build_stubbed_list`. The legacy `FactoryGirl` constant
|
|
55
53
|
# is recognised identically. Implicit-receiver calls
|
|
56
54
|
# (`create(:name)` inside an `include FactoryBot::Syntax::Methods`
|
|
57
|
-
# context) are NOT recognised
|
|
58
|
-
#
|
|
59
|
-
#
|
|
55
|
+
# context) are NOT recognised — too many false positives on
|
|
56
|
+
# plain `create` calls outside test files; deferred until
|
|
57
|
+
# receiver-type inference can disambiguate.
|
|
60
58
|
#
|
|
61
59
|
# ## What's recognised inside `factory :name do ... end`
|
|
62
60
|
#
|
|
@@ -87,19 +85,15 @@ module Rigor
|
|
|
87
85
|
]
|
|
88
86
|
)
|
|
89
87
|
|
|
90
|
-
producer :factory_index do |_params|
|
|
88
|
+
producer :factory_index, watch: -> { [[@factory_search_paths, "**/*.rb"]] } do |_params|
|
|
91
89
|
FactoryDiscoverer.new(
|
|
92
90
|
io_boundary: io_boundary,
|
|
93
91
|
search_paths: @factory_search_paths
|
|
94
92
|
).discover
|
|
95
93
|
end
|
|
96
94
|
|
|
97
|
-
def init(
|
|
98
|
-
@services = services
|
|
95
|
+
def init(_services)
|
|
99
96
|
@factory_search_paths = Array(config.fetch("factory_search_paths")).map(&:to_s)
|
|
100
|
-
@factory_index = nil
|
|
101
|
-
@model_index = nil
|
|
102
|
-
@model_index_resolved = false
|
|
103
97
|
end
|
|
104
98
|
|
|
105
99
|
# ADR-37 — per-call factory/attribute validation over the
|
|
@@ -108,42 +102,17 @@ module Rigor
|
|
|
108
102
|
# is positioned via `diagnostic(node, location:)`. No file-level
|
|
109
103
|
# diagnostic remains, so there is no `diagnostics_for_file`.
|
|
110
104
|
node_rule Prism::CallNode do |node, _scope, path|
|
|
111
|
-
index =
|
|
105
|
+
index = producer_value(:factory_index)
|
|
112
106
|
next [] if index.nil? || index.empty?
|
|
113
107
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
private
|
|
125
|
-
|
|
126
|
-
# Phase 1 (c) — lazily resolves the :model_index fact
|
|
127
|
-
# from rigor-activerecord. Returns nil when
|
|
128
|
-
# rigor-activerecord isn't loaded or hasn't published
|
|
129
|
-
# an index; the analyzer treats nil as "no cross-check"
|
|
130
|
-
# and falls back to Phase 1 (a) behaviour (factory
|
|
131
|
-
# attributes only).
|
|
132
|
-
def model_index_or_nil
|
|
133
|
-
return @model_index if @model_index_resolved
|
|
134
|
-
|
|
135
|
-
@model_index = @services.fact_store.read(plugin_id: "activerecord", name: :model_index)
|
|
136
|
-
@model_index_resolved = true
|
|
137
|
-
@model_index
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
def factory_index_or_nil
|
|
141
|
-
return @factory_index if @factory_index
|
|
142
|
-
|
|
143
|
-
descriptor = glob_descriptor(@factory_search_paths, "**/*.rb")
|
|
144
|
-
@factory_index = cache_for(:factory_index, params: {}, descriptor: descriptor).call
|
|
145
|
-
rescue StandardError
|
|
146
|
-
nil
|
|
108
|
+
# `:model_index` is rigor-activerecord's published fact (ADR-9);
|
|
109
|
+
# nil when that plugin isn't loaded, in which case the analyzer
|
|
110
|
+
# falls back to factory-attributes-only checking.
|
|
111
|
+
violations = Analyzer.violations_for(
|
|
112
|
+
call_node: node, factory_index: index,
|
|
113
|
+
model_index: read_fact(plugin_id: "activerecord", name: :model_index)
|
|
114
|
+
)
|
|
115
|
+
diagnostics_for(violations, path: path, node: node)
|
|
147
116
|
end
|
|
148
117
|
end
|
|
149
118
|
|
|
@@ -50,12 +50,13 @@ module Rigor
|
|
|
50
50
|
|
|
51
51
|
# @param paths [Array<String>] absolute paths to `.rb` files
|
|
52
52
|
# the project's `paths:` resolves to.
|
|
53
|
-
# @return [Hash{Symbol => Hash}] frozen
|
|
53
|
+
# @return [Hash{Symbol => Hash}] frozen 4-key result:
|
|
54
54
|
# `:types` (per-`Schema::Object` field table),
|
|
55
|
-
# `:enums` (per-`Schema::Enum` value list),
|
|
56
|
-
# `:input_objects` (per-`Schema::InputObject` argument
|
|
57
|
-
# table).
|
|
58
|
-
#
|
|
55
|
+
# `:enums` (per-`Schema::Enum` value list),
|
|
56
|
+
# `:input_objects` (per-`Schema::InputObject` argument table),
|
|
57
|
+
# `:mutations` (per-`Schema::Mutation` arguments+fields table).
|
|
58
|
+
# Any subset may be empty when no recognisable declaration
|
|
59
|
+
# of that kind is found.
|
|
59
60
|
def scan(paths:)
|
|
60
61
|
acc = empty_accumulator
|
|
61
62
|
paths.each do |path|
|
|
@@ -97,10 +98,9 @@ module Rigor
|
|
|
97
98
|
private_class_method :scan_file
|
|
98
99
|
|
|
99
100
|
# Walks the AST collecting `class X < GraphQL::Schema::Object`,
|
|
100
|
-
# `
|
|
101
|
-
#
|
|
102
|
-
#
|
|
103
|
-
# publish multiple cross-plugin facts from one walk.
|
|
101
|
+
# `Schema::Enum`, `Schema::InputObject`, and `Schema::Mutation`
|
|
102
|
+
# decls at any nesting level. Returns a 4-key hash so the
|
|
103
|
+
# caller can publish multiple cross-plugin facts from one walk.
|
|
104
104
|
def collect_definitions(node, qualified_prefix)
|
|
105
105
|
return empty_accumulator if node.nil?
|
|
106
106
|
|
|
@@ -203,10 +203,9 @@ module Rigor
|
|
|
203
203
|
# The first positional must be a String literal — the
|
|
204
204
|
# graphql-ruby `value` API also accepts a Symbol form
|
|
205
205
|
# (`value :ACTIVE`) but the documented idiom is String.
|
|
206
|
-
#
|
|
207
|
-
#
|
|
208
|
-
#
|
|
209
|
-
# the floor.
|
|
206
|
+
# Only the GraphQL-side value name is stored; the optional
|
|
207
|
+
# `value:` kwarg (Ruby-side override) and `description:`
|
|
208
|
+
# are omitted from the published table.
|
|
210
209
|
def collect_values(body)
|
|
211
210
|
return [] if body.nil?
|
|
212
211
|
|
|
@@ -25,10 +25,10 @@ module Rigor
|
|
|
25
25
|
# resolver methods themselves; rigor's value here is producing a
|
|
26
26
|
# static type table downstream consumers can cross-reference.
|
|
27
27
|
#
|
|
28
|
-
# ## What downstream consumers DO with
|
|
28
|
+
# ## What downstream consumers DO with the published facts
|
|
29
29
|
#
|
|
30
|
-
# The
|
|
31
|
-
# demand-driven,
|
|
30
|
+
# The tables are the substrate for two future capabilities
|
|
31
|
+
# (demand-driven, not yet implemented):
|
|
32
32
|
#
|
|
33
33
|
# - Resolver-method check: for each `field :name, Type` whose
|
|
34
34
|
# `name` is also defined as a Ruby method on the class, verify
|
|
@@ -37,33 +37,25 @@ module Rigor
|
|
|
37
37
|
# plugin could type `Schema.execute(query).to_h` against the
|
|
38
38
|
# queried fields.
|
|
39
39
|
#
|
|
40
|
-
# ##
|
|
40
|
+
# ## What's recognised
|
|
41
41
|
#
|
|
42
|
-
#
|
|
42
|
+
# - `class T < GraphQL::Schema::Object` subclasses (including
|
|
43
|
+
# nested namespaces); `field :name, Type, null: ...`
|
|
44
|
+
# declarations with constant-reference or list-array types
|
|
45
|
+
# and GraphQL→Ruby scalar mapping.
|
|
46
|
+
# - `class T < GraphQL::Schema::Enum`; `value "ACTIVE"` calls.
|
|
47
|
+
# - `class T < GraphQL::Schema::InputObject` /
|
|
48
|
+
# `GraphQL::Schema::Mutation`; `argument :name, Type,
|
|
49
|
+
# required: ...` declarations.
|
|
50
|
+
# - No user-facing diagnostics yet.
|
|
43
51
|
#
|
|
44
|
-
#
|
|
45
|
-
# (including nested namespaces: `class Types::User < ...`,
|
|
46
|
-
# `module Types; class User < ...; end; end`).
|
|
47
|
-
# - Recognises the `field :name, Type, **opts` declaration with:
|
|
48
|
-
# - `Type` as a `ConstantReadNode` / `ConstantPathNode` (`String`
|
|
49
|
-
# / `Integer` / `Boolean` / `Float` / `ID`, or a user-defined
|
|
50
|
-
# `Types::OtherObject`).
|
|
51
|
-
# - `null: true` / `null: false` keyword extracts nullability.
|
|
52
|
-
# - Maps the canonical GraphQL scalar names to underlying Ruby
|
|
53
|
-
# classes (`String` → `String`, `Integer` → `Integer`,
|
|
54
|
-
# `Boolean` → `TrueClass`, `Float` → `Float`, `ID` → `String`).
|
|
55
|
-
# - Publishes the table; no user-facing diagnostics yet.
|
|
52
|
+
# ## Deferred (demand-driven)
|
|
56
53
|
#
|
|
57
|
-
# The **ceiling** (future slices, demand-driven):
|
|
58
|
-
#
|
|
59
|
-
# - **`GraphQL::Schema::Enum`** with `value "ACTIVE"` calls.
|
|
60
|
-
# - **`GraphQL::Schema::Mutation`** + **`GraphQL::Schema::InputObject`**.
|
|
61
|
-
# - **List / Non-Null wrappers** (`[String]`, `String.array`).
|
|
62
54
|
# - **`resolver:` / `mutation:` reroute** recognition.
|
|
63
55
|
# - **String type expressions** (`field :foo, "User"`) — defeats
|
|
64
56
|
# static resolution by design (graphql-ruby's `BuildType.parse_type`
|
|
65
57
|
# constantizes at runtime); a future slice could surface these
|
|
66
|
-
# as `graphql.string-type` `:info` diagnostics
|
|
58
|
+
# as `graphql.string-type` `:info` diagnostics pointing the
|
|
67
59
|
# user at the constant-reference form for static typing.
|
|
68
60
|
class Graphql < Rigor::Plugin::Base
|
|
69
61
|
manifest(
|
|
@@ -71,9 +71,9 @@ module Rigor
|
|
|
71
71
|
# - `is_a?(Result::Ok)` / `Some` / `None` exhaustive
|
|
72
72
|
# narrowing — core control-flow analysis over a sealed
|
|
73
73
|
# hierarchy, not a plugin surface.
|
|
74
|
-
# - The `variants do variant Const, Type end` Enum DSL
|
|
75
|
-
#
|
|
76
|
-
#
|
|
74
|
+
# - The `variants do variant Const, Type end` Enum DSL is handled
|
|
75
|
+
# via ADR-36 `nested_class_templates:` in this plugin's manifest
|
|
76
|
+
# (Slice A — see `nested_class_templates:` block below).
|
|
77
77
|
class Mangrove < Rigor::Plugin::Base
|
|
78
78
|
manifest(
|
|
79
79
|
id: "mangrove",
|
|
@@ -99,7 +99,7 @@ module Rigor
|
|
|
99
99
|
receiver_constraint: "Mangrove::Enum",
|
|
100
100
|
block_method: :variants,
|
|
101
101
|
variant_method: :variant,
|
|
102
|
-
|
|
102
|
+
symbol_arg_position: 0,
|
|
103
103
|
inner_arg_position: 1,
|
|
104
104
|
inner_reader: :inner
|
|
105
105
|
)
|
|
@@ -30,10 +30,10 @@ module Rigor
|
|
|
30
30
|
#
|
|
31
31
|
# ## Configuration
|
|
32
32
|
#
|
|
33
|
-
# No knobs
|
|
34
|
-
# in `.rigor.yml`.
|
|
33
|
+
# No configuration knobs. Activate via
|
|
34
|
+
# `plugins: ["rigor-minitest"]` in `.rigor.yml`.
|
|
35
35
|
#
|
|
36
|
-
# ## Limitations
|
|
36
|
+
# ## Limitations
|
|
37
37
|
#
|
|
38
38
|
# - **No `assert_raises(T) { ... }`** — that's a block-shape
|
|
39
39
|
# matcher and Rigor's narrowing model is for
|