kristjan / async_observer forked from kr/async-observer
- Source
- Commits
- Network (6)
- Issues (0)
- Downloads (0)
- Wiki (1)
- Graphs
-
Branch:
master
Kristjan Petursson (author)
Thu Nov 12 17:58:24 -0800 2009
| d9b54328 » | kr | 2007-12-13 | 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 | 18 | require 'async_observer/queue' | |
| 19 | |||||
| 975cd3ee » | kr | 2008-06-02 | 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 | 31 | module AsyncObserver::Extensions | |
| 32 | def async_send(selector, *args) | ||||
| 5e3caaf9 » | kr | 2008-01-04 | 33 | async_send_opts(selector, {}, *args) | |
| 34 | end | ||||
| 35 | |||||
| 36 | def async_send_opts(selector, opts, *args) | ||||
| 016290c2 » | kr | 2008-05-21 | 37 | AsyncObserver::Queue.put_call!(self, selector, opts, args) | |
| 069cb7e5 » | kr | 2007-12-13 | 38 | end | |
| 39 | end | ||||
| 975cd3ee » | kr | 2008-06-02 | 40 | ||
| 41 | CLASSES_TO_EXTEND.each do |c| | ||||
| 069cb7e5 » | kr | 2007-12-13 | 42 | c.send :include, AsyncObserver::Extensions | |
| 43 | end | ||||
| 44 | |||||
| b25e6c7b » | kr | 2008-06-02 | 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 | 70 | if size <= fanout_degree | |
| b25e6c7b » | kr | 2008-06-02 | 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 | 75 | fanout_opts[:pri] = opts[:fanout_pri] || opts[:pri] | |
| fe0eb0e2 » | kr | 2008-07-01 | 76 | fanout_opts = fanout_opts.reject_hash{|k,v| nil.equal?(v)} | |
| b25e6c7b » | kr | 2008-06-02 | 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 | 89 | HOOKS = [:after_create, :after_update, :after_save] | |
| 90 | |||||
| 91 | class << ActiveRecord::Base | ||||
| 92 | HOOKS.each do |hook| | ||||
| 6d02819b » | terry | 2009-05-21 | 93 | code = %Q{def async_#{hook}(*methods, &b) add_async_hook(#{hook.inspect}, *methods, &b) end} | |
| 483404f8 » | kr | 2008-06-03 | 94 | class_eval(code, __FILE__, __LINE__ - 1) | |
| 069cb7e5 » | kr | 2007-12-13 | 95 | end | |
| 96 | |||||
| 6d02819b » | terry | 2009-05-21 | 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 | 104 | end | |
| 105 | |||||
| 27325cc9 » | kr | 2008-06-02 | 106 | def async_hooks | |
| 107 | @async_hooks ||= Hash.new do |hash, hook| | ||||
| 108 | ahook = :"_async_#{hook}" | ||||
| 069cb7e5 » | kr | 2007-12-13 | 109 | ||
| 27325cc9 » | kr | 2008-06-02 | 110 | # This is for the producer's benefit | |
| 111 | send(hook){|o| async_send(ahook, o)} | ||||
| 069cb7e5 » | kr | 2007-12-13 | 112 | ||
| 27325cc9 » | kr | 2008-06-02 | 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 | 116 | ||
| 27325cc9 » | kr | 2008-06-02 | 117 | hash[hook] = [] | |
| 069cb7e5 » | kr | 2007-12-13 | 118 | end | |
| 119 | end | ||||
| 120 | |||||
| 121 | def run_async_hooks(hook, o) | ||||
| 27325cc9 » | kr | 2008-06-02 | 122 | async_hooks[hook].each{|b| b.call(o)} | |
| 069cb7e5 » | kr | 2007-12-13 | 123 | end | |
| a424e838 » | kr | 2008-06-02 | 124 | ||
| 125 | def send_to_instance(id, selector, *args) | ||||
| 169b62d3 » | kr | 2008-06-02 | 126 | x = find_by_id(id) | |
| 127 | x.send(selector, *args) if x | ||||
| a424e838 » | kr | 2008-06-02 | 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 | 140 | end | |
