Repository navigation
Actionable v1.6.0 — valid, guarded RBS
Valid, guarded RBS. The gem's own signatures can only get more typed (CI
ratchet, untyped 46 → 27); generated RBS for your actions declares the real
superclass chain, a typed InputSchema, typed .run keywords and output
ivars, built on field_struct 0.12's FieldStruct::RBS.type_for. Requires
field_struct >= 0.12. No runtime behaviour changes.
Added
Actionable::RBS.generatetypes an action's input and output instance
variables (generated RBS only; no runtime change). For an action with a
fixedinputschema it emits a nestedclass InputSchema < ::FieldStruct::Base
with a typed reader and writer per input field,def input: () -> InputSchema,
and a.runoverload| (InputSchema input) -> ...beside the keyword form.
InputSchemaexists only in RBS (the runtime schema is anonymous), so
generation raisesArgumentErrorwhen Ruby already resolves
YourAction::InputSchema(a constant on the action, an ancestor or an
included module). It also declares each output field as a nullable
instance variable (@total: ::Integer?), so a step that assigns
@total = ...type-checks. Upgrade note: the generated RBS now owns
those instance variables; delete any declaration of them from your own sig
files, since RBS rejects an instance variable declared twice.
Fixed
Actionable::RBS.generatedeclares the real superclass chain. An action
under an intermediate base (class Codes::DecideMapping < ApplicationAction)
was declared< ::Actionable::Action, hiding its bases from the type
checker, and a host that declared the real superclass got
NoSuperclassFoundError/ a failingrbs validatewhen the base had no
signature. The action now names its nearest named superclass, and every
named ancestor up toActionable::Actionis declared first, member-less
(a typed one with its empty result classes), so the RBS resolves on its own.
The declarations repeat byte-identically across files and merge. A namespace
that is a class now opens asclass, notmodule, which RBS rejected as a
duplicate declaration.
Upgrade note: if your own sig files declare one of those bases with a
different superclass (e.g.class ApplicationAction < ::Actionable::Action
while Ruby says otherwise), RBS now reports a superclass mismatch; make your
declaration match Ruby's ancestry or drop it.
Changed
- Optional
.runkeywords are nullable in generated RBS (?note: ::String?): an optional field may be passednilexplicitly, and the run succeeds. - A required input field with a default is an optional
.runkeyword (?amount: ::Integer), sincerunsucceeds without it. An action whose input is chosen by aninput_fordispatch (including one inherited under a fixedinput) gets.run: (*untyped), since the dispatch decides the keywords at run time. - Requires field_struct >= 0.12. The generator now takes every field type
fromFieldStruct::RBS.type_forinstead of its own copy of the mapping, so
generated action RBS matchesFieldStruct::RBS.generate. Consumer-visible
changes to generated RBS: writers onOutputand the per-code output
classes are typed(T? value) -> T?instead of(untyped value) -> untyped; scalar array elements are nullable (::Array[::String?]); a
:valuefield isuntyped, neveruntyped?; a bare genericruby_type
gets type arguments (::Hash[untyped, untyped]); and a field'srbs_type:
override is honoured. A field typed by an anonymous nested struct class now
raisesArgumentError(it previously emitted an invalid::name). - The committed signatures can only get more typed. Contributor tooling
only; the shippedsig/actionable.rbsis unchanged.rake sigs:untyped
(part of the default task) fails when sig/ holds more or fewer than the
committed ceiling of 46 non-commentuntyped, so a typing change lowers
the ceiling with it.sigs:generate/sigs:checkfail on a sord
[OMIT]/[DUCK]fallback that isn't in the committed allowlist (24
today) and on allowlist entries sord no longer emits. sord runs without
--replace-errors-with-untyped(the output is identical) and fails on any
SORD_ERROR_type.sigs:validatevalidates the whole sig/ directory. - Optional, report-only Steep 2.x via
gemfiles/types.gemfileand a
typesCI job on Ruby 3.4 that never fails the build. The Steepfile now
checks the core only (the RSpec and Rails adapters are ignored); Steep is
no longer in the main Gemfile. - More precise signatures (RBS only; no runtime change). Keyword splats
are typed per value (**Object options, not**::Hash[Symbol, Object] options, which RBS reads as "every value is a Hash");.run,#initialize
andJob::Mixin#run_actionabletake*Object, **Object; the gem's
constants (Actionable::HALT,VERSION,Action::GEM_FAILURE_CODES, …)
are declared; and the halt/measurement helpers declare their block. The
DSL methods whose block runs with anotherself(output,input,
fails,skips,case_step,input_for, and thedefinebuilders)
gain a block overload with its self-type in the new
sig/actionable/dsl.rbs.untypedin sig/ falls from 46 to 27, and the
sord fallback allowlist from 24 entries to 4.