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.
- data/CHANGELOG +47 -0
- data/LICENSE +20 -0
- data/README +2 -0
- data/lib/remarkable_activerecord/base.rb +238 -0
- data/lib/remarkable_activerecord/human_names.rb +37 -0
- data/lib/remarkable_activerecord/matchers/allow_mass_assignment_of_matcher.rb +34 -0
- data/lib/remarkable_activerecord/matchers/allow_values_for_matcher.rb +94 -0
- data/lib/remarkable_activerecord/matchers/association_matcher.rb +235 -0
- data/lib/remarkable_activerecord/matchers/have_column_matcher.rb +68 -0
- data/lib/remarkable_activerecord/matchers/have_index_matcher.rb +57 -0
- data/lib/remarkable_activerecord/matchers/have_readonly_attributes_matcher.rb +30 -0
- data/lib/remarkable_activerecord/matchers/have_scope_matcher.rb +80 -0
- data/lib/remarkable_activerecord/matchers/validate_acceptance_of_matcher.rb +51 -0
- data/lib/remarkable_activerecord/matchers/validate_associated_matcher.rb +99 -0
- data/lib/remarkable_activerecord/matchers/validate_confirmation_of_matcher.rb +45 -0
- data/lib/remarkable_activerecord/matchers/validate_exclusion_of_matcher.rb +47 -0
- data/lib/remarkable_activerecord/matchers/validate_inclusion_of_matcher.rb +47 -0
- data/lib/remarkable_activerecord/matchers/validate_length_of_matcher.rb +123 -0
- data/lib/remarkable_activerecord/matchers/validate_numericality_of_matcher.rb +184 -0
- data/lib/remarkable_activerecord/matchers/validate_presence_of_matcher.rb +29 -0
- data/lib/remarkable_activerecord/matchers/validate_uniqueness_of_matcher.rb +151 -0
- data/lib/remarkable_activerecord.rb +29 -0
- data/locale/en.yml +253 -0
- data/spec/allow_mass_assignment_of_matcher_spec.rb +57 -0
- data/spec/allow_values_for_matcher_spec.rb +56 -0
- data/spec/association_matcher_spec.rb +616 -0
- data/spec/have_column_matcher_spec.rb +73 -0
- data/spec/have_index_matcher_spec.rb +68 -0
- data/spec/have_readonly_attributes_matcher_spec.rb +47 -0
- data/spec/have_scope_matcher_spec.rb +69 -0
- data/spec/model_builder.rb +101 -0
- data/spec/rcov.opts +2 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/validate_acceptance_of_matcher_spec.rb +68 -0
- data/spec/validate_associated_matcher_spec.rb +122 -0
- data/spec/validate_confirmation_of_matcher_spec.rb +58 -0
- data/spec/validate_exclusion_of_matcher_spec.rb +88 -0
- data/spec/validate_inclusion_of_matcher_spec.rb +84 -0
- data/spec/validate_length_of_matcher_spec.rb +165 -0
- data/spec/validate_numericality_of_matcher_spec.rb +180 -0
- data/spec/validate_presence_of_matcher_spec.rb +52 -0
- data/spec/validate_uniqueness_of_matcher_spec.rb +150 -0
- 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
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -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
|
+
|