when_committed 0.9.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 +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +50 -0
- data/Rakefile +1 -0
- data/lib/when_committed.rb +30 -0
- data/lib/when_committed/version.rb +3 -0
- data/spec/when_committed_spec.rb +105 -0
- data/when_committed.gemspec +25 -0
- metadata +107 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 PeopleAdmin
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# WhenCommitted
|
2
|
+
|
3
|
+
Provides `#when_commited` to run instance-specific code in an ActiveRecord
|
4
|
+
`#after_commit` callback.
|
5
|
+
|
6
|
+
This is very useful for things like enqueuing a background job that is triggered
|
7
|
+
by a model changing state. Usually, it is not sufficient to enqueue the job in
|
8
|
+
an `#after_save` hook, because there is always the chance that the save will be
|
9
|
+
rolled back (or that the job gets picked up before the save is committed). You
|
10
|
+
could try moving that code to an `after_commit` callback, but then you do not
|
11
|
+
have access to the `#changes` to your model (they have already been applied), so
|
12
|
+
it may be difficult to make decisions on whether to enqueue the job or not.
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
Add this line to your application's Gemfile:
|
17
|
+
|
18
|
+
gem 'when_committed'
|
19
|
+
|
20
|
+
And then execute:
|
21
|
+
|
22
|
+
$ bundle
|
23
|
+
|
24
|
+
Or install it yourself as:
|
25
|
+
|
26
|
+
$ gem install when_committed
|
27
|
+
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
Include the WhenCommitted::ActiveRecord module in your model:
|
31
|
+
|
32
|
+
class Post < ActiveRecord::Base
|
33
|
+
include WhenCommitted::ActiveRecord
|
34
|
+
end
|
35
|
+
|
36
|
+
Call `#when_committed` with a block of code that should run when the transaction
|
37
|
+
is committed:
|
38
|
+
|
39
|
+
def update_score(new_score)
|
40
|
+
self.score = new_score
|
41
|
+
when_committed { Resque.enqueue(RecalculateAggregateScores, self.id) }
|
42
|
+
end
|
43
|
+
|
44
|
+
## Contributing
|
45
|
+
|
46
|
+
1. [Fork it](https://github.com/PeopleAdmin/when_committed/fork_select)
|
47
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
48
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
49
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
50
|
+
5. [Create new Pull Request](https://github.com/PeopleAdmin/when_committed/pull/new/master)
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'when_committed/version'
|
2
|
+
|
3
|
+
module WhenCommitted
|
4
|
+
module ActiveRecord
|
5
|
+
def self.included(base)
|
6
|
+
base.after_commit :run_when_committed_callbacks
|
7
|
+
base.after_rollback :clear_when_committed_callbacks
|
8
|
+
end
|
9
|
+
|
10
|
+
def when_committed(&block)
|
11
|
+
when_committed_callbacks << block
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def when_committed_callbacks
|
17
|
+
@when_committed_callbacks ||= []
|
18
|
+
end
|
19
|
+
|
20
|
+
def run_when_committed_callbacks
|
21
|
+
when_committed_callbacks.each {|cb| cb.call}
|
22
|
+
clear_when_committed_callbacks
|
23
|
+
end
|
24
|
+
|
25
|
+
def clear_when_committed_callbacks
|
26
|
+
when_committed_callbacks.clear
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'when_committed'
|
3
|
+
|
4
|
+
describe "WhenCommitted" do
|
5
|
+
before(:all) do
|
6
|
+
ActiveRecord::Base.establish_connection :adapter => :nulldb
|
7
|
+
ActiveRecord::Migration.verbose = false
|
8
|
+
ActiveRecord::Schema.define do
|
9
|
+
create_table(:widgets) do |t|
|
10
|
+
t.string :name
|
11
|
+
t.integer :size
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it "provides a #when_committed method" do
|
17
|
+
sample_class = Class.new(ActiveRecord::Base)
|
18
|
+
model = sample_class.new
|
19
|
+
model.should_not respond_to(:when_committed)
|
20
|
+
sample_class.send :include, WhenCommitted::ActiveRecord
|
21
|
+
model.should respond_to(:when_committed)
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#when_committed" do
|
25
|
+
before do
|
26
|
+
Backgrounder.reset
|
27
|
+
end
|
28
|
+
let(:model) { Widget.new }
|
29
|
+
|
30
|
+
it "runs the provided block after the transaction is committed" do
|
31
|
+
model.action_that_needs_follow_up_after_commit
|
32
|
+
model.save
|
33
|
+
Backgrounder.jobs.should == [:important_work]
|
34
|
+
end
|
35
|
+
|
36
|
+
it "does not run the provided block until the transaction is committed" do
|
37
|
+
Widget.transaction do
|
38
|
+
model.action_that_needs_follow_up_after_commit
|
39
|
+
Backgrounder.jobs.should be_empty
|
40
|
+
model.save
|
41
|
+
Backgrounder.jobs.should be_empty
|
42
|
+
end
|
43
|
+
Backgrounder.jobs.should == [:important_work]
|
44
|
+
end
|
45
|
+
|
46
|
+
it "does not run the provided block if the transaction is rolled back" do
|
47
|
+
begin
|
48
|
+
Widget.transaction do
|
49
|
+
model.action_that_needs_follow_up_after_commit
|
50
|
+
model.save
|
51
|
+
raise Catastrophe
|
52
|
+
end
|
53
|
+
rescue Catastrophe
|
54
|
+
end
|
55
|
+
Backgrounder.jobs.should be_empty
|
56
|
+
end
|
57
|
+
|
58
|
+
it "allows you to register multiple after_commit blocks" do
|
59
|
+
Widget.transaction do
|
60
|
+
model.action_that_needs_follow_up_after_commit
|
61
|
+
model.another_action_with_follow_up
|
62
|
+
model.save
|
63
|
+
end
|
64
|
+
Backgrounder.jobs.should == [:important_work,:more_work]
|
65
|
+
end
|
66
|
+
|
67
|
+
it "does not run a registered block more than once" do
|
68
|
+
Widget.transaction do
|
69
|
+
model.action_that_needs_follow_up_after_commit
|
70
|
+
model.save
|
71
|
+
end
|
72
|
+
Widget.transaction do
|
73
|
+
model.name = "changed"
|
74
|
+
model.save
|
75
|
+
end
|
76
|
+
Backgrounder.should have(1).job
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class Widget < ActiveRecord::Base
|
82
|
+
include WhenCommitted::ActiveRecord
|
83
|
+
def action_that_needs_follow_up_after_commit
|
84
|
+
when_committed { Backgrounder.enqueue :important_work }
|
85
|
+
end
|
86
|
+
def another_action_with_follow_up
|
87
|
+
when_committed { Backgrounder.enqueue :more_work }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class Backgrounder
|
92
|
+
def self.enqueue job
|
93
|
+
jobs << job
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.jobs
|
97
|
+
@jobs ||= []
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.reset
|
101
|
+
@jobs = []
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
class Catastrophe < StandardError; end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'when_committed/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "when_committed"
|
8
|
+
gem.version = WhenCommitted::VERSION
|
9
|
+
gem.authors = ["Joshua Flanagan"]
|
10
|
+
gem.email = ["jflanagan@peopleadmin.com"]
|
11
|
+
gem.description = %q{Run a piece of code after the current transaction is committed}
|
12
|
+
gem.summary = %q{Some actions (like enqueuing a background job) should not run until
|
13
|
+
the current ActiveRecord transaction has committed. ActiveRecord defines an `#after_commit` callback,
|
14
|
+
but it run for every transaction, for every instance of a class. `#when_committed` allows you to
|
15
|
+
dynamically define a block of code that should run when the transaction is committed.}
|
16
|
+
gem.homepage = "https://github.com/PeopleAdmin/when_committed"
|
17
|
+
|
18
|
+
gem.files = `git ls-files`.split($/)
|
19
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
20
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
21
|
+
gem.require_paths = ["lib"]
|
22
|
+
gem.add_dependency "activerecord", ">=3.1"
|
23
|
+
gem.add_development_dependency "activerecord-nulldb-adapter"
|
24
|
+
gem.add_development_dependency "rspec"
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: when_committed
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Joshua Flanagan
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-03-06 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activerecord
|
16
|
+
prerelease: false
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '3.1'
|
22
|
+
none: false
|
23
|
+
type: :runtime
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ! '>='
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '3.1'
|
29
|
+
none: false
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: activerecord-nulldb-adapter
|
32
|
+
prerelease: false
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
none: false
|
39
|
+
type: :development
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
none: false
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
prerelease: false
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
none: false
|
55
|
+
type: :development
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ! '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
none: false
|
62
|
+
description: Run a piece of code after the current transaction is committed
|
63
|
+
email:
|
64
|
+
- jflanagan@peopleadmin.com
|
65
|
+
executables: []
|
66
|
+
extensions: []
|
67
|
+
extra_rdoc_files: []
|
68
|
+
files:
|
69
|
+
- .gitignore
|
70
|
+
- Gemfile
|
71
|
+
- LICENSE.txt
|
72
|
+
- README.md
|
73
|
+
- Rakefile
|
74
|
+
- lib/when_committed.rb
|
75
|
+
- lib/when_committed/version.rb
|
76
|
+
- spec/when_committed_spec.rb
|
77
|
+
- when_committed.gemspec
|
78
|
+
homepage: https://github.com/PeopleAdmin/when_committed
|
79
|
+
licenses: []
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ! '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
none: false
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
none: false
|
96
|
+
requirements: []
|
97
|
+
rubyforge_project:
|
98
|
+
rubygems_version: 1.8.24
|
99
|
+
signing_key:
|
100
|
+
specification_version: 3
|
101
|
+
summary: Some actions (like enqueuing a background job) should not run until the current
|
102
|
+
ActiveRecord transaction has committed. ActiveRecord defines an `#after_commit`
|
103
|
+
callback, but it run for every transaction, for every instance of a class. `#when_committed`
|
104
|
+
allows you to dynamically define a block of code that should run when the transaction
|
105
|
+
is committed.
|
106
|
+
test_files:
|
107
|
+
- spec/when_committed_spec.rb
|