shoulda-callback-matchers 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,7 @@
1
+ module Shoulda
2
+ module Callback
3
+ module Matchers
4
+ VERSION = '0.1.0'.freeze
5
+ end
6
+ end
7
+ 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,6 @@
1
+ first:
2
+ id: 1
3
+ name: Some dude
4
+ age: 2
5
+ email: none@none.com
6
+ ssn: 123456789
@@ -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
@@ -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,9 @@
1
+ RSpec.configure do |c|
2
+ def active_model_3_1?
3
+ ::ActiveModel::VERSION::MAJOR == 3 && ::ActiveModel::VERSION::MINOR >= 1
4
+ end
5
+
6
+ def active_model_3_2?
7
+ ::ActiveModel::VERSION::MAJOR == 3 && ::ActiveModel::VERSION::MINOR >= 2
8
+ end
9
+ 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