rspec_sequel_matchers 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.rdoc +47 -0
- data/Rakefile +46 -0
- data/lib/rspec_sequel/association.rb +30 -0
- data/lib/rspec_sequel/base.rb +33 -0
- data/lib/rspec_sequel/matchers/have_column.rb +35 -0
- data/lib/rspec_sequel/matchers/have_many_to_many.rb +22 -0
- data/lib/rspec_sequel/matchers/have_many_to_one.rb +22 -0
- data/lib/rspec_sequel/matchers/have_one_to_many.rb +22 -0
- data/lib/rspec_sequel/matchers/validate_exact_length.rb +25 -0
- data/lib/rspec_sequel/matchers/validate_format.rb +25 -0
- data/lib/rspec_sequel/matchers/validate_includes.rb +25 -0
- data/lib/rspec_sequel/matchers/validate_integer.rb +21 -0
- data/lib/rspec_sequel/matchers/validate_length_range.rb +25 -0
- data/lib/rspec_sequel/matchers/validate_max_length.rb +25 -0
- data/lib/rspec_sequel/matchers/validate_min_length.rb +25 -0
- data/lib/rspec_sequel/matchers/validate_not_string.rb +21 -0
- data/lib/rspec_sequel/matchers/validate_numeric.rb +21 -0
- data/lib/rspec_sequel/matchers/validate_presence.rb +21 -0
- data/lib/rspec_sequel/matchers/validate_unique.rb +33 -0
- data/lib/rspec_sequel/validation.rb +77 -0
- data/lib/rspec_sequel_matchers.rb +9 -0
- data/rspec_sequel_matchers.gemspec +107 -0
- data/spec/have_column_matcher_spec.rb +89 -0
- data/spec/have_many_to_many_matcher_spec.rb +56 -0
- data/spec/have_many_to_one_matcher_spec.rb +55 -0
- data/spec/have_one_to_many_matcher_spec.rb +55 -0
- data/spec/migrations/001_create_items.rb +15 -0
- data/spec/migrations/002_create_comments.rb +17 -0
- data/spec/migrations/003_create_comments_items.rb +15 -0
- data/spec/spec_helper.rb +51 -0
- data/spec/validate_exact_length_matcher_spec.rb +88 -0
- data/spec/validate_format_matcher_spec.rb +88 -0
- data/spec/validate_includes_matcher_spec.rb +88 -0
- data/spec/validate_integer_matcher_spec.rb +77 -0
- data/spec/validate_length_range_matcher_spec.rb +88 -0
- data/spec/validate_max_length_matcher_spec.rb +88 -0
- data/spec/validate_min_length_matcher_spec.rb +88 -0
- data/spec/validate_not_string_matcher_spec.rb +77 -0
- data/spec/validate_numeric_matcher_spec.rb +77 -0
- data/spec/validate_presence_matcher_spec.rb +77 -0
- data/spec/validate_unique_matcher_spec.rb +79 -0
- metadata +153 -0
data/.document
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Jonathan Tron, Joseph Halter
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
= rspec_sequel_matchers
|
2
|
+
|
3
|
+
Some Sequel Matchers for RSpec, using no other gem than rspec and sequel themself. As a consequence, you can use these matchers with Rails, Sinatra or any other framework. It's pretty feature complete.
|
4
|
+
|
5
|
+
Matchers assume that you're using the recommanded validation_helpers plugin. All instance validation methods are supported, namely:
|
6
|
+
* validates_exact_length
|
7
|
+
* validates_format
|
8
|
+
* validates_includes
|
9
|
+
* validates_integer
|
10
|
+
* validates_length_range
|
11
|
+
* validates_max_length
|
12
|
+
* validates_min_length
|
13
|
+
* validates_not_string
|
14
|
+
* validates_numeric
|
15
|
+
* validates_presence
|
16
|
+
* validates_unique
|
17
|
+
|
18
|
+
Each one with all possible options, namely:
|
19
|
+
* :allow_blank
|
20
|
+
* :allow_missing
|
21
|
+
* :allow_nil
|
22
|
+
* :message
|
23
|
+
|
24
|
+
There're also matchers for associations class methods:
|
25
|
+
* :many_to_many
|
26
|
+
* :many_to_one
|
27
|
+
* :one_to_many
|
28
|
+
|
29
|
+
And there's an additionnal matcher have_column to check columns existance and their type, see example usage bellow.
|
30
|
+
|
31
|
+
RspecSequel::Matchers has an extensive test suite and will give you as much explanation as possible in failure messages such as expected column type versus column type found in database.
|
32
|
+
|
33
|
+
== Install
|
34
|
+
|
35
|
+
sudo gem install openhood-rspec_sequel_matchers
|
36
|
+
|
37
|
+
== Example usage
|
38
|
+
|
39
|
+
describe Item do
|
40
|
+
it{ should have_column :name, :type => String }
|
41
|
+
it{ should_not have_column :wrong_name }
|
42
|
+
it{ should_validate_presence :name, :allow_nil => true }
|
43
|
+
end
|
44
|
+
|
45
|
+
== Copyright
|
46
|
+
|
47
|
+
Copyright (c) 2009 Jonathan Tron - Joseph Halter. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "rake"
|
3
|
+
|
4
|
+
begin
|
5
|
+
require "jeweler"
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.version = "0.2.0"
|
8
|
+
gem.name = "rspec_sequel_matchers"
|
9
|
+
gem.summary = %Q{RSpec Matchers for Sequel}
|
10
|
+
gem.email = "team@openhood.com"
|
11
|
+
gem.homepage = "http://github.com/openhood/rspec_sequel_matchers"
|
12
|
+
gem.authors = ["Jonathan Tron", "Joseph Halter"]
|
13
|
+
gem.add_dependency "rspec", ">= 2.0.0.a"
|
14
|
+
gem.add_dependency "sequel", ">= 3.8.0"
|
15
|
+
end
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
19
|
+
end
|
20
|
+
|
21
|
+
require "rspec/core/rake_task"
|
22
|
+
Rspec::Core::RakeTask.new(:spec) do |spec|
|
23
|
+
spec.pattern = "spec/**/*_spec.rb"
|
24
|
+
end
|
25
|
+
|
26
|
+
Rspec::Core::RakeTask.new(:rcov) do |spec|
|
27
|
+
spec.pattern = "spec/**/*_spec.rb"
|
28
|
+
spec.rcov = true
|
29
|
+
end
|
30
|
+
|
31
|
+
task :default => :spec
|
32
|
+
|
33
|
+
require "rake/rdoctask"
|
34
|
+
Rake::RDocTask.new do |rdoc|
|
35
|
+
if File.exist?("VERSION.yml")
|
36
|
+
config = YAML.load(File.read("VERSION.yml"))
|
37
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
38
|
+
else
|
39
|
+
version = ""
|
40
|
+
end
|
41
|
+
|
42
|
+
rdoc.rdoc_dir = "rdoc"
|
43
|
+
rdoc.title = "test #{version}"
|
44
|
+
rdoc.rdoc_files.include("README*")
|
45
|
+
rdoc.rdoc_files.include("lib/**/*.rb")
|
46
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module RspecSequel
|
2
|
+
|
3
|
+
class Association < Base
|
4
|
+
|
5
|
+
def description
|
6
|
+
desc = "have a #{association_type} association #{@attribute.inspect}"
|
7
|
+
desc << " with option(s) #{hash_to_nice_string @options}" unless @options.empty?
|
8
|
+
desc
|
9
|
+
end
|
10
|
+
|
11
|
+
def valid?(db, i, c, attribute, options)
|
12
|
+
@association = c.association_reflection(attribute) || {}
|
13
|
+
if @association.empty?
|
14
|
+
@suffix << "(no association #{@attribute.inspect} found)"
|
15
|
+
false
|
16
|
+
else
|
17
|
+
matching = @association[:type] == association_type
|
18
|
+
options.each{|key, value|
|
19
|
+
if @association[key]!=value
|
20
|
+
@suffix << "expected #{key.inspect} == #{value.inspect} but found #{@association[key].inspect} instead"
|
21
|
+
matching = false
|
22
|
+
end
|
23
|
+
}
|
24
|
+
matching
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "sequel/extensions/inflector"
|
2
|
+
|
3
|
+
module RspecSequel
|
4
|
+
|
5
|
+
class Base
|
6
|
+
def initialize(attribute, options={})
|
7
|
+
raise ArgumentError, "not enough params for matcher, required attribute is missing" if attribute.nil?
|
8
|
+
@attribute = attribute
|
9
|
+
@options = options
|
10
|
+
@description = []
|
11
|
+
end
|
12
|
+
def matches?(target)
|
13
|
+
@suffix = []
|
14
|
+
if target.is_a?(Sequel::Model)
|
15
|
+
@prefix = "expected #{target.inspect} to"
|
16
|
+
valid?(target.db, target, target.class, @attribute, @options)
|
17
|
+
else
|
18
|
+
@prefix = "expected #{target.table_name.to_s.classify} to"
|
19
|
+
valid?(target.db, target.new, target, @attribute, @options)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
def failure_message
|
23
|
+
[@prefix, description, @suffix].flatten.compact.join(" ")
|
24
|
+
end
|
25
|
+
def negative_failure_message
|
26
|
+
[@prefix, "not", description, @suffix].flatten.compact.join(" ")
|
27
|
+
end
|
28
|
+
def hash_to_nice_string(hash)
|
29
|
+
hash.sort{|a,b| a[0].to_s<=>b[0].to_s}.collect{|param| param.collect{|v| v.inspect}.join(" => ")}.join(", ")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module RspecSequel
|
2
|
+
module Matchers
|
3
|
+
|
4
|
+
class HaveColumnMatcher < RspecSequel::Base
|
5
|
+
def description
|
6
|
+
desc = "have a column #{@attribute.inspect}"
|
7
|
+
desc << " with type #{@options[:type]}" if @options[:type]
|
8
|
+
desc
|
9
|
+
end
|
10
|
+
|
11
|
+
def valid?(db, i, c, attribute, options)
|
12
|
+
|
13
|
+
# check column existance
|
14
|
+
col = db.schema(c.table_name).detect{|col| col[0]==attribute}
|
15
|
+
matching = !col.nil?
|
16
|
+
|
17
|
+
# check type
|
18
|
+
if @options[:type]
|
19
|
+
expected = db.send(:type_literal, {:type => options[:type]}).to_s
|
20
|
+
if matching
|
21
|
+
found = [col[1][:type].to_s, col[1][:db_type].to_s]
|
22
|
+
@suffix << "(type found: #{found.uniq.join(", ")})"
|
23
|
+
matching &&= found.include?(expected)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
matching
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def have_column(*args)
|
31
|
+
HaveColumnMatcher.new(*args)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module RspecSequel
|
2
|
+
module Matchers
|
3
|
+
|
4
|
+
class HaveManyToManyMatcher < RspecSequel::Association
|
5
|
+
def association_type
|
6
|
+
:many_to_many
|
7
|
+
end
|
8
|
+
|
9
|
+
def valid?(db, i, c, attribute, options)
|
10
|
+
matching = super
|
11
|
+
|
12
|
+
# check that association model exists, etc.
|
13
|
+
matching
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def have_many_to_many(*args)
|
18
|
+
HaveManyToManyMatcher.new(*args)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module RspecSequel
|
2
|
+
module Matchers
|
3
|
+
|
4
|
+
class HaveManyToOneMatcher < RspecSequel::Association
|
5
|
+
def association_type
|
6
|
+
:many_to_one
|
7
|
+
end
|
8
|
+
|
9
|
+
def valid?(db, i, c, attribute, options)
|
10
|
+
matching = super
|
11
|
+
|
12
|
+
# check that association model exists, etc.
|
13
|
+
matching
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def have_many_to_one(*args)
|
18
|
+
HaveManyToOneMatcher.new(*args)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module RspecSequel
|
2
|
+
module Matchers
|
3
|
+
|
4
|
+
class HaveOneToManyMatcher < RspecSequel::Association
|
5
|
+
def association_type
|
6
|
+
:one_to_many
|
7
|
+
end
|
8
|
+
|
9
|
+
def valid?(db, i, c, attribute, options)
|
10
|
+
matching = super
|
11
|
+
|
12
|
+
# check that association model exists, etc.
|
13
|
+
matching
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def have_one_to_many(*args)
|
18
|
+
HaveOneToManyMatcher.new(*args)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module RspecSequel
|
2
|
+
module Matchers
|
3
|
+
|
4
|
+
class ValidateExactLengthMatcher < RspecSequel::Validation
|
5
|
+
def description
|
6
|
+
desc = "validate length of #{@attribute.inspect} is exactly #{@additionnal.inspect}"
|
7
|
+
desc << " with option(s) #{hash_to_nice_string @options}" unless @options.empty?
|
8
|
+
desc
|
9
|
+
end
|
10
|
+
|
11
|
+
def additionnal_param_type
|
12
|
+
Fixnum
|
13
|
+
end
|
14
|
+
|
15
|
+
def validation_type
|
16
|
+
:validates_exact_length
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def validate_exact_length(*args)
|
21
|
+
ValidateExactLengthMatcher.new(*args)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module RspecSequel
|
2
|
+
module Matchers
|
3
|
+
|
4
|
+
class ValidateFormatMatcher < RspecSequel::Validation
|
5
|
+
def description
|
6
|
+
desc = "validate format of #{@attribute.inspect} against #{@additionnal.inspect}"
|
7
|
+
desc << " with option(s) #{hash_to_nice_string @options}" unless @options.empty?
|
8
|
+
desc
|
9
|
+
end
|
10
|
+
|
11
|
+
def additionnal_param_type
|
12
|
+
Regexp
|
13
|
+
end
|
14
|
+
|
15
|
+
def validation_type
|
16
|
+
:validates_format
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def validate_format(*args)
|
21
|
+
ValidateFormatMatcher.new(*args)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module RspecSequel
|
2
|
+
module Matchers
|
3
|
+
|
4
|
+
class ValidateIncludesMatcher < RspecSequel::Validation
|
5
|
+
def description
|
6
|
+
desc = "validate that #{@attribute.inspect} is included in #{@additionnal.inspect}"
|
7
|
+
desc << " with option(s) #{hash_to_nice_string @options}" unless @options.empty?
|
8
|
+
desc
|
9
|
+
end
|
10
|
+
|
11
|
+
def additionnal_param_type
|
12
|
+
Enumerable
|
13
|
+
end
|
14
|
+
|
15
|
+
def validation_type
|
16
|
+
:validates_includes
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def validate_includes(*args)
|
21
|
+
ValidateIncludesMatcher.new(*args)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module RspecSequel
|
2
|
+
module Matchers
|
3
|
+
|
4
|
+
class ValidateIntegerMatcher < RspecSequel::Validation
|
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(*args)
|
17
|
+
ValidateIntegerMatcher.new(*args)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module RspecSequel
|
2
|
+
module Matchers
|
3
|
+
|
4
|
+
class ValidateLengthRangeMatcher < RspecSequel::Validation
|
5
|
+
def description
|
6
|
+
desc = "validate length of #{@attribute.inspect} is included in #{@additionnal.inspect}"
|
7
|
+
desc << " with option(s) #{hash_to_nice_string @options}" unless @options.empty?
|
8
|
+
desc
|
9
|
+
end
|
10
|
+
|
11
|
+
def additionnal_param_type
|
12
|
+
Enumerable
|
13
|
+
end
|
14
|
+
|
15
|
+
def validation_type
|
16
|
+
:validates_length_range
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def validate_length_range(*args)
|
21
|
+
ValidateLengthRangeMatcher.new(*args)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module RspecSequel
|
2
|
+
module Matchers
|
3
|
+
|
4
|
+
class ValidateMaxLengthMatcher < RspecSequel::Validation
|
5
|
+
def description
|
6
|
+
desc = "validate length of #{@attribute.inspect} is less than or equal to #{@additionnal.inspect}"
|
7
|
+
desc << " with option(s) #{hash_to_nice_string @options}" unless @options.empty?
|
8
|
+
desc
|
9
|
+
end
|
10
|
+
|
11
|
+
def additionnal_param_type
|
12
|
+
Fixnum
|
13
|
+
end
|
14
|
+
|
15
|
+
def validation_type
|
16
|
+
:validates_max_length
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def validate_max_length(*args)
|
21
|
+
ValidateMaxLengthMatcher.new(*args)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module RspecSequel
|
2
|
+
module Matchers
|
3
|
+
|
4
|
+
class ValidateMinLengthMatcher < RspecSequel::Validation
|
5
|
+
def description
|
6
|
+
desc = "validate length of #{@attribute.inspect} is greater than or equal to #{@additionnal.inspect}"
|
7
|
+
desc << " with option(s) #{hash_to_nice_string @options}" unless @options.empty?
|
8
|
+
desc
|
9
|
+
end
|
10
|
+
|
11
|
+
def additionnal_param_type
|
12
|
+
Fixnum
|
13
|
+
end
|
14
|
+
|
15
|
+
def validation_type
|
16
|
+
:validates_min_length
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def validate_min_length(*args)
|
21
|
+
ValidateMinLengthMatcher.new(*args)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module RspecSequel
|
2
|
+
module Matchers
|
3
|
+
|
4
|
+
class ValidateNotStringMatcher < RspecSequel::Validation
|
5
|
+
def description
|
6
|
+
desc = "validate that #{@attribute.inspect} is not a string"
|
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_string
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def validate_not_string(*args)
|
17
|
+
ValidateNotStringMatcher.new(*args)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module RspecSequel
|
2
|
+
module Matchers
|
3
|
+
|
4
|
+
class ValidateNumericMatcher < RspecSequel::Validation
|
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(*args)
|
17
|
+
ValidateNumericMatcher.new(*args)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module RspecSequel
|
2
|
+
module Matchers
|
3
|
+
|
4
|
+
class ValidatePresenceMatcher < RspecSequel::Validation
|
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(*args)
|
17
|
+
ValidatePresenceMatcher.new(*args)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module RspecSequel
|
2
|
+
module Matchers
|
3
|
+
|
4
|
+
class ValidateUniqueMatcher < RspecSequel::Validation
|
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 valid_options
|
16
|
+
[:message]
|
17
|
+
end
|
18
|
+
|
19
|
+
def args_to_called_attributes(args)
|
20
|
+
called_attributes = []
|
21
|
+
until args.empty?
|
22
|
+
called_attributes << args.shift
|
23
|
+
end
|
24
|
+
called_attributes
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def validate_unique(*args)
|
29
|
+
ValidateUniqueMatcher.new(*args)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module RspecSequel
|
2
|
+
|
3
|
+
class Validation < Base
|
4
|
+
|
5
|
+
def initialize(*args)
|
6
|
+
# initialize Base
|
7
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
8
|
+
super(args.pop, options)
|
9
|
+
|
10
|
+
# check additionnal param
|
11
|
+
if additionnal_param_required?
|
12
|
+
if args.size>1
|
13
|
+
raise ArgumentError, "too many params for matcher"
|
14
|
+
else
|
15
|
+
@additionnal = args.pop
|
16
|
+
raise ArgumentError, "expected matcher first parameter to be #{additionnal_param_type.inspect} but received #{@additionnal.class.inspect} instead" unless @additionnal.kind_of?(additionnal_param_type)
|
17
|
+
end
|
18
|
+
else
|
19
|
+
raise ArgumentError, "too many params for matcher" unless args.empty?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def additionnal_param_type
|
24
|
+
NilClass
|
25
|
+
end
|
26
|
+
|
27
|
+
def additionnal_param_required?
|
28
|
+
additionnal_param_type!=NilClass
|
29
|
+
end
|
30
|
+
|
31
|
+
def valid_options
|
32
|
+
[:allow_blank, :allow_missing, :allow_nil, :message]
|
33
|
+
end
|
34
|
+
|
35
|
+
def valid?(db, i, c, attribute, options)
|
36
|
+
# check options
|
37
|
+
invalid_options = options.keys.reject{|o| valid_options.include?(o)}
|
38
|
+
invalid_options.each{|o|
|
39
|
+
@suffix << "but option #{o.inspect} is not valid"
|
40
|
+
}
|
41
|
+
return false unless invalid_options.empty?
|
42
|
+
|
43
|
+
# check validation itself
|
44
|
+
called_count = 0
|
45
|
+
i = i.dup
|
46
|
+
i.class.columns # ensure colums are read again after .dup
|
47
|
+
i.stub!(validation_type).and_return{|*args|
|
48
|
+
called_options = args.last.is_a?(Hash) ? args.pop : {}
|
49
|
+
called_attributes = args_to_called_attributes(args)
|
50
|
+
called_additionnal = args.shift if additionnal_param_required?
|
51
|
+
if !args.empty?
|
52
|
+
@suffix << "but called with too many params"
|
53
|
+
elsif called_attributes.include?(attribute)
|
54
|
+
if additionnal_param_required? && @additionnal!=called_additionnal
|
55
|
+
@suffix << "but called with #{called_additionnal} instead"
|
56
|
+
elsif !options.empty? && called_options!=options
|
57
|
+
@suffix << "but called with option(s) #{hash_to_nice_string called_options} instead"
|
58
|
+
else
|
59
|
+
called_count += 1
|
60
|
+
end
|
61
|
+
end
|
62
|
+
}
|
63
|
+
i.valid?
|
64
|
+
if called_count>1
|
65
|
+
@suffix << "but validation is called too many times"
|
66
|
+
return false
|
67
|
+
end
|
68
|
+
called_count==1
|
69
|
+
end
|
70
|
+
|
71
|
+
def args_to_called_attributes(args)
|
72
|
+
[args.pop].flatten
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# Load base
|
2
|
+
["base", "association", "validation"].each do |file|
|
3
|
+
require File.join(File.dirname(__FILE__), "rspec_sequel", file)
|
4
|
+
end
|
5
|
+
|
6
|
+
# Add matchers
|
7
|
+
Dir[File.join(File.dirname(__FILE__), "rspec_sequel", "matchers", "*.rb")].each do |file|
|
8
|
+
require file
|
9
|
+
end
|