background_lite 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,79 @@
1
+ === BackgroundLite
2
+
3
+ This Rails plugin supersedes the background plugin, available here:
4
+
5
+ http://github.com/imedo/background/tree/master
6
+
7
+ It allows you to execute methods in a background task with a very intuitive and
8
+ easy syntax. It is as easy as that:
9
+
10
+ class MyModel
11
+ def complex_operation(argument)
12
+ ...
13
+ end
14
+ background_method :complex_operation
15
+ end
16
+
17
+ Now, whenever MyModel#complex_operation is called, it will be run in the
18
+ background process.
19
+
20
+ Refer to the Class#background_method documentation for details.
21
+
22
+ === What this plugin is
23
+
24
+ This plugin is an easy-to-use interface for background processing frameworks.
25
+ Theoretically, you can use it in combination with any of the messaging /
26
+ background processing frameworks out there, though there are not many handlers
27
+ implemented yet. However, implementing a handler is very easy.
28
+
29
+ === What this plugin is NOT
30
+
31
+ This plugin is NOT a background processing framework. To make it work
32
+ efficiently, you need an existing framework installed and configured. Right now,
33
+ support for the ActiveMessaging framework is implemented.
34
+
35
+ However, there is out-of-the-box support for using script/runner to perform
36
+ background tasks, as well as forking.
37
+
38
+ === When to use it
39
+
40
+ * Your Rails process gets unresponsive due to some time-consuming task.
41
+ * The task does not need to show an immediate effect.
42
+
43
+ === Features
44
+
45
+ * Background processing using multiple background processing frameworks.
46
+ * Fallback processing, if your background processing framework isn't responding
47
+ correctly.
48
+ * Works out-of-the-box with script/runner and forking.
49
+ * Fallback handler that streams messages containing background tasks to disk, to
50
+ later .
51
+ * Error reporting through different channels, depending on the task at hand.
52
+ * Exception notification.
53
+ * Stdout (useful for debugging).
54
+ * Stderr (useful for debugging).
55
+ * Silent, which swallows all exceptions.
56
+ * Supported processing frameworks.
57
+ * ActiveMessaging.
58
+ * script/runner.
59
+ * fork (works only on Unix-like environments).
60
+ * others might follow (it's really easy to write a handler).
61
+ * Easy configuration, depending on the environment, in config/background.yml.
62
+ * Ability to override the configuration per method.
63
+
64
+ === Dependencies
65
+
66
+ There are no dependencies besides ActiveSupport, which is required by Rails
67
+ anyways.
68
+
69
+ === Installation
70
+
71
+ Just copy the background_lite folder into vendor/plugins, and you're good to go.
72
+
73
+ === Configuration
74
+
75
+ Depending on the background processing framework that you are using, you might
76
+ have to do some configuration. See the documentation of the respective
77
+ background handler for details.
78
+
79
+ Copyright (c) 2008-2009 imedo GmbH, released under the MIT license
data/init.rb ADDED
@@ -0,0 +1,13 @@
1
+ require File.dirname(__FILE__) + '/lib/background'
2
+ require File.dirname(__FILE__) + '/lib/core_ext/object'
3
+ require File.dirname(__FILE__) + '/lib/core_ext/class'
4
+ require File.dirname(__FILE__) + '/lib/core_ext/numeric'
5
+ require File.dirname(__FILE__) + '/lib/core_ext/symbol'
6
+ require File.dirname(__FILE__) + '/lib/core_ext/nil_class'
7
+ Dir.glob(File.dirname(__FILE__) + '/lib/core_ext/handlers/*.rb').each do |handler|
8
+ require handler
9
+ end
10
+ Dir.glob(File.dirname(__FILE__) + '/lib/core_ext/error_reporters/*.rb').each do |reporter|
11
+ require reporter
12
+ end
13
+ require File.dirname(__FILE__) + '/lib/rails_ext/activerecord/base'
data/lib/background.rb ADDED
@@ -0,0 +1,100 @@
1
+ # This module holds methods for background handling
2
+ module BackgroundLite
3
+ # This class is for configuring defaults of the background processing
4
+ # framework(s) you use. You can configure the frameworks either using the
5
+ # accessors in this class, or by listing them in the config/background.yml
6
+ # configuration file. See Class#background_method for details.
7
+ class Config
8
+ # Contains the default background handler that is chosen, if none is
9
+ # specified in the call to Kernel#background.
10
+ @@default_handler = [:in_process, :forget]
11
+ cattr_accessor :default_handler
12
+
13
+ # Contains the default error reporter.
14
+ @@default_error_reporter = :stdout
15
+ cattr_accessor :default_error_reporter
16
+
17
+ def self.config #:nodoc:
18
+ @config ||= YAML.load(File.read("#{RAILS_ROOT}/config/background.yml")) rescue { RAILS_ENV => {} }
19
+ end
20
+
21
+ def self.default_config #:nodoc:
22
+ @default_config ||= (config['default'] || {})
23
+ end
24
+
25
+ def self.load(configuration) #:nodoc:
26
+ if configuration.blank?
27
+ default_config
28
+ else
29
+ loaded_config = ((config[RAILS_ENV] || {})[configuration] || {})
30
+ default_config.merge(loaded_config.symbolize_keys || {})
31
+ end
32
+ end
33
+ end
34
+
35
+ # holds whether or not background handling is disabled.
36
+ mattr_accessor :disabled
37
+ self.disabled = false
38
+
39
+ # Disables background handling.
40
+ def self.disable!
41
+ BackgroundLite.disabled = true
42
+ end
43
+
44
+ # Enables background handling.
45
+ def self.enable!
46
+ BackgroundLite.disabled = false
47
+ end
48
+
49
+ # Disables background handling for the given block.
50
+ def self.disable(&block)
51
+ value = BackgroundLite.disabled
52
+ begin
53
+ BackgroundLite.disable!
54
+ yield
55
+ ensure
56
+ BackgroundLite.disabled = value
57
+ end
58
+ end
59
+
60
+ # Sends a message to the background. The message contains an object, a method,
61
+ # and the methods arguments. The object and the arguments will be cloned for
62
+ # background handling.
63
+ #
64
+ # The options hash lets you choose the background handler(s) and their
65
+ # configuration, if available.
66
+ #
67
+ # You should rarely need to use this method directly. Rather use
68
+ # Class#background_method to mark a method to be executed in the background.
69
+ def self.send_to_background(object, method, args = [], options = {})
70
+ object = object.clone_for_background
71
+ args = args.collect { |a| a.clone_for_background }
72
+
73
+ config = (BackgroundLite::Config.load(options[:config].to_s) || {})
74
+ handler = if BackgroundLite.disabled
75
+ [:in_process]
76
+ else
77
+ [options.delete(:handler) || config[:handler] || BackgroundLite::Config.default_handler].flatten
78
+ end
79
+ reporter = options.delete(:reporter) || config[:reporter] || BackgroundLite::Config.default_error_reporter
80
+
81
+ handler.each do |hand|
82
+ options = {}
83
+ if hand.is_a? Hash
84
+ raise "Malformed handler options Hash" if hand.keys.size != 1
85
+ options = hand.values.first
86
+ hand = hand.keys.first
87
+ end
88
+
89
+ begin
90
+ BackgroundLite.disable do
91
+ "BackgroundLite::#{hand.to_s.camelize}Handler".constantize.handle(object, method, args, options)
92
+ end
93
+
94
+ return hand
95
+ rescue Exception => e
96
+ "BackgroundLite::#{reporter.to_s.camelize}ErrorReporter".constantize.report(e)
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,113 @@
1
+ class Class
2
+ def clone_for_background
3
+ self
4
+ end
5
+
6
+ # Decorates a method to be executed in the background.
7
+ #
8
+ # To decorate a class' method, background_method must be called from inside a
9
+ # class and the first argument must be a symbol, containing the name of the
10
+ # method to decorate.
11
+ #
12
+ # class FactorialClass
13
+ # def factorial(number)
14
+ # result = (1..number).inject(1) { |num, res| res * num }
15
+ # Logger.log("The result is #{result}")
16
+ # end
17
+ #
18
+ # # execute all calls to FactorialClass#factorial in the background
19
+ # background_method :factorial
20
+ # end
21
+ #
22
+ # === Choosing a handler
23
+ #
24
+ # There are several ways to execute a task in the background. To choose your
25
+ # particular handler and optionally some fallback handlers, in case the
26
+ # background process doesn't respond, use the :handler option.
27
+ #
28
+ # background_method :handler => [:active_messaging, :disk]
29
+ #
30
+ # To configure a handler, use a Hash instead of a Symbol like this:
31
+ #
32
+ # background_method :handler => [{ :active_messaging => { :queue => :my_queue } }, :disk]
33
+ #
34
+ # === Options
35
+ #
36
+ # handler:: The background handler to use.
37
+ # If none is specified, the BackgroundLite::Config.default_handler
38
+ # is used. Available handlers are :active_messaging, :in_process,
39
+ # :forget, :disk, and :test. This option can also be an array, in
40
+ # which case all of the handlers are tried in order, until one
41
+ # succeeds. Each element of the array may be a Symbol or a hash with
42
+ # one element. If it is a hash, the key is the handler name, and the
43
+ # value contains configuration options for the handler.
44
+ #
45
+ # reporter:: A reporter class that reports errors to the user. Available
46
+ # reporters are :stdout, :silent, :exception_notification, and
47
+ # :test.
48
+ #
49
+ # === Background Configurations
50
+ #
51
+ # Instead of specifying the :handler: and :reporter: params directly, you can
52
+ # also specify a configuration for your particular background call, which is
53
+ # configured in RAILS_ROOT/config/background.yml. This file has the following
54
+ # format:
55
+ #
56
+ # test:
57
+ # queue:
58
+ # :handler: test
59
+ # :reporter: silent
60
+ # production
61
+ # queue:
62
+ # :handler:
63
+ # - :active_messaging:
64
+ # :queue: background
65
+ # :reporter: exception_notification
66
+ #
67
+ # You can also specify a default configuration like this:
68
+ #
69
+ # default:
70
+ # :handler:
71
+ # - :in_process:
72
+ # - :disk:
73
+ #
74
+ # === Precedence
75
+ #
76
+ # For the handler and reporter options, the precedence is as follows, from
77
+ # high to low:
78
+ #
79
+ # - method argument
80
+ # - background.yml configuration, if supplied
81
+ # - background.yml default configuration
82
+ # - BackgroundLite::Config.default_handler / BackgroundLite::Config.default_error_reporter
83
+ #
84
+ # === Writing own handlers
85
+ #
86
+ # Writing handlers is easy. A background handler class must implement a
87
+ # self.handle method that accepts a hash containing local variables as well as
88
+ # an options hash for the block to execute. An error reporter must implement a
89
+ # self.report method that accepts an exception object. Note that for most
90
+ # non-fallback handlers you need to write a background task that accepts and
91
+ # executes the block. See BackgroundLite::ActiveMessagingHandler for an
92
+ # example on how to do that.
93
+ #
94
+ # === Things to note
95
+ #
96
+ # * Since it is not possible to serialize singleton objects, all objects are
97
+ # dup'ed before serialization. This means that all singleton methods get
98
+ # stripped away on serialization.
99
+ # * Every class used in a background method must be available in the
100
+ # background process as well.
101
+ # * Subject to the singleton restriction mentioned above, the self object is
102
+ # correctly and automatically serialized and can be referenced in the
103
+ # background method using the self keyword.
104
+ def background_method(method, options = {})
105
+ alias_method_chain method, :background do |aliased_target, punctuation|
106
+ self.class_eval do
107
+ define_method "#{aliased_target}_with_background#{punctuation}" do |*args|
108
+ BackgroundLite.send_to_background(self, "#{aliased_target}_without_background#{punctuation}", args, options)
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,47 @@
1
+ class MockController #:nodoc:
2
+ def controller_name
3
+ "BackgroundLite"
4
+ end
5
+ def action_name
6
+ "send_to_background"
7
+ end
8
+ end
9
+
10
+ class MockRequest #:nodoc:
11
+ attr_accessor :format
12
+
13
+ def initialize(message, options = {})
14
+ @message = message
15
+ options.each do |k, v|
16
+ self.instance_variable_set(:"@#{k}", v)
17
+ end
18
+ end
19
+
20
+ def env
21
+ {}
22
+ end
23
+ def protocol
24
+ "none"
25
+ end
26
+ def request_uri
27
+ "none"
28
+ end
29
+ def parameters
30
+ @message || "nil message. this does not happen (TM)."
31
+ end
32
+ def session
33
+ "none"
34
+ end
35
+ end
36
+
37
+ module BackgroundLite
38
+ # Notifies developers about errors per e-mail.
39
+ class ExceptionNotificationErrorReporter
40
+ # This method uses the exception notification plugin to deliver the exception
41
+ # together with a backtrace to the developers. Refer to the ExceptionNotification
42
+ # documentation for details.
43
+ def self.report(error)
44
+ ExceptionNotifier.deliver_exception_notification(error, MockController.new, MockRequest.new(error.message), {})
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,9 @@
1
+ module BackgroundLite
2
+ # Does not report errors at all.
3
+ class SilentNotificationErrorReporter
4
+ # Suppresses the error message by not reporting anything.
5
+ def self.report(error)
6
+ # do nothing
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module BackgroundLite
2
+ # Reports error on $stdout.
3
+ class StderrErrorReporter
4
+ # Prints the exception's error message on $stderr.
5
+ def self.report(error)
6
+ $stderr.puts error.message
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ module BackgroundLite
2
+ # Reports error on $stdout.
3
+ class StdoutErrorReporter
4
+ # Prints the exception's error message on $stdout.
5
+ def self.report(error)
6
+ puts error.message
7
+ puts error.backtrace
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ module BackgroundLite
2
+ # This class is used for reporting errors in a test environment.
3
+ class TestErrorReporter
4
+ # Stores the last error
5
+ cattr_accessor :last_error
6
+ # Does not actually report any error, but stores it in last_error.
7
+ def self.report(error)
8
+ self.last_error = error
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,65 @@
1
+ module BackgroundLite
2
+ # This background handler sends the method as well as the arguments through
3
+ # ActiveMessaging to the background poller. If you don't use the
4
+ # ActiveMessaging plugin, then this handler won't work.
5
+ #
6
+ # To make the background_lite plugin work with ActiveMessaging, you need to
7
+ # put the following processor in app/processors:
8
+ #
9
+ # class BackgroundProcessor < ApplicationProcessor
10
+ # subscribes_to :background
11
+ #
12
+ # def on_message(message)
13
+ # puts "BackgroundProcessor"
14
+ # BackgroundLite::ActiveMessagingHandler.execute(message)
15
+ # end
16
+ # end
17
+ class ActiveMessagingHandler
18
+ # The ActiveMessaging queue name through which the message should be
19
+ # serialized.
20
+ @@queue_name = :background
21
+ cattr_accessor :queue_name
22
+
23
+ # Marshals the method and the arguments and sends it through ActiveMessaging
24
+ # to the background processor.
25
+ #
26
+ # === Options
27
+ #
28
+ # queue:: The name of the queue to use to send the message to the background
29
+ # process.
30
+ def self.handle(object, method, args, options = {})
31
+ ActiveMessaging::Gateway.publish((options[:queue] || self.queue_name).to_sym, Marshal.dump([object, method, args]))
32
+ end
33
+
34
+ # Decodes a marshalled message which was previously sent over
35
+ # ActiveMessaging. Returns an array containing the object, the method name
36
+ # as a string, and the method arguments.
37
+ def self.decode(message)
38
+ begin
39
+ object, method, args = Marshal.load(message)
40
+ rescue ArgumentError => e
41
+ # Marshal.load does not trigger const_missing, so we have to do this
42
+ # ourselves.
43
+ e.message.split(' ').last.constantize
44
+ retry
45
+ end
46
+ [object, method, args]
47
+ end
48
+
49
+ # Executes a marshalled message which was previously sent over
50
+ # ActiveMessaging, in the context of the object, with all the arguments
51
+ # passed.
52
+ def self.execute(message)
53
+ begin
54
+ object, method, args = self.decode(message)
55
+ puts "--- executing method: #{method}\n--- with variables: #{args.inspect}\n--- in object: #{object.class.name}, #{object.id}"
56
+
57
+ object.send(method, *args)
58
+ puts "--- it happened!"
59
+ rescue Exception => e
60
+ puts e.message
61
+ puts e.backtrace
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,32 @@
1
+ module BackgroundLite
2
+ # Stores the serialized message on disk. This handler is probably most useful
3
+ # as a fallback handler.
4
+ class DiskHandler
5
+ # The directory in which the serialized messages should be stored.
6
+ @@dirname = nil
7
+ cattr_accessor :dirname
8
+
9
+ # Marshals the message and the locals into a file in the folder specified by
10
+ # dirname.
11
+ def self.handle(object, method, args, options = {})
12
+ filename = "background_#{Time.now.to_f.to_s}"
13
+ File.open("#{dirname}/#{filename}", 'w') do |file|
14
+ file.print(Marshal.dump([object, method, args]))
15
+ end
16
+ end
17
+
18
+ # Replays all marshalled background tasks in the order in which they were
19
+ # stored into the folder specified by dirname.
20
+ def self.recover(handler)
21
+ handler_class = "BackgroundLite::#{handler.to_s.camelize}Handler".constantize
22
+ Dir.entries(dirname).grep(/^background/).sort.each do |filename|
23
+ path = "#{dirname}/#{filename}"
24
+ File.open(path, 'r') do |file|
25
+ object, method, args = Marshal.load(file)
26
+ handler_class.handle(object, method, args)
27
+ end
28
+ FileUtils.rm(path)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,51 @@
1
+ module BackgroundLite
2
+ # This background handler sends the method as well as the arguments through
3
+ # DRb to the background process.
4
+ class DrbHandler
5
+ def self.background_queue
6
+ @background_queue ||= begin
7
+ require 'drb'
8
+
9
+ DRb.start_service
10
+ DRbObject.new(nil, "druby://localhost:2251")
11
+ end
12
+ end
13
+
14
+ # Marshals the method and the arguments and sends it through DRb
15
+ # to the background process.
16
+ def self.handle(object, method, args, options = {})
17
+ background_queue.push(Marshal.dump([object, method, args]))
18
+ end
19
+
20
+ # Decodes a marshalled message which was previously sent over
21
+ # DRb. Returns an array containing the object, the method name
22
+ # as a string, and the method arguments.
23
+ def self.decode(message)
24
+ begin
25
+ object, method, args = Marshal.load(message)
26
+ rescue ArgumentError => e
27
+ # Marshal.load does not trigger const_missing, so we have to do this
28
+ # ourselves.
29
+ e.message.split(' ').last.constantize
30
+ retry
31
+ end
32
+ [object, method, args]
33
+ end
34
+
35
+ # Executes a marshalled message which was previously sent over
36
+ # DRb, in the context of the object, with all the arguments
37
+ # passed.
38
+ def self.execute(message)
39
+ begin
40
+ object, method, args = self.decode(message)
41
+ puts "--- executing method: #{method}\n--- with variables: #{args.inspect}\n--- in object: #{object.class.name}, #{object.id}"
42
+
43
+ object.send(method, *args)
44
+ puts "--- it happened!"
45
+ rescue Exception => e
46
+ puts e.message
47
+ puts e.backtrace
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,10 @@
1
+ module BackgroundLite
2
+ # Forgets the background task. This handler is probably most useful as a
3
+ # fallback handler.
4
+ class ForgetHandler
5
+ # Does nothing
6
+ def self.handle(object, method, args, options = {})
7
+ # do nothing
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ module BackgroundLite
2
+ # This background handler runs the given method in a forked child process.
3
+ class ForkHandler
4
+ # Runs the method in a forked child process
5
+ def self.handle(object, method, args, options = {})
6
+ fork do
7
+ object.send(method, *args)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ module BackgroundLite
2
+ # Executes the method in-process. This handler is probably most useful as a
3
+ # fallback handler.
4
+ class InProcessHandler
5
+ # Executes the method in-process.
6
+ def self.handle(object, method, args, options = {})
7
+ object.send(method, *args)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,45 @@
1
+ module BackgroundLite
2
+ # This background handler sends the method as well as the arguments through
3
+ # Resque to the background process.
4
+ class ResqueHandler
5
+ @queue = :background
6
+
7
+ # Marshals the method and the arguments and sends it through Resque
8
+ # to the background process.
9
+ def self.handle(object, method, args, options = {})
10
+ require 'resque'
11
+ Resque.enqueue(self, Base64.encode64(Marshal.dump([object, method, args])))
12
+ end
13
+
14
+ # Decodes a marshalled message which was previously sent over
15
+ # Resque. Returns an array containing the object, the method name
16
+ # as a string, and the method arguments.
17
+ def self.decode(message)
18
+ begin
19
+ object, method, args = Marshal.load(Base64.decode64(message))
20
+ rescue ArgumentError => e
21
+ # Marshal.load does not trigger const_missing, so we have to do this
22
+ # ourselves.
23
+ e.message.split(' ').last.constantize
24
+ retry
25
+ end
26
+ [object, method, args]
27
+ end
28
+
29
+ # Executes a marshalled message which was previously sent over
30
+ # Resque, in the context of the object, with all the arguments
31
+ # passed.
32
+ def self.perform(message)
33
+ begin
34
+ object, method, args = self.decode(message)
35
+ puts "--- executing method: #{method}\n--- with variables: #{args.inspect}\n--- in object: #{object.class.name}, #{object.id}"
36
+
37
+ object.send(method, *args)
38
+ puts "--- it happened!"
39
+ rescue Exception => e
40
+ puts e.message
41
+ puts e.backtrace
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,40 @@
1
+ require 'base64'
2
+
3
+ module BackgroundLite
4
+ # This background handler runs the method block via script/runner.
5
+ class RunnerHandler
6
+ # Marshals the method and arguments and sends them to script/runner.
7
+ def self.handle(object, method, args, options = {})
8
+ fork do
9
+ system(%{script/runner "BackgroundLite::RunnerHandler.execute '#{encode(object)}', '#{method}', '#{encode(args)}'"})
10
+ end
11
+ end
12
+
13
+ # Executes an encoded message which was sent via command line to runner
14
+ def self.execute(object, method, args)
15
+ object, args = self.decode(object), self.decode(args)
16
+ puts "--- executing method: #{method}\n--- with variables: #{args.inspect}\n--- in object: #{object.inspect}"
17
+
18
+ object.send(method, *args)
19
+ puts "--- it happened!"
20
+ end
21
+
22
+ protected
23
+ def self.encode(obj)
24
+ Base64.encode64(Marshal.dump(obj))
25
+ end
26
+
27
+ def self.decode(string)
28
+ message = Base64.decode64(string)
29
+ begin
30
+ obj = Marshal.load(message)
31
+ rescue ArgumentError => e
32
+ # Marshal.load does not trigger const_missing, so we have to do this
33
+ # ourselves.
34
+ e.message.split(' ').last.constantize
35
+ retry
36
+ end
37
+ obj
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,35 @@
1
+ module BackgroundLite
2
+ # This handler is used in a testing environment. It allows for introspection
3
+ # of last call to the handle method.
4
+ class TestHandler
5
+ # contains the object on which the method is executed.
6
+ cattr_accessor :object
7
+ # contains the method name to execute
8
+ cattr_accessor :method
9
+ # contains the method's arguments
10
+ cattr_accessor :args
11
+ # If true, the execution of TestHandler#handle will fail the next time it's
12
+ # called.
13
+ cattr_accessor :fail_next_time
14
+ # True, if TestHandler#handle was executed.
15
+ cattr_accessor :executed
16
+ # Stores the last options hash given to handle
17
+ cattr_accessor :options
18
+
19
+ # Does not call the block, but sets some variables for introspection.
20
+ def self.handle(object, method, args, options = {})
21
+ self.executed = true
22
+ if self.fail_next_time
23
+ self.fail_next_time = false
24
+ raise "TestHandler.handle: Failed on purpose"
25
+ end
26
+
27
+ self.object, self.method, self.args, self.options = object, method, args, options
28
+ end
29
+
30
+ # Resets the class' accessors.
31
+ def self.reset
32
+ object = method = args = options = fail_next_time = executed = nil
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,5 @@
1
+ class NilClass
2
+ def clone_for_background
3
+ self
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Numeric
2
+ def clone_for_background
3
+ self
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ class Object
2
+ # Clones the object to for transmission to the background process. The default
3
+ # implementation is dupping the object.
4
+ def clone_for_background
5
+ dup
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ class Symbol
2
+ def clone_for_background
3
+ self
4
+ end
5
+ end
@@ -0,0 +1,31 @@
1
+ module ActiveRecord
2
+ class Base
3
+ # Override this method to strip your object from data that doesn't have to
4
+ # be transmitted to the background process. Note that you don't need to
5
+ # clear the association cache, as this is already done for you in
6
+ # clone_for_background.
7
+ def cleanup_for_background
8
+ end
9
+
10
+ # Prepares the object to be transmitted to the background. This method dups
11
+ # the object and strips some instance variables, most notably the
12
+ # association cache, in order to prevent all associations to be transmitted
13
+ # with the object in full length.
14
+ #
15
+ # To clean up data specific to your class, use cleanup_for_background.
16
+ def clone_for_background
17
+ returning dup do |x|
18
+ x.cleanup_for_background
19
+
20
+ # taken from ActiveRecord::AttributeMethods::ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
21
+ type_to_preserve = [DateTime, Time, Date]
22
+ attr_cache = x.instance_variable_get(:@attributes_cache)
23
+ attr_cache.each do |key, value|
24
+ attr_cache[key] = nil unless type_to_preserve.include?(attr_cache[key].class)
25
+ end
26
+ x.instance_variable_set(:@errors, nil)
27
+ x.clear_association_cache
28
+ end
29
+ end
30
+ end
31
+ end
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + '/../init'
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: background_lite
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Thomas Kadauke
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-07-17 00:00:00 +02:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description:
22
+ email: tkadauke@imedo.de
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files:
28
+ - README.rdoc
29
+ files:
30
+ - lib/background.rb
31
+ - lib/core_ext/class.rb
32
+ - lib/core_ext/error_reporters/exception_notification_error_reporter.rb
33
+ - lib/core_ext/error_reporters/silent_error_reporter.rb
34
+ - lib/core_ext/error_reporters/stderr_error_reporter.rb
35
+ - lib/core_ext/error_reporters/stdout_error_reporter.rb
36
+ - lib/core_ext/error_reporters/test_error_reporter.rb
37
+ - lib/core_ext/handlers/active_messaging_handler.rb
38
+ - lib/core_ext/handlers/disk_handler.rb
39
+ - lib/core_ext/handlers/drb_handler.rb
40
+ - lib/core_ext/handlers/forget_handler.rb
41
+ - lib/core_ext/handlers/fork_handler.rb
42
+ - lib/core_ext/handlers/in_process_handler.rb
43
+ - lib/core_ext/handlers/resque_handler.rb
44
+ - lib/core_ext/handlers/runner_handler.rb
45
+ - lib/core_ext/handlers/test_handler.rb
46
+ - lib/core_ext/nil_class.rb
47
+ - lib/core_ext/numeric.rb
48
+ - lib/core_ext/object.rb
49
+ - lib/core_ext/symbol.rb
50
+ - lib/rails_ext/activerecord/base.rb
51
+ - rails/init.rb
52
+ - init.rb
53
+ - README.rdoc
54
+ has_rdoc: true
55
+ homepage: http://www.imedo.de/
56
+ licenses: []
57
+
58
+ post_install_message:
59
+ rdoc_options: []
60
+
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ required_rubygems_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ requirements: []
78
+
79
+ rubyforge_project:
80
+ rubygems_version: 1.3.6
81
+ signing_key:
82
+ specification_version: 3
83
+ summary: Run any method in the background
84
+ test_files: []
85
+