Skip to content

Instantly share code, notes, and snippets.

@isubas
Created December 31, 2018 11:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save isubas/a935319bc56b6d450eb791b595adaaad to your computer and use it in GitHub Desktop.
Save isubas/a935319bc56b6d450eb791b595adaaad to your computer and use it in GitHub Desktop.
Basit Validasyon Modulü ve Örnekleri
module VirtualPos
# TypeCheckError
class TypeCheckerError < StandardError
attr_reader :attribute, :object
def initialize(attribute: nil, message: nil, object: nil)
@attribute = attribute
@object = object
super(message || "Type in not valid for #{attribute}")
end
end
# RuleCheckError
class RuleCheckerError < StandardError
attr_reader :attribute, :object
def initialize(attribute: nil, message: nil, object: nil)
@attribute = attribute
@object = object
super(message || "Rule failed for #{attribute}")
end
end
end
class Test
include VirtualPos::SimpleValidation
validate :foo, type: :string, rule: ->(value) { value.length > 3 }
validate :bar, type: :integer, rule: ->(value) { value >= 1 }
attr_reader :foo, :bar
def initialize(foo, bar)
@foo = foo
@bar = bar
end
end
# Examples
Test.validations
test = Test.new('ABC', 0)
test.valid? # => false
test.errors # => {:foo=>[#<VirtualPos::RuleCheckerError: Rule failed for foo>], :bar=>[#<VirtualPos::RuleCheckerError: Rule failed for bar>]}
test = Test.new('ABCD', '1')
test.valid? # => false
test.errors # => {:bar=>[#<VirtualPos::TypeCheckerError: Type in not valid for bar>, #<VirtualPos::RuleCheckerError: Rule failed for bar>]}
test.valid! # raise VirtualPos::TypeCheckerError: Type in not valid for bar
test = Test.new('ABCD', 1)
test.valid? # => true
module ProcSafeCall
refine Proc do
def safe_call(args)
call(args)
rescue ArgumentError, NoMethodError
nil
end
end
end
module VirtualPos
# SimpleValidation
module SimpleValidation
# InvalidRule
class InvalidRule < StandardError
def initialize(attribute)
@attribute = attribute
end
def message
"Rule invalid for #{@attribute}"
end
end
# Validator
class Validator
using ProcSafeCall
TYPES = {
string: [String],
hash: [Hash],
integer: [Integer, Fixnum, Float],
bolean: [TrueClass, FalseClass]
}.freeze
attr_reader :attribute, :type, :rule, :object, :strict
def initialize(attribute, type: nil, rule: -> { true })
raise InvalidRule, attribute unless rule.class == Proc || rule.nil?
@attribute = attribute
@type = type
@rule = rule
end
def valid?(object, strict: false)
type_check(object)
rule_check(object)
error_for_object = errors(object, init: false)
return true if error_for_object.empty?
raise error_for_object.first if strict
end
private
def type_check(object)
return true if TYPES.fetch(type, [type]).include?(
object.public_send(attribute).class
)
errors(object) << TypeCheckerError.new(attribute: attribute, object: object)
end
def rule_check(object)
return true if rule.nil? || rule.safe_call(object.public_send(attribute))
errors(object) << RuleCheckerError.new(attribute: attribute, object: object)
end
def errors(object, init: true)
object.errors[attribute] = [] if !object.errors.key?(attribute) && init
object.errors[attribute] || []
end
end
def self.included(base)
base.send :include, InstanceMethods
base.extend ClassMethods
end
# ClassMethods
module ClassMethods
attr_reader :validations
def validate(attribute, type: nil, rule: nil)
@validations ||= []
@validations << Validator.new(attribute, type: type, rule: rule)
end
end
# InstanceMethods
module InstanceMethods
attr_reader :errors
def valid!
return true if valid?(strict: true)
end
def valid?(strict: false)
@errors = {}
self.class.validations.each { |validator| validator.valid?(self, strict: strict) }
errors.empty?
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment