Skip to content

Commit

Permalink
Dj/validation using blocks and procs (#131)
Browse files Browse the repository at this point in the history
* validation using blocks and procs

* remove trailing spaces

* add specs for validators
  • Loading branch information
drujensen authored Jan 30, 2018
1 parent e306360 commit 2efbbbb
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 22 deletions.
74 changes: 74 additions & 0 deletions spec/granite_orm/validations/validator_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
require "../../spec_helper"

class NameTest < Granite::ORM::Base
adapter pg
field name : String

validate :name, "cannot be blank", ->(s : NameTest) do
!s.name.to_s.blank?
end
end

class EmailTest < Granite::ORM::Base
adapter pg
field email : String

validate :email, "cannot be blank" do |email_test|
!email_test.email.to_s.blank?
end
end

class PasswordTest < Granite::ORM::Base
adapter pg
field password : String
field password_validation : String

validate "password and validation should match" do |password_test|
password_test.password == password_test.password_validation
end
end

describe Granite::ORM::Validators do
describe "validates using proc" do
it "returns true if name is set" do
subject = NameTest.new
subject.name = "name"
subject.valid?.should eq true
end

it "returns false if name is blank" do
subject = NameTest.new
subject.name = ""
subject.valid?.should eq false
end
end

describe "validates using block" do
it "returns true if email is set" do
subject = EmailTest.new
subject.email = "test@example.com"
subject.valid?.should eq true
end

it "returns false if email is blank" do
subject = EmailTest.new
subject.email = ""
subject.valid?.should eq false
end
end
describe "validates using block without field" do
it "returns true if passwords match" do
subject = PasswordTest.new
subject.password = "123"
subject.password_validation = "123"
subject.valid?.should eq true
end

it "returns false if password does not match" do
subject = PasswordTest.new
subject.password = "123"
subject.password_validation = "1234"
subject.valid?.should eq false
end
end
end
5 changes: 2 additions & 3 deletions spec/granite_orm_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ class WebSite < Granite::ORM::Base
primary custom_id : Int32
field name : String

validate :name, "Name cannot be blank" do
!(name.not_nil!.blank?)
validate :name, "Name cannot be blank", ->(s : WebSite) do
!s.name.to_s.blank?
end
end

Expand Down Expand Up @@ -114,7 +114,6 @@ describe Granite::ORM::Base do
context "without a name" do
it "is not valid" do
s = WebSite.new(name: "")

s.valid?.should eq false
s.errors.first.message.should eq "Name cannot be blank"
end
Expand Down
4 changes: 2 additions & 2 deletions spec/spec_models.cr
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ end

has_many :student_{{ adapter_literal }}s

validate :name, "Name cannot be blank" do
!(name.not_nil!.blank?)
validate :name, "Name cannot be blank" do |parent|
!parent.name.to_s.blank?
end

def self.drop_and_create
Expand Down
48 changes: 31 additions & 17 deletions src/granite_orm/validators.cr
Original file line number Diff line number Diff line change
@@ -1,34 +1,48 @@
require "./error"

# Analyze validation blocks and procs
#
# By example:
# ```
# validate :name, "can't be blank" do |user|
# !user.name.to_s.blank?
# end
#
# validate :name, "can't be blank", -> (user : User) do
# !user.name.to_s.blank?
# end
#
# name_required = ->(model : Granite::ORM::Base) { !model.name.to_s.blank? }
# validate :name, "can't be blank", name_required
# ```
module Granite::ORM::Validators
getter errors = [] of Error

macro included
macro inherited
@validators = Array({field: Symbol, message: String, block: Proc(Bool)}).new
def validate!
@@validators = Array({field: String, message: String, block: Proc(self, Bool)}).new

def self.validate(message : String, &block : self -> Bool)
self.validate(:base, message, block)
end
end
end

macro validate(message)
def validate!
previous_def
@validators << {field: :base, message: {{message}}, block: ->{{{yield}}}}
end
end
def self.validate(field : (Symbol | String), message : String, &block : self -> Bool)
self.validate(field, message, block)
end

def self.validate(message : String, block : self -> Bool)
self.validate(:base, message, block)
end

macro validate(field, message)
def validate!
previous_def
@validators << {field: {{field}}, message: {{message}}, block: ->{{{yield}}}}
def self.validate(field : (Symbol | String), message : String, block : self -> Bool)
@@validators << {field: field.to_s, message: message, block: block}
end
end
end

def valid?
validate!
@validators.each do |validator|
unless validator[:block].call
@@validators.each do |validator|
unless validator[:block].call(self)
errors << Error.new(validator[:field], validator[:message])
end
end
Expand Down

0 comments on commit 2efbbbb

Please sign in to comment.