feat(orm): add validations with error messages
This commit is contained in:
1 parent
25544c9d00
commit
ec563d4189
2 files changed
+124
No files matched your search
@@ -0,0 +1,43 @@
|
|||||||
|
module Prism
|
||||||
|
module Orm
|
||||||
|
class Errors
|
||||||
|
include Enumerable
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@messages = Hash.new { |hash, key| hash[key] = [] }
|
||||||
|
end
|
||||||
|
|
||||||
|
def add(attribute, message)
|
||||||
|
@messages[attribute.to_sym] << message
|
||||||
|
end
|
||||||
|
|
||||||
|
def [](attribute)
|
||||||
|
@messages[attribute.to_sym]
|
||||||
|
end
|
||||||
|
|
||||||
|
def key?(attribute)
|
||||||
|
@messages.key?(attribute.to_sym)
|
||||||
|
end
|
||||||
|
|
||||||
|
def clear
|
||||||
|
@messages.clear
|
||||||
|
end
|
||||||
|
|
||||||
|
def empty?
|
||||||
|
@messages.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
def any?
|
||||||
|
!empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
def each(&block)
|
||||||
|
@messages.each(&block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def full_messages
|
||||||
|
@messages.flat_map { |attribute, messages| messages.map { |message| "#{attribute} #{message}" } }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
module Prism
|
||||||
|
module Orm
|
||||||
|
module Validatable
|
||||||
|
def self.included(base)
|
||||||
|
base.extend(ClassMethods)
|
||||||
|
end
|
||||||
|
|
||||||
|
module ClassMethods
|
||||||
|
def validators
|
||||||
|
@validators ||= []
|
||||||
|
end
|
||||||
|
|
||||||
|
def validates(*attributes, **rules)
|
||||||
|
attributes.each do |attribute|
|
||||||
|
rules.each { |kind, config| validators << { attribute: attribute, kind: kind, config: config } }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def errors
|
||||||
|
@errors ||= Errors.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def valid?
|
||||||
|
errors.clear
|
||||||
|
self.class.validators.each { |validator| run_validator(validator) }
|
||||||
|
errors.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
def invalid?
|
||||||
|
!valid?
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def run_validator(validator)
|
||||||
|
case validator[:kind]
|
||||||
|
when :presence then validate_presence(validator[:attribute], validator[:config])
|
||||||
|
when :uniqueness then validate_uniqueness(validator[:attribute], validator[:config])
|
||||||
|
when :format then validate_format(validator[:attribute], validator[:config])
|
||||||
|
when :length then validate_length(validator[:attribute], validator[:config])
|
||||||
|
else raise ArgumentError, "unknown validator #{validator[:kind]}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_presence(attribute, config)
|
||||||
|
return unless config
|
||||||
|
value = self[attribute]
|
||||||
|
return unless value.nil? || (value.respond_to?(:empty?) && value.empty?)
|
||||||
|
errors.add(attribute, message_for(config, 'must be present'))
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_uniqueness(attribute, config)
|
||||||
|
return unless config
|
||||||
|
value = self[attribute]
|
||||||
|
return if value.nil?
|
||||||
|
conflict = self.class.where(attribute => value).any? { |record| !persisted? || record.id != id }
|
||||||
|
errors.add(attribute, message_for(config, 'must be unique')) if conflict
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_format(attribute, config)
|
||||||
|
pattern = config.is_a?(Hash) ? config[:with] : config
|
||||||
|
return unless pattern
|
||||||
|
value = self[attribute]
|
||||||
|
return if value.nil?
|
||||||
|
return if value.to_s.match?(pattern)
|
||||||
|
errors.add(attribute, message_for(config, 'is invalid'))
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_length(attribute, config)
|
||||||
|
value = self[attribute].to_s
|
||||||
|
errors.add(attribute, "is too short (minimum #{config[:minimum]})") if config[:minimum] && value.length < config[:minimum]
|
||||||
|
errors.add(attribute, "is too long (maximum #{config[:maximum]})") if config[:maximum] && value.length > config[:maximum]
|
||||||
|
end
|
||||||
|
|
||||||
|
def message_for(config, default)
|
||||||
|
config.is_a?(Hash) && config[:message] ? config[:message] : default
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in new issue
Block a user