sucker_punch-backgroundable 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MGYyZWEyZWM0ZWJhMTFlNmI5MTFmZjRjOWQxYzk5NmY5ZDc2NTMwYQ==
5
+ data.tar.gz: !binary |-
6
+ YjgyMzgyYTZlNzAzMWQwZTgzZjgwNTc4ZjRlMGJkNzcyODFjZTkwZA==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ NTAyYzMxZDQxYmFkMzk2NzMwODFiYmI5NmMxYWZlYmU0OWVlNDZmOTQ5Njcz
10
+ MDkxZmFjOThjNjNjMDMwNmUyOWU1N2Y5YTY2Mzk0ZTMyZGY3YWI4ODJmMjM2
11
+ ZDVmNDk1NzBlMzliNzkxZGRkOTQwMmY1YThiOWI5ODBjMjZiYzU=
12
+ data.tar.gz: !binary |-
13
+ ZTY0M2Q3MGMxMTJkZTY4ODQ3ZDc4N2M2NjE0YzNkZGQ4YTcyYzZlNjU3NTRm
14
+ MjdiOTBhMGE5ZDNlMDhkY2M4ZjYwMWEzYTliYzU5ZGMwYmNjODEwNzdkY2Uy
15
+ NmFiNzAyMGE2ZDRiNmNiNTE3NWZiNjZlOWUxZjQ2MDg3MzIzOWY=
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format documentation
data/.travis ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
6
+ - jruby-19mode
7
+ - rbx
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## 0.0.1 (19/5/2014)
2
+
3
+ * First Release
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+ gem "sucker_punch", ">= 1.0.0"
6
+
7
+ # Add dependencies to develop your gem here.
8
+ # Include everything needed to run rake, tests, features, etc.
9
+ group :development do
10
+ gem "rspec", "~> 2.8.0"
11
+ gem "rdoc", "~> 3.12"
12
+ gem "bundler", "~> 1.0"
13
+ gem "jeweler", "~> 2.0.1"
14
+ gem "simplecov", ">= 0"
15
+ gem "activerecord"
16
+ gem "sqlite3"
17
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,101 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activemodel (4.1.0)
5
+ activesupport (= 4.1.0)
6
+ builder (~> 3.1)
7
+ activerecord (4.1.0)
8
+ activemodel (= 4.1.0)
9
+ activesupport (= 4.1.0)
10
+ arel (~> 5.0.0)
11
+ activesupport (4.1.0)
12
+ i18n (~> 0.6, >= 0.6.9)
13
+ json (~> 1.7, >= 1.7.7)
14
+ minitest (~> 5.1)
15
+ thread_safe (~> 0.1)
16
+ tzinfo (~> 1.1)
17
+ addressable (2.3.6)
18
+ arel (5.0.1.20140414130214)
19
+ builder (3.2.2)
20
+ celluloid (0.15.2)
21
+ timers (~> 1.1.0)
22
+ descendants_tracker (0.0.4)
23
+ thread_safe (~> 0.3, >= 0.3.1)
24
+ diff-lcs (1.1.3)
25
+ docile (1.1.3)
26
+ faraday (0.9.0)
27
+ multipart-post (>= 1.2, < 3)
28
+ git (1.2.6)
29
+ github_api (0.11.3)
30
+ addressable (~> 2.3)
31
+ descendants_tracker (~> 0.0.1)
32
+ faraday (~> 0.8, < 0.10)
33
+ hashie (>= 1.2)
34
+ multi_json (>= 1.7.5, < 2.0)
35
+ nokogiri (~> 1.6.0)
36
+ oauth2
37
+ hashie (2.1.1)
38
+ highline (1.6.21)
39
+ i18n (0.6.9)
40
+ jeweler (2.0.1)
41
+ builder
42
+ bundler (>= 1.0)
43
+ git (>= 1.2.5)
44
+ github_api
45
+ highline (>= 1.6.15)
46
+ nokogiri (>= 1.5.10)
47
+ rake
48
+ rdoc
49
+ json (1.8.1)
50
+ jwt (0.1.13)
51
+ multi_json (>= 1.5)
52
+ mini_portile (0.6.0)
53
+ minitest (5.3.4)
54
+ multi_json (1.10.0)
55
+ multi_xml (0.5.5)
56
+ multipart-post (2.0.0)
57
+ nokogiri (1.6.2.1)
58
+ mini_portile (= 0.6.0)
59
+ oauth2 (0.9.3)
60
+ faraday (>= 0.8, < 0.10)
61
+ jwt (~> 0.1.8)
62
+ multi_json (~> 1.3)
63
+ multi_xml (~> 0.5)
64
+ rack (~> 1.2)
65
+ rack (1.5.2)
66
+ rake (10.3.2)
67
+ rdoc (3.12.2)
68
+ json (~> 1.4)
69
+ rspec (2.8.0)
70
+ rspec-core (~> 2.8.0)
71
+ rspec-expectations (~> 2.8.0)
72
+ rspec-mocks (~> 2.8.0)
73
+ rspec-core (2.8.0)
74
+ rspec-expectations (2.8.0)
75
+ diff-lcs (~> 1.1.2)
76
+ rspec-mocks (2.8.0)
77
+ simplecov (0.8.2)
78
+ docile (~> 1.1.0)
79
+ multi_json
80
+ simplecov-html (~> 0.8.0)
81
+ simplecov-html (0.8.0)
82
+ sqlite3 (1.3.9)
83
+ sucker_punch (1.0.5)
84
+ celluloid (~> 0.15.2)
85
+ thread_safe (0.3.3)
86
+ timers (1.1.0)
87
+ tzinfo (1.1.0)
88
+ thread_safe (~> 0.1)
89
+
90
+ PLATFORMS
91
+ ruby
92
+
93
+ DEPENDENCIES
94
+ activerecord
95
+ bundler (~> 1.0)
96
+ jeweler (~> 2.0.1)
97
+ rdoc (~> 3.12)
98
+ rspec (~> 2.8.0)
99
+ simplecov
100
+ sqlite3
101
+ sucker_punch (>= 1.0.0)
data/LICENSE.txt ADDED
@@ -0,0 +1,8 @@
1
+ Copyright (c) 2014 Michaël Van Damme
2
+
3
+ sucker_punch-backgroundable is an Open Source project licensed under the terms of
4
+ the LGPLv3 license. Please see <http://www.gnu.org/licenses/lgpl-3.0.html>
5
+ for the license text.
6
+
7
+ Parts of sucker_punch-backgroundable were taken from the TorqueBox project, which is
8
+ also licensed under LGPLv3 (see <http://torquebox.org/license/>).
data/README.md ADDED
@@ -0,0 +1,150 @@
1
+ # sucker_punch-backgroundable
2
+ [![Build Status](https://travis-ci.org/mvdamme/sucker_punch-backgroundable.png)](https://travis-ci.org/mvdamme/sucker_punch-backgroundable)
3
+
4
+ This gem allows you to background any method call with [SuckerPunch](https://github.com/brandonhilkert/sucker_punch) without
5
+ having to write a special job class.
6
+ It provides the same API as the Backgroundable module from [TorqueBox](http://torquebox.org/), and a large part of the code
7
+ comes directly from the TorqueBox project.
8
+
9
+ ## Installation
10
+
11
+ Add the following to your Gemfile:
12
+
13
+ ```ruby
14
+ gem 'sucker_punch-backgroundable'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ ```ruby
20
+ bundle install
21
+ ```
22
+
23
+ ## Usage
24
+
25
+ Include the `SuckerPunch::Backgroundable` module in your class. Then you can use `always_background :method1, :method2, ...` to
26
+ cause the supplied methods to run asynchronously (in the background). Example:
27
+
28
+ ```ruby
29
+ class MyClass
30
+ SuckerPunch::Backgroundable
31
+
32
+ always_background :send_email
33
+ def send_email
34
+ # ...
35
+ end
36
+
37
+ always_background :calculate_statistics
38
+ def self.calculate_statistics
39
+ # ...
40
+ end
41
+ end
42
+ ```
43
+
44
+ In this example, calls to instance method `send_email` and class method `calculate_statistics` will automatically run in the background:
45
+
46
+ ```ruby
47
+ obj = MyClass.new
48
+ obj.send_email # returns immediately, method runs in the background
49
+
50
+ Myclass.calculate_statistics # returns immediately, method runs in the background
51
+ ```
52
+
53
+ Methods that have not been marked with `always_background` can also be backgrounded when you call them:
54
+
55
+ ```ruby
56
+ class MyClass
57
+ SuckerPunch::Backgroundable
58
+
59
+ def notify
60
+ # ...
61
+ end
62
+ end
63
+
64
+ obj = MyClass.new
65
+
66
+ # This will run in the background (and return immediately)
67
+ obj.background.notify
68
+
69
+ # This will run the method normally (synchronously, returning after the method is finished)
70
+ obj.background.notify
71
+ ```
72
+
73
+ It is also possible to specify a delay in seconds:
74
+
75
+ ```ruby
76
+ # This will return immediately and run the method in the background after a delay of 60 seconds
77
+ obj.later(60).notify
78
+ ```
79
+
80
+ Both `background` and `later` also work with class methods (e.g. `MyClass.background.my_class_method(argument)`).
81
+
82
+ ### Reloading
83
+
84
+ When backgrounding an instance method, the method is called on the object, but in a different thread (using SuckerPunch).
85
+ If you don't use the object anymore in the current thread, this is ok. If the object is still being used in the current thread,
86
+ it may be better to reload it from the data store (assuming a data store backed object) in the background thread to avoid any
87
+ threading issues. The gem can do this automatically, although currently only ActiveRecord is supported:
88
+
89
+ ```ruby
90
+ class MyModel < ActiveRecord::Base
91
+ SuckerPunch::Backgroundable
92
+
93
+ always_background :send_email, :reload => true
94
+ def send_email
95
+ # ...
96
+ end
97
+ end
98
+ ```
99
+
100
+ Or, when using `background` or `later`:
101
+
102
+ ```ruby
103
+ obj.background(:reload => true).my_instance_method
104
+ obj.later(60, :reload => true).my_instance_method
105
+ ```
106
+
107
+ It is also possible to specify the `reload` option globally by using an initializer:
108
+
109
+ ```ruby
110
+ SuckerPunch::Backgroundable.configure do |config|
111
+ config.reload = true
112
+ end
113
+ ```
114
+
115
+ ## Configuration
116
+
117
+ Apart from the `reload` option described above, there is only one other configuration setting:
118
+
119
+ ```ruby
120
+ SuckerPunch::Backgroundable.configure do |config|
121
+ config.reload = true
122
+ config.workers = 4 # default is 2
123
+ end
124
+ ```
125
+
126
+ The number of workers sets the number of background threads that SuckerPunch will use. The default (and minimum) is 2.
127
+
128
+ ## Usage with ActiveRecord
129
+
130
+ When using this gem with ActiveRecord models, it is recommended to set the `reload` option to true (see above).
131
+ If you want sucker_punch-backgroundable to be available in all models you can use the following in an initializer:
132
+
133
+ ```ruby
134
+ ActiveRecord::Base.send(:include, SuckerPunch::Backgroundable)
135
+ ```
136
+
137
+ ## Contributing to sucker_punch-backgroundable
138
+
139
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
140
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
141
+ * Fork the project.
142
+ * Start a feature/bugfix branch.
143
+ * Commit and push until you are happy with your contribution.
144
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
145
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
146
+
147
+ ## Copyright
148
+
149
+ Copyright (c) 2014 Michaël Van Damme. See LICENSE.txt for
150
+ further details.
data/Rakefile ADDED
@@ -0,0 +1,50 @@
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://guides.rubygems.org/specification-reference/ for more options
17
+ gem.name = "sucker_punch-backgroundable"
18
+ gem.homepage = "http://github.com/mvdamme/sucker_punch-backgroundable"
19
+ gem.license = "LGPL-3"
20
+ gem.summary = %Q{Easily execute an object's methods in the background with sucker_punch}
21
+ gem.description = %Q{This gem allows you to background any method call without having to write a special job class. Heavily inspired by the Backgroundable module in TorqueBox.}
22
+ gem.email = "michael.vandamme@vub.ac.be"
23
+ gem.authors = ["Michaël Van Damme"]
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
+ desc "Code coverage detail"
35
+ task :simplecov do
36
+ ENV['COVERAGE'] = "true"
37
+ Rake::Task['spec'].execute
38
+ end
39
+
40
+ task :default => :spec
41
+
42
+ require 'rdoc/task'
43
+ Rake::RDocTask.new do |rdoc|
44
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
45
+
46
+ rdoc.rdoc_dir = 'rdoc'
47
+ rdoc.title = "sucker_punch-backgroundable #{version}"
48
+ rdoc.rdoc_files.include('README*')
49
+ rdoc.rdoc_files.include('lib/**/*.rb')
50
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,155 @@
1
+ # The majority of this file (> 95%) was copied from the Backgroundable module in Torquebox
2
+ # (see https://github.com/torquebox/torquebox/blob/master/gems/messaging/lib/torquebox/messaging/backgroundable.rb).
3
+ # The code was slightly adapted so it works with Sucker Punch instead of Torquebox messaging.
4
+
5
+ module SuckerPunch
6
+
7
+ # Backgroundable provides mechanism for executing an object's
8
+ # methods asynchronously.
9
+ module Backgroundable
10
+
11
+ def self.included(base)
12
+ base.extend(BackgroundableClassMethods)
13
+ end
14
+
15
+ # Allows you to background any method that has not been marked
16
+ # as a backgrounded method via {BackgroundableClassMethods#always_background}.
17
+ def background(options = { })
18
+ BackgroundProxy.new(self, options)
19
+ end
20
+
21
+ # Allows you to background any method that has not been marked
22
+ # as a backgrounded method via {BackgroundableClassMethods#always_background}.
23
+ # The method will not be executed immediately, but only after 'seconds' seconds.
24
+ def later(seconds, options = { })
25
+ BackgroundProxy.new(self, options, seconds)
26
+ end
27
+
28
+ module BackgroundableClassMethods
29
+
30
+ # Marks methods to always be backgrounded. Takes one or more
31
+ # method symbols, and an optional options hash as the final
32
+ # argument.
33
+ def always_background(*methods)
34
+ options = methods.last.is_a?(Hash) ? methods.pop : {}
35
+ @__backgroundable_methods ||= {}
36
+
37
+ methods.each do |method|
38
+ method = method.to_s
39
+ if !@__backgroundable_methods[method]
40
+ @__backgroundable_methods[method] ||= { }
41
+ @__backgroundable_methods[method][:options] = options
42
+ if Util.singleton_methods_include?(self, method) ||
43
+ Util.instance_methods_include?(self, method)
44
+ __enable_backgrounding(method)
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ # Allows you to background any method that has not been marked
51
+ # as a backgrounded method via {BackgroundableClassMethods#always_background}.
52
+ def background(options = { })
53
+ BackgroundProxy.new(self, options)
54
+ end
55
+
56
+ # Allows you to background any method that has not been marked
57
+ # as a backgrounded method via {BackgroundableClassMethods#always_background}.
58
+ # The method will not be executed immediately, but only after 'seconds' seconds.
59
+ def later(seconds, options = { })
60
+ BackgroundProxy.new(self, options, seconds)
61
+ end
62
+
63
+ # @api private
64
+ def method_added(method)
65
+ super
66
+ __method_added(method)
67
+ end
68
+
69
+ # @api private
70
+ def singleton_method_added(method)
71
+ super
72
+ __method_added(method)
73
+ end
74
+
75
+ private
76
+
77
+ def __method_added(method)
78
+ method = method.to_s
79
+ if @__backgroundable_methods &&
80
+ @__backgroundable_methods[method] &&
81
+ !@__backgroundable_methods[method][:backgrounding]
82
+ __enable_backgrounding(method)
83
+ end
84
+ end
85
+
86
+ def __enable_backgrounding(method)
87
+ singleton_method = Util.singleton_methods_include?(self, method)
88
+ singleton = (class << self; self; end)
89
+
90
+ if singleton_method
91
+
92
+ SuckerPunch.logger.
93
+ warn("always_background called for :#{method}, but :#{method} " +
94
+ "exists as both a class and instance method. Only the " +
95
+ "class method will be backgrounded.") if Util.instance_methods_include?(self, method)
96
+
97
+ privatize = Util.private_singleton_methods_include?(self, method)
98
+ protect = Util.protected_singleton_methods_include?(self, method) unless privatize
99
+ else
100
+ privatize = Util.private_instance_methods_include?(self, method)
101
+ protect = Util.protected_instance_methods_include?(self, method) unless privatize
102
+ end
103
+
104
+ async_method = "__async_#{method}"
105
+ sync_method = "__sync_#{method}"
106
+
107
+ @__backgroundable_methods[method][:backgrounding] = true
108
+ options = @__backgroundable_methods[method][:options]
109
+
110
+ (singleton_method ? singleton : self).class_eval do
111
+ define_method async_method do |*args|
112
+ # run sucker punch job asynchronously
113
+ Job.new.async.perform(self, sync_method, args, options)
114
+ end
115
+ end
116
+
117
+ code = singleton_method ? "class << self" : ""
118
+ code << %Q{
119
+ alias_method :#{sync_method}, :#{method}
120
+ alias_method :#{method}, :#{async_method}
121
+ }
122
+ code << %Q{
123
+ #{privatize ? "private" : "protected"} :#{method}, :#{sync_method}, :#{async_method}
124
+ } if privatize || protect
125
+ code << "end" if singleton_method
126
+
127
+ class_eval code
128
+ ensure
129
+ @__backgroundable_methods[method][:backgrounding] = nil
130
+ end
131
+
132
+ end
133
+
134
+ # @api private
135
+ class BackgroundProxy
136
+ def initialize(receiver, options, seconds = 0)
137
+ @receiver = receiver
138
+ @options = options
139
+ @seconds = seconds
140
+ end
141
+
142
+ def method_missing(method, *args, &block)
143
+ @receiver.method_missing(method, *args, &block) unless @receiver.respond_to?(method)
144
+ raise ArgumentError.new("Backgrounding a method with a block argument is not supported.") if block_given?
145
+ if @seconds > 0
146
+ Job.new.async.later(@seconds, @receiver, method, args, @options)
147
+ else
148
+ Job.new.async.perform(@receiver, method, args, @options)
149
+ end
150
+ end
151
+ end
152
+
153
+ end
154
+
155
+ end
@@ -0,0 +1,26 @@
1
+ # configuration, see http://robots.thoughtbot.com/mygem-configure-block.
2
+ module SuckerPunch
3
+ module Backgroundable
4
+ class << self
5
+ attr_accessor :configuration
6
+ end
7
+
8
+ def self.configure
9
+ self.configuration ||= Configuration.new
10
+ yield(configuration)
11
+ end
12
+
13
+ class Configuration
14
+ attr_accessor :workers
15
+ attr_accessor :reload
16
+
17
+ def initialize
18
+ @workers = 2
19
+ @reload = false
20
+ end
21
+ end
22
+
23
+ end
24
+ end
25
+
26
+ SuckerPunch::Backgroundable.configure {}
@@ -0,0 +1,36 @@
1
+ module SuckerPunch
2
+ module Backgroundable
3
+
4
+ class Job
5
+ include SuckerPunch::Job
6
+ workers SuckerPunch::Backgroundable.configuration.workers
7
+
8
+ def perform(receiver, method, args, options)
9
+ receiver = load(receiver) if instantiate?(options)
10
+ if defined?(ActiveRecord)
11
+ ActiveRecord::Base.connection_pool.with_connection do
12
+ receiver.send(method, *args)
13
+ end
14
+ else
15
+ receiver.send(method, *args)
16
+ end
17
+ end
18
+
19
+ def later(sec, *args)
20
+ after(sec) { perform(*args) }
21
+ end
22
+
23
+ private
24
+
25
+ def instantiate?(options)
26
+ return true if SuckerPunch::Backgroundable.configuration.reload && !(!options[:reload].nil? && options[:reload] == false)
27
+ options[:reload]
28
+ end
29
+
30
+ def load(receiver)
31
+ receiver.respond_to?(:id) ? receiver.class.find(receiver.id) : receiver
32
+ end
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,47 @@
1
+ # This code was copied from the Backgroundable module in Torquebox
2
+ # (see https://github.com/torquebox/torquebox/blob/master/gems/messaging/lib/torquebox/messaging/backgroundable.rb).
3
+
4
+ module SuckerPunch
5
+
6
+ module Backgroundable
7
+
8
+ module Util
9
+
10
+ class << self
11
+ def singleton_methods_include?(klass, method)
12
+ methods_include?(klass.singleton_methods, method) ||
13
+ private_singleton_methods_include?(klass, method)
14
+ end
15
+
16
+ def private_singleton_methods_include?(klass, method)
17
+ methods_include?(klass.private_methods, method)
18
+ end
19
+
20
+ def protected_singleton_methods_include?(klass, method)
21
+ methods_include?(klass.protected_methods, method)
22
+ end
23
+
24
+ def instance_methods_include?(klass, method)
25
+ methods_include?(klass.instance_methods, method) ||
26
+ private_instance_methods_include?(klass, method)
27
+ end
28
+
29
+ def private_instance_methods_include?(klass, method)
30
+ methods_include?(klass.private_instance_methods, method)
31
+ end
32
+
33
+ def protected_instance_methods_include?(klass, method)
34
+ methods_include?(klass.protected_instance_methods, method)
35
+ end
36
+
37
+ def methods_include?(methods, method)
38
+ method = (RUBY_VERSION =~ /^1\.8\./ ? method.to_s : method.to_sym)
39
+ methods.include?(method)
40
+ end
41
+ end
42
+
43
+ end
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,6 @@
1
+ require 'sucker_punch'
2
+ require 'sucker_punch/backgroundable/config'
3
+ require 'sucker_punch/backgroundable/util'
4
+ require 'sucker_punch/backgroundable/job'
5
+ require 'sucker_punch/backgroundable/backgroundable'
6
+
@@ -0,0 +1,32 @@
1
+ # Code taken from http://iain.nl/testing-activerecord-in-isolation
2
+
3
+ require 'active_record'
4
+
5
+ db_file = './spec/test.db'
6
+ File.delete(db_file) if File.exist?(db_file)
7
+ ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: db_file
8
+
9
+ ActiveRecord::Migration.create_table :test_models do |t|
10
+ t.integer :value
11
+ t.timestamps
12
+ end
13
+
14
+ module ActiveModel::Validations
15
+ # Extension to enhance `should have` on AR Model instances. Calls
16
+ # model.valid? in order to prepare the object's errors object.
17
+ #
18
+ # You can also use this to specify the content of the error messages.
19
+ #
20
+ # @example
21
+ #
22
+ # model.should have(:no).errors_on(:attribute)
23
+ # model.should have(1).error_on(:attribute)
24
+ # model.should have(n).errors_on(:attribute)
25
+ #
26
+ # model.errors_on(:attribute).should include("can't be blank")
27
+ def errors_on(attribute)
28
+ self.valid?
29
+ [self.errors[attribute]].flatten.compact
30
+ end
31
+ alias :error_on :errors_on
32
+ end
@@ -0,0 +1,29 @@
1
+ require 'simplecov'
2
+
3
+ module SimpleCov::Configuration
4
+ def clean_filters
5
+ @filters = []
6
+ end
7
+ end
8
+
9
+ SimpleCov.configure do
10
+ clean_filters
11
+ load_profile 'test_frameworks'
12
+ end
13
+
14
+ ENV["COVERAGE"] && SimpleCov.start do
15
+ add_filter "/.rvm/"
16
+ end
17
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
18
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
19
+
20
+ require 'rspec'
21
+ require 'sucker_punch-backgroundable'
22
+
23
+ # Requires supporting files with custom matchers and macros, etc,
24
+ # in ./support/ and its subdirectories.
25
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
26
+
27
+ RSpec.configure do |config|
28
+
29
+ end
@@ -0,0 +1,158 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/test_class')
3
+
4
+ describe "sucker_punch-backgroundable" do
5
+ before :each do
6
+ @obj = TestClass.new
7
+ TestClass.clear
8
+ end
9
+
10
+ context 'instance methods' do
11
+ it "backgrounds instance methods declared after :always_background" do
12
+ @obj.always1
13
+ check_queue(1)
14
+ end
15
+
16
+ it "backgrounds instance methods declared before :always_background" do
17
+ @obj.always2(100)
18
+ check_queue(100)
19
+ end
20
+
21
+ it "allows backgrounding of instance methods not declared as :always_background" do
22
+ @obj.background.normal(200)
23
+ check_queue(200)
24
+ end
25
+
26
+ it "allows running instance methods with a specified delay" do
27
+ @obj.later(2).normal(200) # run after 2 seconds
28
+ check_queue(200, 2.2)
29
+ end
30
+
31
+ it "allows running instance methods with a specified delay, even when they are declared as :always_bacground" do
32
+ @obj.later(2).always1 # run after 2 seconds
33
+ check_queue(1, 2.2)
34
+ end
35
+ end
36
+
37
+ context 'class methods' do
38
+ it "backgrounds class methods declared after :always_background" do
39
+ TestClass.class_always1
40
+ check_queue(1)
41
+ end
42
+
43
+ it "backgrounds class methods declared before :always_background" do
44
+ TestClass.class_always2(100)
45
+ check_queue(100)
46
+ end
47
+
48
+ it "allows backgrounding of class methods not declared as :always_background" do
49
+ TestClass.background.class_normal(200)
50
+ check_queue(200)
51
+ end
52
+
53
+ it "allows running class methods with a specified delay" do
54
+ TestClass.later(2).class_normal(200) # run after 2 seconds
55
+ check_queue(200, 2.2)
56
+ end
57
+
58
+ it "allows running class methods with a specified delay, even when they are declared as :always_bacground" do
59
+ TestClass.later(2).class_always1 # run after 2 seconds
60
+ check_queue(1, 2.2)
61
+ end
62
+ end
63
+
64
+ context 'singleton methods' do
65
+ before :each do
66
+ def @obj.my_singleton(value)
67
+ add_to_queue(value + 1)
68
+ end
69
+ end
70
+
71
+ it "allows backgrounding of singleton methods" do
72
+ @obj.background.my_singleton(200)
73
+ check_queue(201)
74
+ end
75
+
76
+ it "allows running singleton methods with a specified delay" do
77
+ @obj.later(2).my_singleton(200) # run after 2 seconds
78
+ check_queue(201, 2.2)
79
+ end
80
+ end
81
+
82
+ def check_queue(value, wait_before = 0.2, wait_after = 0.3)
83
+ sleep( wait_before )
84
+ TestClass.queue.length.should eq(0)
85
+ sleep( wait_after )
86
+ TestClass.queue.length.should eq(1)
87
+ TestClass.queue.pop.should eq(value)
88
+ end
89
+
90
+ context 'reloading' do
91
+ before do
92
+ require File.expand_path(File.dirname(__FILE__) + '/load_active_record')
93
+ require File.expand_path(File.dirname(__FILE__) + '/test_model')
94
+ end
95
+
96
+ before :each do
97
+ @model = TestModel.create(:value => 5)
98
+ end
99
+
100
+ after :each do
101
+ TestModel.delete_all
102
+ end
103
+
104
+ it "fully reloads objects when :reload option is true" do
105
+ @model.later(0.4, :reload => true).copy_value
106
+ @model.value += 1 # change object
107
+ sleep(0.5)
108
+ TestModel.queue.pop.should eq(5)
109
+ end
110
+
111
+ it "doesn't reload objects when :reload option is false" do
112
+ @model.later(0.4).copy_value # :reload defaults to false
113
+ @model.value += 1 # change object
114
+ sleep(0.5)
115
+ TestModel.queue.pop.should eq(6)
116
+ end
117
+
118
+ it "respects :reload => true as option to :always_background" do
119
+ @model.copy_value_in_background
120
+ @model.value += 1 # change object
121
+ sleep(0.5)
122
+ TestModel.queue.pop.should eq(5)
123
+ end
124
+
125
+ context 'global reload option' do
126
+ it "honors when global reload is set to true" do
127
+ SuckerPunch::Backgroundable.configure do |config|
128
+ config.reload = true
129
+ end
130
+ @model.later(0.4).copy_value
131
+ @model.value += 1 # change object
132
+ sleep(0.5)
133
+ TestModel.queue.pop.should eq(5)
134
+ end
135
+
136
+ it "honors :reload => false even when when global reload is set to true" do
137
+ SuckerPunch::Backgroundable.configure do |config|
138
+ config.reload = true
139
+ end
140
+ @model.later(0.4, :reload => false).copy_value
141
+ @model.value += 1 # change object
142
+ sleep(0.5)
143
+ TestModel.queue.pop.should eq(6)
144
+ end
145
+
146
+ it "ignores :reload for class methods" do
147
+ SuckerPunch::Backgroundable.configure do |config|
148
+ config.reload = true
149
+ end
150
+ TestModel.class_always
151
+ sleep(0.5)
152
+ TestModel.queue.pop.should eq(7)
153
+ end
154
+
155
+ end
156
+ end
157
+
158
+ end
@@ -0,0 +1,54 @@
1
+ class TestClass
2
+ include SuckerPunch::Backgroundable
3
+
4
+ @@queue = Queue.new
5
+
6
+ def self.queue
7
+ @@queue
8
+ end
9
+
10
+ def self.clear
11
+ @@queue.clear
12
+ end
13
+
14
+ always_background :always1
15
+ def always1
16
+ add_to_queue
17
+ end
18
+
19
+ def always2(value)
20
+ add_to_queue(value)
21
+ end
22
+ always_background :always2
23
+
24
+ def normal(value)
25
+ add_to_queue(value)
26
+ end
27
+
28
+ always_background :class_always1
29
+ def self.class_always1
30
+ self.class_add_to_queue
31
+ end
32
+
33
+ def self.class_always2(value)
34
+ self.class_add_to_queue(value)
35
+ end
36
+ always_background :class_always2
37
+
38
+ def self.class_normal(value)
39
+ self.class_add_to_queue(value)
40
+ end
41
+
42
+ private
43
+
44
+ def add_to_queue(value = 1)
45
+ sleep(0.4)
46
+ @@queue << value
47
+ end
48
+
49
+ def self.class_add_to_queue(value = 1)
50
+ sleep(0.4)
51
+ @@queue << value
52
+ end
53
+
54
+ end
@@ -0,0 +1,29 @@
1
+ class TestModel < ActiveRecord::Base
2
+ include SuckerPunch::Backgroundable
3
+
4
+ @@queue = Queue.new
5
+
6
+ def self.queue
7
+ @@queue
8
+ end
9
+
10
+ def self.clear
11
+ @@queue.clear
12
+ end
13
+
14
+ def copy_value
15
+ @@queue << value
16
+ end
17
+
18
+ always_background :class_always
19
+ def self.class_always
20
+ sleep(0.4)
21
+ @@queue << 7
22
+ end
23
+
24
+ always_background :copy_value_in_background, :reload => true
25
+ def copy_value_in_background
26
+ sleep(0.4)
27
+ @@queue << value
28
+ end
29
+ end
metadata ADDED
@@ -0,0 +1,178 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sucker_punch-backgroundable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Michaël Van Damme
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sucker_punch
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: 1.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: 1.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 2.8.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 2.8.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: rdoc
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '3.12'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '3.12'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '1.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: jeweler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: 2.0.1
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: 2.0.1
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: activerecord
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: sqlite3
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ! '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description: This gem allows you to background any method call without having to write
126
+ a special job class. Heavily inspired by the Backgroundable module in TorqueBox.
127
+ email: michael.vandamme@vub.ac.be
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files:
131
+ - LICENSE.txt
132
+ - README.md
133
+ files:
134
+ - .document
135
+ - .rspec
136
+ - .travis
137
+ - CHANGELOG.md
138
+ - Gemfile
139
+ - Gemfile.lock
140
+ - LICENSE.txt
141
+ - README.md
142
+ - Rakefile
143
+ - VERSION
144
+ - lib/sucker_punch-backgroundable.rb
145
+ - lib/sucker_punch/backgroundable/backgroundable.rb
146
+ - lib/sucker_punch/backgroundable/config.rb
147
+ - lib/sucker_punch/backgroundable/job.rb
148
+ - lib/sucker_punch/backgroundable/util.rb
149
+ - spec/load_active_record.rb
150
+ - spec/spec_helper.rb
151
+ - spec/sucker_punch-backgroundable_spec.rb
152
+ - spec/test_class.rb
153
+ - spec/test_model.rb
154
+ homepage: http://github.com/mvdamme/sucker_punch-backgroundable
155
+ licenses:
156
+ - LGPL-3
157
+ metadata: {}
158
+ post_install_message:
159
+ rdoc_options: []
160
+ require_paths:
161
+ - lib
162
+ required_ruby_version: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ! '>='
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ required_rubygems_version: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - ! '>='
170
+ - !ruby/object:Gem::Version
171
+ version: '0'
172
+ requirements: []
173
+ rubyforge_project:
174
+ rubygems_version: 2.2.2
175
+ signing_key:
176
+ specification_version: 4
177
+ summary: Easily execute an object's methods in the background with sucker_punch
178
+ test_files: []