openhood-rspec_sequel_matchers 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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