Module: Tuile::Component::HasBadInput
- Includes:
- HasValidation
- Included in:
- BigDecimalField, DateField, DateTimeField, FloatField, IntegerField, TimeField
- Defined in:
- lib/tuile/component/has_bad_input.rb,
sig/tuile.rbs
Overview
The one fact a parsing field knows that Tuile::Component::HasValue#on_value_change cannot carry: the input is something the field's value cannot represent.
field = Component::IntegerField.new
# …the user types a lone minus, which no Integer can represent:
field.value # => nil, exactly as for an untouched field
field.empty? # => true, likewise — empty of *value*
field.bad_input? # => true
field. # => "not a whole number"
So a form asks this before empty?, on every field that can answer:
bad = fields.select { _1.respond_to?(:bad_input?) && _1.bad_input? }
Component::ConfirmWindow.alert("Cannot save", bad.map(&:bad_input_message).join("\n")) if bad.any?
Include it in a field whose parse is partial — whose input can be something its value cannot represent, as a date field's can. Not in a ComboBox, the near miss: its input is a filter rather than a formatting of the value, so a no-match is not a failed conversion and it reverts the query instead.
Implementation details
An includer overrides #bad_input_message and nothing else. Two rules
bind that override, and design/decisions.md D_bad_input has the why:
- Empty input is not bad input. Return
nilfor an empty buffer even though it parses to nothing, or every blank optional field blocks a save. - One frozen constant per field kind, no interpolation —
"not a valid date", never"'xyz' is not a valid date". It is read per call, and a future error ink would read it per paint.
Ask at the moment you need the answer and you always get the current one: it is derived on read, never stored. Which fields can answer is a class fact worth caching; what they answer is not. There is deliberately no change notice, because the fact is continuous — every prefix of a valid date is bad input — so anything reacting per keystroke flashes through the act of typing correctly, while a save gate consulted at a click sees one settled state. The ink is the one consumer that cannot be asked at a click, so it has #bad_input_settled?: a field whose whole grammar is prefix-bad latches the well on its commit gesture instead of painting it per keystroke.
Instance Attribute Summary
Attributes included from HasValidation
Instance Method Summary collapse
-
#bad_input? ⇒ Boolean
@return — true iff the field is holding input its value cannot represent.
-
#bad_input_message ⇒ String?
Why the current input cannot be turned into a Tuile::Component::HasValue#value — the single override point.
-
#bad_input_settled? ⇒ Boolean
Whether bad input may paint the well yet.
-
#error_bg_color ⇒ Color?
The invalid well, picked up by everything this component paints — including the inner face of a composed field and the List of a group, neither of which forwards anything: both declare no background of their own, so the ordinary chain walks up to this (overrides #error_bg_color).
-
#error_ink? ⇒ Boolean
Widens Tuile::Component::HasValidation#error_ink?: bad input paints the invalid well too, with no verdict written — once #bad_input_settled? says the report may be shown.
-
#error_message ⇒ StyledString?
@return — why the field is invalid, or
nilwhen it is not;niluntil something sets it. -
#error_message= ⇒ void
Sets the verdict and repaints the field in Theme#error_color;
nilclears it. -
#inspect_details ⇒ ::Array[String]
Adds
error_message=…to #inspect, omitted while valid — so a Testing.get failure dump says which field is already flagged.
Instance Method Details
#bad_input? ⇒ Boolean
@return — true iff the field is holding input its value cannot represent.
62 |
# File 'lib/tuile/component/has_bad_input.rb', line 62 def bad_input? = !.nil? |
#bad_input_message ⇒ String?
Why the current input cannot be turned into a Tuile::Component::HasValue#value — the single override point.
@return — the reason, or nil when the input converts (a
field holding no input converts: it is empty, not bad).
58 |
# File 'lib/tuile/component/has_bad_input.rb', line 58 def = raise(NotImplementedError, "#{self.class} must implement bad_input_message") |
#bad_input_settled? ⇒ Boolean
Whether bad input may paint the well yet. true here, so the well is
as continuous as the report: a FloatField reddens at the half-typed
"1.", which is a fair warning while the residue is one or two
transient buffers. Override it to latch where the grammar makes
every prefix bad input, or the well is red for the whole time the
user types a correct value:
def bad_input_settled? = @settled # set on commit, cleared on an edit
It gates the ink only: #bad_input? is a pull, and a save gate
asking at a click must get the answer settled or not (design/decisions.md
D_bad_input).
85 |
# File 'lib/tuile/component/has_bad_input.rb', line 85 def bad_input_settled? = true |
#error_bg_color ⇒ Color?
The invalid well, picked up by everything this component paints — including the inner face of a composed field and the List of a group, neither of which forwards anything: both declare no background of their own, so the ordinary chain walks up to this (overrides Tuile::Component#error_bg_color).
9394 |
# File 'sig/tuile.rbs', line 9394
def error_bg_color: () -> Color?
|
#error_ink? ⇒ Boolean
Widens Tuile::Component::HasValidation#error_ink?: bad input paints the invalid well too, with no verdict written — once #bad_input_settled? says the report may be shown.
70 |
# File 'lib/tuile/component/has_bad_input.rb', line 70 def error_ink? = (bad_input? && bad_input_settled?) || super |
#error_message ⇒ StyledString?
@return — why the field is invalid, or nil when it
is not; nil until something sets it.
9377 |
# File 'sig/tuile.rbs', line 9377
def error_message: () -> StyledString?
|
#error_message= ⇒ void
This method returns an undefined value.
Sets the verdict and repaints the field in Theme#error_color; nil
clears it. No-op (no repaint, no listener) when unchanged. A String is
parsed via StyledString.parse, as Tuile::Component::HasCaption#caption= does.
Safe on a detached field — an app validates a form it assembled but has not mounted, and Tuile::Component#invalidate is already a no-op there.
@param new_message
9387 |
# File 'sig/tuile.rbs', line 9387
def error_message=: ((String | StyledString)? new_message) -> void
|
#inspect_details ⇒ ::Array[String]
Adds error_message=… to Tuile::Component#inspect, omitted while valid — so
a Testing.get failure dump says which field is already flagged.
9398 |
# File 'sig/tuile.rbs', line 9398
def inspect_details: () -> ::Array[String]
|