Module: SoftValidation
- Extended by:
- ActiveSupport::Concern
- Included in:
- AssertedDistribution, BiologicalAssociation, CharacterState, Citation, CollectingEvent, CollectionObject, CollectionProfile, Container, ControlledVocabularyTerm, Descriptor, Document, Documentation, Extract, FieldOccurrence, Georeference, Identifier::Global, Image, Loan, ObservationMatrixRow, ObservationMatrixRowItem, Otu, RangedLotCategory, Serial, Source, TaxonDetermination, TaxonName, TaxonNameClassification, TaxonNameRelationship, TypeMaterial
- Defined in:
- lib/soft_validation.rb,
lib/soft_validation/soft_validation.rb,
lib/soft_validation/soft_validations.rb,
lib/soft_validation/soft_validation_method.rb
Overview
Vaguely inspired by concepts from by svn://rubyforge.org/var/svn/softvalidations, but not as elegant.
Soft validations are a means to tie warnings or suggestions to instances of data. Soft validations do not prevent an instance from being saved. They are not intended to be bound to AR callbacks, but this may be possible ultimately. They may be used to alert the user to data issues that need to be addressed, or alert the programmer who is batch parsing data as to the quality of the incoming data, etc..
There are 2 stages to defining a soft validation. First index and provide an option general description of the soft validation itself using the ‘soft_validate` macro in the class. Second, add the method (logic) that is called, and a set of message that the user will see when the logic passes or fails. To state in another way:
-
The name and description (intent) of the soft validation is optionally provided with the macro setting the soft validation (‘Klass.soft_validate()`.
-
The human messages (‘there is a problem here!’, ‘the problem is fixed’, ‘we tried to fix, but failed!’) are defined with the method logic itself. This is intentionally done to
keep the intent of the logic close to the consequences of the logic.
Devloper tips:
-
Protonym.soft_validation( ) <- all technical metadata and a gross description (the intent), optionally, goes here
-
@protonym.sv_xyz( ) <- all human guidance (warning, outcomes) goes here, including the attribute to point to in the UI
-
fix method names should not be exposed to the UI
Usage:
class Foo < ApplicationRecord
include SoftValidation
soft_validate(:a_soft_validation_method, fix: :cook_cheezburgers)
# Validations can be assigned to a set (only one), and validations in a set
# can be called individually.
soft_validate(:other_soft_validation_method, set: :some_set)
soft_validate(:yet_another_method, set: :some_other_set )
soft_validate(:described_method, name: 'the validation for X', description: 'this validation does Z')
soft_validate(:a_third_method, resolution: [:route_name, route_name2]) # Resolution is route name (without _path/_url), id: and model_id: are added to the route.
soft_validate(:a_fourth_example, fix: :fix_method) # the detected issue can be fully resolved by calling this instance method
$hungry = true # demo only, don't use $globals
def a_soft_validation_method
soft_validations.add(:base, 'hungry!', # :base or a model attribute (column)
success_message: 'no longer hungry, cooked a cheezeburger',
failure_message: 'oh no, cat ate your cheezeburger'
) if $hungry
end
def cook_cheezburgers
$hungry = false
end
end
f = Foo.new
f.soft_validations.validated? # => false
f.soft_validations.fixes_run? # => false
f.soft_validations.fixed? # => false
f.soft_validations.complete? # => false
f.soft_validate # => true
f.soft_validated? # => true
f.soft_fixed? # => false
f.soft_valid? # => false # true if there are no SoftValidations produced
f.soft_validations.soft_validations # => [soft_validation, soft_validation1 ... ]
f.soft_validations.soft_validations.size # => 1
f.soft_validations.soft_validations.first # => A SoftValidation instance
# SoftValidation attributes
f.soft_validations.soft_validations.first.attribute # => :base
f.soft_validations.soft_validations.first. # => 'hungry!'
f.soft_validations.soft_validations.first. # => 'no longer hungry, cooked a cheezeburger'
f.soft_validations.soft_validations.first. # => 'oh no, cat ate your cheezeburger'
f.soft_validations.soft_validations.first.fixed? # => false
f.soft_validations.soft_validations.first. # => 'fix not yet run'
f.fix_soft_validations # => true
f.soft_fixed? # => true
f.soft_valid? # => false !! There is still a SoftValidation generated, will be true next time it's run
f.soft_validations.fixes_run # => true
f.soft_validations.soft_validations.first.fixed? # => true
f.soft_validations.soft_validations.first. # => 'no longer hungry, cooked a cheezeburger'
f.soft_validations.on(:base) # => [soft_validation, ... ]
f.soft_validations. # => ['hungry!']
f.soft_validations.(:base) # => ['hungry!']
f.clear_soft_validations
f.soft_validate(only_sets: [:default]) # only run this set, the set of soft validations not assigned to a set
f.soft_validate(only_sets: [:some_other_set]) # only run these sets of validations
f.soft_validate(except_set: [:some_other_set]) # run non-flagged except soft validations in these sets
f.soft_validate(only_methods: :some_method) # run only this soft validation (all other params ignored)
f.soft_validate(except_methods: [:some_method]) # run result except these soft validation methods
f.soft_validate(fixable: true) # run all soft validations that have a fix
f.soft_validate(fixable: false) # run all soft validations without a fix
f.soft_validate(flagged: true) # run all, *including* methods flagged by developers as "a-typical", there is no flagged: false, as it is default)
Defined Under Namespace
Modules: ClassMethods Classes: SoftValidation, SoftValidationError, SoftValidationMethod, SoftValidations
Constant Summary collapse
- ANCESTORS_WITH_SOFT_VALIDATIONS =
An index of the soft validators in superclasses
Hash.new do |h, klass| h[klass.name] = (klass.ancestors.select {|a| a.respond_to?(:soft_validates?) && a.soft_validates?} - [klass]) # a < ApplicationRecord && would be faster but requires AR in spec end
Instance Method Summary collapse
- #clear_soft_validations ⇒ Nil
-
#fix_for(soft_validation_method) ⇒ Object
TODO: should be here?.
-
#fix_soft_validations ⇒ Object
The validation set to fix is set prior to running the fix, at the first step.
- #soft_fixed? ⇒ Boolean
- #soft_valid? ⇒ Boolean
-
#soft_validate(**options) ⇒ true
Run a set of soft validations.
- #soft_validated? ⇒ Boolean
- #soft_validations ⇒ SoftValidations
- #soft_validators(**options) ⇒ Object
Instance Method Details
#clear_soft_validations ⇒ Nil
305 306 307 |
# File 'lib/soft_validation.rb', line 305 def clear_soft_validations @soft_validation_result = nil end |
#fix_for(soft_validation_method) ⇒ Object
TODO: should be here?
370 371 372 |
# File 'lib/soft_validation.rb', line 370 def fix_for(soft_validation_method) soft_validation_methods[soft_validation_method]&.fix end |
#fix_soft_validations ⇒ Object
The validation set to fix is set prior to running the fix, at the first step. It can be refined/restricted there as needed, letting specific contexts (e.g. access in controller) defined the scope.
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 |
# File 'lib/soft_validation.rb', line 337 def fix_soft_validations return false if !soft_validated? soft_validations.soft_validations.each do |v| if fix = fix_for(v.soft_validation_method) if self.send(fix) v.fixed = :fixed else v.fixed = :fix_error end else v.fixed = :no_fix_available end end soft_validations.fixes_run = true true end |
#soft_fixed? ⇒ Boolean
360 361 362 |
# File 'lib/soft_validation.rb', line 360 def soft_fixed? soft_validations.fixes_run? end |
#soft_valid? ⇒ Boolean
365 366 367 |
# File 'lib/soft_validation.rb', line 365 def soft_valid? soft_validations.complete? end |
#soft_validate(**options) ⇒ true
Run a set of soft validations.
-
by default all validations except those with ‘flagged: true` are run
-
when only|except_methods are set then these further restrict the scope of tests run
-
except_methods will exclude methods from any result (i.e. sets are allowed)
317 318 319 320 321 322 323 324 325 326 327 |
# File 'lib/soft_validation.rb', line 317 def soft_validate(**) clear_soft_validations soft_validations soft_validators(**).each do |sv_method| self.send(sv_method) end soft_validations.validated = true true end |
#soft_validated? ⇒ Boolean
355 356 357 |
# File 'lib/soft_validation.rb', line 355 def soft_validated? soft_validations.validated? end |
#soft_validations ⇒ SoftValidations
300 301 302 |
# File 'lib/soft_validation.rb', line 300 def soft_validations @soft_validation_result ||= SoftValidations.new(self) end |
#soft_validators(**options) ⇒ Object
330 331 332 |
# File 'lib/soft_validation.rb', line 330 def soft_validators(**) self.class.soft_validators(**) end |