remarkable_activerecord 3.0.0

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 (44) hide show
  1. data/CHANGELOG +47 -0
  2. data/LICENSE +20 -0
  3. data/README +2 -0
  4. data/lib/remarkable_activerecord/base.rb +238 -0
  5. data/lib/remarkable_activerecord/human_names.rb +37 -0
  6. data/lib/remarkable_activerecord/matchers/allow_mass_assignment_of_matcher.rb +34 -0
  7. data/lib/remarkable_activerecord/matchers/allow_values_for_matcher.rb +94 -0
  8. data/lib/remarkable_activerecord/matchers/association_matcher.rb +235 -0
  9. data/lib/remarkable_activerecord/matchers/have_column_matcher.rb +68 -0
  10. data/lib/remarkable_activerecord/matchers/have_index_matcher.rb +57 -0
  11. data/lib/remarkable_activerecord/matchers/have_readonly_attributes_matcher.rb +30 -0
  12. data/lib/remarkable_activerecord/matchers/have_scope_matcher.rb +80 -0
  13. data/lib/remarkable_activerecord/matchers/validate_acceptance_of_matcher.rb +51 -0
  14. data/lib/remarkable_activerecord/matchers/validate_associated_matcher.rb +99 -0
  15. data/lib/remarkable_activerecord/matchers/validate_confirmation_of_matcher.rb +45 -0
  16. data/lib/remarkable_activerecord/matchers/validate_exclusion_of_matcher.rb +47 -0
  17. data/lib/remarkable_activerecord/matchers/validate_inclusion_of_matcher.rb +47 -0
  18. data/lib/remarkable_activerecord/matchers/validate_length_of_matcher.rb +123 -0
  19. data/lib/remarkable_activerecord/matchers/validate_numericality_of_matcher.rb +184 -0
  20. data/lib/remarkable_activerecord/matchers/validate_presence_of_matcher.rb +29 -0
  21. data/lib/remarkable_activerecord/matchers/validate_uniqueness_of_matcher.rb +151 -0
  22. data/lib/remarkable_activerecord.rb +29 -0
  23. data/locale/en.yml +253 -0
  24. data/spec/allow_mass_assignment_of_matcher_spec.rb +57 -0
  25. data/spec/allow_values_for_matcher_spec.rb +56 -0
  26. data/spec/association_matcher_spec.rb +616 -0
  27. data/spec/have_column_matcher_spec.rb +73 -0
  28. data/spec/have_index_matcher_spec.rb +68 -0
  29. data/spec/have_readonly_attributes_matcher_spec.rb +47 -0
  30. data/spec/have_scope_matcher_spec.rb +69 -0
  31. data/spec/model_builder.rb +101 -0
  32. data/spec/rcov.opts +2 -0
  33. data/spec/spec.opts +4 -0
  34. data/spec/spec_helper.rb +27 -0
  35. data/spec/validate_acceptance_of_matcher_spec.rb +68 -0
  36. data/spec/validate_associated_matcher_spec.rb +122 -0
  37. data/spec/validate_confirmation_of_matcher_spec.rb +58 -0
  38. data/spec/validate_exclusion_of_matcher_spec.rb +88 -0
  39. data/spec/validate_inclusion_of_matcher_spec.rb +84 -0
  40. data/spec/validate_length_of_matcher_spec.rb +165 -0
  41. data/spec/validate_numericality_of_matcher_spec.rb +180 -0
  42. data/spec/validate_presence_of_matcher_spec.rb +52 -0
  43. data/spec/validate_uniqueness_of_matcher_spec.rb +150 -0
  44. metadata +112 -0
@@ -0,0 +1,68 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe 'have_index_matcher' do
4
+ include ModelBuilder
5
+
6
+ before(:each) do
7
+ @model = define_model :users, :table => lambda {|table|
8
+ table.string :name, :null => true
9
+ table.string :email, :limit => '255', :default => 'jose.valim@gmail.com'
10
+ }
11
+
12
+ ActiveRecord::Base.connection.add_index :users, :name
13
+ ActiveRecord::Base.connection.add_index :users, :email, :unique => true
14
+ ActiveRecord::Base.connection.add_index :users, [:email, :name], :unique => true
15
+ end
16
+
17
+ describe 'messages' do
18
+
19
+ it 'should contain a description' do
20
+ @matcher = have_index(:name)
21
+ @matcher.description.should == 'have index for column(s) name'
22
+
23
+ @matcher.unique
24
+ @matcher.description.should == 'have index for column(s) name with unique values'
25
+ end
26
+
27
+ it 'should set index_exists? message' do
28
+ @matcher = have_index(:password)
29
+ @matcher.matches?(@model)
30
+ @matcher.failure_message.should == 'Expected index password to exist on table users'
31
+ end
32
+
33
+ it 'should set is_unique? message' do
34
+ @matcher = have_index(:email, :unique => false)
35
+ @matcher.matches?(@model)
36
+ @matcher.failure_message.should == 'Expected index on email with unique equals to false, got true'
37
+ end
38
+ end
39
+
40
+ describe 'matchers' do
41
+ it { should have_index(:name) }
42
+ it { should have_index(:email) }
43
+ it { should have_index([:email, :name]) }
44
+ it { should have_indices(:name, :email) }
45
+
46
+ it { should have_index(:name).unique(false) }
47
+ it { should have_index(:email).unique }
48
+
49
+ it { should_not have_index(:password) }
50
+ it { should_not have_index(:name).unique(true) }
51
+ it { should_not have_index(:email).unique(false) }
52
+ end
53
+
54
+ describe 'macros' do
55
+ should_have_index :name
56
+ should_have_index :email
57
+ should_have_index [:email, :name]
58
+ should_have_indices :name, :email
59
+
60
+ should_have_index :name, :unique => false
61
+ should_have_index :email, :unique => true
62
+
63
+ should_not_have_index :password
64
+ should_not_have_index :name, :unique => true
65
+ should_not_have_index :email, :unique => false
66
+ end
67
+ end
68
+
@@ -0,0 +1,47 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe 'have_readonly_attributes' do
4
+ include ModelBuilder
5
+
6
+ def define_and_validate(options={})
7
+ @model = define_model :product, :title => :string, :category => :string do
8
+ attr_readonly :title, :category if options[:readonly] == true
9
+ attr_readonly *options[:readonly] if options[:readonly].is_a?(Array)
10
+ end
11
+
12
+ have_readonly_attributes(:title, :category)
13
+ end
14
+
15
+ describe 'messages' do
16
+
17
+ it 'should contain a description' do
18
+ @matcher = have_readonly_attributes(:title, :category)
19
+ @matcher.description.should == 'make title and category read-only'
20
+ end
21
+
22
+ it 'should set is_readonly? message' do
23
+ @matcher = define_and_validate(:readonly => [:another])
24
+ @matcher.matches?(@model)
25
+ @matcher.failure_message.should == 'Expected Product to make title read-only, got ["another"]'
26
+ end
27
+
28
+ end
29
+
30
+ describe 'matchers' do
31
+ it { should define_and_validate(:readonly => true) }
32
+ it { should_not define_and_validate(:readonly => false) }
33
+ it { should_not define_and_validate(:accessible => [:another]) }
34
+ end
35
+
36
+ describe 'macros' do
37
+ before(:each){ define_and_validate(:readonly => true) }
38
+
39
+ should_have_readonly_attributes :title
40
+ should_have_readonly_attributes :category
41
+ should_have_readonly_attributes :title, :category
42
+
43
+ should_not_have_readonly_attributes :another
44
+ should_not_have_readonly_attributes :title, :another
45
+ end
46
+ end
47
+
@@ -0,0 +1,69 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe 'have_scope' do
4
+ include ModelBuilder
5
+
6
+ before(:each) do
7
+ @model = define_model :product, :title => :string, :category => :string do
8
+ named_scope :recent, :order => 'created_at DESC'
9
+ named_scope :latest, lambda {|c| {:limit => c}}
10
+
11
+ def self.beginning(c)
12
+ scoped(:offset => c)
13
+ end
14
+
15
+ def self.null
16
+ nil
17
+ end
18
+ end
19
+ end
20
+
21
+ describe 'messages' do
22
+
23
+ it 'should contain a description' do
24
+ @matcher = have_scope(:title)
25
+ @matcher.description.should == 'have to scope itself to {} when :title is called'
26
+
27
+ @matcher.with(1)
28
+ @matcher.description.should == 'have to scope itself to {} when :title is called with 1 as argument'
29
+ end
30
+
31
+ it 'should set is_scope? message' do
32
+ @matcher = have_scope(:null)
33
+ @matcher.matches?(@model)
34
+ @matcher.failure_message.should == 'Expected :null when called on Product return a ActiveRecord::NamedScope::Scope object'
35
+ end
36
+
37
+ it 'should set options_match? message' do
38
+ @matcher = have_scope(:recent, :conditions => {:special => true})
39
+ @matcher.matches?(@model)
40
+ @matcher.failure_message.should == 'Expected :recent when called on Product scope to {:conditions=>{:special=>true}}, got {:order=>"created_at DESC"}'
41
+ end
42
+
43
+ end
44
+
45
+ describe 'matchers' do
46
+ it { should have_scope(:recent) }
47
+ it { should have_scope(:recent, :order => 'created_at DESC') }
48
+
49
+ it { should have_scope(:latest, :with => 10, :limit => 10) }
50
+ it { should have_scope(:beginning, :with => 10, :offset => 10) }
51
+
52
+ it { should_not have_scope(:null) }
53
+ it { should_not have_scope(:latest, :with => 5, :limit => 10) }
54
+ it { should_not have_scope(:beginning, :with => 5, :offset => 10) }
55
+ end
56
+
57
+ describe 'macros' do
58
+ should_have_scope :recent
59
+ should_have_scope :recent, :order => 'created_at DESC'
60
+
61
+ should_have_scope :latest, :with => 10, :limit => 10
62
+ should_have_scope :beginning, :with => 10, :offset => 10
63
+
64
+ should_not_have_scope :null
65
+ should_not_have_scope :latest, :with => 5, :limit => 10
66
+ should_not_have_scope :beginning, :with => 5, :offset => 10
67
+ end
68
+ end
69
+
@@ -0,0 +1,101 @@
1
+ # This is based on Shoulda model builder for Test::Unit.
2
+ #
3
+ module ModelBuilder
4
+ def self.included(base)
5
+ return unless base.name =~ /^Spec/
6
+
7
+ base.class_eval do
8
+ after(:each) do
9
+ if @defined_constants
10
+ @defined_constants.each do |class_name|
11
+ Object.send(:remove_const, class_name)
12
+ end
13
+ end
14
+
15
+ if @created_tables
16
+ @created_tables.each do |table_name|
17
+ ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS #{table_name}")
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ base.extend ClassMethods
24
+ end
25
+
26
+ def create_table(table_name, &block)
27
+ connection = ActiveRecord::Base.connection
28
+
29
+ begin
30
+ connection.execute("DROP TABLE IF EXISTS #{table_name}")
31
+ connection.create_table(table_name, &block)
32
+ @created_tables ||= []
33
+ @created_tables << table_name
34
+ connection
35
+ rescue Exception => e
36
+ connection.execute("DROP TABLE IF EXISTS #{table_name}")
37
+ raise e
38
+ end
39
+ end
40
+
41
+ def define_constant(class_name, base, &block)
42
+ class_name = class_name.to_s.camelize
43
+
44
+ klass = Class.new(base)
45
+ Object.const_set(class_name, klass)
46
+
47
+ klass.class_eval(&block) if block_given?
48
+
49
+ @defined_constants ||= []
50
+ @defined_constants << class_name
51
+
52
+ klass
53
+ end
54
+
55
+ def define_model_class(class_name, &block)
56
+ define_constant(class_name, ActiveRecord::Base, &block)
57
+ end
58
+
59
+ def define_model(name, columns = {}, &block)
60
+ class_name = name.to_s.pluralize.classify
61
+ table_name = class_name.tableize
62
+
63
+ table = columns.delete(:table) || lambda {|table|
64
+ columns.each do |name, type|
65
+ table.column name, type
66
+ end
67
+ }
68
+
69
+ create_table(table_name, &table)
70
+
71
+ klass = define_model_class(class_name, &block)
72
+ instance = klass.new
73
+
74
+ self.class.subject { instance } if self.class.respond_to?(:subject)
75
+ instance
76
+ end
77
+
78
+ module ClassMethods
79
+ # This is a macro to run validations of boolean optionals such as :allow_nil
80
+ # and :allow_blank. This macro tests all scenarios. The specs must have a
81
+ # define_and_validate method defined.
82
+ #
83
+ def create_optional_boolean_specs(optional, base, options={})
84
+ base.describe "with #{optional} option" do
85
+ it { should define_and_validate(options.merge(optional => true)).send(optional) }
86
+ it { should define_and_validate(options.merge(optional => false)).send(optional, false) }
87
+ it { should_not define_and_validate(options.merge(optional => true)).send(optional, false) }
88
+ it { should_not define_and_validate(options.merge(optional => false)).send(optional) }
89
+ end
90
+ end
91
+
92
+ def create_message_specs(base)
93
+ base.describe "with message option" do
94
+ it { should define_and_validate(:message => 'valid_message').message('valid_message') }
95
+ it { should_not define_and_validate(:message => 'not_valid').message('valid_message') }
96
+ end
97
+ end
98
+ end
99
+
100
+ end
101
+
data/spec/rcov.opts ADDED
@@ -0,0 +1,2 @@
1
+ --exclude "spec/*,gems/*"
2
+ --rails
data/spec/spec.opts ADDED
@@ -0,0 +1,4 @@
1
+ --colour
2
+ --format progress
3
+ --loadby mtime
4
+ --reverse
@@ -0,0 +1,27 @@
1
+ require 'rubygems'
2
+ require 'ruby-debug'
3
+
4
+ RAILS_VERSION = ENV['RAILS_VERSION'] || '=2.2.2'
5
+
6
+ gem 'activesupport', RAILS_VERSION
7
+ require 'active_support'
8
+
9
+ gem 'activerecord', RAILS_VERSION
10
+ require 'active_record'
11
+
12
+ # Configure ActiveRecord connection
13
+ ActiveRecord::Base.establish_connection(
14
+ :adapter => 'sqlite3',
15
+ :dbfile => 'memory'
16
+ )
17
+
18
+ # Load Remarkable core on place to avoid gem to be loaded
19
+ dir = File.dirname(__FILE__)
20
+ require File.join(dir, '..', '..', 'remarkable', 'lib', 'remarkable')
21
+
22
+ # Load Remarkable ActiveRecord
23
+ require File.join(dir, 'model_builder')
24
+ require File.join(dir, '..', 'lib', 'remarkable_activerecord')
25
+
26
+ # Include matchers
27
+ Remarkable.include_matchers!(Remarkable::ActiveRecord, Spec::Example::ExampleGroup)
@@ -0,0 +1,68 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe 'validate_acceptance_of' do
4
+ include ModelBuilder
5
+
6
+ # Defines a model, create a validation and returns a raw matcher
7
+ def define_and_validate(options={})
8
+ @model = define_model :user, :eula => :string, :terms => :string, :name => :string do
9
+ validates_acceptance_of :eula, :terms, options
10
+ end
11
+
12
+ validate_acceptance_of(:eula, :terms)
13
+ end
14
+
15
+ describe 'messages' do
16
+ before(:each){ @matcher = define_and_validate }
17
+
18
+ it 'should contain a description' do
19
+ @matcher.description.should == 'require eula and terms to be accepted'
20
+
21
+ @matcher.accept('true')
22
+ @matcher.description.should == 'require eula and terms to be accepted with value "true"'
23
+
24
+ @matcher.allow_nil
25
+ @matcher.description.should == 'require eula and terms to be accepted with value "true" and allowing nil values'
26
+ end
27
+
28
+ it 'should set requires_acceptance? message' do
29
+ @matcher = validate_acceptance_of(:name)
30
+ @matcher.matches?(@model)
31
+ @matcher.failure_message.should == 'Expected User to be invalid if name is not accepted'
32
+ end
33
+
34
+ it 'should set accept_is_valid? message' do
35
+ @matcher.accept('accept_value').matches?(@model)
36
+ @matcher.failure_message.should == 'Expected User to be valid when eula is accepted with value "accept_value"'
37
+ end
38
+
39
+ end
40
+
41
+ describe 'matchers' do
42
+
43
+ describe 'without options' do
44
+ before(:each){ define_and_validate }
45
+
46
+ it { should validate_acceptance_of(:eula) }
47
+ it { should validate_acceptance_of(:eula, :terms) }
48
+ it { should_not validate_acceptance_of(:eula, :name) }
49
+ end
50
+
51
+ describe 'with accept as option' do
52
+ it { should define_and_validate(:accept => 'accept_value').accept('accept_value') }
53
+ it { should_not define_and_validate(:accept => 'another_value').accept('a_value') }
54
+ end
55
+
56
+ create_message_specs(self)
57
+ create_optional_boolean_specs(:allow_nil, self)
58
+ end
59
+
60
+ describe 'macros' do
61
+ before(:each){ define_and_validate }
62
+
63
+ should_validate_acceptance_of :eula
64
+ should_validate_acceptance_of :eula, :terms
65
+ should_not_validate_acceptance_of :eula, :name
66
+ end
67
+
68
+ end
@@ -0,0 +1,122 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe 'validate_associated' do
4
+ include ModelBuilder
5
+
6
+ # Defines a model, create a validation and returns a raw matcher
7
+ def define_and_validate(macro, association, options={})
8
+ define_model association, :name => :string do
9
+ if options[:with_builder]
10
+ validates_acceptance_of :name
11
+ else
12
+ validates_presence_of :name
13
+ end
14
+ end
15
+
16
+ options[:message] ||= :invalid
17
+
18
+ @model = define_model :project do
19
+ send(macro, association, :validate => false) unless options[:skip_association]
20
+ validates_associated association, options.slice(:message) unless options[:skip_validation]
21
+ end
22
+
23
+ validate_associated association
24
+ end
25
+
26
+ describe 'messages' do
27
+ it 'should contain a description' do
28
+ define_and_validate(:belongs_to, :company).description.should == 'require associated company to be valid'
29
+ end
30
+
31
+ it 'should set is_valid? message' do
32
+ matcher = define_and_validate(:belongs_to, :company, :skip_validation => true)
33
+ matcher.matches?(@model)
34
+ matcher.failure_message.should == 'Expected Project to be invalid when company is invalid'
35
+ end
36
+ end
37
+
38
+ describe 'error' do
39
+ it 'should raise an error if the association does not exist' do
40
+ lambda {
41
+ should define_and_validate(:belongs_to, :company, :skip_association => true)
42
+ }.should raise_error(ScriptError, 'Could not find association company on Project.')
43
+ end
44
+
45
+ it 'should raise an error if a singular association cannot be built' do
46
+ lambda {
47
+ matcher = define_and_validate(:belongs_to, :company)
48
+ @model.should_receive(:build_company).and_raise(NoMethodError)
49
+ should matcher
50
+ }.should raise_error(ScriptError, 'The association object company could not be built. ' <<
51
+ 'You can give me :builder as option or a block which ' <<
52
+ 'returns an association.')
53
+ end
54
+
55
+ it 'should raise an error if a plural association cannot be built' do
56
+ lambda {
57
+ matcher = define_and_validate(:has_many, :tasks)
58
+ @model.should_receive(:tasks).and_return(mock=mock('proxy'))
59
+ mock.should_receive(:build).and_raise(NoMethodError)
60
+ should matcher
61
+ }.should raise_error(ScriptError, 'The association object tasks could not be built. ' <<
62
+ 'You can give me :builder as option or a block which ' <<
63
+ 'returns an association.')
64
+ end
65
+
66
+ it 'should raise an error if the associated object cannot be saved as invalid' do
67
+ lambda {
68
+ should define_and_validate(:belongs_to, :company, :with_builder => true)
69
+ }.should raise_error(ScriptError, 'The associated object company is not invalid. ' <<
70
+ 'You can give me :builder as option or a block which ' <<
71
+ 'returns an invalid association.')
72
+ end
73
+
74
+ it 'should raise an error if the associated object cannot be saved even when a build is supplied' do
75
+ lambda {
76
+ should define_and_validate(:belongs_to, :company, :with_builder => true).builder(proc{ |p| p.build_company })
77
+ }.should raise_error(ScriptError, 'The associated object company is not invalid. ' <<
78
+ 'You can give me :builder as option or a block which ' <<
79
+ 'returns an invalid association.')
80
+ end
81
+ end
82
+
83
+ describe 'matchers' do
84
+ it { should define_and_validate(:belongs_to, :company) }
85
+ it { should define_and_validate(:has_one, :manager) }
86
+ it { should define_and_validate(:has_many, :tasks) }
87
+ it { should define_and_validate(:has_and_belongs_to_many, :tags) }
88
+
89
+ it { should define_and_validate(:belongs_to, :company, :with_builder => true).builder(proc{|p| p.build_company(:name => true)}) }
90
+ it { should define_and_validate(:has_one, :manager, :with_builder => true).builder(proc{|p| p.build_manager(:name => true)}) }
91
+ it { should define_and_validate(:has_many, :tasks, :with_builder => true).builder(proc{|p| p.tasks.build(:name => true)}) }
92
+ it { should define_and_validate(:has_and_belongs_to_many, :tags, :with_builder => true).builder(proc{|p| p.tags.build(:name => true)}) }
93
+
94
+ it { should_not define_and_validate(:belongs_to, :company, :skip_validation => true) }
95
+ it { should_not define_and_validate(:has_one, :manager, :skip_validation => true) }
96
+ it { should_not define_and_validate(:has_many, :tasks, :skip_validation => true) }
97
+ it { should_not define_and_validate(:has_and_belongs_to_many, :tags, :skip_validation => true) }
98
+
99
+ describe "with message option" do
100
+ it { should define_and_validate(:belongs_to, :company, :message => 'valid_message').message('valid_message') }
101
+ it { should_not define_and_validate(:belongs_to, :company, :message => 'not_valid').message('valid_message') }
102
+ end
103
+ end
104
+
105
+ describe 'macros' do
106
+ describe 'belongs to' do
107
+ before(:each){ define_and_validate(:belongs_to, :company) }
108
+ should_validate_associated(:company)
109
+ end
110
+
111
+ describe 'has_many with builder' do
112
+ before(:each){ define_and_validate(:has_many, :tasks, :with_builder => true) }
113
+ should_validate_associated(:tasks){ |p| p.tasks.build(:name => true) }
114
+ end
115
+
116
+ describe 'has_and_belongs_to_many with skip validation' do
117
+ before(:each){ define_and_validate(:has_and_belongs_to_many, :tags, :skip_validation => true) }
118
+ should_not_validate_associated(:tags)
119
+ end
120
+ end
121
+
122
+ end
@@ -0,0 +1,58 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe 'validate_confirmation_of' do
4
+ include ModelBuilder
5
+
6
+ # Defines a model, create a validation and returns a raw matcher
7
+ def define_and_validate(options={})
8
+ @model = define_model :user, :name => :string, :email => :string, :age => :string do
9
+ validates_confirmation_of :name, :email, options
10
+ end
11
+
12
+ validate_confirmation_of(:name, :email)
13
+ end
14
+
15
+ describe 'messages' do
16
+ before(:each){ @matcher = define_and_validate }
17
+
18
+ it 'should contain a description' do
19
+ @matcher.description.should == 'require name and email to be confirmed'
20
+ end
21
+
22
+ it 'should set responds_to_confirmation? message' do
23
+ @matcher = validate_confirmation_of(:age)
24
+ @matcher.matches?(@model)
25
+ @matcher.failure_message.should == 'Expected User instance responds to age_confirmation'
26
+ end
27
+
28
+ it 'should set confirms? message' do
29
+ @model.instance_eval{ def age_confirmation=(*args); end }
30
+ @matcher = validate_confirmation_of(:age)
31
+ @matcher.matches?(@model)
32
+ @matcher.failure_message.should == 'Expected User to be valid only when age is confirmed'
33
+ end
34
+
35
+ end
36
+
37
+ describe 'matchers' do
38
+
39
+ describe 'without options' do
40
+ before(:each){ define_and_validate }
41
+
42
+ it { should validate_confirmation_of(:name) }
43
+ it { should validate_confirmation_of(:name, :email) }
44
+ it { should_not validate_confirmation_of(:name, :age) }
45
+ end
46
+
47
+ create_message_specs(self)
48
+ end
49
+
50
+ describe 'macros' do
51
+ before(:each){ define_and_validate }
52
+
53
+ should_validate_confirmation_of :name
54
+ should_validate_confirmation_of :name, :email
55
+ should_not_validate_confirmation_of :name, :age
56
+ end
57
+
58
+ end
@@ -0,0 +1,88 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe 'validate_exclusion_of' do
4
+ include ModelBuilder
5
+
6
+ # Defines a model, create a validation and returns a raw matcher
7
+ def define_and_validate(options={})
8
+ @model = define_model :product, :title => :string, :size => :string do
9
+ validates_exclusion_of :title, :size, options
10
+ end
11
+
12
+ validate_exclusion_of(:title, :size)
13
+ end
14
+
15
+ describe 'messages' do
16
+
17
+ it 'should contain a description' do
18
+ @matcher = define_and_validate(:in => 2..10)
19
+ @matcher.in(2..10)
20
+ @matcher.description.should == 'ensure exclusion of title and size in 2..10'
21
+
22
+ @matcher = validate_exclusion_of(:title, :size).in('X', 'Y', 'Z')
23
+ @matcher.description.should == 'ensure exclusion of title and size in "X", "Y", and "Z"'
24
+ end
25
+
26
+ it 'should set is_invalid? message' do
27
+ @matcher = define_and_validate(:in => 2..10)
28
+ @matcher.in(1..10).matches?(@model)
29
+ @matcher.failure_message.should == 'Expected Product to be invalid when title is set to 1'
30
+ end
31
+
32
+ it 'should set is_valid? message' do
33
+ @matcher = define_and_validate(:in => 2..10)
34
+ @matcher.in(3..10).matches?(@model)
35
+ @matcher.failure_message.should == 'Expected Product to be valid when title is set to 2'
36
+ end
37
+
38
+ it 'should set allow_nil? message' do
39
+ @matcher = define_and_validate(:in => [nil])
40
+ @matcher.allow_nil.matches?(@model)
41
+ @matcher.failure_message.should == 'Expected Product to allow nil values for title'
42
+ end
43
+
44
+ it 'should set allow_blank? message' do
45
+ @matcher = define_and_validate(:in => [''])
46
+ @matcher.allow_blank.matches?(@model)
47
+ @matcher.failure_message.should == 'Expected Product to allow blank values for title'
48
+ end
49
+ end
50
+
51
+ describe 'matchers' do
52
+ it { should define_and_validate(:in => ['X', 'Y', 'Z']).in('X', 'Y', 'Z') }
53
+ it { should_not define_and_validate(:in => ['X', 'Y', 'Z']).in('A') }
54
+
55
+ it { should define_and_validate(:in => 2..3).in(2..3) }
56
+ it { should define_and_validate(:in => 2..20).in(2..20) }
57
+ it { should_not define_and_validate(:in => 2..20).in(1..20) }
58
+ it { should_not define_and_validate(:in => 2..20).in(3..20) }
59
+ it { should_not define_and_validate(:in => 2..20).in(2..19) }
60
+ it { should_not define_and_validate(:in => 2..20).in(2..21) }
61
+
62
+ it { should define_and_validate(:in => ['X', 'Y', 'Z'], :message => 'valid').in('X', 'Y', 'Z').message('valid') }
63
+
64
+ create_optional_boolean_specs(:allow_nil, self, :in => [nil])
65
+ create_optional_boolean_specs(:allow_blank, self, :in => [''])
66
+ end
67
+
68
+ describe 'macros' do
69
+ describe 'with array' do
70
+ before(:each){ define_and_validate(:in => ['X', 'Y', 'Z']) }
71
+
72
+ should_validate_exclusion_of :title, :in => ['X']
73
+ should_validate_exclusion_of :title, :size, :in => ['X']
74
+ should_not_validate_exclusion_of :title, :size, :in => ['A']
75
+ end
76
+
77
+ describe 'with range' do
78
+ before(:each){ define_and_validate(:in => 2..20) }
79
+
80
+ should_validate_exclusion_of :title, :in => 2..20
81
+ should_not_validate_exclusion_of :title, :in => 1..20
82
+ should_not_validate_exclusion_of :title, :in => 3..20
83
+ should_not_validate_exclusion_of :title, :in => 2..19
84
+ should_not_validate_exclusion_of :title, :in => 2..21
85
+ end
86
+ end
87
+ end
88
+