prefactory 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,39 @@
1
+ # encoding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'prefactory/version'
5
+
6
+ active_record_version = case ENV["ACTIVE_RECORD_VERSION"].to_s
7
+ when 'master'
8
+ { github: "rails/activerecord" }
9
+ when ''
10
+ "~> 4"
11
+ else
12
+ "~> #{ENV['ACTIVE_RECORD_VERSION']}"
13
+ end
14
+
15
+ Gem::Specification.new do |spec|
16
+ spec.name = 'prefactory'
17
+ spec.version = Prefactory::VERSION
18
+ spec.authors = ['developers@socialcast.com']
19
+ spec.email = ['developers@socialcast.com']
20
+ spec.summary = %q{Transaction-wrapped RSpec example groups with FactoryGirl integration}
21
+ spec.description = %q{Create factory objects in before-all blocks for fixture-like performance}
22
+ spec.homepage = 'https://github.socialcast.com/prefactory'
23
+ spec.license = 'MIT'
24
+
25
+ spec.files = `git ls-files`.split($/)
26
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
27
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
28
+ spec.require_paths = ['lib']
29
+
30
+ spec.add_dependency 'rspec_around_all', '~> 0'
31
+ spec.add_dependency 'activerecord', active_record_version
32
+ spec.add_dependency 'factory_girl_rails', '~> 4'
33
+
34
+ spec.add_development_dependency 'mysql2', '~> 0.3'
35
+ spec.add_development_dependency 'sqlite3', '~> 1'
36
+ spec.add_development_dependency 'bundler', '~> 1'
37
+ spec.add_development_dependency 'rake', '~> 10.0'
38
+ spec.add_development_dependency 'socialcast-git-extensions', '~> 3'
39
+ end
@@ -0,0 +1,10 @@
1
+ sqlite3:
2
+ adapter: sqlite3
3
+ database: spec/<%= ENV['NO_PREFACTORY'] ? 'no_' : '' %>prefactory_test.sqlite3
4
+
5
+ mysql2:
6
+ adapter: mysql2
7
+ database: <%= ENV['NO_PREFACTORY'] ? 'no_' : '' %>prefactory_test
8
+ username: root
9
+ password:
10
+ host: localhost
@@ -0,0 +1,73 @@
1
+ # encoding: UTF-8
2
+
3
+ # Copyright (c) 2014, VMware, Inc. All Rights Reserved.
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights to
8
+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9
+ # of the Software, and to permit persons to whom the Software is furnished to do
10
+ # so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ require 'logger'
24
+ require 'erb'
25
+ config = YAML.load(ERB.new(File.read(File.dirname(__FILE__) + '/database.yml')).result)
26
+ ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
27
+ ActiveRecord::Base.establish_connection(config[ENV['DB'] || 'sqlite3'])
28
+
29
+ ActiveRecord::Schema.define(:version => 2) do
30
+ create_table :blogs, :force => true do |t|
31
+ t.column :title, :string
32
+ t.column :counter, :integer
33
+ t.timestamps
34
+ end
35
+ create_table :comments, :force => true do |t|
36
+ t.column :blog_id, :integer
37
+ t.column :counter, :integer
38
+ t.column :text, :string
39
+ t.timestamps
40
+ end
41
+ create_table :links, :force => true do |t|
42
+ t.column :blog_id, :integer
43
+ t.column :counter, :integer
44
+ t.column :name, :string
45
+ t.timestamps
46
+ end
47
+ end
48
+
49
+ class Blog < ActiveRecord::Base
50
+ has_many :comments, :dependent => :destroy
51
+ has_many :links, :dependent => :destroy
52
+ include CallbackMatcher::ActiveRecordHooks
53
+ end
54
+
55
+ class Comment < ActiveRecord::Base
56
+ belongs_to :blog
57
+ include CallbackMatcher::ActiveRecordHooks
58
+ end
59
+
60
+ class Link < ActiveRecord::Base
61
+ belongs_to :blog
62
+ include CallbackMatcher::ActiveRecordHooks
63
+ end
64
+
65
+ FactoryGirl.define do
66
+ factory :blog, :aliases => [:another_blog] do
67
+ sequence(:title) { |n| "Title #{n}" }
68
+ end
69
+
70
+ factory :comment, :aliases => [:another_comment] do
71
+ sequence(:text) { |n| "Comment #{n}" }
72
+ end
73
+ end
@@ -0,0 +1,267 @@
1
+ # encoding: UTF-8
2
+
3
+ # Copyright (c) 2014, VMware, Inc. All Rights Reserved.
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights to
8
+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9
+ # of the Software, and to permit persons to whom the Software is furnished to do
10
+ # so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ require 'spec_helper'
24
+
25
+ describe Prefactory do
26
+
27
+ describe "#in_before_all?" do
28
+ before(:all) do
29
+ @in_before_all = in_before_all?
30
+ end
31
+ before(:each) do
32
+ @in_before_each = in_before_all?
33
+ end
34
+ it "is only true when called inside a before(:all)" do
35
+ @in_before_all.should be_true
36
+ @in_before_each.should be_false
37
+ in_before_all?.should be_false # in this it-block context
38
+ end
39
+ after(:each) do
40
+ flunk if in_before_all?
41
+ end
42
+ after(:all) do
43
+ flunk if in_before_all?
44
+ end
45
+ end
46
+
47
+ describe "#prefactory_add" do
48
+
49
+ context "when passing a block with no FactoryGirl activity" do
50
+ before :all do
51
+ prefactory_add(:my_blog) do
52
+ Blog.create! :title => 'My Blog'
53
+ end
54
+ end
55
+ it "creates an object using the provided name" do
56
+ my_blog.should be_present
57
+ my_blog.title.should == 'My Blog'
58
+ my_blog.id.should be_present
59
+ Blog.where(:id => my_blog.id).should exist
60
+ prefactory(:my_blog).should == my_blog
61
+ my_blog.counter.should be_nil
62
+ end
63
+ end
64
+
65
+ context "when passing just a FactoryGirl factory name" do
66
+ before :all do
67
+ prefactory_add :blog
68
+ end
69
+ it "creates an object based on the factory name, via FactoryGirl::Syntax::Methods" do
70
+ blog.should be_present
71
+ blog.title.should =~ /\ATitle [0-9]+\z/
72
+ blog.id.should be_present
73
+ Blog.where(:id => blog.id).should exist
74
+ prefactory(:blog).should == blog
75
+ blog.counter.should be_nil
76
+ end
77
+ end
78
+ context "when passing a factory name and additional attributes" do
79
+ before :all do
80
+ prefactory_add :blog, :counter => 12
81
+ end
82
+ it "uses the additional attributes" do
83
+ blog.should be_present
84
+ blog.title.should =~ /\ATitle [0-9]+\z/
85
+ blog.id.should be_present
86
+ Blog.where(:id => blog.id).should exist
87
+ prefactory(:blog).should == blog
88
+ blog.counter.should == 12
89
+ end
90
+ end
91
+ context "when using a different name and an explicit create call in a block" do
92
+ before :all do
93
+ prefactory_add(:some_other_blog) { create :blog, :counter => 24 }
94
+ end
95
+ it "uses the other name" do
96
+ some_other_blog.should be_present
97
+ some_other_blog.title.should =~ /\ATitle [0-9]+\z/
98
+ some_other_blog.id.should be_present
99
+ Blog.where(:id => some_other_blog.id).should exist
100
+ prefactory(:some_other_blog).should == some_other_blog
101
+ some_other_blog.counter.should == 24
102
+ end
103
+ end
104
+ context "when referencing the object within the before-all block" do
105
+ before :all do
106
+ prefactory_add :blog
107
+ blog.update_attributes! :counter => 42
108
+ end
109
+ it "works" do
110
+ blog.counter.should == 42
111
+ end
112
+ end
113
+ context "nesting a before-all with a prefactory_add" do
114
+ before :all do
115
+ prefactory_add :blog, :title => 'the big book of trains'
116
+ end
117
+ it { blog.should be_present }
118
+ context "and another before-all prefactory_add" do
119
+ before :all do
120
+ prefactory_add :comment, :blog => blog, :text => 'old text'
121
+ end
122
+ it do
123
+ comment.should be_present
124
+ comment.blog.should == blog
125
+ end
126
+ it do
127
+ blog.should be_present
128
+ blog.title.should == 'the big book of trains'
129
+ end
130
+ context "when modifying the object in a before-each" do
131
+ before do
132
+ comment.update_attributes! :text => 'new text'
133
+ blog.update_attributes! :title => 'the little book of calm'
134
+ end
135
+ it do
136
+ comment.text.should == 'new text'
137
+ end
138
+ it do
139
+ blog.title.should == 'the little book of calm'
140
+ end
141
+ end
142
+ context "when modifying the object in a before-all" do
143
+ before :all do
144
+ comment.update_attributes! :text => 'new text'
145
+ blog.update_attributes! :title => 'the little book of calm'
146
+ end
147
+ it do
148
+ comment.text.should == 'new text'
149
+ end
150
+ it do
151
+ blog.title.should == 'the little book of calm'
152
+ end
153
+ end
154
+ context "when not modifying the object" do
155
+ it do
156
+ comment.text.should == 'old text'
157
+ end
158
+ it do
159
+ blog.title.should == 'the big book of trains'
160
+ end
161
+ end
162
+ end
163
+ it "preserves the title" do
164
+ blog.title.should == 'the big book of trains'
165
+ end
166
+ end
167
+
168
+ context "when called in a context which is not a before-all context" do
169
+ before :all do
170
+ prefactory_add :blog
171
+ end
172
+ before do
173
+ @before_each_exception = nil
174
+ begin
175
+ prefactory_add(:another_blog)
176
+ rescue => e
177
+ @before_each_exception = e
178
+ end
179
+ end
180
+ it "raises an error in a before-each context" do
181
+ @before_each_exception.should be_present
182
+ end
183
+ it "raises an error in an it-context" do
184
+ expect { prefactory_add(:comment) }.to raise_error
185
+ end
186
+ it "works in a before-all context" do
187
+ prefactory(:blog).should be_present
188
+ end
189
+ end
190
+ end
191
+
192
+ describe "#prefactory and its method_missing :key fallback" do
193
+ before :all do
194
+ prefactory_add :blog
195
+ end
196
+ subject { prefactory(key) }
197
+ context "when passed a key associated with a prefactory_add'ed object" do
198
+ let(:key) { :blog }
199
+ it do
200
+ should be_present
201
+ should == blog
202
+ should be_a Blog
203
+ end
204
+ context "that has since been destroyed in a before-each" do
205
+ before { blog.destroy }
206
+ it do
207
+ should be_present
208
+ should be_destroyed
209
+ blog.should be_present
210
+ Blog.where(:id => blog.id).should_not exist
211
+ end
212
+ end
213
+ context "that has since been destroyed in a before-all" do
214
+ before(:all) { blog.destroy }
215
+ it { should be_nil }
216
+ it "does not raise an error when calling the key name as a method" do
217
+ blog.should be_nil
218
+ end
219
+ end
220
+ end
221
+ context "when passed a nonexistent key" do
222
+ let(:key) { :frog }
223
+ it do
224
+ should be_nil
225
+ expect { frog }.to raise_error(NameError)
226
+ end
227
+ end
228
+
229
+ context "with multiple before(:all) blocks" do
230
+ before(:all) { prefactory_add :blog }
231
+ before(:all) { prefactory_add :comment }
232
+ before(:all) { prefactory_add :another_blog, :title => blog.id.to_s }
233
+ it do
234
+ blog.should be_present
235
+ Blog.where(:id => blog.id).should exist
236
+ end
237
+ it do
238
+ comment.should be_present
239
+ Comment.where(:id => comment.id).should exist
240
+ end
241
+ it do
242
+ another_blog.title.should == blog.id.to_s
243
+ end
244
+ end
245
+ end
246
+
247
+ describe ".set!" do
248
+ set!(:blog)
249
+ set! :comment, :counter => 12
250
+ set!(:some_other_comment) do
251
+ FactoryGirl.create :comment, :text => blog.title
252
+ end
253
+ it do
254
+ blog.should be_present
255
+ Blog.where(:id => blog.id).should exist
256
+ end
257
+ it do
258
+ comment.should be_present
259
+ comment.counter.should == 12
260
+ Comment.where(:id => comment.id).should exist
261
+ end
262
+ it do
263
+ some_other_comment.text.should be_present
264
+ some_other_comment.text.should == blog.title
265
+ end
266
+ end
267
+ end
@@ -0,0 +1,53 @@
1
+ # encoding: UTF-8
2
+
3
+ # Copyright (c) 2014, VMware, Inc. All Rights Reserved.
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights to
8
+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9
+ # of the Software, and to permit persons to whom the Software is furnished to do
10
+ # so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ require 'rubygems'
24
+ require 'bundler'
25
+ require 'yaml'
26
+ begin
27
+ Bundler.setup
28
+ rescue Bundler::BundlerError => e
29
+ $stderr.puts e.message
30
+ $stderr.puts "Run `bundle install` to install missing gems"
31
+ exit e.status_code
32
+ end
33
+ require 'rspec/matchers'
34
+
35
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
36
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
37
+
38
+ require 'factory_girl_rails'
39
+ require 'support/callback_matcher'
40
+ require 'active_record'
41
+ require 'database_setup'
42
+ RSpec.configure do |config|
43
+ config.include FactoryGirl::Syntax::Methods
44
+ end
45
+
46
+ unless ENV['NO_PREFACTORY']
47
+ require 'prefactory'
48
+
49
+ RSpec.configure do |config|
50
+ config.include Prefactory
51
+ end
52
+ end
53
+
@@ -0,0 +1,109 @@
1
+ # encoding: UTF-8
2
+
3
+ # Copyright (c) 2014, VMware, Inc. All Rights Reserved.
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights to
8
+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9
+ # of the Software, and to permit persons to whom the Software is furnished to do
10
+ # so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in all
13
+ # copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ # SOFTWARE.
22
+
23
+ class CallbackMatcher
24
+ CALLBACK_EVENTS = [:before, :after]
25
+ CALLBACK_TYPES = [:create, :update, :destroy, :save, :commit]
26
+
27
+ module ActiveRecordHooks
28
+
29
+ def self.included(base)
30
+ base.class_eval do
31
+ class_attribute :callback_tester_attrs
32
+ self.callback_tester_attrs = []
33
+
34
+ CALLBACK_EVENTS.each do |ce|
35
+ CALLBACK_TYPES.each do |ct|
36
+ next if ce == :before && ct == :commit
37
+ callback_name = :"#{ce}_#{ct}"
38
+ callback_attr = :"called_#{callback_name}"
39
+
40
+ callback_tester_attrs << callback_attr
41
+ attr_accessor callback_attr
42
+
43
+ send( callback_name ) {
44
+ send(:"#{callback_attr}=", send(:"#{callback_attr}").to_i + 1)
45
+ }
46
+ end
47
+ end
48
+ alias_method_chain :initialize, :callback_init
49
+ end
50
+ end
51
+
52
+ def initialize_with_callback_init(*args)
53
+ reset_callback_flags!
54
+ initialize_without_callback_init(*args)
55
+ end
56
+
57
+ def reset_callback_flags!
58
+ self.class.callback_tester_attrs.each do |attr|
59
+ send("#{attr}=", 0)
60
+ end
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+
67
+ require 'rspec/matchers'
68
+
69
+ RSpec::Matchers.define :trigger_callbacks_for do |types|
70
+
71
+ check_for_match = ->(model_instance, types) {
72
+ @called = []
73
+ @not_called = []
74
+ Array.wrap(types).each do |ct|
75
+ CallbackMatcher::CALLBACK_EVENTS.each do |ce|
76
+ callback_name = "#{ce}_#{ct}"
77
+ result = model_instance.send("called_#{callback_name}".to_sym)
78
+ @called << callback_name if result
79
+ @not_called << callback_name unless result
80
+ end
81
+ end
82
+ }
83
+
84
+ match_for_should do |model_instance|
85
+ check_for_match.call(model_instance, types)
86
+ result = true
87
+ result = false unless @called.present?
88
+ result = false if @not_called.present?
89
+ result
90
+ end
91
+
92
+ match_for_should_not do |model_instance|
93
+ check_for_match.call(model_instance, types)
94
+ result = true
95
+ result = false unless @not_called.present?
96
+ result = false if @called.present?
97
+ result
98
+ end
99
+
100
+ failure_message_for_should do |actual|
101
+ ["Called:\t#{@called.join("\n\t")}", "Not called:\t#{@called.join("\n\t")}"].join("\n")
102
+ end
103
+
104
+ failure_message_for_should_not do |actual|
105
+ ["Called:\t#{@called.join("\n\t")}", "Not called:\t#{@called.join("\n\t")}"].join("\n")
106
+ end
107
+
108
+ end
109
+