Skip to content

Commit

Permalink
feat: improve composition, add .rule macro
Browse files Browse the repository at this point in the history
Also allow to override `#validate` in modules.

Closes #1
  • Loading branch information
vladfaust committed Sep 29, 2018
1 parent fea6921 commit be7bcc1
Show file tree
Hide file tree
Showing 20 changed files with 467 additions and 223 deletions.
18 changes: 14 additions & 4 deletions README.md
Expand Up @@ -27,11 +27,22 @@ This shard follows [Semantic Versioning 2.0.0](https://semver.org/), so see [rel

## Usage

This shards allows validation composition (i.e. inclusion of modules with custom validations and rules).

```crystal
require "validations"
module CustomValidations
include Validations
rule :email do |attr, value, rule|
invalidate(attr, "must be an email") unless /@/.match(value)
end
end
struct User
include Validations
include CustomValidations
property name : String
property email : String
Expand All @@ -42,7 +53,7 @@ struct User
end
validate name, size: (1..16)
validate email, size: (6..64), regex: /\w+@\w+\.\w{2,}/
validate email, size: (6..64), email: true
validate @age, gte: 18
# Will not be run if `@nilable.nil?`
Expand All @@ -56,12 +67,11 @@ struct User
end
user = User.new("Vadim", "e-mail", 17)
pp user.valid?
# false
user.valid? # false
pp user.invalid_attributes
# {
# "name" => ["must have size in (1..16)", "must not be equal to Vadim"],
# "email" => ["must have size in (6..64)", "must match /\\w+@\\w+\\.\\w{2,}/"],
# "email" => ["must have size in (6..64)", "must be an email"],
# "@age" => ["must be greater than or equal to 18"]
# }
```
Expand Down
28 changes: 28 additions & 0 deletions spec/validations/rules/gt_spec.cr
@@ -0,0 +1,28 @@
require "../../spec_helper"

record GTObject, x : Int32

struct GTObject
include Validations
validate x, gt: 5
end

describe ":gt" do
context "when valid" do
o = GTObject.new(6)

it do
o.valid?.should be_true
o.invalid_attributes.empty?.should be_true
end
end

context "when invalid" do
o = GTObject.new(5)

it do
o.valid?.should be_false
o.invalid_attributes.should eq ({"x" => ["must be greater than 5"]})
end
end
end
28 changes: 28 additions & 0 deletions spec/validations/rules/gte_spec.cr
@@ -0,0 +1,28 @@
require "../../spec_helper"

record GTEObject, x : Int32

struct GTEObject
include Validations
validate x, gte: 5
end

describe ":gte" do
context "when valid" do
o = GTEObject.new(5)

it do
o.valid?.should be_true
o.invalid_attributes.empty?.should be_true
end
end

context "when invalid" do
o = GTEObject.new(4)

it do
o.valid?.should be_false
o.invalid_attributes.should eq ({"x" => ["must be greater than or equal to 5"]})
end
end
end
15 changes: 15 additions & 0 deletions spec/validations/rules/in_spec.cr
@@ -0,0 +1,15 @@
require "../../spec_helper"

record InObject, x : Int32

struct InObject
include Validations
validate x, in: {1, 2}
end

describe ":in" do
it do
InObject.new(1).valid?.should be_true
InObject.new(3).valid?.should be_false
end
end
15 changes: 15 additions & 0 deletions spec/validations/rules/is_spec.cr
@@ -0,0 +1,15 @@
require "../../spec_helper"

record IsObject, x : Int32

struct IsObject
include Validations
validate x, is: 1
end

describe ":is" do
it do
IsObject.new(1).valid?.should be_true
IsObject.new(2).valid?.should be_false
end
end
28 changes: 28 additions & 0 deletions spec/validations/rules/lt_spec.cr
@@ -0,0 +1,28 @@
require "../../spec_helper"

record LTObject, x : Int32

struct LTObject
include Validations
validate x, lt: 5
end

describe ":lt" do
context "when valid" do
o = LTObject.new(4)

it do
o.valid?.should be_true
o.invalid_attributes.empty?.should be_true
end
end

context "when invalid" do
o = LTObject.new(5)

it do
o.valid?.should be_false
o.invalid_attributes.should eq ({"x" => ["must be less than 5"]})
end
end
end
28 changes: 28 additions & 0 deletions spec/validations/rules/lte_spec.cr
@@ -0,0 +1,28 @@
require "../../spec_helper"

record LTEObject, x : Int32

struct LTEObject
include Validations
validate x, lte: 5
end

describe ":lte" do
context "when valid" do
o = LTEObject.new(5)

it do
o.valid?.should be_true
o.invalid_attributes.empty?.should be_true
end
end

context "when invalid" do
o = LTEObject.new(6)

it do
o.valid?.should be_false
o.invalid_attributes.should eq ({"x" => ["must be less than or equal to 5"]})
end
end
end
15 changes: 15 additions & 0 deletions spec/validations/rules/regex_spec.cr
@@ -0,0 +1,15 @@
require "../../spec_helper"

record RegexObject, x : String

struct RegexObject
include Validations
validate x, regex: /@/
end

describe ":regex" do
it do
RegexObject.new("foo@bar").valid?.should be_true
RegexObject.new("foo").valid?.should be_false
end
end
56 changes: 56 additions & 0 deletions spec/validations/rules/size_spec.cr
@@ -0,0 +1,56 @@
require "../../spec_helper"

record NumberSizeObject, x : String
record RangeSizeObject, x : String

struct NumberSizeObject
include Validations
validate x, size: 1
end

struct RangeSizeObject
include Validations
validate x, size: (1..2)
end

describe ":size" do
context "with number" do
context "when valid" do
o = NumberSizeObject.new("a")

it do
o.valid?.should be_true
o.invalid_attributes.empty?.should be_true
end
end

context "when invalid" do
o = NumberSizeObject.new("aa")

it do
o.valid?.should be_false
o.invalid_attributes.should eq ({"x" => ["must have size equal to 1"]})
end
end
end

context "with range" do
context "when valid" do
o = RangeSizeObject.new("a")

it do
o.valid?.should be_true
o.invalid_attributes.empty?.should be_true
end
end

context "when invalid" do
o = RangeSizeObject.new("aaa")

it do
o.valid?.should be_false
o.invalid_attributes.should eq ({"x" => ["must have size in 1..2"]})
end
end
end
end

0 comments on commit be7bcc1

Please sign in to comment.