shoulda-callback-matchers 0.1.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/.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
|