sequel_spec 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +91 -0
- data/Rakefile +1 -0
- data/lib/sequel_spec.rb +8 -0
- data/lib/sequel_spec/association.rb +13 -0
- data/lib/sequel_spec/association/association_matcher.rb +32 -0
- data/lib/sequel_spec/association/have_many_to_many_matcher.rb +15 -0
- data/lib/sequel_spec/association/have_many_to_one_matcher.rb +15 -0
- data/lib/sequel_spec/association/have_one_through_one_matcher.rb +15 -0
- data/lib/sequel_spec/association/have_one_to_many_matcher.rb +15 -0
- data/lib/sequel_spec/association/have_one_to_one_matcher.rb +15 -0
- data/lib/sequel_spec/base.rb +45 -0
- data/lib/sequel_spec/integration/rspec.rb +6 -0
- data/lib/sequel_spec/validation.rb +18 -0
- data/lib/sequel_spec/validation/validate_format_matcher.rb +42 -0
- data/lib/sequel_spec/validation/validate_includes_matcher.rb +43 -0
- data/lib/sequel_spec/validation/validate_integer_matcher.rb +23 -0
- data/lib/sequel_spec/validation/validate_length_matcher.rb +81 -0
- data/lib/sequel_spec/validation/validate_matcher.rb +67 -0
- data/lib/sequel_spec/validation/validate_not_null_matcher.rb +21 -0
- data/lib/sequel_spec/validation/validate_numeric_matcher.rb +23 -0
- data/lib/sequel_spec/validation/validate_presence_matcher.rb +23 -0
- data/lib/sequel_spec/validation/validate_schema_types_matcher.rb +23 -0
- data/lib/sequel_spec/validation/validate_type_matcher.rb +42 -0
- data/lib/sequel_spec/validation/validate_unique_matcher.rb +31 -0
- data/lib/sequel_spec/version.rb +3 -0
- data/sequel_spec.gemspec +25 -0
- data/spec/migrations/001_create_tables.rb +26 -0
- data/spec/sequel_spec/association/have_many_to_many_matcher_spec.rb +57 -0
- data/spec/sequel_spec/association/have_many_to_one_matcher_spec.rb +57 -0
- data/spec/sequel_spec/association/have_one_through_one_matcher_spec.rb +57 -0
- data/spec/sequel_spec/association/have_one_to_many_matcher_spec.rb +57 -0
- data/spec/sequel_spec/association/have_one_to_one_matcher_spec.rb +57 -0
- data/spec/sequel_spec/validation/validate_format_matcher_spec.rb +88 -0
- data/spec/sequel_spec/validation/validate_includes_matcher_spec.rb +88 -0
- data/spec/sequel_spec/validation/validate_integer_matcher_spec.rb +76 -0
- data/spec/sequel_spec/validation/validate_length_matcher_spec.rb +252 -0
- data/spec/sequel_spec/validation/validate_not_null_matcher_spec.rb +83 -0
- data/spec/sequel_spec/validation/validate_numeric_matcher_spec.rb +76 -0
- data/spec/sequel_spec/validation/validate_presence_matcher_spec.rb +77 -0
- data/spec/sequel_spec/validation/validate_schema_types_matcher_spec.rb +83 -0
- data/spec/sequel_spec/validation/validate_type_matcher_spec.rb +88 -0
- data/spec/sequel_spec/validation/validate_unique_matcher_spec.rb +83 -0
- data/spec/spec_helper.rb +33 -0
- data/spec/support/test_helpers.rb +19 -0
- metadata +164 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
module SequelSpec
|
2
|
+
module Matchers
|
3
|
+
module Validation
|
4
|
+
class ValidateIntegerMatcher < ValidateMatcher
|
5
|
+
def description
|
6
|
+
desc = "validate that #{@attribute.inspect} is a valid integer"
|
7
|
+
desc << " with option(s) #{hash_to_nice_string @options}" unless @options.empty?
|
8
|
+
desc
|
9
|
+
end
|
10
|
+
|
11
|
+
def validation_type
|
12
|
+
:validates_integer
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def validate_integer(attribute)
|
17
|
+
ValidateIntegerMatcher.new(attribute)
|
18
|
+
end
|
19
|
+
|
20
|
+
alias :validate_integrity_of :validate_integer
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module SequelSpec
|
2
|
+
module Matchers
|
3
|
+
module Validation
|
4
|
+
class ValidateLengthMatcher < ValidateMatcher
|
5
|
+
attr_reader :validation_type
|
6
|
+
|
7
|
+
def description
|
8
|
+
desc = "validate that length of #{@attribute.inspect} "
|
9
|
+
desc << case validation_type
|
10
|
+
when :validates_exact_length then "is exactly"
|
11
|
+
when :validates_min_length then "is greater than or equal to"
|
12
|
+
when :validates_max_length then "is less than or equal to"
|
13
|
+
when :validates_length_range then "is included in"
|
14
|
+
end << " #{@additionnal.inspect}"
|
15
|
+
|
16
|
+
desc << " with option(s) #{hash_to_nice_string @options}" unless @options.empty?
|
17
|
+
desc
|
18
|
+
end
|
19
|
+
|
20
|
+
def additionnal_param_required?
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
def additionnal_param_check
|
25
|
+
unless validation_type && @additionnal
|
26
|
+
raise ArgumentError, "You should specify the type of validation using #is, #is_at_least, #is_at_most or #is_between"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def is(value)
|
31
|
+
unless value.is_a?(Fixnum)
|
32
|
+
raise ArgumentError, "#is expects a Fixnum, #{value.class} given"
|
33
|
+
end
|
34
|
+
|
35
|
+
@additionnal = value
|
36
|
+
@validation_type = :validates_exact_length
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
alias :is_equal_to :is
|
41
|
+
|
42
|
+
def is_at_least(value)
|
43
|
+
unless value.is_a?(Fixnum)
|
44
|
+
raise ArgumentError, "#is_at_least expects a Fixnum, #{value.class} given"
|
45
|
+
end
|
46
|
+
|
47
|
+
@additionnal = value
|
48
|
+
@validation_type = :validates_min_length
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
def is_at_most(value)
|
53
|
+
unless value.is_a?(Fixnum)
|
54
|
+
raise ArgumentError, "#is_at_most expects a Fixnum, #{value.class} given"
|
55
|
+
end
|
56
|
+
|
57
|
+
@additionnal = value
|
58
|
+
@validation_type = :validates_max_length
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
def is_between(value)
|
63
|
+
unless value.is_a?(Range)
|
64
|
+
raise ArgumentError, "#is_between expects a Range, #{value.class} given"
|
65
|
+
end
|
66
|
+
|
67
|
+
@additionnal = value
|
68
|
+
@validation_type = :validates_length_range
|
69
|
+
self
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def validate_length(attribute)
|
74
|
+
ValidateLengthMatcher.new(attribute)
|
75
|
+
end
|
76
|
+
|
77
|
+
alias :validate_length_of :validate_length
|
78
|
+
alias :ensure_length_of :validate_length
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module SequelSpec
|
2
|
+
module Matchers
|
3
|
+
module Validation
|
4
|
+
class ValidateMatcher < Base
|
5
|
+
def valid?(db, instance, klass, attribute, options)
|
6
|
+
additionnal_param_check if self.respond_to?(:additionnal_param_check)
|
7
|
+
|
8
|
+
instance = instance.dup
|
9
|
+
instance.class.columns
|
10
|
+
|
11
|
+
called = false
|
12
|
+
|
13
|
+
proxy = Proc.new do |args|
|
14
|
+
called_options = args.last.is_a?(Hash) ? args.pop : {}
|
15
|
+
called_attributes = args_to_called_attributes(args)
|
16
|
+
called_additionnal = args.shift if additionnal_param_required?
|
17
|
+
|
18
|
+
if !args.empty?
|
19
|
+
@suffix << "but called with too many params"
|
20
|
+
elsif called_attributes.include?(attribute)
|
21
|
+
if additionnal_param_required? && @additionnal != called_additionnal
|
22
|
+
@suffix << "but called with #{called_additionnal} instead"
|
23
|
+
elsif !options.empty? && called_options != options
|
24
|
+
@suffix << "but called with option(s) #{hash_to_nice_string called_options} instead"
|
25
|
+
else
|
26
|
+
called = true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
instance.singleton_class.send(:define_method, validation_type,
|
32
|
+
Proc.new { |*args| proxy.call(args) }
|
33
|
+
)
|
34
|
+
|
35
|
+
# Run validations
|
36
|
+
instance.valid?
|
37
|
+
|
38
|
+
called
|
39
|
+
end
|
40
|
+
|
41
|
+
def allowing_nil
|
42
|
+
with_options :allow_nil => true
|
43
|
+
end
|
44
|
+
|
45
|
+
def allowing_blank
|
46
|
+
with_options :allow_blank => true
|
47
|
+
end
|
48
|
+
|
49
|
+
def allowing_missing
|
50
|
+
with_options :allow_missing => true
|
51
|
+
end
|
52
|
+
|
53
|
+
def with_message(message)
|
54
|
+
with_options :message => message
|
55
|
+
end
|
56
|
+
|
57
|
+
def additionnal_param_required?
|
58
|
+
false
|
59
|
+
end
|
60
|
+
|
61
|
+
def args_to_called_attributes(args)
|
62
|
+
[args.pop].flatten
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module SequelSpec
|
2
|
+
module Matchers
|
3
|
+
module Validation
|
4
|
+
class ValidateNotNullMatcher < ValidateMatcher
|
5
|
+
def description
|
6
|
+
desc = "validate that #{@attribute.inspect} is not null"
|
7
|
+
desc << " with option(s) #{hash_to_nice_string @options}" unless @options.empty?
|
8
|
+
desc
|
9
|
+
end
|
10
|
+
|
11
|
+
def validation_type
|
12
|
+
:validates_not_null
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def validate_not_null(attribute)
|
17
|
+
ValidateNotNullMatcher.new(attribute)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module SequelSpec
|
2
|
+
module Matchers
|
3
|
+
module Validation
|
4
|
+
class ValidateNumericMatcher < ValidateMatcher
|
5
|
+
def description
|
6
|
+
desc = "validate that #{@attribute.inspect} is a valid float"
|
7
|
+
desc << " with option(s) #{hash_to_nice_string @options}" unless @options.empty?
|
8
|
+
desc
|
9
|
+
end
|
10
|
+
|
11
|
+
def validation_type
|
12
|
+
:validates_numeric
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def validate_numeric(attribute)
|
17
|
+
ValidateNumericMatcher.new(attribute)
|
18
|
+
end
|
19
|
+
|
20
|
+
alias :validate_numericality_of :validate_numeric
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module SequelSpec
|
2
|
+
module Matchers
|
3
|
+
module Validation
|
4
|
+
class ValidatePresenceMatcher < ValidateMatcher
|
5
|
+
def description
|
6
|
+
desc = "validate presence of #{@attribute.inspect}"
|
7
|
+
desc << " with option(s) #{hash_to_nice_string @options}" unless @options.empty?
|
8
|
+
desc
|
9
|
+
end
|
10
|
+
|
11
|
+
def validation_type
|
12
|
+
:validates_presence
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def validate_presence(attribute)
|
17
|
+
ValidatePresenceMatcher.new(attribute)
|
18
|
+
end
|
19
|
+
|
20
|
+
alias :validate_presence_of :validate_presence
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module SequelSpec
|
2
|
+
module Matchers
|
3
|
+
module Validation
|
4
|
+
class ValidateSchemaTypesMatcher < ValidateMatcher
|
5
|
+
def description
|
6
|
+
desc = "validate schema types of #{@attribute.inspect}"
|
7
|
+
desc << " with option(s) #{hash_to_nice_string @options}" unless @options.empty?
|
8
|
+
desc
|
9
|
+
end
|
10
|
+
|
11
|
+
def validation_type
|
12
|
+
:validates_schema_types
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def validate_schema_types(attribute)
|
17
|
+
ValidateSchemaTypesMatcher.new(attribute)
|
18
|
+
end
|
19
|
+
|
20
|
+
alias :validate_schema_types_of :validate_schema_types
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module SequelSpec
|
2
|
+
module Matchers
|
3
|
+
module Validation
|
4
|
+
class ValidateTypeMatcher < ValidateMatcher
|
5
|
+
def description
|
6
|
+
desc = "validate that type of #{@attribute.inspect} is #{@additionnal.inspect}"
|
7
|
+
desc << " with option(s) #{hash_to_nice_string @options}" unless @options.empty?
|
8
|
+
desc
|
9
|
+
end
|
10
|
+
|
11
|
+
def additionnal_param_required?
|
12
|
+
true
|
13
|
+
end
|
14
|
+
|
15
|
+
def additionnal_param_check
|
16
|
+
unless @additionnal
|
17
|
+
raise ArgumentError, "You should specify the accepted types using #is"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def is(value)
|
22
|
+
unless value.is_a?(Class) || value.is_a?(Array)
|
23
|
+
raise ArgumentError, "#with expects a Class or an array of Classes"
|
24
|
+
end
|
25
|
+
|
26
|
+
@additionnal = value
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def validation_type
|
31
|
+
:validates_type
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def validate_type(attribute)
|
36
|
+
ValidateTypeMatcher.new(attribute)
|
37
|
+
end
|
38
|
+
|
39
|
+
alias :validate_type_of :validate_type
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module SequelSpec
|
2
|
+
module Matchers
|
3
|
+
module Validation
|
4
|
+
class ValidateUniqueMatcher < ValidateMatcher
|
5
|
+
def description
|
6
|
+
desc = "validate uniqueness of #{@attribute.inspect}"
|
7
|
+
desc << " with option(s) #{hash_to_nice_string @options}" unless @options.empty?
|
8
|
+
desc
|
9
|
+
end
|
10
|
+
|
11
|
+
def validation_type
|
12
|
+
:validates_unique
|
13
|
+
end
|
14
|
+
|
15
|
+
def args_to_called_attributes(args)
|
16
|
+
called_attributes = []
|
17
|
+
until args.empty?
|
18
|
+
called_attributes << args.shift
|
19
|
+
end
|
20
|
+
called_attributes
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def validate_unique(attribute)
|
25
|
+
ValidateUniqueMatcher.new(attribute)
|
26
|
+
end
|
27
|
+
|
28
|
+
alias :validate_uniqueness_of :validate_unique
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/sequel_spec.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
$: << File.expand_path('../lib', __FILE__)
|
3
|
+
|
4
|
+
require 'sequel_spec/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "sequel_spec"
|
8
|
+
spec.version = SequelSpec::VERSION
|
9
|
+
spec.authors = ["Adrià Planas", "Jonathan Tron", "Joseph Halter"]
|
10
|
+
spec.email = ["adriaplanas@liquidcodeworks.com"]
|
11
|
+
spec.summary = %q{RSpec Matchers for Sequel}
|
12
|
+
spec.homepage = "https://github.com/planas/sequel_spec"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.test_files = spec.files.grep('spec')
|
17
|
+
spec.require_paths = ["lib"]
|
18
|
+
|
19
|
+
spec.add_runtime_dependency "sequel", "~> 4.0"
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
22
|
+
spec.add_development_dependency "rspec", "~> 2.0"
|
23
|
+
spec.add_development_dependency "database_cleaner"
|
24
|
+
spec.add_development_dependency "sqlite3"
|
25
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
Sequel.migration do
|
2
|
+
change do
|
3
|
+
create_table :items do
|
4
|
+
primary_key :id
|
5
|
+
String :name
|
6
|
+
String :manufacturer
|
7
|
+
String :origin
|
8
|
+
String :owner
|
9
|
+
Float :price
|
10
|
+
end
|
11
|
+
|
12
|
+
create_table :comments do
|
13
|
+
primary_key :id
|
14
|
+
foreign_key :item_id, :items, :type => Fixnum
|
15
|
+
String :content
|
16
|
+
DateTime :created_at
|
17
|
+
index :item_id
|
18
|
+
end
|
19
|
+
|
20
|
+
create_table :comments_items do
|
21
|
+
foreign_key :comment_id, :comments, :type => Fixnum
|
22
|
+
foreign_key :item_id, :items, :type => Fixnum
|
23
|
+
index [:comment_id, :item_id], :unique => true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "have_many_to_many_matcher" do
|
4
|
+
before :all do
|
5
|
+
define_model :item
|
6
|
+
define_model :comment do
|
7
|
+
many_to_many :items
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
subject{ Comment }
|
12
|
+
|
13
|
+
describe "messages" do
|
14
|
+
describe "without option" do
|
15
|
+
it "should contain a description" do
|
16
|
+
@matcher = have_many_to_many :items
|
17
|
+
@matcher.description.should == "have a many_to_many association :items"
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should set failure messages" do
|
21
|
+
@matcher = have_many_to_many :items
|
22
|
+
@matcher.matches? subject
|
23
|
+
@matcher.failure_message.should == "expected Comment to " + @matcher.description
|
24
|
+
@matcher.negative_failure_message.should == "expected Comment to not " + @matcher.description
|
25
|
+
end
|
26
|
+
end
|
27
|
+
describe "with options" do
|
28
|
+
it "should contain a description" do
|
29
|
+
@matcher = have_many_to_many(:items).with_options :class_name => "Item"
|
30
|
+
@matcher.description.should == 'have a many_to_many association :items with option(s) :class_name => "Item"'
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should set failure messages" do
|
34
|
+
@matcher = have_many_to_many(:items).with_options :class_name => "Item"
|
35
|
+
@matcher.matches? subject
|
36
|
+
@matcher.failure_message.should == "expected Comment to " + @matcher.description
|
37
|
+
@matcher.negative_failure_message.should == "expected Comment to not " + @matcher.description
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should explicit used options if different than expected" do
|
41
|
+
@matcher = have_many_to_many(:items).with_options :class_name => "Price"
|
42
|
+
@matcher.matches? subject
|
43
|
+
explanation = ' expected :class_name == "Price" but found "Item" instead'
|
44
|
+
@matcher.failure_message.should == "expected Comment to " + @matcher.description + explanation
|
45
|
+
@matcher.negative_failure_message.should == "expected Comment to not " + @matcher.description + explanation
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "matchers" do
|
51
|
+
it{ should have_many_to_many(:items) }
|
52
|
+
it{ should have_many_to_many(:items).with_options :class_name => "Item", :join_table => :comments_items }
|
53
|
+
it{ should_not have_many_to_many(:prices) }
|
54
|
+
it{ should_not have_many_to_many(:items).with_options :class_name => "Price" }
|
55
|
+
it{ should_not have_many_to_many(:items).with_options :join_table => :items_comments }
|
56
|
+
end
|
57
|
+
end
|