background_lite 0.0.1

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/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
+