shoulda-callback-matchers 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +11 -0
- data/.rvmrc +1 -0
- data/.travis.yml +18 -0
- data/Appraisals +15 -0
- data/CONTRIBUTING.md +0 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +148 -0
- data/MIT-LICENSE +22 -0
- data/NEWS.md +2 -0
- data/README.md +40 -0
- data/Rakefile +19 -0
- data/gemfiles/3.0.gemfile +13 -0
- data/gemfiles/3.0.gemfile.lock +133 -0
- data/gemfiles/3.1.gemfile +15 -0
- data/gemfiles/3.1.gemfile.lock +155 -0
- data/gemfiles/3.2.gemfile +15 -0
- data/gemfiles/3.2.gemfile.lock +153 -0
- data/lib/shoulda-callback-matchers.rb +1 -0
- data/lib/shoulda/callback/matchers.rb +7 -0
- data/lib/shoulda/callback/matchers/active_model.rb +107 -0
- data/lib/shoulda/callback/matchers/integrations/rspec.rb +13 -0
- data/lib/shoulda/callback/matchers/integrations/test_unit.rb +34 -0
- data/lib/shoulda/callback/matchers/version.rb +7 -0
- data/shoulda-callback-matchers.gemspec +28 -0
- data/spec/fixtures/users.yml +6 -0
- data/spec/shoulda/active_model/callback_matcher_spec.rb +229 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/support/active_model_versions.rb +9 -0
- data/spec/support/class_builder.rb +42 -0
- data/spec/support/model_builder.rb +73 -0
- metadata +208 -0
@@ -0,0 +1,13 @@
|
|
1
|
+
# :enddoc:
|
2
|
+
|
3
|
+
if defined?(::ActiveRecord)
|
4
|
+
require 'shoulda/callback/matchers/active_model'
|
5
|
+
module RSpec::Matchers
|
6
|
+
include Shoulda::Callback::Matchers::ActiveModel
|
7
|
+
end
|
8
|
+
elsif defined?(::ActiveModel)
|
9
|
+
require 'shoulda/callback/matchers/active_model'
|
10
|
+
module RSpec::Matchers
|
11
|
+
include Shoulda::Callback::Matchers::ActiveModel
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# :enddoc:
|
2
|
+
|
3
|
+
# in environments where test/unit is not required, this is necessary
|
4
|
+
unless defined?(Test::Unit::TestCase)
|
5
|
+
begin
|
6
|
+
require 'test/unit'
|
7
|
+
rescue LoadError
|
8
|
+
# silent
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
if defined?(ActiveRecord)
|
13
|
+
require 'shoulda/callback/matchers/active_model'
|
14
|
+
|
15
|
+
module Test
|
16
|
+
module Unit
|
17
|
+
class TestCase
|
18
|
+
include Shoulda::Callback::Matchers::ActiveModel
|
19
|
+
extend Shoulda::Callback::Matchers::ActiveModel
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
elsif defined?(ActiveModel)
|
24
|
+
require 'shoulda/callback/matchers/active_model'
|
25
|
+
|
26
|
+
module Test
|
27
|
+
module Unit
|
28
|
+
class TestCase
|
29
|
+
include Shoulda::Callback::Matchers::ActiveModel
|
30
|
+
extend Shoulda::Callback::Matchers::ActiveModel
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
$LOAD_PATH << File.join(File.dirname(__FILE__), 'lib')
|
2
|
+
require 'shoulda/callback/matchers/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "shoulda-callback-matchers"
|
6
|
+
s.version = Shoulda::Callback::Matchers::VERSION.dup
|
7
|
+
s.authors = ["Beat Richartz"]
|
8
|
+
s.date = Time.now.strftime("%Y-%m-%d")
|
9
|
+
s.email = "attraccessor@gmail.com"
|
10
|
+
s.homepage = "http://github.com/beatrichartz/shoulda-callback-matchers"
|
11
|
+
s.summary = "Making callback tests easy on the fingers and eyes"
|
12
|
+
s.description = "Making callback tests easy on the fingers and eyes"
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
s.add_dependency('activesupport', '>= 3.0.0')
|
20
|
+
|
21
|
+
s.add_development_dependency('appraisal', '~> 0.4.0')
|
22
|
+
s.add_development_dependency('aruba')
|
23
|
+
s.add_development_dependency('bourne', '~> 1.1.2')
|
24
|
+
s.add_development_dependency('bundler', '>= 1.1.0')
|
25
|
+
s.add_development_dependency('rails', '~> 3.0')
|
26
|
+
s.add_development_dependency('rake', '~> 0.9.2')
|
27
|
+
s.add_development_dependency('rspec-rails', '~> 2.8.1')
|
28
|
+
end
|
@@ -0,0 +1,229 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Shoulda::Callback::Matchers::ActiveModel do
|
4
|
+
|
5
|
+
context "invalid use" do
|
6
|
+
before do
|
7
|
+
@model = define_model(:example, :attr => :string,
|
8
|
+
:other => :integer) do
|
9
|
+
attr_accessible :attr, :other
|
10
|
+
before_create :dance!, :if => :evaluates_to_false!
|
11
|
+
after_save :shake!, :unless => :evaluates_to_true!
|
12
|
+
end.new
|
13
|
+
end
|
14
|
+
it "should return a meaningful failure message when used without a defined lifecycle" do
|
15
|
+
matcher = callback(:dance!)
|
16
|
+
matcher.matches?(@model).should be_false
|
17
|
+
matcher.failure_message.should == "callback dance! can not be tested against an undefined lifecycle, use .before, .after or .around"
|
18
|
+
matcher.negative_failure_message.should == "callback dance! can not be tested against an undefined lifecycle, use .before, .after or .around"
|
19
|
+
end
|
20
|
+
it "should return a meaningful failure message when used with an optional lifecycle without the original lifecycle being validation" do
|
21
|
+
matcher = callback(:dance!).after(:create).on(:save)
|
22
|
+
matcher.matches?(@model).should be_false
|
23
|
+
matcher.failure_message.should == "The .on option is only valid for the validation lifecycle and cannot be used with create, use with .before(:validation) or .after(:validation)"
|
24
|
+
matcher.negative_failure_message.should == "The .on option is only valid for the validation lifecycle and cannot be used with create, use with .before(:validation) or .after(:validation)"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
[:save, :create, :update, :destroy].each do |lifecycle|
|
29
|
+
context "on #{lifecycle}" do
|
30
|
+
before do
|
31
|
+
@model = define_model(:example, :attr => :string,
|
32
|
+
:other => :integer) do
|
33
|
+
attr_accessible :attr, :other
|
34
|
+
send(:"before_#{lifecycle}", :dance!, :if => :evaluates_to_false!)
|
35
|
+
send(:"after_#{lifecycle}", :shake!, :unless => :evaluates_to_true!)
|
36
|
+
send(:"around_#{lifecycle}", :giggle!)
|
37
|
+
end.new
|
38
|
+
end
|
39
|
+
context "as a simple callback test" do
|
40
|
+
it "should find the callback before the fact" do
|
41
|
+
@model.should callback(:dance!).before(lifecycle)
|
42
|
+
end
|
43
|
+
it "should find the callback after the fact" do
|
44
|
+
@model.should callback(:shake!).after(lifecycle)
|
45
|
+
end
|
46
|
+
it "should find the callback around the fact" do
|
47
|
+
@model.should callback(:giggle!).around(lifecycle)
|
48
|
+
end
|
49
|
+
it "should not find callbacks that are not there" do
|
50
|
+
@model.should_not callback(:scream!).around(lifecycle)
|
51
|
+
end
|
52
|
+
it "should have a meaningful description" do
|
53
|
+
matcher = callback(:dance!).before(lifecycle)
|
54
|
+
matcher.description.should == "callback dance! before #{lifecycle}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
context "with conditions" do
|
58
|
+
it "should match the if condition" do
|
59
|
+
@model.should callback(:dance!).before(lifecycle).if(:evaluates_to_false!)
|
60
|
+
end
|
61
|
+
it "should match the unless condition" do
|
62
|
+
@model.should callback(:shake!).after(lifecycle).unless(:evaluates_to_true!)
|
63
|
+
end
|
64
|
+
it "should not find callbacks not matching the conditions" do
|
65
|
+
@model.should_not callback(:giggle!).around(lifecycle).unless(:evaluates_to_false!)
|
66
|
+
end
|
67
|
+
it "should not find callbacks that are not there entirely" do
|
68
|
+
@model.should_not callback(:scream!).before(lifecycle).unless(:evaluates_to_false!)
|
69
|
+
end
|
70
|
+
it "should have a meaningful description" do
|
71
|
+
matcher = callback(:dance!).after(lifecycle).unless(:evaluates_to_false!)
|
72
|
+
matcher.description.should == "callback dance! after #{lifecycle} unless evaluates_to_false! evaluates to false"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context "on validation" do
|
79
|
+
before do
|
80
|
+
@model = define_model(:example, :attr => :string,
|
81
|
+
:other => :integer) do
|
82
|
+
attr_accessible :attr, :other
|
83
|
+
before_validation :dance!, :if => :evaluates_to_false!
|
84
|
+
after_validation :shake!, :unless => :evaluates_to_true!
|
85
|
+
before_validation :dress!, :on => :create
|
86
|
+
after_validation :shriek!, :on => :update, :unless => :evaluates_to_true!
|
87
|
+
after_validation :pucker!, :on => :save, :if => :evaluates_to_false!
|
88
|
+
end.new
|
89
|
+
end
|
90
|
+
|
91
|
+
context "as a simple callback test" do
|
92
|
+
it "should find the callback before the fact" do
|
93
|
+
@model.should callback(:dance!).before(:validation)
|
94
|
+
end
|
95
|
+
it "should find the callback after the fact" do
|
96
|
+
@model.should callback(:shake!).after(:validation)
|
97
|
+
end
|
98
|
+
it "should not find a callback around the fact" do
|
99
|
+
@model.should_not callback(:giggle!).around(:validation)
|
100
|
+
end
|
101
|
+
it "should not find callbacks that are not there" do
|
102
|
+
@model.should_not callback(:scream!).around(:validation)
|
103
|
+
end
|
104
|
+
it "should have a meaningful description" do
|
105
|
+
matcher = callback(:dance!).before(:validation)
|
106
|
+
matcher.description.should == "callback dance! before validation"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context "with additinal lifecycles defined" do
|
111
|
+
it "should find the callback before the fact on create" do
|
112
|
+
@model.should callback(:dress!).before(:validation).on(:create)
|
113
|
+
end
|
114
|
+
it "should find the callback after the fact on update" do
|
115
|
+
@model.should callback(:shriek!).after(:validation).on(:update)
|
116
|
+
end
|
117
|
+
it "should find the callback after the fact on save" do
|
118
|
+
@model.should callback(:pucker!).after(:validation).on(:save)
|
119
|
+
end
|
120
|
+
it "should not find a callback for pucker! after the fact on update" do
|
121
|
+
@model.should_not callback(:pucker!).after(:validation).on(:update)
|
122
|
+
end
|
123
|
+
it "should have a meaningful description" do
|
124
|
+
matcher = callback(:dance!).after(:validation).on(:update)
|
125
|
+
matcher.description.should == "callback dance! after validation on update"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
context "with conditions" do
|
130
|
+
it "should match the if condition" do
|
131
|
+
@model.should callback(:dance!).before(:validation).if(:evaluates_to_false!)
|
132
|
+
end
|
133
|
+
it "should match the unless condition" do
|
134
|
+
@model.should callback(:shake!).after(:validation).unless(:evaluates_to_true!)
|
135
|
+
end
|
136
|
+
it "should not find callbacks not matching the conditions" do
|
137
|
+
@model.should_not callback(:giggle!).around(:validation).unless(:evaluates_to_false!)
|
138
|
+
end
|
139
|
+
it "should not find callbacks that are not there entirely" do
|
140
|
+
@model.should_not callback(:scream!).before(:validation).unless(:evaluates_to_false!)
|
141
|
+
end
|
142
|
+
it "should have a meaningful description" do
|
143
|
+
matcher = callback(:dance!).after(:validation).unless(:evaluates_to_false!)
|
144
|
+
matcher.description.should == "callback dance! after validation unless evaluates_to_false! evaluates to false"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context "with conditions and additional lifecycles" do
|
149
|
+
it "should find the callback before the fact on create" do
|
150
|
+
@model.should callback(:dress!).before(:validation).on(:create)
|
151
|
+
end
|
152
|
+
it "should find the callback after the fact on update with the unless condition" do
|
153
|
+
@model.should callback(:shriek!).after(:validation).on(:update).unless(:evaluates_to_true!)
|
154
|
+
end
|
155
|
+
it "should find the callback after the fact on save with the if condition" do
|
156
|
+
@model.should callback(:pucker!).after(:validation).on(:save).if(:evaluates_to_false!)
|
157
|
+
end
|
158
|
+
it "should not find a callback for pucker! after the fact on save with the wrong condition" do
|
159
|
+
@model.should_not callback(:pucker!).after(:validation).on(:save).unless(:evaluates_to_false!)
|
160
|
+
end
|
161
|
+
it "should have a meaningful description" do
|
162
|
+
matcher = callback(:dance!).after(:validation).on(:save).unless(:evaluates_to_false!)
|
163
|
+
matcher.description.should == "callback dance! after validation on save unless evaluates_to_false! evaluates to false"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
[:initialize, :find, :touch].each do |lifecycle|
|
169
|
+
|
170
|
+
context "on #{lifecycle}" do
|
171
|
+
before do
|
172
|
+
@model = define_model(:example, :attr => :string,
|
173
|
+
:other => :integer) do
|
174
|
+
attr_accessible :attr, :other
|
175
|
+
send(:"after_#{lifecycle}", :dance!, :if => :evaluates_to_false!)
|
176
|
+
send(:"after_#{lifecycle}", :shake!, :unless => :evaluates_to_true!)
|
177
|
+
|
178
|
+
define_method :evaluates_to_false! do
|
179
|
+
false
|
180
|
+
end
|
181
|
+
|
182
|
+
define_method :evaluates_to_true! do
|
183
|
+
true
|
184
|
+
end
|
185
|
+
|
186
|
+
end.new
|
187
|
+
end
|
188
|
+
|
189
|
+
context "as a simple callback test" do
|
190
|
+
it "should not find a callback before the fact" do
|
191
|
+
@model.should_not callback(:dance!).before(lifecycle)
|
192
|
+
end
|
193
|
+
it "should find the callback after the fact" do
|
194
|
+
@model.should callback(:shake!).after(lifecycle)
|
195
|
+
end
|
196
|
+
it "should not find a callback around the fact" do
|
197
|
+
@model.should_not callback(:giggle!).around(lifecycle)
|
198
|
+
end
|
199
|
+
it "should not find callbacks that are not there" do
|
200
|
+
@model.should_not callback(:scream!).around(lifecycle)
|
201
|
+
end
|
202
|
+
it "should have a meaningful description" do
|
203
|
+
matcher = callback(:dance!).before(lifecycle)
|
204
|
+
matcher.description.should == "callback dance! before #{lifecycle}"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
context "with conditions" do
|
209
|
+
it "should match the if condition" do
|
210
|
+
@model.should callback(:dance!).after(lifecycle).if(:evaluates_to_false!)
|
211
|
+
end
|
212
|
+
it "should match the unless condition" do
|
213
|
+
@model.should callback(:shake!).after(lifecycle).unless(:evaluates_to_true!)
|
214
|
+
end
|
215
|
+
it "should not find callbacks not matching the conditions" do
|
216
|
+
@model.should_not callback(:giggle!).around(lifecycle).unless(:evaluates_to_false!)
|
217
|
+
end
|
218
|
+
it "should not find callbacks that are not there entirely" do
|
219
|
+
@model.should_not callback(:scream!).before(lifecycle).unless(:evaluates_to_false!)
|
220
|
+
end
|
221
|
+
it "should have a meaningful description" do
|
222
|
+
matcher = callback(:dance!).after(lifecycle).unless(:evaluates_to_false!)
|
223
|
+
matcher.description.should == "callback dance! after #{lifecycle} unless evaluates_to_false! evaluates to false"
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# Create Rails environment based on the version given from Appraisal
|
2
|
+
TESTAPP_ROOT = File.join(File.dirname(__FILE__), '..', 'tmp', 'aruba', 'testapp')
|
3
|
+
FileUtils.rm_rf(TESTAPP_ROOT) if File.exists?(TESTAPP_ROOT)
|
4
|
+
`rails new #{TESTAPP_ROOT}`
|
5
|
+
|
6
|
+
ENV['RAILS_ENV'] = 'test'
|
7
|
+
ENV['BUNDLE_GEMFILE'] ||= TESTAPP_ROOT + '/Gemfile'
|
8
|
+
|
9
|
+
require "#{TESTAPP_ROOT}/config/environment"
|
10
|
+
require 'rspec'
|
11
|
+
require 'bourne'
|
12
|
+
require 'shoulda-callback-matchers'
|
13
|
+
require 'rspec/rails'
|
14
|
+
|
15
|
+
PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..')).freeze
|
16
|
+
|
17
|
+
$LOAD_PATH << File.join(PROJECT_ROOT, 'lib')
|
18
|
+
|
19
|
+
Dir[File.join(PROJECT_ROOT, 'spec', 'support', '**', '*.rb')].each { |file| require(file) }
|
20
|
+
|
21
|
+
# Run the migrations
|
22
|
+
ActiveRecord::Migration.verbose = false
|
23
|
+
ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate")
|
24
|
+
|
25
|
+
RSpec.configure do |config|
|
26
|
+
config.mock_with :mocha
|
27
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module ClassBuilder
|
2
|
+
def self.included(example_group)
|
3
|
+
example_group.class_eval do
|
4
|
+
after do
|
5
|
+
teardown_defined_constants
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def define_class(class_name, base = Object, &block)
|
11
|
+
class_name = class_name.to_s.camelize
|
12
|
+
|
13
|
+
# FIXME: ActionMailer 3.2 calls `name.underscore` immediately upon
|
14
|
+
# subclassing. Class.new.name == nil. So, Class.new(ActionMailer::Base)
|
15
|
+
# errors out since it's trying to do `nil.underscore`. This is very ugly but
|
16
|
+
# allows us to test against ActionMailer 3.2.x.
|
17
|
+
eval <<-A_REAL_CLASS_FOR_ACTION_MAILER_3_2
|
18
|
+
class ::#{class_name} < #{base}
|
19
|
+
end
|
20
|
+
A_REAL_CLASS_FOR_ACTION_MAILER_3_2
|
21
|
+
|
22
|
+
Object.const_get(class_name).tap do |constant_class|
|
23
|
+
constant_class.unloadable
|
24
|
+
|
25
|
+
if block_given?
|
26
|
+
constant_class.class_eval(&block)
|
27
|
+
end
|
28
|
+
|
29
|
+
if constant_class.respond_to?(:reset_column_information)
|
30
|
+
constant_class.reset_column_information
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def teardown_defined_constants
|
36
|
+
ActiveSupport::Dependencies.clear
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
RSpec.configure do |config|
|
41
|
+
config.include ClassBuilder
|
42
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module ModelBuilder
|
2
|
+
def self.included(example_group)
|
3
|
+
example_group.class_eval do
|
4
|
+
before do
|
5
|
+
@created_tables ||= []
|
6
|
+
end
|
7
|
+
|
8
|
+
after do
|
9
|
+
drop_created_tables
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def create_table(table_name, options = {}, &block)
|
15
|
+
connection = ActiveRecord::Base.connection
|
16
|
+
|
17
|
+
begin
|
18
|
+
connection.execute("DROP TABLE IF EXISTS #{table_name}")
|
19
|
+
connection.create_table(table_name, options, &block)
|
20
|
+
@created_tables << table_name
|
21
|
+
connection
|
22
|
+
rescue Exception => e
|
23
|
+
connection.execute("DROP TABLE IF EXISTS #{table_name}")
|
24
|
+
raise e
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def define_model_class(class_name, &block)
|
29
|
+
define_class(class_name, ActiveRecord::Base, &block)
|
30
|
+
end
|
31
|
+
|
32
|
+
def define_active_model_class(class_name, options = {}, &block)
|
33
|
+
define_class(class_name) do
|
34
|
+
include ActiveModel::Validations
|
35
|
+
extend ActiveModel::Callbacks
|
36
|
+
define_model_callbacks :initialize, :find, :touch, :only => :after
|
37
|
+
define_model_callbacks :save, :create, :update, :destroy
|
38
|
+
|
39
|
+
options[:accessors].each do |column|
|
40
|
+
attr_accessor column.to_sym
|
41
|
+
end
|
42
|
+
|
43
|
+
if block_given?
|
44
|
+
class_eval(&block)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def define_model(name, columns = {}, &block)
|
50
|
+
class_name = name.to_s.pluralize.classify
|
51
|
+
table_name = class_name.tableize
|
52
|
+
|
53
|
+
create_table(table_name) do |table|
|
54
|
+
columns.each do |name, type|
|
55
|
+
table.column name, type
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
define_model_class(class_name, &block)
|
60
|
+
end
|
61
|
+
|
62
|
+
def drop_created_tables
|
63
|
+
connection = ActiveRecord::Base.connection
|
64
|
+
|
65
|
+
@created_tables.each do |table_name|
|
66
|
+
connection.execute("DROP TABLE IF EXISTS #{table_name}")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
RSpec.configure do |config|
|
72
|
+
config.include ModelBuilder
|
73
|
+
end
|