Module: DatabaseValidations::Rescuer
- Defined in:
- lib/database_validations/lib/rescuer.rb
Class Method Summary collapse
- .find_matching_validator(instance, keys) ⇒ Object
- .foreign_key_invalid?(instance, attribute_validator) ⇒ Boolean
- .handled?(instance, error, validate) ⇒ Boolean
- .process(validate, instance, error, key_types) ⇒ Object
- .process_validator(validate, instance, attribute_validator) ⇒ Object
- .resolve_keys(instance, error, key_types) ⇒ Object
Class Method Details
.find_matching_validator(instance, keys) ⇒ Object
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/database_validations/lib/rescuer.rb', line 42 def find_matching_validator(instance, keys) first_match = nil keys.each do |key| attribute_validator = instance._db_validators[key] next unless attribute_validator first_match ||= attribute_validator next if (keys.size > 1) && !foreign_key_invalid?(instance, attribute_validator) return attribute_validator end # TOCTOU fallback: if disambiguate queries all passed (concurrent insert), # use the first matching validator rather than leaving the error unhandled. first_match end |
.foreign_key_invalid?(instance, attribute_validator) ⇒ Boolean
61 62 63 64 65 66 67 68 69 70 |
# File 'lib/database_validations/lib/rescuer.rb', line 61 def foreign_key_invalid?(instance, attribute_validator) attribute = attribute_validator.attribute reflection = instance.class._reflect_on_association(attribute) return true unless reflection fk_value = instance.read_attribute(reflection.foreign_key) return true if fk_value.blank? !reflection.klass.exists?(reflection.association_primary_key => fk_value) end |
.handled?(instance, error, validate) ⇒ Boolean
5 6 7 8 9 10 11 12 13 14 15 |
# File 'lib/database_validations/lib/rescuer.rb', line 5 def handled?(instance, error, validate) Storage.prepare(instance.class) unless Storage.prepared?(instance.class) case error when ActiveRecord::RecordNotUnique process(validate, instance, error, for_unique_index: :unique_index_name, for_db_uniqueness: :unique_error_columns) when ActiveRecord::InvalidForeignKey process(validate, instance, error, for_db_presence: :foreign_key_error_column) else false end end |
.process(validate, instance, error, key_types) ⇒ Object
17 18 19 20 21 22 23 |
# File 'lib/database_validations/lib/rescuer.rb', line 17 def process(validate, instance, error, key_types) keys = resolve_keys(instance, error, key_types) attribute_validator = find_matching_validator(instance, keys) return false unless attribute_validator process_validator(validate, instance, attribute_validator) end |
.process_validator(validate, instance, attribute_validator) ⇒ Object
72 73 74 75 76 77 |
# File 'lib/database_validations/lib/rescuer.rb', line 72 def process_validator(validate, instance, attribute_validator) return false unless attribute_validator.validator.perform_rescue?(validate) attribute_validator.validator.apply_error(instance, attribute_validator.attribute) true end |
.resolve_keys(instance, error, key_types) ⇒ Object
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
# File 'lib/database_validations/lib/rescuer.rb', line 25 def resolve_keys(instance, error, key_types) adapter = Adapters.factory(instance.class) key_types.flat_map do |key_generator, error_processor| result = adapter.public_send(error_processor, error) # FK adapters return an array of candidate columns, each generating a # separate key. Uniqueness adapters return columns that form a single # composite key, passed together to the key generator. if key_generator == :for_db_presence Array(result).map { |column| KeyGenerator.public_send(key_generator, column) } else [KeyGenerator.public_send(key_generator, result)] end end end |