remarkable_activerecord 3.0.1 → 3.0.2
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 +46 -46
- data/LICENSE +1 -1
- data/README +64 -64
- data/lib/remarkable_activerecord.rb +1 -1
- data/lib/remarkable_activerecord/base.rb +40 -40
- data/lib/remarkable_activerecord/human_names.rb +24 -24
- data/lib/remarkable_activerecord/matchers/allow_mass_assignment_of_matcher.rb +14 -14
- data/lib/remarkable_activerecord/matchers/allow_values_for_matcher.rb +70 -70
- data/lib/remarkable_activerecord/matchers/association_matcher.rb +197 -197
- data/lib/remarkable_activerecord/matchers/have_column_matcher.rb +29 -29
- data/lib/remarkable_activerecord/matchers/have_index_matcher.rb +20 -20
- data/lib/remarkable_activerecord/matchers/have_readonly_attributes_matcher.rb +7 -7
- data/lib/remarkable_activerecord/matchers/have_scope_matcher.rb +34 -34
- data/lib/remarkable_activerecord/matchers/validate_acceptance_of_matcher.rb +37 -37
- data/lib/remarkable_activerecord/matchers/validate_associated_matcher.rb +75 -75
- data/lib/remarkable_activerecord/matchers/validate_confirmation_of_matcher.rb +44 -44
- data/lib/remarkable_activerecord/matchers/validate_exclusion_of_matcher.rb +17 -17
- data/lib/remarkable_activerecord/matchers/validate_inclusion_of_matcher.rb +20 -20
- data/lib/remarkable_activerecord/matchers/validate_numericality_of_matcher.rb +14 -14
- data/lib/remarkable_activerecord/matchers/validate_uniqueness_of_matcher.rb +80 -61
- data/locale/en.yml +253 -253
- data/spec/allow_mass_assignment_of_matcher_spec.rb +50 -50
- data/spec/allow_values_for_matcher_spec.rb +45 -45
- data/spec/association_matcher_spec.rb +612 -612
- data/spec/have_column_matcher_spec.rb +67 -67
- data/spec/have_index_matcher_spec.rb +61 -61
- data/spec/have_readonly_attributes_matcher_spec.rb +40 -40
- data/spec/have_scope_matcher_spec.rb +60 -60
- data/spec/model_builder.rb +101 -101
- data/spec/spec_helper.rb +25 -25
- data/spec/validate_acceptance_of_matcher_spec.rb +64 -64
- data/spec/validate_associated_matcher_spec.rb +118 -118
- data/spec/validate_confirmation_of_matcher_spec.rb +54 -54
- data/spec/validate_exclusion_of_matcher_spec.rb +76 -76
- data/spec/validate_inclusion_of_matcher_spec.rb +72 -72
- data/spec/validate_numericality_of_matcher_spec.rb +100 -100
- data/spec/validate_presence_of_matcher_spec.rb +40 -40
- data/spec/validate_uniqueness_of_matcher_spec.rb +158 -139
- metadata +3 -3
data/spec/model_builder.rb
CHANGED
@@ -1,101 +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
|
-
|
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/spec_helper.rb
CHANGED
@@ -1,27 +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
|
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
13
|
ActiveRecord::Base.establish_connection(
|
14
|
-
:adapter => 'sqlite3',
|
15
|
-
:dbfile => 'memory'
|
16
|
-
)
|
17
|
-
|
14
|
+
:adapter => 'sqlite3',
|
15
|
+
:dbfile => 'memory'
|
16
|
+
)
|
17
|
+
|
18
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)
|
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)
|
@@ -1,68 +1,68 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
|
3
|
-
describe 'validate_acceptance_of' do
|
3
|
+
describe 'validate_acceptance_of' do
|
4
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
|
-
|
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
68
|
end
|
@@ -1,122 +1,122 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
|
3
|
-
describe 'validate_associated' do
|
3
|
+
describe 'validate_associated' do
|
4
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
|
-
|
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
122
|
end
|