prefactory 0.2.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.
@@ -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
+