buffered_job 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.
- data/.document +5 -0
- data/.rspec +2 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +99 -0
- data/LICENSE.txt +20 -0
- data/README.md +90 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/buffered_job.gemspec +80 -0
- data/lib/buffered_job/ext.rb +36 -0
- data/lib/buffered_job/main.rb +107 -0
- data/lib/buffered_job.rb +13 -0
- data/lib/generators/buffered_job/buffered_job_generator.rb +18 -0
- data/lib/generators/buffered_job/templates/migration.rb +15 -0
- data/spec/buffered_job_spec.rb +38 -0
- data/spec/database.yml +4 -0
- data/spec/spec_helper.rb +103 -0
- metadata +212 -0
data/.document
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
group :development do
|
4
|
+
gem "rspec", "~> 2.8.0"
|
5
|
+
gem "rdoc", "~> 3.12"
|
6
|
+
gem "bundler"
|
7
|
+
gem "jeweler", "~> 1.8.3"
|
8
|
+
gem "simplecov", ">= 0"
|
9
|
+
gem "sqlite3"
|
10
|
+
end
|
11
|
+
|
12
|
+
gem "activesupport"
|
13
|
+
gem "delayed_job_active_record"
|
14
|
+
gem "actionmailer"
|
15
|
+
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
actionmailer (3.1.3)
|
5
|
+
actionpack (= 3.1.3)
|
6
|
+
mail (~> 2.3.0)
|
7
|
+
actionpack (3.1.3)
|
8
|
+
activemodel (= 3.1.3)
|
9
|
+
activesupport (= 3.1.3)
|
10
|
+
builder (~> 3.0.0)
|
11
|
+
erubis (~> 2.7.0)
|
12
|
+
i18n (~> 0.6)
|
13
|
+
rack (~> 1.3.5)
|
14
|
+
rack-cache (~> 1.1)
|
15
|
+
rack-mount (~> 0.8.2)
|
16
|
+
rack-test (~> 0.6.1)
|
17
|
+
sprockets (~> 2.0.3)
|
18
|
+
activemodel (3.1.3)
|
19
|
+
activesupport (= 3.1.3)
|
20
|
+
builder (~> 3.0.0)
|
21
|
+
i18n (~> 0.6)
|
22
|
+
activerecord (3.1.3)
|
23
|
+
activemodel (= 3.1.3)
|
24
|
+
activesupport (= 3.1.3)
|
25
|
+
arel (~> 2.2.1)
|
26
|
+
tzinfo (~> 0.3.29)
|
27
|
+
activesupport (3.1.3)
|
28
|
+
multi_json (~> 1.0)
|
29
|
+
arel (2.2.3)
|
30
|
+
builder (3.0.0)
|
31
|
+
delayed_job (3.0.2)
|
32
|
+
activesupport (~> 3.0)
|
33
|
+
delayed_job_active_record (0.3.2)
|
34
|
+
activerecord (> 2.1.0)
|
35
|
+
delayed_job (~> 3.0.0)
|
36
|
+
diff-lcs (1.1.3)
|
37
|
+
erubis (2.7.0)
|
38
|
+
git (1.2.5)
|
39
|
+
hike (1.2.1)
|
40
|
+
i18n (0.6.0)
|
41
|
+
jeweler (1.8.3)
|
42
|
+
bundler (~> 1.0)
|
43
|
+
git (>= 1.2.5)
|
44
|
+
rake
|
45
|
+
rdoc
|
46
|
+
json (1.7.3)
|
47
|
+
mail (2.3.3)
|
48
|
+
i18n (>= 0.4.0)
|
49
|
+
mime-types (~> 1.16)
|
50
|
+
treetop (~> 1.4.8)
|
51
|
+
mime-types (1.18)
|
52
|
+
multi_json (1.3.6)
|
53
|
+
polyglot (0.3.3)
|
54
|
+
rack (1.3.6)
|
55
|
+
rack-cache (1.2)
|
56
|
+
rack (>= 0.4)
|
57
|
+
rack-mount (0.8.3)
|
58
|
+
rack (>= 1.0.0)
|
59
|
+
rack-test (0.6.1)
|
60
|
+
rack (>= 1.0)
|
61
|
+
rake (0.9.2.2)
|
62
|
+
rdoc (3.12)
|
63
|
+
json (~> 1.4)
|
64
|
+
rspec (2.8.0)
|
65
|
+
rspec-core (~> 2.8.0)
|
66
|
+
rspec-expectations (~> 2.8.0)
|
67
|
+
rspec-mocks (~> 2.8.0)
|
68
|
+
rspec-core (2.8.0)
|
69
|
+
rspec-expectations (2.8.0)
|
70
|
+
diff-lcs (~> 1.1.2)
|
71
|
+
rspec-mocks (2.8.0)
|
72
|
+
simplecov (0.6.4)
|
73
|
+
multi_json (~> 1.0)
|
74
|
+
simplecov-html (~> 0.5.3)
|
75
|
+
simplecov-html (0.5.3)
|
76
|
+
sprockets (2.0.4)
|
77
|
+
hike (~> 1.2)
|
78
|
+
rack (~> 1.0)
|
79
|
+
tilt (~> 1.1, != 1.3.0)
|
80
|
+
sqlite3 (1.3.6)
|
81
|
+
tilt (1.3.3)
|
82
|
+
treetop (1.4.10)
|
83
|
+
polyglot
|
84
|
+
polyglot (>= 0.3.1)
|
85
|
+
tzinfo (0.3.33)
|
86
|
+
|
87
|
+
PLATFORMS
|
88
|
+
ruby
|
89
|
+
|
90
|
+
DEPENDENCIES
|
91
|
+
actionmailer
|
92
|
+
activesupport
|
93
|
+
bundler
|
94
|
+
delayed_job_active_record
|
95
|
+
jeweler (~> 1.8.3)
|
96
|
+
rdoc (~> 3.12)
|
97
|
+
rspec (~> 2.8.0)
|
98
|
+
simplecov
|
99
|
+
sqlite3
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Masaki Sawamura
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# buffered_job
|
2
|
+
|
3
|
+
Buffering jobs for a certain period and invoke specific method if two or more similer jobs in
|
4
|
+
a buffer.
|
5
|
+
|
6
|
+
## Scenario
|
7
|
+
|
8
|
+
Supposing that you are running sort of social media that has user articles which has comments from his/her
|
9
|
+
friends. You probably want to have email notification for incomming comments on a article.
|
10
|
+
If you implement straightforward, the auther of popular article would receive tons of email for a article.
|
11
|
+
That must be bothering. To avoid sending too many mails to a receipient, you can merge multiple
|
12
|
+
notificaiton messages into one another method (in this casse that would be sending method) .
|
13
|
+
|
14
|
+
That mean, if a user gets a comment notification for her blog article as follows:
|
15
|
+
|
16
|
+
```
|
17
|
+
@article = Article.create(:user => @user,:text => "my blog article here")
|
18
|
+
c = @article.comments.create(:user => @jon, :text => "i love this article!")
|
19
|
+
@user.notify(c)
|
20
|
+
```
|
21
|
+
|
22
|
+
Then. With this module,you can buffer `notify` method
|
23
|
+
|
24
|
+
```
|
25
|
+
c1 = @article.comments.create(:user => @jon, :text => "i love this article!")
|
26
|
+
c2 = @article.comments.create(:user => @ken, :text => "i hate this article!")
|
27
|
+
|
28
|
+
@user.buffer.notify(c1)
|
29
|
+
@user.buffer.notify(c2)
|
30
|
+
# these two methods would be translated to
|
31
|
+
@user.merge_notify([c1,c2])
|
32
|
+
# then you can build other notification email with arryed comment objects
|
33
|
+
```
|
34
|
+
|
35
|
+
|
36
|
+
## Install
|
37
|
+
|
38
|
+
in Gemfile
|
39
|
+
|
40
|
+
```
|
41
|
+
gem 'buffered_job',:git => 'git://github.com/sawamur/buffered_job.git'
|
42
|
+
```
|
43
|
+
|
44
|
+
then, `bundle install`
|
45
|
+
|
46
|
+
|
47
|
+
## Peparation
|
48
|
+
|
49
|
+
```
|
50
|
+
$ (bundle exec) rails generate buffered_job
|
51
|
+
$ (bundle exec) rake db:migrate
|
52
|
+
```
|
53
|
+
|
54
|
+
|
55
|
+
## Dependancies
|
56
|
+
|
57
|
+
This module depends on delayed_job.Set up delayed_job and intended to use in rails
|
58
|
+
applications. You have to run delayed_job worker.
|
59
|
+
|
60
|
+
|
61
|
+
## Usage
|
62
|
+
|
63
|
+
```
|
64
|
+
@user.buffer.post_to_twitter(@article1)
|
65
|
+
@user.buffer.post_to_twitter(@aritcle2)
|
66
|
+
```
|
67
|
+
|
68
|
+
invoke merge_* method in User model,so you must define
|
69
|
+
|
70
|
+
```
|
71
|
+
def merge_post_to_twitter(articles)
|
72
|
+
..
|
73
|
+
end
|
74
|
+
```
|
75
|
+
|
76
|
+
|
77
|
+
## Configuration
|
78
|
+
|
79
|
+
default buffering time is tree munites. To modify,
|
80
|
+
|
81
|
+
```
|
82
|
+
BufferedJob.delay_time = 30.seconds
|
83
|
+
```
|
84
|
+
|
85
|
+
|
86
|
+
## Copyright
|
87
|
+
|
88
|
+
Copyright (c) 2012 Masaki Sawamura. See LICENSE.txt for
|
89
|
+
further details.
|
90
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "buffered_job"
|
18
|
+
gem.homepage = "http://github.com/sawamur/buffered_job"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{buffering jobs to merge similer jobs}
|
21
|
+
gem.description = %Q{Buffering jobs for a certain period and invoke specific method if two or more similer jobs in a buffer.}
|
22
|
+
gem.email = "masaki.sw@gmail.com"
|
23
|
+
gem.authors = ["Masaki Sawamura"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rspec/core'
|
29
|
+
require 'rspec/core/rake_task'
|
30
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
31
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
32
|
+
end
|
33
|
+
|
34
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
35
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
36
|
+
spec.rcov = true
|
37
|
+
end
|
38
|
+
|
39
|
+
task :default => :spec
|
40
|
+
|
41
|
+
require 'rdoc/task'
|
42
|
+
Rake::RDocTask.new do |rdoc|
|
43
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
44
|
+
|
45
|
+
rdoc.rdoc_dir = 'rdoc'
|
46
|
+
rdoc.title = "buffered_job #{version}"
|
47
|
+
rdoc.rdoc_files.include('README*')
|
48
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
49
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.0
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "buffered_job"
|
8
|
+
s.version = "0.2.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Masaki Sawamura"]
|
12
|
+
s.date = "2012-06-04"
|
13
|
+
s.description = "Buffering jobs for a certain period and invoke specific method if two or more similer jobs in a buffer."
|
14
|
+
s.email = "masaki.sw@gmail.com"
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.md"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".rspec",
|
22
|
+
"Gemfile",
|
23
|
+
"Gemfile.lock",
|
24
|
+
"LICENSE.txt",
|
25
|
+
"README.md",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"buffered_job.gemspec",
|
29
|
+
"lib/buffered_job.rb",
|
30
|
+
"lib/buffered_job/ext.rb",
|
31
|
+
"lib/buffered_job/main.rb",
|
32
|
+
"lib/generators/buffered_job/buffered_job_generator.rb",
|
33
|
+
"lib/generators/buffered_job/templates/migration.rb",
|
34
|
+
"spec/buffered_job_spec.rb",
|
35
|
+
"spec/database.yml",
|
36
|
+
"spec/spec_helper.rb"
|
37
|
+
]
|
38
|
+
s.homepage = "http://github.com/sawamur/buffered_job"
|
39
|
+
s.licenses = ["MIT"]
|
40
|
+
s.require_paths = ["lib"]
|
41
|
+
s.rubygems_version = "1.8.24"
|
42
|
+
s.summary = "buffering jobs to merge similer jobs"
|
43
|
+
|
44
|
+
if s.respond_to? :specification_version then
|
45
|
+
s.specification_version = 3
|
46
|
+
|
47
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
48
|
+
s.add_runtime_dependency(%q<activesupport>, [">= 0"])
|
49
|
+
s.add_runtime_dependency(%q<delayed_job_active_record>, [">= 0"])
|
50
|
+
s.add_runtime_dependency(%q<actionmailer>, [">= 0"])
|
51
|
+
s.add_development_dependency(%q<rspec>, ["~> 2.8.0"])
|
52
|
+
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
53
|
+
s.add_development_dependency(%q<bundler>, [">= 0"])
|
54
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
|
55
|
+
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
56
|
+
s.add_development_dependency(%q<sqlite3>, [">= 0"])
|
57
|
+
else
|
58
|
+
s.add_dependency(%q<activesupport>, [">= 0"])
|
59
|
+
s.add_dependency(%q<delayed_job_active_record>, [">= 0"])
|
60
|
+
s.add_dependency(%q<actionmailer>, [">= 0"])
|
61
|
+
s.add_dependency(%q<rspec>, ["~> 2.8.0"])
|
62
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
63
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
64
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
|
65
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
66
|
+
s.add_dependency(%q<sqlite3>, [">= 0"])
|
67
|
+
end
|
68
|
+
else
|
69
|
+
s.add_dependency(%q<activesupport>, [">= 0"])
|
70
|
+
s.add_dependency(%q<delayed_job_active_record>, [">= 0"])
|
71
|
+
s.add_dependency(%q<actionmailer>, [">= 0"])
|
72
|
+
s.add_dependency(%q<rspec>, ["~> 2.8.0"])
|
73
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
74
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
75
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
|
76
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
77
|
+
s.add_dependency(%q<sqlite3>, [">= 0"])
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
|
2
|
+
class BufferedJob
|
3
|
+
class Proxy
|
4
|
+
def initialize(receiver,user,opt={})
|
5
|
+
@receiver = receiver
|
6
|
+
@user = user
|
7
|
+
@opt = opt
|
8
|
+
end
|
9
|
+
|
10
|
+
def method_missing(method,target)
|
11
|
+
cat = @opt[:category]
|
12
|
+
cat ||= ( @receiver.class == Class ? @receiver.to_s : @receiver.class.to_s) + '#' + method.to_s
|
13
|
+
merge_method = @opt[:action] || "merge_#{method}".to_sym
|
14
|
+
BufferedJob.buf(:user_id => @user.id,
|
15
|
+
:category => cat,
|
16
|
+
:receiver => @receiver,
|
17
|
+
:method => method.to_sym,
|
18
|
+
:merge_method => merge_method,
|
19
|
+
:target => target)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module Ext
|
24
|
+
def buffer_for(user,opt={})
|
25
|
+
Proxy.new(self,user,opt)
|
26
|
+
end
|
27
|
+
|
28
|
+
def buffer(opt={})
|
29
|
+
raise NoBufferTargetError unless self.respond_to?(:id)
|
30
|
+
buffer_for(self,opt)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class NoBufferTargetError < StandardError
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
class BufferedJob < ActiveRecord::Base
|
4
|
+
after_create :set_delayed_job
|
5
|
+
DEFAULT_DELAY_TIME = 3.minutes
|
6
|
+
@@delay_time = DEFAULT_DELAY_TIME
|
7
|
+
|
8
|
+
|
9
|
+
def self.cache
|
10
|
+
@@cache ||= defined?(Rails) ? Rails.cache : ActiveSupport::Cache::MemoryStore.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.delay_time=(sec)
|
14
|
+
@@delay_time = sec
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.reset_delay_time
|
18
|
+
@@delay_time = DEFAULT_DELAY_TIME
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.buf(params)
|
22
|
+
params[:receiver] = YAML.dump params[:receiver]
|
23
|
+
params[:target] = YAML.dump params[:target]
|
24
|
+
self.create(params)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.mailer?(obj)
|
28
|
+
obj.respond_to?(:superclass) and obj.superclass == ActionMailer::Base
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.flush!
|
32
|
+
return if locked?
|
33
|
+
lock!
|
34
|
+
jobs = self.all
|
35
|
+
skip = []
|
36
|
+
@last_results = []
|
37
|
+
jobs.each do |j|
|
38
|
+
if skip[j.id]
|
39
|
+
j.destroy
|
40
|
+
next
|
41
|
+
end
|
42
|
+
cojobs = jobs.select{ |o| o.user_id = j.user_id and o.category == j.category }
|
43
|
+
receiver = YAML.load(j.receiver)
|
44
|
+
if cojobs.size > 1
|
45
|
+
begin
|
46
|
+
targets = cojobs.map{|c|
|
47
|
+
YAML.load(c.target)
|
48
|
+
}
|
49
|
+
if mailer?(receiver)
|
50
|
+
r = receiver.send(j.merge_method,targets).deliver
|
51
|
+
else
|
52
|
+
r = receiver.send(j.merge_method,targets)
|
53
|
+
end
|
54
|
+
rescue => er
|
55
|
+
$stderr.puts er.to_s
|
56
|
+
end
|
57
|
+
cojobs.each do |c|
|
58
|
+
skip[c.id] = true
|
59
|
+
end
|
60
|
+
else
|
61
|
+
begin
|
62
|
+
target = YAML.load(j.target)
|
63
|
+
if mailer?(receiver)
|
64
|
+
r = receiver.send(j.method,target).deliver
|
65
|
+
else
|
66
|
+
r = receiver.send(j.method,target)
|
67
|
+
end
|
68
|
+
rescue => er
|
69
|
+
$stderr.puts er.to_s
|
70
|
+
end
|
71
|
+
end
|
72
|
+
j.destroy
|
73
|
+
@last_results << r
|
74
|
+
end
|
75
|
+
unlock!
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.last_results
|
79
|
+
@last_results or []
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.lock!
|
83
|
+
cache.write("mail_buffer_lock",true,:expires_in => 10.minutes)
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.unlock!
|
87
|
+
cache.delete("mail_buffer_lock")
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.locked?
|
91
|
+
cache.exist?("mail_buffer_lock")
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
def set_delayed_job
|
96
|
+
delay_time = @@delay_time or DEFAULT_DELAY_TIME
|
97
|
+
self.class.delay(:run_at => delay_time.from_now).flush!
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
|
103
|
+
|
104
|
+
|
105
|
+
|
106
|
+
|
107
|
+
|
data/lib/buffered_job.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
|
2
|
+
require 'active_support'
|
3
|
+
require 'active_record'
|
4
|
+
require 'action_mailer'
|
5
|
+
|
6
|
+
require 'buffered_job/main'
|
7
|
+
require 'buffered_job/ext'
|
8
|
+
|
9
|
+
require 'delayed_job_active_record'
|
10
|
+
|
11
|
+
|
12
|
+
ActiveRecord::Base.send(:include,BufferedJob::Ext)
|
13
|
+
ActionMailer::Base.send(:extend,BufferedJob::Ext)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/migration'
|
3
|
+
|
4
|
+
|
5
|
+
class BufferedJobGenerator < Rails::Generators::Base
|
6
|
+
desc "This generator creates migration file for buffered_job"
|
7
|
+
include Rails::Generators::Migration
|
8
|
+
|
9
|
+
self.source_paths << File.join(File.dirname(__FILE__), 'templates')
|
10
|
+
|
11
|
+
def self.next_migration_number(path)
|
12
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
13
|
+
end
|
14
|
+
|
15
|
+
def create_migrateion
|
16
|
+
migration_template 'migration.rb', 'db/migrate/create_buffered_job.rb'
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
class CreateBufferedJob < ActiveRecord::Migration
|
4
|
+
def change
|
5
|
+
create_table :buffered_jobs do |t|
|
6
|
+
t.integer :user_id
|
7
|
+
t.string :category
|
8
|
+
t.string :receiver
|
9
|
+
t.string :method
|
10
|
+
t.string :merge_method
|
11
|
+
t.string :target
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "BufferedJob" do
|
4
|
+
before do
|
5
|
+
@john = User.create(:name => "john")
|
6
|
+
@paul = User.create(:name => "paul")
|
7
|
+
@george = User.create(:name => "george")
|
8
|
+
@article = Article.create(:user => @john,:text => "foo")
|
9
|
+
BufferedJob::Spec.result = []
|
10
|
+
end
|
11
|
+
|
12
|
+
after do
|
13
|
+
BufferedJob.destroy_all
|
14
|
+
end
|
15
|
+
|
16
|
+
it "can buffer jobs" do
|
17
|
+
comment = @article.comments.create(:user => @paul,:text => "I love this!")
|
18
|
+
@john.buffer.notify(comment)
|
19
|
+
BufferedJob.last.user_id.should == @john.id
|
20
|
+
BufferedJob.last.receiver.should == YAML.dump(@john)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "can exec one job " do
|
24
|
+
comment = @article.comments.create(:user => @paul,:text => "I love this!")
|
25
|
+
@john.buffer.notify(comment)
|
26
|
+
BufferedJob.flush!
|
27
|
+
BufferedJob::Spec.result.should == comment
|
28
|
+
end
|
29
|
+
|
30
|
+
it "can exec merge method with a array of target objects " do
|
31
|
+
c1 = @article.comments.create(:user => @paul,:text => "I love this!")
|
32
|
+
@john.buffer.notify(c1)
|
33
|
+
c2 = @article.comments.create(:user => @george,:text => "I hate this!")
|
34
|
+
@john.buffer.notify(c2)
|
35
|
+
BufferedJob.flush!
|
36
|
+
BufferedJob::Spec.result.should == [c1,c2]
|
37
|
+
end
|
38
|
+
end
|
data/spec/database.yml
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
require 'rspec'
|
4
|
+
require 'buffered_job'
|
5
|
+
|
6
|
+
# Requires supporting files with custom matchers and macros, etc,
|
7
|
+
# in ./support/ and its subdirectories.
|
8
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
|
+
|
10
|
+
ENV['RAILS_ENV'] = 'test'
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
config = YAML.load(File.read('spec/database.yml'))
|
17
|
+
ActiveRecord::Base.establish_connection config['sqlite']
|
18
|
+
ActiveRecord::Base.logger = Delayed::Worker.logger
|
19
|
+
ActiveRecord::Migration.verbose = false
|
20
|
+
|
21
|
+
ActiveRecord::Schema.define do
|
22
|
+
create_table :buffered_jobs do |t|
|
23
|
+
t.integer :user_id
|
24
|
+
t.string :category
|
25
|
+
t.string :receiver
|
26
|
+
t.string :method
|
27
|
+
t.string :merge_method
|
28
|
+
t.string :target
|
29
|
+
t.timestamps
|
30
|
+
end
|
31
|
+
|
32
|
+
create_table :delayed_jobs do |table|
|
33
|
+
table.integer :priority, :default => 0
|
34
|
+
table.integer :attempts, :default => 0
|
35
|
+
table.text :handler
|
36
|
+
table.text :last_error
|
37
|
+
table.datetime :run_at
|
38
|
+
table.datetime :locked_at
|
39
|
+
table.datetime :failed_at
|
40
|
+
table.string :locked_by
|
41
|
+
table.string :queue
|
42
|
+
table.timestamps
|
43
|
+
end
|
44
|
+
add_index :delayed_jobs, [:priority, :run_at], :name => 'delayed_jobs_priority'
|
45
|
+
|
46
|
+
|
47
|
+
create_table :users do |t|
|
48
|
+
t.string :name
|
49
|
+
end
|
50
|
+
|
51
|
+
create_table :articles do |t|
|
52
|
+
t.integer :user_id
|
53
|
+
t.string :text
|
54
|
+
end
|
55
|
+
|
56
|
+
create_table :comments do |t|
|
57
|
+
t.integer :user_id
|
58
|
+
t.integer :article_id
|
59
|
+
t.string :text
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
class BufferedJob::Spec
|
65
|
+
@@result = []
|
66
|
+
|
67
|
+
def self.result=(args)
|
68
|
+
@@result = args
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.result
|
72
|
+
@@result
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
class User < ActiveRecord::Base
|
79
|
+
has_many :articles
|
80
|
+
attr_accessor :posted
|
81
|
+
# include BufferedJob::Ext
|
82
|
+
|
83
|
+
def notify(comment)
|
84
|
+
BufferedJob::Spec.result = comment
|
85
|
+
end
|
86
|
+
|
87
|
+
def merge_notify(comments)
|
88
|
+
BufferedJob::Spec.result = comments
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class Article < ActiveRecord::Base
|
93
|
+
belongs_to :user
|
94
|
+
has_many :comments
|
95
|
+
end
|
96
|
+
|
97
|
+
class Comment < ActiveRecord::Base
|
98
|
+
belongs_to :user
|
99
|
+
belongs_to :article
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
|
metadata
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: buffered_job
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Masaki Sawamura
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-06-04 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activesupport
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: delayed_job_active_record
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: actionmailer
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rspec
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 2.8.0
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 2.8.0
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: rdoc
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '3.12'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '3.12'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: bundler
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: jeweler
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 1.8.3
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ~>
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 1.8.3
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: simplecov
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: sqlite3
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ! '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
type: :development
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
description: Buffering jobs for a certain period and invoke specific method if two
|
159
|
+
or more similer jobs in a buffer.
|
160
|
+
email: masaki.sw@gmail.com
|
161
|
+
executables: []
|
162
|
+
extensions: []
|
163
|
+
extra_rdoc_files:
|
164
|
+
- LICENSE.txt
|
165
|
+
- README.md
|
166
|
+
files:
|
167
|
+
- .document
|
168
|
+
- .rspec
|
169
|
+
- Gemfile
|
170
|
+
- Gemfile.lock
|
171
|
+
- LICENSE.txt
|
172
|
+
- README.md
|
173
|
+
- Rakefile
|
174
|
+
- VERSION
|
175
|
+
- buffered_job.gemspec
|
176
|
+
- lib/buffered_job.rb
|
177
|
+
- lib/buffered_job/ext.rb
|
178
|
+
- lib/buffered_job/main.rb
|
179
|
+
- lib/generators/buffered_job/buffered_job_generator.rb
|
180
|
+
- lib/generators/buffered_job/templates/migration.rb
|
181
|
+
- spec/buffered_job_spec.rb
|
182
|
+
- spec/database.yml
|
183
|
+
- spec/spec_helper.rb
|
184
|
+
homepage: http://github.com/sawamur/buffered_job
|
185
|
+
licenses:
|
186
|
+
- MIT
|
187
|
+
post_install_message:
|
188
|
+
rdoc_options: []
|
189
|
+
require_paths:
|
190
|
+
- lib
|
191
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
192
|
+
none: false
|
193
|
+
requirements:
|
194
|
+
- - ! '>='
|
195
|
+
- !ruby/object:Gem::Version
|
196
|
+
version: '0'
|
197
|
+
segments:
|
198
|
+
- 0
|
199
|
+
hash: -4598416312079143167
|
200
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
201
|
+
none: false
|
202
|
+
requirements:
|
203
|
+
- - ! '>='
|
204
|
+
- !ruby/object:Gem::Version
|
205
|
+
version: '0'
|
206
|
+
requirements: []
|
207
|
+
rubyforge_project:
|
208
|
+
rubygems_version: 1.8.24
|
209
|
+
signing_key:
|
210
|
+
specification_version: 3
|
211
|
+
summary: buffering jobs to merge similer jobs
|
212
|
+
test_files: []
|