remarkable_activerecord 3.0.7 → 3.0.8
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +9 -3
- data/lib/remarkable_activerecord/matchers/accept_nested_attributes_for_matcher.rb +121 -0
- data/lib/remarkable_activerecord/matchers/have_default_scope_matcher.rb +67 -0
- data/lib/remarkable_activerecord/matchers/have_scope_matcher.rb +7 -2
- data/lib/remarkable_activerecord/matchers/validate_presence_of_matcher.rb +36 -1
- data/lib/remarkable_activerecord/matchers/validate_uniqueness_of_matcher.rb +10 -1
- data/locale/en.yml +20 -1
- data/spec/accept_nested_attributes_for_matcher_spec.rb +119 -0
- data/spec/have_default_scope_matcher_spec.rb +52 -0
- data/spec/have_scope_matcher_spec.rb +22 -17
- metadata +11 -7
data/CHANGELOG
CHANGED
@@ -1,9 +1,15 @@
|
|
1
|
-
*
|
1
|
+
* Added accept_nested_attributes_for matcher [#39]
|
2
|
+
|
3
|
+
* Added have_default_scope matcher [#38]
|
4
|
+
|
5
|
+
* Allow :include, :join, :group and :having as quick accessors to have_scope matcher
|
6
|
+
|
7
|
+
* Allow all objects to be sent to have_scope (thanks to Szymon Nowak and Nolan Eakins) [#53]
|
2
8
|
|
3
9
|
* Added support to sql options in association_matcher: select, conditions, include,
|
4
|
-
group, having, order, limit and offset. [#48]
|
10
|
+
group, having, order, limit and offset, plus finder_sql and counter_sql. [#48]
|
5
11
|
|
6
|
-
* :source and :source_type are
|
12
|
+
* :source and :source_type are now supported by association matcher [#47]
|
7
13
|
|
8
14
|
* validate_inclusion_of became smarter since it now tests invalid values too [#36]
|
9
15
|
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module Remarkable
|
2
|
+
module ActiveRecord
|
3
|
+
module Matchers
|
4
|
+
class AcceptNestedAttributesForMatcher < Remarkable::ActiveRecord::Base #:nodoc:
|
5
|
+
arguments :collection => :associations, :as => :association
|
6
|
+
|
7
|
+
collection_assertions :association_exists?, :is_autosave?, :responds_to_attributes?,
|
8
|
+
:allows_destroy?, :accepts?, :rejects?
|
9
|
+
|
10
|
+
optionals :allow_destroy, :default => true
|
11
|
+
optionals :accept, :reject, :splat => true
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def association_exists?
|
16
|
+
reflection
|
17
|
+
end
|
18
|
+
|
19
|
+
def is_autosave?
|
20
|
+
reflection.options[:autosave] == true
|
21
|
+
end
|
22
|
+
|
23
|
+
def responds_to_attributes?
|
24
|
+
@subject.respond_to?(:"#{@association}_attributes=", true)
|
25
|
+
end
|
26
|
+
|
27
|
+
def allows_destroy?
|
28
|
+
return true unless @options.key?(:allow_destroy)
|
29
|
+
|
30
|
+
@subject.instance_eval <<-ALLOW_DESTROY
|
31
|
+
def assign_nested_attributes_for_#{reflection_type}_association(association, attrs, allow)
|
32
|
+
return allow
|
33
|
+
end
|
34
|
+
ALLOW_DESTROY
|
35
|
+
|
36
|
+
actual = @subject.send(:"#{@association}_attributes=", {})
|
37
|
+
return actual == @options[:allow_destroy], :actual => actual
|
38
|
+
end
|
39
|
+
|
40
|
+
def accepts?
|
41
|
+
return true unless @options.key?(:accept)
|
42
|
+
|
43
|
+
[@options[:accept]].flatten.each do |attributes|
|
44
|
+
return false, :attributes => attributes.inspect if reject_if_proc.call(attributes)
|
45
|
+
end
|
46
|
+
|
47
|
+
true
|
48
|
+
end
|
49
|
+
|
50
|
+
def rejects?
|
51
|
+
return true unless @options.key?(:reject)
|
52
|
+
|
53
|
+
[@options[:reject]].flatten.each do |attributes|
|
54
|
+
return false, :attributes => attributes.inspect unless reject_if_proc.call(attributes)
|
55
|
+
end
|
56
|
+
|
57
|
+
true
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def reflection
|
63
|
+
@reflection ||= subject_class.reflect_on_association(@association.to_sym)
|
64
|
+
end
|
65
|
+
|
66
|
+
def reflection_type
|
67
|
+
case reflection.macro
|
68
|
+
when :has_one, :belongs_to
|
69
|
+
:one_to_one
|
70
|
+
when :has_many, :has_and_belongs_to_many
|
71
|
+
:collection
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def reject_if_proc
|
76
|
+
subject_class.reject_new_nested_attributes_procs[@association.to_sym]
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
# Ensures that the model accepts nested attributes for the given associations.
|
82
|
+
#
|
83
|
+
# == Options
|
84
|
+
#
|
85
|
+
# * <tt>allow_destroy</tt> - When true allows the association to be destroyed
|
86
|
+
# * <tt>accept</tt> - attributes that should be accepted by the :reject_if proc
|
87
|
+
# * <tt>reject</tt> - attributes that should be rejected by the :reject_if proc
|
88
|
+
#
|
89
|
+
# == Examples
|
90
|
+
#
|
91
|
+
# should_accept_nested_attributes_for :tasks
|
92
|
+
# should_accept_nested_attributes_for :tasks, :allow_destroy => true
|
93
|
+
#
|
94
|
+
# :accept and :reject takes objects that are verified against the proc. So
|
95
|
+
# having a model:
|
96
|
+
#
|
97
|
+
# class Projects < ActiveRecord::Base
|
98
|
+
# has_many :tasks
|
99
|
+
# accepts_nested_attributes_for :tasks, :reject_if => proc { |a| a[:name].blank? }
|
100
|
+
# end
|
101
|
+
#
|
102
|
+
# You can have the following specs:
|
103
|
+
#
|
104
|
+
# should_accept_nested_attributes_for :tasks, :reject => { :name => '' } # Passes
|
105
|
+
# should_accept_nested_attributes_for :tasks, :accept => { :name => 'My task' } # Passes
|
106
|
+
#
|
107
|
+
# should_accept_nested_attributes_for :tasks, :accept => { :name => 'My task' },
|
108
|
+
# :reject => { :name => '' } # Passes
|
109
|
+
#
|
110
|
+
# should_accept_nested_attributes_for :tasks, :accept => { :name => '' } # Fail
|
111
|
+
# should_accept_nested_attributes_for :tasks, :reject => { :name => 'My task' } # Fail
|
112
|
+
#
|
113
|
+
# You can also give arrays to :accept and :reject to verify multiple attributes.
|
114
|
+
#
|
115
|
+
def accept_nested_attributes_for(*args)
|
116
|
+
AcceptNestedAttributesForMatcher.new(*args).spec(self)
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Remarkable
|
2
|
+
module ActiveRecord
|
3
|
+
module Matchers
|
4
|
+
class HaveDefaultScopeMatcher < Remarkable::ActiveRecord::Base #:nodoc:
|
5
|
+
arguments
|
6
|
+
assertions :options_match?
|
7
|
+
|
8
|
+
optionals :select, :conditions, :join, :include, :group, :having, :order, :limit, :offset
|
9
|
+
|
10
|
+
protected
|
11
|
+
|
12
|
+
def options_match?
|
13
|
+
default_scope.include?(@options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def default_scope
|
17
|
+
@default_scope ||= if @subject
|
18
|
+
scopes = subject_class.default_scoping || []
|
19
|
+
scopes.map!{ |s| s[:find] }
|
20
|
+
else
|
21
|
+
[]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def interpolation_options
|
26
|
+
{ :options => @options.inspect, :actual => default_scope.inspect }
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
# Ensures that the model has a default scope with the given options.
|
32
|
+
#
|
33
|
+
# == Options
|
34
|
+
#
|
35
|
+
# All options that the default scope would pass on to find: select, conditions,
|
36
|
+
# join, include, group, having, order, limit e offset.
|
37
|
+
#
|
38
|
+
# == Examples
|
39
|
+
#
|
40
|
+
# it { should have_default_scope(:conditions => {:visible => true}) }
|
41
|
+
# it { should have_default_scope.conditions(:visible => true) }
|
42
|
+
#
|
43
|
+
# Passes for:
|
44
|
+
#
|
45
|
+
# default_scope :conditions => { :visible => true }
|
46
|
+
#
|
47
|
+
# If you set two different default scopes, you have to spec them
|
48
|
+
# separatedly. Given the scopes:
|
49
|
+
#
|
50
|
+
# default_scope :conditions => { :visible => true }
|
51
|
+
# default_scope :conditions => { :published => true }
|
52
|
+
#
|
53
|
+
# Then we have the matchers:
|
54
|
+
#
|
55
|
+
# should_have_default_scope :conditions => { :visible => true } # Passes
|
56
|
+
# should_have_default_scope :conditions => { :published => true } # Passes
|
57
|
+
#
|
58
|
+
# should_have_default_scope :conditions => { :published => true,
|
59
|
+
# :visible => true } # Fails
|
60
|
+
#
|
61
|
+
def have_default_scope(*args)
|
62
|
+
HaveDefaultScopeMatcher.new(*args).spec(self)
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -5,13 +5,18 @@ module Remarkable
|
|
5
5
|
arguments :scope_name
|
6
6
|
assertions :is_scope?, :options_match?
|
7
7
|
|
8
|
-
optionals :with, :
|
8
|
+
optionals :with, :splat => true
|
9
|
+
optionals :select, :conditions, :join, :include, :group, :having, :order, :limit, :offset
|
9
10
|
|
10
11
|
protected
|
11
12
|
|
12
13
|
def is_scope?
|
13
14
|
@scope_object = if @options[:with]
|
14
|
-
|
15
|
+
if @options[:with].is_a?(Array)
|
16
|
+
subject_class.send(@scope_name, *@options[:with])
|
17
|
+
else
|
18
|
+
subject_class.send(@scope_name, @options[:with])
|
19
|
+
end
|
15
20
|
else
|
16
21
|
subject_class.send(@scope_name)
|
17
22
|
end
|
@@ -1,5 +1,40 @@
|
|
1
1
|
module Remarkable
|
2
|
-
module ActiveRecord
|
2
|
+
module ActiveRecord
|
3
|
+
# Holds ActiveRecord matchers.
|
4
|
+
#
|
5
|
+
# == Validations matchers
|
6
|
+
#
|
7
|
+
# Remarkable supports all ActiveRecord validations, and the only options
|
8
|
+
# not supported in those matchers is the :on options. So whenever you have
|
9
|
+
# to test that a validation runs on update, you have to do reproduce the
|
10
|
+
# state in your tests:
|
11
|
+
#
|
12
|
+
# describe Project do
|
13
|
+
# describe 'validations on create' do
|
14
|
+
# should_validate_presence_of :title
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# describe 'validations on update' do
|
18
|
+
# subject { Post.create!(@valid_attributes) }
|
19
|
+
# should_validate_presence_of :updated_at
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# Another behavior in validations is the :message option. Whenever you change
|
24
|
+
# the message in your model, it must be given in your tests too:
|
25
|
+
#
|
26
|
+
# class Post < ActiveRecord::Base
|
27
|
+
# validates_presence_of :title, :message => 'must be filled'
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# describe Post do
|
31
|
+
# should_validate_presence_of :title #=> fails
|
32
|
+
# should_validate_presence_of :title, :message => 'must be filled'
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# However, if you change the title using the I18n API, you don't need to
|
36
|
+
# specify the message in your tests, because it's retrieved properly.
|
37
|
+
#
|
3
38
|
module Matchers
|
4
39
|
class ValidatePresenceOfMatcher < Remarkable::ActiveRecord::Base #:nodoc:
|
5
40
|
arguments :collection => :attributes, :as => :attribute
|
@@ -165,7 +165,16 @@ module Remarkable
|
|
165
165
|
#
|
166
166
|
# Requires an existing record in the database. If you supply :allow_nil as
|
167
167
|
# option, you need to have in the database a record which is not nil in the
|
168
|
-
# given attributes. The same is required for allow_blank option.
|
168
|
+
# given attributes. The same is required for allow_blank option.
|
169
|
+
#
|
170
|
+
# Notice that the record being validate should not be the same as in the
|
171
|
+
# database. In other words, you can't do this:
|
172
|
+
#
|
173
|
+
# subject { Post.create!(@valid_attributes) }
|
174
|
+
# should_validate_uniquness_of :title
|
175
|
+
#
|
176
|
+
# But don't worry, if you eventually do that, a helpful error message
|
177
|
+
# will be raised.
|
169
178
|
#
|
170
179
|
# == Options
|
171
180
|
#
|
data/locale/en.yml
CHANGED
@@ -11,6 +11,20 @@ en:
|
|
11
11
|
allow_blank:
|
12
12
|
positive: "allowing blank values"
|
13
13
|
negative: "not allowing blank values"
|
14
|
+
|
15
|
+
accept_nested_attributes_for:
|
16
|
+
description: "accept nested attributes for {{associations}}"
|
17
|
+
expectations:
|
18
|
+
association_exists: "{{subject_name}} to have association {{association}}, but does not"
|
19
|
+
is_autosave: "{{subject_name}} to have association {{association}} with autosave true, got false"
|
20
|
+
responds_to_attributes: "{{subject_name}} to respond to :{{association}}_attributes=, but does not"
|
21
|
+
allows_destroy: "{{subject_name}} with allow destroy equals to {{allow_destroy}}, got {{actual}}"
|
22
|
+
accepts: "{{subject_name}} to accept attributes {{attributes}} for {{association}}, but does not"
|
23
|
+
rejects: "{{subject_name}} to reject attributes {{attributes}} for {{association}}, but does not"
|
24
|
+
optionals:
|
25
|
+
allow_destroy:
|
26
|
+
positive: "allowing destroy"
|
27
|
+
negative: "not allowing destroy"
|
14
28
|
|
15
29
|
allow_values_for:
|
16
30
|
description: "allow {{in}} as values for {{attributes}}"
|
@@ -103,7 +117,12 @@ en:
|
|
103
117
|
positive: "with default value {{inspect}}"
|
104
118
|
negative: "with default value {{inspect}}"
|
105
119
|
limit:
|
106
|
-
positive: "with limit {{inspect}}"
|
120
|
+
positive: "with limit {{inspect}}"
|
121
|
+
|
122
|
+
have_default_scope:
|
123
|
+
description: "have a default scope with {{options}}"
|
124
|
+
expectations:
|
125
|
+
options_match: "default scope with {{options}}, got {{actual}}"
|
107
126
|
|
108
127
|
have_index:
|
109
128
|
description: "have index for column(s) {{columns}}"
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
if RAILS_VERSION == '2.3.2'
|
4
|
+
describe 'accept_nested_attributes_for' do
|
5
|
+
include ModelBuilder
|
6
|
+
|
7
|
+
def define_and_validate(default=:category, options={})
|
8
|
+
@model = define_model :product do
|
9
|
+
has_one :category
|
10
|
+
has_many :orders
|
11
|
+
has_many :labels
|
12
|
+
has_many :tags, :autosave => true
|
13
|
+
|
14
|
+
accepts_nested_attributes_for :category, options
|
15
|
+
accepts_nested_attributes_for :orders, options
|
16
|
+
end
|
17
|
+
|
18
|
+
accept_nested_attributes_for *default
|
19
|
+
end
|
20
|
+
|
21
|
+
describe 'messages' do
|
22
|
+
|
23
|
+
it 'should contain a description' do
|
24
|
+
matcher = define_and_validate
|
25
|
+
matcher.description.should == 'accept nested attributes for category'
|
26
|
+
|
27
|
+
matcher.allow_destroy
|
28
|
+
matcher.description.should == 'accept nested attributes for category allowing destroy'
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should set association_match? message' do
|
32
|
+
matcher = define_and_validate(:nothing)
|
33
|
+
matcher.matches?(@model)
|
34
|
+
matcher.failure_message.should == 'Expected Product to have association nothing, but does not'
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should set is_autosave? message' do
|
38
|
+
matcher = define_and_validate(:labels)
|
39
|
+
matcher.matches?(@model)
|
40
|
+
matcher.failure_message.should == 'Expected Product to have association labels with autosave true, got false'
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should set responds_to_attributes? message' do
|
44
|
+
matcher = define_and_validate(:tags)
|
45
|
+
matcher.matches?(@model)
|
46
|
+
matcher.failure_message.should == 'Expected Product to respond to :tags_attributes=, but does not'
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should set allows_destroy? message' do
|
50
|
+
matcher = define_and_validate(:category, :allow_destroy => false)
|
51
|
+
matcher.allow_destroy.matches?(@model)
|
52
|
+
matcher.failure_message.should == 'Expected Product with allow destroy equals to true, got false'
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should set accepts? message' do
|
56
|
+
matcher = define_and_validate(:category, :reject_if => proc{|a| true })
|
57
|
+
matcher.accept({}).matches?(@model)
|
58
|
+
matcher.failure_message.should == 'Expected Product to accept attributes {} for category, but does not'
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should set rejects? message' do
|
62
|
+
matcher = define_and_validate(:category, :reject_if => proc{|a| false })
|
63
|
+
matcher.reject({}).matches?(@model)
|
64
|
+
matcher.failure_message.should == 'Expected Product to reject attributes {} for category, but does not'
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
describe 'matchers' do
|
70
|
+
it { should define_and_validate(:category) }
|
71
|
+
it { should define_and_validate(:orders) }
|
72
|
+
|
73
|
+
it { should_not define_and_validate(:nothing) }
|
74
|
+
it { should_not define_and_validate(:labels) }
|
75
|
+
it { should_not define_and_validate(:tags) }
|
76
|
+
|
77
|
+
describe 'with allow destroy as option' do
|
78
|
+
it { should define_and_validate(:category, :allow_destroy => true).allow_destroy }
|
79
|
+
it { should define_and_validate(:category, :allow_destroy => false).allow_destroy(false) }
|
80
|
+
it { should_not define_and_validate(:category, :allow_destroy => false).allow_destroy }
|
81
|
+
it { should_not define_and_validate(:category, :allow_destroy => true).allow_destroy(false) }
|
82
|
+
|
83
|
+
it { should define_and_validate(:orders, :allow_destroy => true).allow_destroy }
|
84
|
+
it { should define_and_validate(:orders, :allow_destroy => false).allow_destroy(false) }
|
85
|
+
it { should_not define_and_validate(:orders, :allow_destroy => false).allow_destroy }
|
86
|
+
it { should_not define_and_validate(:orders, :allow_destroy => true).allow_destroy(false) }
|
87
|
+
end
|
88
|
+
|
89
|
+
describe 'with accept as option' do
|
90
|
+
it { should define_and_validate(:category, :reject_if => proc{ |a| a[:name].blank? }).accept({ :name => 'Jose' }) }
|
91
|
+
it { should define_and_validate(:category, :reject_if => proc{ |a| a[:name].blank? }).accept({ :name => 'Jose' }, { :name => 'Maria' }) }
|
92
|
+
it { should_not define_and_validate(:category, :reject_if => proc{ |a| a[:name].blank? }).accept({ :name => '' }) }
|
93
|
+
end
|
94
|
+
|
95
|
+
describe 'with reject as option' do
|
96
|
+
it { should define_and_validate(:category, :reject_if => proc{ |a| !a[:name].blank? }).reject({ :name => 'Jose' }) }
|
97
|
+
it { should define_and_validate(:category, :reject_if => proc{ |a| !a[:name].blank? }).reject({ :name => 'Jose' }, { :name => 'Maria' }) }
|
98
|
+
it { should_not define_and_validate(:category, :reject_if => proc{ |a| !a[:name].blank? }).reject({ :name => '' }) }
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe 'macros' do
|
103
|
+
before(:each){ define_and_validate(:category, :allow_destroy => true, :reject_if => proc{ |a| a[:name].blank? }) }
|
104
|
+
|
105
|
+
should_accept_nested_attributes_for :category
|
106
|
+
should_accept_nested_attributes_for :category, :allow_destroy => true
|
107
|
+
should_accept_nested_attributes_for :category, :accept => { :name => 'Jose' }
|
108
|
+
should_accept_nested_attributes_for :category, :accept => [ { :name => 'Jose' }, { :name => 'Maria' } ]
|
109
|
+
should_accept_nested_attributes_for :category, :reject => [ { :name => '' } ]
|
110
|
+
|
111
|
+
should_not_accept_nested_attributes_for :nothing
|
112
|
+
should_not_accept_nested_attributes_for :labels
|
113
|
+
should_not_accept_nested_attributes_for :tags
|
114
|
+
should_not_accept_nested_attributes_for :category, :allow_destroy => false
|
115
|
+
should_not_accept_nested_attributes_for :category, :accept => [ { :name => '' } ]
|
116
|
+
should_not_accept_nested_attributes_for :category, :reject => [ { :name => 'Jose' }, { :name => 'Maria' } ]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
if RAILS_VERSION == '2.3.2'
|
4
|
+
describe 'have_default_scope' do
|
5
|
+
include ModelBuilder
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@model = define_model :product, :published => :boolean do
|
9
|
+
default_scope :order => 'created_at DESC'
|
10
|
+
default_scope :conditions => { :published => true }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe 'messages' do
|
15
|
+
|
16
|
+
it 'should contain a description' do
|
17
|
+
matcher = have_default_scope(:conditions => {:special => true})
|
18
|
+
matcher.description.should == 'have a default scope with {:conditions=>{:special=>true}}'
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should set options_match? message' do
|
22
|
+
matcher = have_default_scope(:conditions => {:special => true})
|
23
|
+
matcher.matches?(@model)
|
24
|
+
matcher.failure_message.should == 'Expected default scope with {:conditions=>{:special=>true}}, got [{:order=>"created_at DESC"}, {:conditions=>{:published=>true}}]'
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
describe 'matchers' do
|
30
|
+
it { should have_default_scope(:order => 'created_at DESC') }
|
31
|
+
it { should have_default_scope(:conditions => { :published => true }) }
|
32
|
+
|
33
|
+
it { should_not have_default_scope(:order => 'created_at ASC') }
|
34
|
+
it { should_not have_default_scope(:conditions => { :published => false }) }
|
35
|
+
it { should_not have_default_scope(:conditions => { :published => true }, :order => 'created_at DESC') }
|
36
|
+
|
37
|
+
describe 'when the model has no default scope' do
|
38
|
+
before(:each){ @model = define_model(:task) }
|
39
|
+
it { @model.should_not have_default_scope }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe 'macros' do
|
44
|
+
should_have_default_scope :order => 'created_at DESC'
|
45
|
+
should_have_default_scope :conditions => { :published => true }
|
46
|
+
|
47
|
+
should_not_have_default_scope :order => 'created_at ASC'
|
48
|
+
should_not_have_default_scope :conditions => { :published => false }
|
49
|
+
should_not_have_default_scope :conditions => { :published => true }, :order => 'created_at DESC'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -8,6 +8,7 @@ describe 'have_scope' do
|
|
8
8
|
named_scope :recent, :order => 'created_at DESC'
|
9
9
|
named_scope :latest, lambda {|c| {:limit => c}}
|
10
10
|
named_scope :since, lambda {|t| {:conditions => ['created_at > ?', t]}}
|
11
|
+
named_scope :between, lambda { |a, b| { :conditions => [ 'created_at > ? and created_at < ?', a, b ] } }
|
11
12
|
|
12
13
|
def self.beginning(c)
|
13
14
|
scoped(:offset => c)
|
@@ -22,23 +23,23 @@ describe 'have_scope' do
|
|
22
23
|
describe 'messages' do
|
23
24
|
|
24
25
|
it 'should contain a description' do
|
25
|
-
|
26
|
-
|
26
|
+
matcher = have_scope(:title)
|
27
|
+
matcher.description.should == 'have to scope itself to {} when :title is called'
|
27
28
|
|
28
|
-
|
29
|
-
|
29
|
+
matcher.with(1)
|
30
|
+
matcher.description.should == 'have to scope itself to {} when :title is called with [1] as argument'
|
30
31
|
end
|
31
32
|
|
32
33
|
it 'should set is_scope? message' do
|
33
|
-
|
34
|
-
|
35
|
-
|
34
|
+
matcher = have_scope(:null)
|
35
|
+
matcher.matches?(@model)
|
36
|
+
matcher.failure_message.should == 'Expected :null when called on Product return an instance of ActiveRecord::NamedScope::Scope'
|
36
37
|
end
|
37
38
|
|
38
39
|
it 'should set options_match? message' do
|
39
|
-
|
40
|
-
|
41
|
-
|
40
|
+
matcher = have_scope(:recent, :conditions => {:special => true})
|
41
|
+
matcher.matches?(@model)
|
42
|
+
matcher.failure_message.should == 'Expected :recent when called on Product scope to {:conditions=>{:special=>true}}, got {:order=>"created_at DESC"}'
|
42
43
|
end
|
43
44
|
|
44
45
|
end
|
@@ -47,14 +48,16 @@ describe 'have_scope' do
|
|
47
48
|
it { should have_scope(:recent) }
|
48
49
|
it { should have_scope(:recent, :order => 'created_at DESC') }
|
49
50
|
|
50
|
-
it { should have_scope(:latest
|
51
|
-
it { should have_scope(:beginning
|
52
|
-
it { should have_scope(:since
|
51
|
+
it { should have_scope(:latest).with(10).limit(10) }
|
52
|
+
it { should have_scope(:beginning).with(10).offset(10) }
|
53
|
+
it { should have_scope(:since).with(Time.at(0)).conditions(["created_at > ?", Time.at(0)]) }
|
54
|
+
it { should have_scope(:between).with(2, 10).conditions(["created_at > ? and created_at < ?", 2, 10]) }
|
53
55
|
|
54
|
-
it { should_not have_scope(:null) }
|
55
|
-
it { should_not have_scope(:latest
|
56
|
-
it { should_not have_scope(:beginning
|
57
|
-
it { should_not have_scope(:since
|
56
|
+
it { should_not have_scope(:null) }
|
57
|
+
it { should_not have_scope(:latest).with(5).limit(10) }
|
58
|
+
it { should_not have_scope(:beginning).with(5).offset(10) }
|
59
|
+
it { should_not have_scope(:since).with(Time.at(0)).conditions(["created_at > ?", Time.at(1)]) }
|
60
|
+
it { should_not have_scope(:between).with(2, 10).conditions(["updated_at > ? and updated_at < ?", 2, 10]) }
|
58
61
|
end
|
59
62
|
|
60
63
|
describe 'macros' do
|
@@ -64,10 +67,12 @@ describe 'have_scope' do
|
|
64
67
|
should_have_scope :latest, :with => 10, :limit => 10
|
65
68
|
should_have_scope :beginning, :with => 10, :offset => 10
|
66
69
|
should_have_scope :since, :with => Time.at(0), :conditions => ["created_at > ?", Time.at(0)]
|
70
|
+
should_have_scope :between, :with => [ 2, 10 ], :conditions => [ "created_at > ? and created_at < ?", 2, 10 ]
|
67
71
|
|
68
72
|
should_not_have_scope :null
|
69
73
|
should_not_have_scope :latest, :with => 5, :limit => 10
|
70
74
|
should_not_have_scope :beginning, :with => 5, :offset => 10
|
71
75
|
should_not_have_scope :since, :with => Time.at(0), :conditions => ["created_at > ?", Time.at(1)]
|
76
|
+
should_not_have_scope :between, :with => [ 2, 10 ], :conditions => [ "updated_at > ? and updated_at < ?", 2, 10 ]
|
72
77
|
end
|
73
78
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: remarkable_activerecord
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.
|
4
|
+
version: 3.0.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Carlos Brando
|
@@ -11,7 +11,7 @@ autorequire:
|
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
13
|
|
14
|
-
date: 2009-04-
|
14
|
+
date: 2009-04-24 00:00:00 +02:00
|
15
15
|
default_executable:
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
@@ -22,7 +22,7 @@ dependencies:
|
|
22
22
|
requirements:
|
23
23
|
- - ">="
|
24
24
|
- !ruby/object:Gem::Version
|
25
|
-
version: 3.0.
|
25
|
+
version: 3.0.8
|
26
26
|
version:
|
27
27
|
description: "Remarkable ActiveRecord: collection of matchers and macros with I18n for ActiveRecord"
|
28
28
|
email:
|
@@ -41,13 +41,13 @@ files:
|
|
41
41
|
- README
|
42
42
|
- LICENSE
|
43
43
|
- CHANGELOG
|
44
|
-
- lib/remarkable_activerecord
|
45
44
|
- lib/remarkable_activerecord/base.rb
|
46
45
|
- lib/remarkable_activerecord/human_names.rb
|
47
|
-
- lib/remarkable_activerecord/matchers
|
48
46
|
- lib/remarkable_activerecord/matchers/validate_confirmation_of_matcher.rb
|
49
47
|
- lib/remarkable_activerecord/matchers/validate_length_of_matcher.rb
|
48
|
+
- lib/remarkable_activerecord/matchers/have_default_scope_matcher.rb
|
50
49
|
- lib/remarkable_activerecord/matchers/have_readonly_attributes_matcher.rb
|
50
|
+
- lib/remarkable_activerecord/matchers/accept_nested_attributes_for_matcher.rb
|
51
51
|
- lib/remarkable_activerecord/matchers/validate_exclusion_of_matcher.rb
|
52
52
|
- lib/remarkable_activerecord/matchers/validate_associated_matcher.rb
|
53
53
|
- lib/remarkable_activerecord/matchers/have_column_matcher.rb
|
@@ -65,6 +65,8 @@ files:
|
|
65
65
|
- locale/en.yml
|
66
66
|
has_rdoc: true
|
67
67
|
homepage: http://github.com/carlosbrando/remarkable
|
68
|
+
licenses: []
|
69
|
+
|
68
70
|
post_install_message:
|
69
71
|
rdoc_options: []
|
70
72
|
|
@@ -85,9 +87,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
85
87
|
requirements: []
|
86
88
|
|
87
89
|
rubyforge_project: remarkable
|
88
|
-
rubygems_version: 1.3.
|
90
|
+
rubygems_version: 1.3.2
|
89
91
|
signing_key:
|
90
|
-
specification_version:
|
92
|
+
specification_version: 3
|
91
93
|
summary: "Remarkable ActiveRecord: collection of matchers and macros with I18n for ActiveRecord"
|
92
94
|
test_files:
|
93
95
|
- spec/validate_associated_matcher_spec.rb
|
@@ -95,6 +97,7 @@ test_files:
|
|
95
97
|
- spec/rcov.opts
|
96
98
|
- spec/spec_helper.rb
|
97
99
|
- spec/validate_acceptance_of_matcher_spec.rb
|
100
|
+
- spec/accept_nested_attributes_for_matcher_spec.rb
|
98
101
|
- spec/allow_mass_assignment_of_matcher_spec.rb
|
99
102
|
- spec/have_scope_matcher_spec.rb
|
100
103
|
- spec/validate_inclusion_of_matcher_spec.rb
|
@@ -104,6 +107,7 @@ test_files:
|
|
104
107
|
- spec/allow_values_for_matcher_spec.rb
|
105
108
|
- spec/validate_uniqueness_of_matcher_spec.rb
|
106
109
|
- spec/have_column_matcher_spec.rb
|
110
|
+
- spec/have_default_scope_matcher_spec.rb
|
107
111
|
- spec/have_index_matcher_spec.rb
|
108
112
|
- spec/association_matcher_spec.rb
|
109
113
|
- spec/validate_exclusion_of_matcher_spec.rb
|