kristjan / async_observer forked from kr/async-observer

Rails plugin that provides deep integration with Beanstalk.

This URL has Read+Write access

d9b54328 » kr 2007-12-13 Add copyright notice and li... 1 # async-observer - Rails plugin for asynchronous job execution
2
3 # Copyright (C) 2007 Philotic Inc.
4
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
17
069cb7e5 » kr 2007-12-13 Initial commit. 18 require 'async_observer/queue'
19
975cd3ee » kr 2008-06-02 Support async messages to r... 20 CLASSES_TO_EXTEND = [
21 ActiveRecord::Base,
22 Array,
23 Hash,
24 Module,
25 Numeric,
26 Range,
27 String,
28 Symbol,
29 ]
30
069cb7e5 » kr 2007-12-13 Initial commit. 31 module AsyncObserver::Extensions
32 def async_send(selector, *args)
5e3caaf9 » kr 2008-01-04 Support setting the priorit... 33 async_send_opts(selector, {}, *args)
34 end
35
36 def async_send_opts(selector, opts, *args)
016290c2 » kr 2008-05-21 Call after_put hook after p... 37 AsyncObserver::Queue.put_call!(self, selector, opts, args)
069cb7e5 » kr 2007-12-13 Initial commit. 38 end
39 end
975cd3ee » kr 2008-06-02 Support async messages to r... 40
41 CLASSES_TO_EXTEND.each do |c|
069cb7e5 » kr 2007-12-13 Initial commit. 42 c.send :include, AsyncObserver::Extensions
43 end
44
b25e6c7b » kr 2008-06-02 New feature: Range#async_each. 45 class Range
46 DEFAULT_FANOUT_FUZZ = 0
47 DEFAULT_FANOUT_DEGREE = 1000
48
49 def split_to(n)
50 split_by((size + n - 1) / n) { |group| yield(group) }
51 end
52
53 def split_by(n)
54 raise ArgumentError.new('invalid slice size') if n < 1
55 n -= 1 if !exclude_end?
56 i = first
57 while member?(i)
58 j = [i + n, last].min
59 yield(Range.new(i, j, exclude_end?))
60 i = j + (exclude_end? ? 0 : 1)
61 end
62 end
63
64 def size
65 last - first + (exclude_end? ? 0 : 1)
66 end
67
68 def async_each_opts(rcv, selector, opts, *extra)
69 fanout_degree = opts.fetch(:fanout_degree, DEFAULT_FANOUT_DEGREE)
8b9fdbdd » kr 2008-06-03 Avoid generating single-ele... 70 if size <= fanout_degree
b25e6c7b » kr 2008-06-02 New feature: Range#async_each. 71 each {|i| rcv.async_send_opts(selector, opts, i, *extra)}
72 else
73 fanout_opts = opts.merge(:fuzz => opts.fetch(:fanout_fuzz,
74 DEFAULT_FANOUT_FUZZ))
4bc4fa58 » kr 2008-06-20 Add a :fanout_pri option to... 75 fanout_opts[:pri] = opts[:fanout_pri] || opts[:pri]
fe0eb0e2 » kr 2008-07-01 Whoops, this isn't standard... 76 fanout_opts = fanout_opts.reject_hash{|k,v| nil.equal?(v)}
b25e6c7b » kr 2008-06-02 New feature: Range#async_each. 77 split_to(fanout_degree) do |subrange|
78 subrange.async_send_opts(:async_each_opts, fanout_opts, rcv, selector,
79 opts, *extra)
80 end
81 end
82 end
83
84 def async_each(rcv, selector, *extra)
85 async_each_opts(rcv, selector, {}, *extra)
86 end
87 end
88
069cb7e5 » kr 2007-12-13 Initial commit. 89 HOOKS = [:after_create, :after_update, :after_save]
90
91 class << ActiveRecord::Base
92 HOOKS.each do |hook|
6d02819b » terry 2009-05-21 allow symbols for AR async ... 93 code = %Q{def async_#{hook}(*methods, &b) add_async_hook(#{hook.inspect}, *methods, &b) end}
483404f8 » kr 2008-06-03 Simpler, more comprehensibl... 94 class_eval(code, __FILE__, __LINE__ - 1)
069cb7e5 » kr 2007-12-13 Initial commit. 95 end
96
6d02819b » terry 2009-05-21 allow symbols for AR async ... 97 def add_async_hook(hook, *args, &block)
98 if args && args.first.is_a?(Symbol)
99 method = args.shift
100 async_hooks[hook] << lambda{|o| o.send(method)}
101 else
102 async_hooks[hook] << block
103 end
069cb7e5 » kr 2007-12-13 Initial commit. 104 end
105
27325cc9 » kr 2008-06-02 Fix async_hook handling, ev... 106 def async_hooks
107 @async_hooks ||= Hash.new do |hash, hook|
108 ahook = :"_async_#{hook}"
069cb7e5 » kr 2007-12-13 Initial commit. 109
27325cc9 » kr 2008-06-02 Fix async_hook handling, ev... 110 # This is for the producer's benefit
111 send(hook){|o| async_send(ahook, o)}
069cb7e5 » kr 2007-12-13 Initial commit. 112
27325cc9 » kr 2008-06-02 Fix async_hook handling, ev... 113 # This is for the worker's benefit
114 code = "def #{ahook}(o) run_async_hooks(#{hook.inspect}, o) end"
115 instance_eval(code, __FILE__, __LINE__ - 1)
069cb7e5 » kr 2007-12-13 Initial commit. 116
27325cc9 » kr 2008-06-02 Fix async_hook handling, ev... 117 hash[hook] = []
069cb7e5 » kr 2007-12-13 Initial commit. 118 end
119 end
120
121 def run_async_hooks(hook, o)
27325cc9 » kr 2008-06-02 Fix async_hook handling, ev... 122 async_hooks[hook].each{|b| b.call(o)}
069cb7e5 » kr 2007-12-13 Initial commit. 123 end
a424e838 » kr 2008-06-02 Easy way to run a method on... 124
125 def send_to_instance(id, selector, *args)
169b62d3 » kr 2008-06-02 Quietly do nothing if the r... 126 x = find_by_id(id)
127 x.send(selector, *args) if x
a424e838 » kr 2008-06-02 Easy way to run a method on... 128 end
129
130 def async_each_opts(selector, opts, *args)
131 min = opts.fetch(:min, minimum(:id))
132 max = opts.fetch(:max, maximum(:id))
133
134 (min..max).async_each_opts(self, :send_to_instance, opts, selector, *args)
135 end
136
137 def async_each(selector, *args)
138 async_each_opts(selector, {}, *args)
139 end
069cb7e5 » kr 2007-12-13 Initial commit. 140 end