Skip to content

Commit

Permalink
resque-failed-job-mailer integration
Browse files Browse the repository at this point in the history
  • Loading branch information
take-five committed Apr 4, 2013
1 parent f0498d4 commit 913cbf8
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 12 deletions.
31 changes: 23 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Resque::Integration

Интеграция Resque в Rails-приложения с поддержкой плагинов [resque-progress](https://github.com/idris/resque-progress), [resque-lock](https://github.com/defunkt/resque-lock) и [resque-multi-job-forks](https://github.com/stulentsev/resque-multi-job-forks).
Интеграция Resque в Rails-приложения с поддержкой следующих плагинов:
* [resque-progress](https://github.com/idris/resque-progress)
* [resque-lock](https://github.com/defunkt/resque-lock)
* [resque-multi-job-forks](https://github.com/stulentsev/resque-multi-job-forks)
* [resque-failed-job-mailer](https://github.com/anandagrawal84/resque_failed_job_mailer)

Этот гем существует затем, чтобы избежать повторения чужих ошибок и сократить время, необходимое для включения resque в проект.

## Установка
Expand All @@ -17,13 +22,6 @@ mount Resque::Integration::Application => "/_job_", :as => "job_status"

Вместо `_job_` можно прописать любой другой адрес. По этому адресу прогресс-бар будет узнавать о состоянии джоба.

Создайте `config/initializers/resque.rb`:
```ruby
Resque.redis = $redis
Resque.inline = Rails.env.test?
Resque.redis.namespace = "your_project_resque"
```

Если вы до сих пор не используете sprockets, то сделайте что-то вроде этого:
```bash
$ rails generate resque:integration:install
Expand Down Expand Up @@ -119,6 +117,23 @@ workers:
minutes_per_fork: 30 # альтернатива предыдущей настройке - сколько минут должен работать воркер, прежде чем форкнуться заново
env: # переменные окружение, специфичные для данного воркера
RUBY_HEAP_SLOTS_GROWTH_FACTOR: 0.5

# конфигурация failure-бэкэндов
failure:
# конфигурация отправщика отчетов об ошибках
notifier:
enabled: true
# адреса, на которые надо посылать уведомления об ошибках
to: [[email protected], [email protected], [email protected]]
# необязательные настройки
# от какого адреса слать
from: [email protected]
# включать в письмо payload (аргументы, с которыми вызвана задача)
include_payload: true
# класс отправщика (должен быть наследником ActionMailer::Base, по умолчанию ResqueFailedJobMailer::Mailer
mailer: "Blizko::ResqueMailer"
# метод, который вызывается у отправщика (по умолчанию alert)
mail: alert
```
Для разработки можно (и нужно) создать файл `config/resque.local.yml`, в котором можно переопределить любые параметры:
Expand Down
58 changes: 58 additions & 0 deletions lib/resque/integration/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ def initialize(queue, config)
super(data)
end

# Returns workers count for given queue
def count
[super || 1, 1].max
end

# Returns hash of ENV variables that should be associated with this worker
def env
env = super || {}
Expand All @@ -35,12 +40,54 @@ def env
end
end

# Failure notifier configuration
class Notifier < OpenStruct
def initialize(config)
super(config || {})
end

# Is notifier enabled
def enabled?
to.any? && enabled.nil? ? true : enabled
end

# Returns true if payload should be included into reports
def include_payload?
include_payload.nil? ?
true :
include_payload
end

# Returns recipients list
def to
super || []
end

# Returns sender address
def from
super || '[email protected]'
end

# Returns mailer method
def mail
(super || :alert).to_sym
end

# Returns mailer class
def mailer
super || 'ResqueFailedJobMailer::Mailer'
end
end

# Create configuration from given +paths+
def initialize(*paths)
@configuration = {}
paths.each { |f| load f }
end

# Returns Resque redis configuration
#
# @return [OpenStruct]
def redis
@redis ||= OpenStruct.new :host => self['redis.host'] || 'localhost',
:port => self['redis.port'] || 6379,
Expand All @@ -49,18 +96,29 @@ def redis
:namespace => self['redis.namespace']
end

# Returns workers configuration
#
# @return [Array<Worker>]
def workers
@workers ||= (self[:workers] || {}).map { |k, v| Worker.new(k, v) }
end

# Returns failure notifier config
def failure_notifier
@notifier ||= Notifier.new(self['failure.notifier'])
end

# Returns Resque polling interval
def interval
(self['resque.interval'] || 5).to_i
end

# Returns Resque verbosity level
def verbosity
(self['resque.verbosity'] || 0).to_i
end

# Returns path to resque log file
def log_file
self['resque.log_file']
end
Expand Down
23 changes: 20 additions & 3 deletions lib/resque/integration/engine.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
# coding: utf-8

require "rails/engine"
require 'rails/engine'
require 'active_support/core_ext/string/inflections'

module Resque::Integration
# Rails engine
# @see http://guides.rubyonrails.org/engines.html
class Engine < Rails::Engine
rake_tasks do
load "resque/integration/tasks/resque.rake"
load "resque/integration/tasks/supervisor.rake"
load 'resque/integration/tasks/resque.rake'
load 'resque/integration/tasks/supervisor.rake'
end

initializer 'resque-integration.config' do
Expand All @@ -25,6 +26,22 @@ class Engine < Rails::Engine
Resque.redis.namespace = redis.namespace
end

initializer 'resque-integration.failure_notifier' do
notifier = Resque.config.failure_notifier

if notifier.enabled?
require 'resque_failed_job_mailer'

Resque::Failure::Notifier.configure do |config|
config.to = notifier.to
config.from = notifier.from
config.include_payload = notifier.include_payload?
config.mail = notifier.mail
config.mailer = notifier.mailer.constantize
end
end
end

initializer 'resque-multi-job-forks.hook' do
# Support for resque-multi-job-forks
if ENV['JOBS_PER_FORK'] || ENV['MINUTES_PER_FORK']
Expand Down
11 changes: 10 additions & 1 deletion lib/resque/integration/supervisor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,17 @@ def start
# write pid to file
File.write(pid_file.to_s, Process.pid)

$0 = "Resque: supervisor"

loop do
Resque.workers.each(&:prune_dead_workers)
Resque.workers.each do |worker|
begin
worker.prune_dead_workers
rescue
# ignore
end
end

sleep(INTERVAL)
end
rescue
Expand Down
1 change: 1 addition & 0 deletions resque-integration.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Gem::Specification.new do |gem|
gem.add_runtime_dependency 'resque-meta', '>= 2.0.0'
gem.add_runtime_dependency 'resque-progress', '~> 1.0.1'
gem.add_runtime_dependency 'resque-multi-job-forks', '~> 0.3.4'
gem.add_runtime_dependency 'resque-failed-job-mailer', '~> 0.0.3'

gem.add_runtime_dependency 'multi_json'
gem.add_runtime_dependency 'rake'
Expand Down
85 changes: 85 additions & 0 deletions spec/resque/integration/configuration_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# coding: utf-8

require 'spec_helper'

describe Resque::Integration::Configuration::Notifier do
context 'when NilClass given as config' do
subject(:config) { described_class::new(nil) }

it { should_not be_enabled }
its(:include_payload?) { should be_true }
its(:to) { should be_empty }
its(:from) { should eq '[email protected]' }
its(:mail) { should eq :alert }
its(:mailer) { should eq 'ResqueFailedJobMailer::Mailer' }
end

context 'when Hash given as config' do
let :configuration do
{to: ['to1@mail', 'to2@mail'],
from: 'from@mail',
enabled: false,
include_payload: false,
mail: 'notify',
mailer: 'MyMailer'}
end

subject(:config) { described_class::new(configuration) }

it { should_not be_enabled }
its(:include_payload?) { should be_false }
its(:to) { should include 'to1@mail' }
its(:to) { should include 'to2@mail' }
its(:from) { should eq 'from@mail' }
its(:mail) { should eq :notify }
its(:mailer) { should eq 'MyMailer' }
end
end

describe Resque::Integration::Configuration::Worker do
describe '.new' do
context 'when Integer given as config' do
subject(:config) { described_class::new(:default, 2) }

its(:queue) { should eq :default }
its(:count) { should eq 2 }
end

context 'when Hash given as config' do
subject(:config) { described_class::new(:default, :count => 2) }

its(:queue) { should eq :default }
its(:count) { should eq 2 }
end
end

describe '#count' do
context 'when initialized without count paramter' do
subject { described_class::new(:default, {}) }

its(:count) { should eq 1 }
end

context 'when initialized with count <= 0' do
subject { described_class::new(:default, :count => 0) }

its(:count) { should eq 1 }
end
end

describe '#env' do
let :config do
described_class::new(:default,
:count => 2,
:jobs_per_fork => 10,
:minutes_per_fork => 5,
:env => {:VAR => 2})
end
subject { config.env }

its([:QUEUE]) { should eq 'default' }
its([:JOBS_PER_FORK]) { should eq '10' }
its([:MINUTES_PER_FORK]) { should eq '5' }
its([:VAR]) { should eq '2' }
end
end

0 comments on commit 913cbf8

Please sign in to comment.