openhood-rspec_sequel_matchers 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +47 -0
  3. data/Rakefile +48 -0
  4. data/VERSION.yml +4 -0
  5. data/lib/rspec_sequel/association.rb +30 -0
  6. data/lib/rspec_sequel/base.rb +33 -0
  7. data/lib/rspec_sequel/matchers/have_column.rb +35 -0
  8. data/lib/rspec_sequel/matchers/have_many_to_many.rb +22 -0
  9. data/lib/rspec_sequel/matchers/have_many_to_one.rb +22 -0
  10. data/lib/rspec_sequel/matchers/have_one_to_many.rb +22 -0
  11. data/lib/rspec_sequel/matchers/validate_exact_length.rb +25 -0
  12. data/lib/rspec_sequel/matchers/validate_format.rb +25 -0
  13. data/lib/rspec_sequel/matchers/validate_includes.rb +25 -0
  14. data/lib/rspec_sequel/matchers/validate_integer.rb +21 -0
  15. data/lib/rspec_sequel/matchers/validate_length_range.rb +25 -0
  16. data/lib/rspec_sequel/matchers/validate_max_length.rb +25 -0
  17. data/lib/rspec_sequel/matchers/validate_min_length.rb +25 -0
  18. data/lib/rspec_sequel/matchers/validate_not_string.rb +21 -0
  19. data/lib/rspec_sequel/matchers/validate_numeric.rb +21 -0
  20. data/lib/rspec_sequel/matchers/validate_presence.rb +21 -0
  21. data/lib/rspec_sequel/matchers/validate_unique.rb +33 -0
  22. data/lib/rspec_sequel/validation.rb +77 -0
  23. data/lib/rspec_sequel_matchers.rb +9 -0
  24. data/spec/have_column_matcher_spec.rb +89 -0
  25. data/spec/have_many_to_many_matcher_spec.rb +56 -0
  26. data/spec/have_many_to_one_matcher_spec.rb +55 -0
  27. data/spec/have_one_to_many_matcher_spec.rb +55 -0
  28. data/spec/migrations/001_create_items.rb +15 -0
  29. data/spec/migrations/002_create_comments.rb +17 -0
  30. data/spec/migrations/003_create_comments_items.rb +15 -0
  31. data/spec/spec_helper.rb +51 -0
  32. data/spec/validate_exact_length_matcher_spec.rb +88 -0
  33. data/spec/validate_format_matcher_spec.rb +88 -0
  34. data/spec/validate_includes_matcher_spec.rb +88 -0
  35. data/spec/validate_integer_matcher_spec.rb +77 -0
  36. data/spec/validate_length_range_matcher_spec.rb +88 -0
  37. data/spec/validate_max_length_matcher_spec.rb +88 -0
  38. data/spec/validate_min_length_matcher_spec.rb +88 -0
  39. data/spec/validate_not_string_matcher_spec.rb +77 -0
  40. data/spec/validate_numeric_matcher_spec.rb +77 -0
  41. data/spec/validate_presence_matcher_spec.rb +77 -0
  42. data/spec/validate_unique_matcher_spec.rb +79 -0
  43. metadata +134 -0
@@ -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
@@ -0,0 +1,89 @@
1
+ require File.dirname(__FILE__) + "/spec_helper"
2
+
3
+ describe "have_column_matcher" do
4
+
5
+ before :all do
6
+ define_model :item
7
+ end
8
+
9
+ subject{ Item }
10
+
11
+ describe "messages" do
12
+ describe "without type" do
13
+ it "should contain a description" do
14
+ @matcher = have_column :name
15
+ @matcher.description.should == "have a column :name"
16
+ end
17
+ it "should set failure messages" do
18
+ @matcher = have_column :name
19
+ @matcher.matches? subject
20
+ @matcher.failure_message.should == "expected Item to " + @matcher.description
21
+ @matcher.negative_failure_message.should == "expected Item to not " + @matcher.description
22
+ end
23
+ end
24
+ describe "with type as symbol" do
25
+ it "should contain a description" do
26
+ @matcher = have_column :name, :type => :string
27
+ @matcher.description.should == "have a column :name with type string"
28
+ end
29
+ it "should set failure messages" do
30
+ @matcher = have_column :password, :type => :string
31
+ @matcher.matches? subject
32
+ @matcher.failure_message.should == "expected Item to " + @matcher.description
33
+ @matcher.negative_failure_message.should == "expected Item to not " + @matcher.description
34
+ end
35
+ end
36
+ describe "with type as object" do
37
+ it "should contain a description" do
38
+ @matcher = have_column :name, :type => String
39
+ @matcher.description.should == "have a column :name with type String"
40
+ end
41
+ it "should set failure messages" do
42
+ @matcher = have_column :password, :type => String
43
+ @matcher.matches? subject
44
+ @matcher.failure_message.should == "expected Item to " + @matcher.description
45
+ @matcher.negative_failure_message.should == "expected Item to not " + @matcher.description
46
+ end
47
+ it "should explicit found type if different than expected" do
48
+ @matcher = have_column :name, :type => Integer
49
+ @matcher.matches? subject
50
+ explanation = " (type found: string, varchar(255))"
51
+ @matcher.failure_message.should == "expected Item to " + @matcher.description + explanation
52
+ @matcher.negative_failure_message.should == "expected Item to not " + @matcher.description + explanation
53
+ end
54
+ end
55
+ describe "on anonymous Sequel::Model class" do
56
+ it "should set failure messages" do
57
+ @matcher = have_column :password
58
+ @matcher.matches? Sequel::Model(:comments)
59
+ @matcher.failure_message.should == "expected Comment to " + @matcher.description
60
+ @matcher.negative_failure_message.should == "expected Comment to not " + @matcher.description
61
+ end
62
+ end
63
+ describe "on Sequel::Model class" do
64
+ it "should set failure messages" do
65
+ @matcher = have_column :password
66
+ @matcher.matches? Item
67
+ @matcher.failure_message.should == "expected Item to " + @matcher.description
68
+ @matcher.negative_failure_message.should == "expected Item to not " + @matcher.description
69
+ end
70
+ end
71
+ describe "on Sequel::Model instance" do
72
+ it "should set failure messages" do
73
+ @matcher = have_column :password
74
+ @matcher.matches? Item.new
75
+ @matcher.failure_message.should == "expected #<Item @values={}> to " + @matcher.description
76
+ @matcher.negative_failure_message.should == "expected #<Item @values={}> to not " + @matcher.description
77
+ end
78
+ end
79
+ end
80
+
81
+ describe "matchers" do
82
+ it{ should have_column(:name) }
83
+ it{ should have_column(:name, :type => :string) }
84
+ it{ should have_column(:name, :type => String) }
85
+ it{ should_not have_column(:password) }
86
+ it{ should_not have_column(:name, :type => :integer) }
87
+ end
88
+
89
+ end
@@ -0,0 +1,56 @@
1
+ require File.dirname(__FILE__) + "/spec_helper"
2
+
3
+ describe "have_many_to_many_matcher" do
4
+
5
+ before :all do
6
+ define_model :item
7
+ define_model :comment do
8
+ many_to_many :items
9
+ end
10
+ end
11
+
12
+ subject{ Comment }
13
+
14
+ describe "messages" do
15
+ describe "without option" do
16
+ it "should contain a description" do
17
+ @matcher = have_many_to_many :items
18
+ @matcher.description.should == "have a many_to_many association :items"
19
+ end
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, :class_name => "Item"
30
+ @matcher.description.should == 'have a many_to_many association :items with option(s) :class_name => "Item"'
31
+ end
32
+ it "should set failure messages" do
33
+ @matcher = have_many_to_many :items, :class_name => "Item"
34
+ @matcher.matches? subject
35
+ @matcher.failure_message.should == "expected Comment to " + @matcher.description
36
+ @matcher.negative_failure_message.should == "expected Comment to not " + @matcher.description
37
+ end
38
+ it "should explicit used options if different than expected" do
39
+ @matcher = have_many_to_many :items, :class_name => "Price"
40
+ @matcher.matches? subject
41
+ explanation = ' expected :class_name == "Price" but found "Item" instead'
42
+ @matcher.failure_message.should == "expected Comment to " + @matcher.description + explanation
43
+ @matcher.negative_failure_message.should == "expected Comment to not " + @matcher.description + explanation
44
+ end
45
+ end
46
+ end
47
+
48
+ describe "matchers" do
49
+ it{ should have_many_to_many(:items) }
50
+ it{ should have_many_to_many(:items, :class_name => "Item", :join_table => :comments_items) }
51
+ it{ should_not have_many_to_many(:prices) }
52
+ it{ should_not have_many_to_many(:items, :class_name => "Price") }
53
+ it{ should_not have_many_to_many(:items, :join_table => :items_comments) }
54
+ end
55
+
56
+ end
@@ -0,0 +1,55 @@
1
+ require File.dirname(__FILE__) + "/spec_helper"
2
+
3
+ describe "have_many_to_one_matcher" do
4
+
5
+ before :all do
6
+ define_model :item
7
+ define_model :comment do
8
+ many_to_one :item
9
+ end
10
+ end
11
+
12
+ subject{ Comment }
13
+
14
+ describe "messages" do
15
+ describe "without option" do
16
+ it "should contain a description" do
17
+ @matcher = have_many_to_one :item
18
+ @matcher.description.should == "have a many_to_one association :item"
19
+ end
20
+ it "should set failure messages" do
21
+ @matcher = have_many_to_one :item
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_one :item, :class_name => "Item"
30
+ @matcher.description.should == 'have a many_to_one association :item with option(s) :class_name => "Item"'
31
+ end
32
+ it "should set failure messages" do
33
+ @matcher = have_many_to_one :item, :class_name => "Item"
34
+ @matcher.matches? subject
35
+ @matcher.failure_message.should == "expected Comment to " + @matcher.description
36
+ @matcher.negative_failure_message.should == "expected Comment to not " + @matcher.description
37
+ end
38
+ it "should explicit used options if different than expected" do
39
+ @matcher = have_many_to_one :item, :class_name => "Price"
40
+ @matcher.matches? subject
41
+ explanation = ' expected :class_name == "Price" but found "Item" instead'
42
+ @matcher.failure_message.should == "expected Comment to " + @matcher.description + explanation
43
+ @matcher.negative_failure_message.should == "expected Comment to not " + @matcher.description + explanation
44
+ end
45
+ end
46
+ end
47
+
48
+ describe "matchers" do
49
+ it{ should have_many_to_one(:item) }
50
+ it{ should have_many_to_one(:item, :class_name => "Item") }
51
+ it{ should_not have_many_to_one(:price) }
52
+ it{ should_not have_many_to_one(:item, :class_name => "Price") }
53
+ end
54
+
55
+ end
@@ -0,0 +1,55 @@
1
+ require File.dirname(__FILE__) + "/spec_helper"
2
+
3
+ describe "have_one_to_many_matcher" do
4
+
5
+ before :all do
6
+ define_model :item do
7
+ one_to_many :comments
8
+ end
9
+ define_model :comment
10
+ end
11
+
12
+ subject{ Item }
13
+
14
+ describe "messages" do
15
+ describe "without option" do
16
+ it "should contain a description" do
17
+ @matcher = have_one_to_many :comments
18
+ @matcher.description.should == "have a one_to_many association :comments"
19
+ end
20
+ it "should set failure messages" do
21
+ @matcher = have_one_to_many :comments
22
+ @matcher.matches? subject
23
+ @matcher.failure_message.should == "expected Item to " + @matcher.description
24
+ @matcher.negative_failure_message.should == "expected Item to not " + @matcher.description
25
+ end
26
+ end
27
+ describe "with options" do
28
+ it "should contain a description" do
29
+ @matcher = have_one_to_many :comments, :class_name => "Comment"
30
+ @matcher.description.should == 'have a one_to_many association :comments with option(s) :class_name => "Comment"'
31
+ end
32
+ it "should set failure messages" do
33
+ @matcher = have_one_to_many :comments, :class_name => "Comment"
34
+ @matcher.matches? subject
35
+ @matcher.failure_message.should == "expected Item to " + @matcher.description
36
+ @matcher.negative_failure_message.should == "expected Item to not " + @matcher.description
37
+ end
38
+ it "should explicit used options if different than expected" do
39
+ @matcher = have_one_to_many :comments, :class_name => "Price"
40
+ @matcher.matches? subject
41
+ explanation = ' expected :class_name == "Price" but found "Comment" instead'
42
+ @matcher.failure_message.should == "expected Item to " + @matcher.description + explanation
43
+ @matcher.negative_failure_message.should == "expected Item to not " + @matcher.description + explanation
44
+ end
45
+ end
46
+ end
47
+
48
+ describe "matchers" do
49
+ it{ should have_one_to_many(:comments) }
50
+ it{ should have_one_to_many(:comments, :class_name => "Comment") }
51
+ it{ should_not have_one_to_many(:prices) }
52
+ it{ should_not have_one_to_many(:comments, :class_name => "Price") }
53
+ end
54
+
55
+ end