prefactory 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1332489daa6ce963998267eb39bf65c86230c965
4
+ data.tar.gz: 87cbdfcf4efeee19253b6faeefdd70f9063c3431
5
+ SHA512:
6
+ metadata.gz: e0955140a9e5b688c1126ec1cfef0535feb29b0fd01340e7aca0c030b0efc9975721d0b5440473599e4b6150ec1cff9b6b7705515537170148c74f06f425cfff
7
+ data.tar.gz: db7508fef186aad0c0a9b2e49cd421ec86bd09865ba10efcc9db0bd28e83cbd082f6b17dd5850549572db571e66f5f5ecce9e0c9a1f591a1859b3e6352ea5108
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ spec/*.log
16
+ spec/*.sqlite3
17
+ test/tmp
18
+ test/version_tmp
19
+ tmp
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use default@prefactory --create
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.2
6
+ env:
7
+ - "ACTIVE_RECORD_VERSION=4.0.0"
8
+ - "ACTIVE_RECORD_VERSION=4.1.0"
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in prefactory.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
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
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all 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
21
+ THE SOFTWARE.
@@ -0,0 +1,114 @@
1
+ [![Build Status](https://secure.travis-ci.org/socialcast/prefactory.png?branch=master)](http://travis-ci.org/socialcast/prefactory)
2
+
3
+ # Prefactory
4
+
5
+ The ease and fidelity of factories with the performance of static fixtures.
6
+
7
+ Prefactory allows you to create factory objects and perform other
8
+ expensive ActiveRecord operations in RSpec before(:all) blocks, transparently
9
+ wrapping example groups in nested transactions to automatically roll back
10
+ any data changes that occur during a specific test.
11
+
12
+ ## Requirements
13
+
14
+ * ActiveRecord >= 4
15
+ * RSpec
16
+ * FactoryGirl
17
+ * A database for which ActiveRecord supports [nested transactions](http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html#module-ActiveRecord::Transactions::ClassMethods-label-Nested+transactions) (e.g. MySQL, Postgresql)
18
+
19
+ ## Installation
20
+
21
+ Add the gem to the :test group in your Rails application's Gemfile:
22
+
23
+ ``` ruby
24
+ group :test do
25
+ gem 'prefactory'
26
+ end
27
+ ```
28
+
29
+ Add this to your RSpec `spec_helper.rb`
30
+
31
+ ``` ruby
32
+ RSpec.configure do |config|
33
+ config.include Prefactory
34
+
35
+ # ensure Rails' transaction fixtures are disabled
36
+ config.use_transactional_fixtures = false
37
+
38
+ # optional, to enable shorthand creation
39
+ # using only Factory name (see examples, below)
40
+ config.include FactoryGirl::Syntax::Methods
41
+ end
42
+ ```
43
+
44
+ ## Example
45
+
46
+ ``` ruby
47
+ describe User do
48
+ before :all do # executes once
49
+
50
+ # invokes FactoryGirl.create(:user)
51
+ # reference object as 'friend' in tests
52
+ prefactory_add(:friend) { FactoryGirl.create :user }
53
+
54
+ # invokes create(:user) if available, e.g
55
+ # if rspec is configured with:
56
+ # config.include FactoryGirl::Syntax::Methods
57
+ # reference object as 'user' in examples
58
+ prefactory_add :user
59
+
60
+ end
61
+
62
+ # convenience method, equivalent to:
63
+ # before(:all) do
64
+ # prefactory_add(:other_friend) do
65
+ # create :user
66
+ # end
67
+ # end
68
+ set!(:other_friend) { create :user }
69
+
70
+ context 'a new user has no friends' do
71
+
72
+ it { user.friends.count.should == 0 }
73
+
74
+ context 'with a friend' do
75
+ before(:all) { user.add_friend(friend) } # executes once
76
+
77
+ it { user.friends.count.should == 1 }
78
+
79
+ # these changes will be transparently rolled back
80
+ it "allows removing the friend" do
81
+ expect { user.remove_friend(friend) }.to_not raise_error
82
+ user.friends.count.should == 0
83
+ end
84
+
85
+ it "disallows adding the same friend again" do
86
+ expect { user.add_friend(friend) }.to raise_error
87
+ user.friends.count.should == 1
88
+ end
89
+
90
+ # these changes will be transparently rolled back
91
+ it "allows adding a different friend" do
92
+ expect { user.add_friend(other_friend) }.to_not raise_error
93
+ user.friends.count.should == 2
94
+ end
95
+
96
+ it { user.friends.should include friend }
97
+ it { user.friends.should_not include other_friend }
98
+ end
99
+ end
100
+ end
101
+ ```
102
+
103
+ ## Contributing
104
+
105
+ 1. Fork it ( http://github.com/socialcast/prefactory/fork )
106
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
107
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
108
+ 4. Push to the branch (`git push origin my-new-feature`)
109
+ 5. Create a pull request
110
+
111
+ ## Copyright
112
+
113
+ Copyright (c) 2012-2014 VMware, Inc. All Rights Reserved.
114
+ Released under the terms of the MIT license. See LICENSE for details.
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake'
4
+
5
+ require 'rspec/core/rake_task'
6
+
7
+ desc "Run specs"
8
+ RSpec::Core::RakeTask.new do |t|
9
+ end
10
+ task :default => :spec
11
+ task :test => :spec
@@ -0,0 +1,24 @@
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 'prefactory/version'
24
+ require 'rspec/core/prefactory.rb'
@@ -0,0 +1,112 @@
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 'active_record/connection_adapters/abstract/database_statements'
24
+
25
+ module ActiveRecord
26
+ module ConnectionAdapters
27
+ module DatabaseStatements
28
+
29
+ # Allow for setting a 'base' number of open transactions at which
30
+ # a commit should fire commit callbacks, when nesting transactional
31
+ # example groups.
32
+
33
+ attr_writer :commit_at_open_transaction_level
34
+
35
+ def commit_at_open_transaction_level
36
+ @commit_at_open_transaction_level || 1
37
+ end
38
+
39
+ def transaction_with_transactional_specs(options = {}, &block)
40
+ return_value = nil
41
+ rolled_back = false
42
+
43
+ transaction_without_transactional_specs(options.merge(:requires_new => true)) do
44
+ begin
45
+ return_value = yield
46
+ rescue StandardError => e
47
+ rolled_back = true
48
+ raise e
49
+ end
50
+ if !rolled_back && open_transactions <= commit_at_open_transaction_level + 1
51
+ current_transaction.instance_variable_set(:@fake_commit, true)
52
+ end
53
+ end
54
+ return_value
55
+ end
56
+ alias_method_chain :transaction, :transactional_specs
57
+
58
+ end
59
+ end
60
+ end
61
+
62
+ require 'active_record/connection_adapters/abstract/transaction'
63
+ module ActiveRecord
64
+ module ConnectionAdapters
65
+ class SavepointTransaction
66
+
67
+ # If the savepoint was already released, we have an exception in an after-commit callback.
68
+ # That means *this* transaction cannot and should not be rolled back, unlike the parent
69
+ # transactions, which will roll back as the exception bubbles upwards.
70
+ def perform_rollback
71
+ unless @savepoint_already_released
72
+ connection.rollback_to_savepoint
73
+ rollback_records
74
+ end
75
+ end
76
+
77
+ def perform_commit
78
+ connection.release_savepoint
79
+ if @fake_commit
80
+ @savepoint_already_released = true
81
+ commit_records
82
+ else
83
+ records.each { |r| parent.add_record(r) }
84
+ end
85
+ end
86
+
87
+ end
88
+ end
89
+ end
90
+
91
+ require 'active_record/base'
92
+ module WithDisposableTransactionExtension
93
+
94
+ # do a transaction with a silent rollback (unless another
95
+ # exception is doing a loud rollback already) and trigger
96
+ # commit logic for any transaction above this open-transaction level.
97
+ def with_disposable_transaction(&block)
98
+ return_value = nil
99
+ ActiveRecord::Base.connection.transaction(:requires_new => true) do
100
+ ActiveRecord::Base.connection.commit_at_open_transaction_level = ActiveRecord::Base.connection.open_transactions
101
+ begin
102
+ return_value = yield
103
+ rescue StandardError => e
104
+ raise e
105
+ end
106
+ raise ActiveRecord::Rollback
107
+ end
108
+ return_value
109
+ end
110
+
111
+ end
112
+ ActiveRecord::Base.send(:extend, WithDisposableTransactionExtension)
@@ -0,0 +1,25 @@
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
+ module Prefactory
24
+ VERSION = "0.2.0"
25
+ end
@@ -0,0 +1,159 @@
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 'prefactory/active_record_integration'
24
+ require 'rspec_around_all'
25
+
26
+ module Prefactory
27
+
28
+ def prefactory_lookup(key)
29
+ @prefactory[key]
30
+ end
31
+
32
+ # instantiate, or access an already-instantiated-and-memoized, prefabricated object.
33
+ def prefactory(key)
34
+ lookup = prefactory_lookup(key)
35
+ return nil unless lookup.present?
36
+ @prefactory_memo ||= {}
37
+ begin
38
+ @prefactory_memo[key] ||= lookup[:class].find(lookup[:id])
39
+ rescue
40
+ end
41
+ @prefactory_memo[key]
42
+ end
43
+
44
+ def in_before_all?
45
+ @before_all_context
46
+ end
47
+
48
+ # prefabricate an object. Can be passed any block that returns a class accessible by Klass.find(id),
49
+ # or, if no block is passed, invokes create(key, options) to use e.g. a FactoryGirl factory of that key name.
50
+ def prefactory_add(key, *args, &block)
51
+ raise "prefactory_add can only be used in a before(:all) context. Change to a before(:all) or set!, or use let/let! instead." unless in_before_all?
52
+ result = nil
53
+ clear_prefactory_memoizations
54
+ if block
55
+ result = yield
56
+ else
57
+ result = create(key, *args) if respond_to?(:create)
58
+ end
59
+ if result.present?
60
+ @prefactory[key] = { :class => result.class, :id => result.id }
61
+ else
62
+ warn "WARNING: Failed to add #{key} to prefactory: block result not present"
63
+ end
64
+ clear_prefactory_memoizations
65
+ result
66
+ end
67
+
68
+ def self.included(base)
69
+ base.extend RSpecAroundAll
70
+
71
+ # Wrap outermost describe block in a transaction, so before(:all) data is rolled back at the end of this suite.
72
+ base.before(:all) do
73
+ clear_prefactory_map
74
+ clear_prefactory_memoizations
75
+ end
76
+ base.around(:all) do |group|
77
+ ActiveRecord::Base.with_disposable_transaction { group.run_examples }
78
+ end
79
+ base.after(:all) do
80
+ clear_prefactory_memoizations
81
+ clear_prefactory_map
82
+ end
83
+
84
+ # Wrap each example in a transaction, instead of using Rails' transactional
85
+ # fixtures, which does not support itself being wrapped in an outermost transaction.
86
+ base.around(:each) do |example|
87
+ clear_prefactory_memoizations
88
+ ActiveRecord::Base.with_disposable_transaction { example.run }
89
+ clear_prefactory_memoizations
90
+ end
91
+
92
+ # Wrap each ExampleGroup in a transaction, so group-level before(:all) settings
93
+ # are scoped only to the group.
94
+ base.instance_eval do
95
+ def describe_with_transaction(*args, &block)
96
+ modified_block = proc do
97
+ instance_eval do
98
+ before(:all) { clear_prefactory_memoizations }
99
+ around(:all) do |group|
100
+ ActiveRecord::Base.with_disposable_transaction { group.run_examples }
101
+ end
102
+ after(:all) { clear_prefactory_memoizations }
103
+ end
104
+ instance_eval(&block)
105
+ end
106
+ describe_without_transaction(*args, &modified_block)
107
+ end
108
+
109
+ def before_with_detect_before_all(*args, &block)
110
+ before_all_context = (args.first == :all)
111
+ modified_block = proc do
112
+ @before_all_context = before_all_context
113
+ instance_eval(&block)
114
+ @before_all_context = nil
115
+ end
116
+ before_without_detect_before_all(*args, &modified_block)
117
+ end
118
+
119
+ class << self
120
+ alias_method_chain :describe, :transaction
121
+ alias_method :context, :describe
122
+ alias_method_chain :before, :detect_before_all
123
+ end
124
+
125
+ def set!(key, *args, &set_block)
126
+ before(:all) do
127
+ modified_block = proc { instance_eval(&set_block) } if set_block
128
+ prefactory_add(key, *args, &modified_block)
129
+ end
130
+ end
131
+
132
+ end
133
+
134
+ # allow shorthand access to a prefabricated object
135
+ # e.g. with prefactory_add(:active_user), calling the method active_user will return the (memoized) object,
136
+ # by invoking the 'prefactory' method.
137
+ base.class_eval do
138
+ def method_missing(key, *args, &block)
139
+ if prefactory_lookup(key)
140
+ prefactory(key)
141
+ else
142
+ super
143
+ end
144
+ end
145
+ end
146
+
147
+ end
148
+
149
+ private
150
+
151
+ def clear_prefactory_memoizations
152
+ @prefactory_memo = {}
153
+ end
154
+
155
+ def clear_prefactory_map
156
+ @prefactory = {}
157
+ end
158
+
159
+ end