resque-integration 3.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/.drone.yml +28 -0
  3. data/.gitignore +21 -0
  4. data/.rspec +3 -0
  5. data/Appraisals +27 -0
  6. data/CHANGELOG.md +311 -0
  7. data/Gemfile +4 -0
  8. data/README.md +281 -0
  9. data/Rakefile +1 -0
  10. data/app/assets/javascripts/standalone/progress_bar.js +47 -0
  11. data/app/controllers/resque/jobs_controller.rb +42 -0
  12. data/app/controllers/resque/queues/info_controller.rb +9 -0
  13. data/app/controllers/resque/queues/status_controller.rb +38 -0
  14. data/app/views/shared/job_progress_bar.html.haml +17 -0
  15. data/bin/resque-status +59 -0
  16. data/config/routes.rb +13 -0
  17. data/config.ru +9 -0
  18. data/dip.yml +44 -0
  19. data/docker-compose.development.yml +12 -0
  20. data/docker-compose.drone.yml +6 -0
  21. data/docker-compose.yml +10 -0
  22. data/lib/generators/resque/integration/install/install_generator.rb +38 -0
  23. data/lib/resque/integration/backtrace.rb +29 -0
  24. data/lib/resque/integration/configuration.rb +238 -0
  25. data/lib/resque/integration/continuous.rb +75 -0
  26. data/lib/resque/integration/engine.rb +103 -0
  27. data/lib/resque/integration/extensions/job.rb +17 -0
  28. data/lib/resque/integration/extensions/worker.rb +17 -0
  29. data/lib/resque/integration/extensions.rb +8 -0
  30. data/lib/resque/integration/failure_backends/queues_totals.rb +37 -0
  31. data/lib/resque/integration/failure_backends.rb +7 -0
  32. data/lib/resque/integration/god.erb +99 -0
  33. data/lib/resque/integration/hooks.rb +72 -0
  34. data/lib/resque/integration/logs_rotator.rb +95 -0
  35. data/lib/resque/integration/monkey_patch/verbose_formatter.rb +10 -0
  36. data/lib/resque/integration/ordered.rb +142 -0
  37. data/lib/resque/integration/priority.rb +89 -0
  38. data/lib/resque/integration/queues_info/age.rb +53 -0
  39. data/lib/resque/integration/queues_info/config.rb +96 -0
  40. data/lib/resque/integration/queues_info/size.rb +33 -0
  41. data/lib/resque/integration/queues_info.rb +55 -0
  42. data/lib/resque/integration/tasks/hooks.rake +49 -0
  43. data/lib/resque/integration/tasks/lock.rake +37 -0
  44. data/lib/resque/integration/tasks/resque.rake +101 -0
  45. data/lib/resque/integration/unique.rb +218 -0
  46. data/lib/resque/integration/version.rb +5 -0
  47. data/lib/resque/integration.rb +146 -0
  48. data/lib/resque-integration.rb +1 -0
  49. data/resque-integration.gemspec +40 -0
  50. data/spec/fixtures/resque_queues.yml +45 -0
  51. data/spec/resque/controllers/jobs_controller_spec.rb +65 -0
  52. data/spec/resque/integration/configuration_spec.rb +147 -0
  53. data/spec/resque/integration/continuous_spec.rb +122 -0
  54. data/spec/resque/integration/failure_backends/queues_totals_spec.rb +105 -0
  55. data/spec/resque/integration/ordered_spec.rb +87 -0
  56. data/spec/resque/integration/priority_spec.rb +94 -0
  57. data/spec/resque/integration/queues_info_spec.rb +402 -0
  58. data/spec/resque/integration/unique_spec.rb +184 -0
  59. data/spec/resque/integration_spec.rb +105 -0
  60. data/spec/shared/resque_inline.rb +10 -0
  61. data/spec/spec_helper.rb +28 -0
  62. data/vendor/assets/images/progressbar/white.gif +0 -0
  63. data/vendor/assets/javascripts/jquery.progressbar.js +177 -0
  64. data/vendor/assets/stylesheets/jquery.progressbar.css.erb +33 -0
  65. data/vendor/assets/stylesheets/jquery.progressbar.no_pipeline.css +33 -0
  66. metadata +402 -0
data/README.md ADDED
@@ -0,0 +1,281 @@
1
+ # Resque::Integration
2
+
3
+ <a href="http://dolly.railsc.ru/projects/47/builds/latest/?ref=master"><img src="http://dolly.railsc.ru/badges/abak-press/resque-integration/master" height=18 /></a>
4
+
5
+ Интеграция Resque в Rails-приложения с поддержкой следующих плагинов:
6
+ * [resque-progress](https://github.com/idris/resque-progress)
7
+ * [resque-lock](https://github.com/defunkt/resque-lock)
8
+ * [resque-multi-job-forks](https://github.com/stulentsev/resque-multi-job-forks)
9
+ * [resque-failed-job-mailer](https://github.com/anandagrawal84/resque_failed_job_mailer)
10
+ * [resque-scheduler](https://github.com/resque/resque-scheduler)
11
+ * [resque-retry](https://github.com/lantins/resque-retry)
12
+
13
+ Этот гем существует затем, чтобы избежать повторения чужих ошибок и сократить время, необходимое для включения resque в проект.
14
+
15
+ ## Установка
16
+
17
+ Добавьте в `Gemfile`:
18
+ ```ruby
19
+ gem 'resque-integration'
20
+ ```
21
+
22
+ Добавьте в `config/routes.rb`:
23
+ ```ruby
24
+ mount Resque::Integration::Application => "/_job_", :as => "job_status"
25
+ ```
26
+
27
+ Вместо `_job_` можно прописать любой другой адрес. По этому адресу прогресс-бар будет узнавать о состоянии джоба.
28
+
29
+ Если вы до сих пор не используете sprockets, то сделайте что-то вроде этого:
30
+ ```bash
31
+ $ rails generate resque:integration:install
32
+ ```
33
+ (результат не гарантирован, т.к. не тестировалось)
34
+
35
+ ## Задачи
36
+
37
+ Создайте файл `app/jobs/resque_job_test.rb`:
38
+ ```ruby
39
+ class ResqueJobTest
40
+ include Resque::Integration
41
+
42
+ # это название очереди, в которой будет выполняться джоб
43
+ queue :my_queue
44
+
45
+ # с помощью unique можно указать, что задача является уникальной, и какие аргументы определяют уникальность задачи.
46
+ # в данном случае не может быть двух одновременных задач ResqueJobTest с одинаковым первым аргументом
47
+ # (второй аргумент может быть любым)
48
+ unique { |id, description| [id] }
49
+
50
+ # В отличие от обычных джобов resque, надо определять метод execute.
51
+ #
52
+ # Либо же вы можете определить метод perform, но первым аргументом должен быть указан meta_id (уникальный ID джоба):
53
+ # def self.perform(meta_id, id, description)
54
+ # ...
55
+ # end
56
+ def self.execute(id, description)
57
+ (1..100).each do |t|
58
+ at(t, 100, "Processing #{id}: at #{t} of 100")
59
+ sleep 0.5
60
+ end
61
+ end
62
+ end
63
+ ```
64
+
65
+ ### Перезапуск задачи
66
+
67
+ Допустим, у вас есть очень длинная задача и вы не хотите, чтобы возникло переполнение очереди (или памяти). Тогда можно выполнить часть задачи, а потом заново поставить задачу в очередь.
68
+ ```ruby
69
+ class ResqueJobTest
70
+ include Resque::Integration
71
+
72
+ unique
73
+ continuous # теперь вы можете перезапускать задачу
74
+
75
+ def self.execute(company_id)
76
+ products = company.products.where('updated_at < ?', 1.day.ago)
77
+
78
+ products.limit(1000).each do |product|
79
+ product.touch
80
+ end
81
+
82
+ # В очередь поставится задача с теми же аргументами, что и текущая.
83
+ # Можно передать другие аргументы: `continue(another_argument)`
84
+ continue if products.count > 0
85
+ end
86
+ end
87
+ ```
88
+
89
+ Такая задача будет выполняться по частям, не потребляя много памяти. Еще один плюс: другие задачи из очереди тоже смогут выполниться.
90
+ ОПАСНО! Избегайте бесконечных циклов, т.к. это в некотором роде "рекурсия".
91
+
92
+ ## Конфигурация воркеров resque
93
+
94
+ Создайте файл `config/resque.yml` с несколькими секциями:
95
+ ```yaml
96
+ # конфигурация redis
97
+ # секция не обязательная, вы сами можете настроить подключение через Resque.redis = Redis.new
98
+ redis:
99
+ host: bz-redis
100
+ port: 6379
101
+ namespace: blizko
102
+
103
+ resque:
104
+ interval: 5 # частота, с которой resque берет задачи из очереди в секундах (по умолчанию 5)
105
+ verbosity: 1 # "шумность" логера (0 - ничего не пишет, 1 - пишет о начале/конце задачи, 2 - пишет все)
106
+ root: "/home/pc/current" # (production) абсолютный путь до корня проекта
107
+ log_file: "/home/pc/static/pulscen/local/log/resque.log" # (production) абсолютный путь до лога
108
+ config_file: "/home/pc/static/pulscen/local/log/resque.god" # (production) абсолютный путь до кофига god
109
+ pids: "/home/pc/static/pulscen/local/pids" # (production) абсолютный путь до папки с пид файлами
110
+
111
+ # переменные окружения, которые надобно передавать в resque
112
+ env:
113
+ RUBY_HEAP_MIN_SLOTS: 2500000
114
+ RUBY_HEAP_SLOTS_INCREMENT: 1000000
115
+ RUBY_HEAP_SLOTS_GROWTH_FACTOR: 1
116
+ RUBY_GC_MALLOC_LIMIT: 50000000
117
+
118
+ # конфигурация воркеров (названия воркеров являются названиями очередей)
119
+ workers:
120
+ kirby: 2 # 2 воркера в очереди kirby
121
+ images:
122
+ count: 8 # 8 воркеров в очереди images
123
+ jobs_per_fork: 250 # каждый воркер обрабатывает 250 задач прежде, чем форкается заново
124
+ minutes_per_fork: 30 # альтернатива предыдущей настройке - сколько минут должен работать воркер, прежде чем форкнуться заново
125
+ stop_timeout: 5 # максимальное время, отпущенное воркеру для остановки/рестарта
126
+ env: # переменные окружение, специфичные для данного воркера
127
+ RUBY_HEAP_SLOTS_GROWTH_FACTOR: 0.5
128
+ 'companies,images': 2 # совмещённая очередь, приоритет будет у companies
129
+ 'xls,yml':
130
+ shuffle: true # совмещённая очередь, приоритета не будет
131
+
132
+ # конфигурация failure-бэкэндов
133
+ failure:
134
+ # конфигурация отправщика отчетов об ошибках
135
+ notifier:
136
+ enabled: true
137
+ # адреса, на которые надо посылать уведомления об ошибках
138
+ to: [teamlead@apress.ru, pm@apress.ru, programmer@apress.ru]
139
+ # необязательные настройки
140
+ # от какого адреса слать
141
+ from: no-reply@blizko.ru
142
+ # включать в письмо payload (аргументы, с которыми вызвана задача)
143
+ include_payload: true
144
+ # класс отправщика (должен быть наследником ActionMailer::Base, по умолчанию ResqueFailedJobMailer::Mailer
145
+ mailer: "Blizko::ResqueMailer"
146
+ # метод, который вызывается у отправщика (по умолчанию alert)
147
+ mail: alert
148
+ ```
149
+
150
+ Обратите внимание на параметр `stop_timeout` в секции конфигурирования воркеров.
151
+ Это очень важный параметр. По умолчанию воркеру отводится всего 10 секунд на то,
152
+ чтобы остановиться. Если воркер не укладывается в это время, супервизор (мы используем
153
+ [god](http://godrb.com/)) посылает воркеру сигнал `KILL`, который "прибьет" задачу.
154
+ Если у вас есть длинные задачи (навроде импорта из XML), то для таких воркеров
155
+ лучше ставить `stop_timeout` побольше.
156
+
157
+ Для разработки можно (и нужно) создать файл `config/resque.local.yml`, в котором можно переопределить любые параметры:
158
+ ```yaml
159
+ redis:
160
+ host: localhost
161
+ port: 6379
162
+
163
+ resque:
164
+ verbosity: 2
165
+
166
+ workers:
167
+ '*': 1
168
+ ```
169
+
170
+ ## Запуск воркеров
171
+
172
+ Ручной запуск воркера (см. [официальную документацию resque](https://github.com/resque/resque/blob/1-x-stable/README.markdown))
173
+ ```bash
174
+ $ QUEUE=* rake resque:work
175
+ ```
176
+
177
+ Запуск всех воркеров так, как они сконфигурированы в `config/resque.yml`:
178
+ ```bash
179
+ $ rake resque:start
180
+ ```
181
+
182
+ Останов всех воркеров:
183
+ ```bash
184
+ $ rake resque:stop
185
+ ```
186
+
187
+ Перезапуск воркеров:
188
+ ```bash
189
+ $ rake resque:restart
190
+ ```
191
+
192
+ ## Постановка задач в очередь
193
+
194
+ ### Для задач, в который включен модуль `Resque::Integration`
195
+ ```ruby
196
+ meta = ResqueJobTest.enqueue(id=2)
197
+ @job_id = meta.meta_id
198
+ ```
199
+
200
+ Вот так можно показать прогресс-бар:
201
+ ```haml
202
+ %div#progressbar
203
+
204
+ :javascript
205
+ $('#progressbar').progressBar({
206
+ url: #{job_status_path.to_json}, // адрес джоб-бэкенда (определяется в ваших маршрутах)
207
+ pid: #{@job_id.to_json}, // job id
208
+ interval: 1100, // частота опроса джоб-бэкэнда в миллисекундах
209
+ text: "Initializing" // initializing text appears on progress bar when job is already queued but not started yet
210
+ }).show();
211
+ ```
212
+
213
+ ### Для обычных задач Resque
214
+ ```ruby
215
+ Resque.enqueue(ImageProcessingJob, id=2)
216
+ ```
217
+
218
+ ## Resque Scheduler
219
+
220
+ Расписание для cron-like заданий должно храниться здесь `config/resque_schedule.yml`
221
+
222
+ ## Resque Retry
223
+
224
+ В силу несовместимостей почти всех плагинов с resque-meta (unique основан на нём) - объявить задание перезапускаемым
225
+ в случае ошибки нужно ДО `unique`
226
+
227
+ ```ruby
228
+ class ResqueJobTest
229
+ include Resque::Integration
230
+
231
+ retrys delay: 10, limit: 2
232
+ unique
233
+ end
234
+ ```
235
+
236
+ ## Resque Multi Job Forks
237
+
238
+ ```yaml
239
+ workers:
240
+ 'high':
241
+ count: 1
242
+ jobs_per_fork: 10
243
+ ```
244
+
245
+ ## Resque Ordered
246
+
247
+ Уникальный по каким-то параметрам джоб может выполняться в одно и тоже время только на одном из воркеров
248
+
249
+ ```ruby
250
+ class ResqueJobTest
251
+ include Resque::Integration
252
+
253
+ unique { |company_id, param1| [company_id] }
254
+ ordered max_iterations: 20 # max_iterations - сколько раз запустится метод `execute` с аргументами в очереди,
255
+ # прежде чем джоб перепоставится
256
+
257
+ def self.execute(ordered_meta, company_id, param1)
258
+ heavy_lifting_work
259
+ end
260
+ end
261
+ ```
262
+
263
+ При необходимости, можно добиться уникальности упорядоченных джобов, указав параметры в опции `unique`
264
+
265
+ ```ruby
266
+ class UniqueOrderedJob
267
+ include Resque::Integration
268
+
269
+ unique { |company_id, param1| [company_id] }
270
+ ordered max_iterations: 10, unique: ->(_company_id, param1) { [param1] }
271
+ ...
272
+ end
273
+ ```
274
+
275
+ ## Contributing
276
+
277
+ 1. Fork it ( https://github.com/abak-press/resque-integration/fork )
278
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
279
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
280
+ 4. Push to the branch (`git push origin my-new-feature`)
281
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,47 @@
1
+ //модуль для показа прогресс-бара создания отчетов
2
+
3
+ app.modules.progressBar = (function(self) {
4
+
5
+ function _requestError(data) {
6
+ var payload = data.payload;
7
+
8
+ if (app.config.progressBar.documentLocationError) {
9
+ alert('Ошибка');
10
+ location.href = app.config.progressBar.documentLocationError;
11
+ }
12
+
13
+ if (payload && payload.error_messages) {
14
+ $('.js-pberrors').html(payload.error_messages.join('<br>'));
15
+ }
16
+ }
17
+
18
+ function _requestSuccess(data) {
19
+ var
20
+ progress = data.progress;
21
+
22
+ if (progress && progress.message) {
23
+ $('.js-pbstat').text(progress.num + ' из ' + progress.total + ' (' + Math.round(progress.percent) + '%)');
24
+ }
25
+ }
26
+
27
+ function _init() {
28
+ $('.js-progressbar').progressBar({
29
+ url: app.config.progressBar.url,
30
+ pid: app.config.progressBar.pid,
31
+ onSuccess: function() {
32
+ location.href = app.config.progressBar.documentLocation;
33
+ },
34
+ onError: function(data) {
35
+ _requestError(data);
36
+ },
37
+ onRequestSuccess: function(data) {
38
+ _requestSuccess(data);
39
+ }
40
+ });
41
+ }
42
+
43
+ self.load = function() {
44
+ _init();
45
+ };
46
+ return self;
47
+ }(app.modules.progressBar || {}));
@@ -0,0 +1,42 @@
1
+ module Resque
2
+ class JobsController < ActionController::Metal
3
+ include ActionController::ConditionalGet
4
+
5
+ JOB_ID_PATTERN = /([a-f0-9]{32})/
6
+
7
+ def show
8
+ unless meta
9
+ self.status = 404
10
+ self.content_type = "application/json; charset=utf-8"
11
+ self.response_body = '{"message":"not found"}'
12
+ return
13
+ end
14
+
15
+ data = {
16
+ enqueued_at: meta.enqueued_at,
17
+ started_at: meta.started_at,
18
+ finished_at: meta.finished_at,
19
+ succeeded: meta.succeeded?,
20
+ failed: meta.failed?,
21
+ progress: meta.progress,
22
+ payload: meta['payload']
23
+ }
24
+
25
+ self.status = 200
26
+ self.content_type = "application/json; charset=utf-8"
27
+ self.response_body = MultiJson.dump(data)
28
+ expires_now
29
+ end
30
+
31
+ private
32
+
33
+ def meta
34
+ @meta ||= Resque::Plugins::Meta.get_meta(meta_id) if meta_id
35
+ end
36
+
37
+ def meta_id
38
+ id = params[:id]
39
+ id if JOB_ID_PATTERN =~ id
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,9 @@
1
+ module Resque
2
+ module Queues
3
+ class InfoController < ActionController::Metal
4
+ def show
5
+ self.response_body = {data: Resque.queues_info.data}.to_json
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,38 @@
1
+ module Resque
2
+ module Queues
3
+ class StatusController < ActionController::Metal
4
+ def show
5
+ request = params.fetch('request')
6
+ self.response_body =
7
+ case request
8
+ when 'age'
9
+ age(params['queue'])
10
+ when 'size'
11
+ size(params['queue'])
12
+ when 'failures_count'
13
+ Resque.queues_info.failures_count_for_queue(params['queue'])
14
+ when 'threshold_size'
15
+ Resque.queues_info.threshold_size(params.fetch('queue'))
16
+ when 'threshold_age'
17
+ Resque.queues_info.threshold_age(params.fetch('queue'))
18
+ when /^threshold_failures_per_(?<period>\w+)$/
19
+ Resque.queues_info.threshold_failures_count(params.fetch('queue'), $LAST_MATCH_INFO['period'])
20
+ when 'channel'
21
+ Resque.queues_info.channel(params.fetch('queue'))
22
+ else
23
+ 0
24
+ end.to_s
25
+ end
26
+
27
+ private
28
+
29
+ def age(queue)
30
+ queue ? Resque.queues_info.age_for_queue(queue) : Resque.queues_info.age_overall
31
+ end
32
+
33
+ def size(queue)
34
+ queue ? Resque.queues_info.size_for_queue(queue) : Resque.queues_info.size_overall
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,17 @@
1
+ - content_for :javascripts_bottom do
2
+ = javascript_include_tag 'standalone/progress_bar', :defer => true
3
+
4
+ #progress
5
+ .progressbar.js-progressbar
6
+ #pbstat.js-pbstat
7
+ #pberrors.pberrors.js-pberrors
8
+ - if @back_url.present?
9
+ = link_to 'Закрыть', @back_url
10
+
11
+ :javascript
12
+ app.config.progressBar = {
13
+ url: #{job_status_path(domain: :current).to_json},
14
+ pid: #{@job_id.to_json},
15
+ documentLocation: #{@success_url.to_json},
16
+ documentLocationError: #{@error_url ? @error_url.to_json : nil.to_json}
17
+ };
data/bin/resque-status ADDED
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'redis'
5
+ require 'resque'
6
+ require 'active_support/core_ext/hash/slice'
7
+ require 'active_support/time'
8
+
9
+ options = {}
10
+
11
+ opt_parser = OptionParser.new do |opt|
12
+ opt.banner = 'Usage: resque-status [OPTIONS]'
13
+ opt.separator ''
14
+ opt.separator 'Resque Status Tool'
15
+ opt.separator ''
16
+
17
+ opt.separator 'Options:'
18
+
19
+ opt.on('-k', '--key [KEY]', 'resque info key, default pending') do |value|
20
+ options[:key] = value
21
+ end
22
+
23
+ opt.on('-h', '--host HOST', 'redis host') do |value|
24
+ options[:host] = value
25
+ end
26
+
27
+ opt.on('-p', '--port PORT', 'redis port') do |value|
28
+ options[:port] = value
29
+ end
30
+
31
+ opt.on('-n', '--namespace NAMESPACE', 'redis namespace') do |value|
32
+ options[:namespace] = value
33
+ end
34
+
35
+ opt.on('--help', 'help') do
36
+ puts opt_parser
37
+ exit
38
+ end
39
+ end
40
+
41
+ opt_parser.parse!
42
+
43
+ Resque.redis = Redis.new(options.slice(:host, :port))
44
+ Resque.redis.namespace = options.fetch(:namespace)
45
+
46
+ info = Resque.info
47
+ key = options.fetch(:key, :pending).to_sym
48
+
49
+ if info.key?(key)
50
+ puts info[key]
51
+ elsif key == :oldest
52
+ # oldest job work time in seconds
53
+ workers = Resque.workers
54
+ jobs = workers.map(&:job)
55
+ worker_jobs = workers.zip(jobs).reject { |w, j| w.idle? || j['queue'].nil? }
56
+ puts worker_jobs.map { |_, job| (Time.now.utc - DateTime.strptime(job['run_at'] ,'%Y-%m-%dT%H:%M:%S').utc).to_i }.max || 'null'
57
+ else
58
+ $stderr.puts "Unknown key. Should be one of the [#{info.keys.join(', ')}]"
59
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,13 @@
1
+ Rails.application.routes.draw do
2
+ namespace :resque do
3
+ namespace :queues do
4
+ resource :info, only: :show, controller: :info
5
+ resource :status, only: :show, controller: :status
6
+ end
7
+ end
8
+
9
+ get "#{Rails.application.config.resque_job_status.fetch(:route_path)}(/:id)",
10
+ to: 'resque/jobs#show',
11
+ as: 'job_status',
12
+ constraints: ::Rails.application.config.resque_job_status.fetch(:route_constraints)
13
+ end
data/config.ru ADDED
@@ -0,0 +1,9 @@
1
+ # coding: utf-8
2
+ require "rubygems"
3
+ require "bundler"
4
+
5
+ Bundler.setup
6
+
7
+ require "resque/integration/application"
8
+
9
+ run Resque::Integration::Application
data/dip.yml ADDED
@@ -0,0 +1,44 @@
1
+ version: '1'
2
+
3
+ environment:
4
+ DOCKER_RUBY_VERSION: '2.2'
5
+ RUBY_IMAGE_TAG: 2.2-latest
6
+ COMPOSE_FILE_EXT: development
7
+ RAILS_ENV: test
8
+ APRESS_GEMS_CREDENTIALS: ""
9
+
10
+ compose:
11
+ files:
12
+ - docker-compose.yml
13
+ - docker-compose.${COMPOSE_FILE_EXT}.yml
14
+
15
+ interaction:
16
+ sh:
17
+ service: app
18
+
19
+ irb:
20
+ service: app
21
+ command: irb
22
+
23
+ bundle:
24
+ service: app
25
+ command: bundle
26
+
27
+ appraisal:
28
+ service: app
29
+ command: bundle exec appraisal
30
+
31
+ rspec:
32
+ service: app
33
+ command: bundle exec appraisal bundle exec rspec
34
+
35
+ clean:
36
+ service: app
37
+ command: rm -f Gemfile.lock gemfiles/*.gemfile.*
38
+
39
+ provision:
40
+ - docker volume create --name bundler_data
41
+ - dip bundle config --local https://gems.railsc.ru/ ${APRESS_GEMS_CREDENTIALS}
42
+ - dip clean
43
+ - dip bundle install
44
+ - dip appraisal install
@@ -0,0 +1,12 @@
1
+ version: '2'
2
+
3
+ services:
4
+ app:
5
+ volumes:
6
+ - ../:/localgems
7
+ - bundler-data:/bundle
8
+
9
+ volumes:
10
+ bundler-data:
11
+ external:
12
+ name: bundler_data
@@ -0,0 +1,6 @@
1
+ version: '2'
2
+
3
+ services:
4
+ app:
5
+ volumes:
6
+ - /bundle:/bundle
@@ -0,0 +1,10 @@
1
+ version: '2'
2
+
3
+ services:
4
+ app:
5
+ image: abakpress/ruby-app:$RUBY_IMAGE_TAG
6
+ environment:
7
+ - BUNDLE_PATH=/bundle/$DOCKER_RUBY_VERSION
8
+ command: bash
9
+ volumes:
10
+ - .:/app
@@ -0,0 +1,38 @@
1
+ require 'rails'
2
+
3
+ # Supply generator for Rails 3.0.x or if asset pipeline is not enabled
4
+ if !defined?(Sprockets) || !::Rails.application.config.assets.enabled
5
+ module Resque::Integration
6
+ module Generators
7
+ class InstallGenerator < ::Rails::Generators::Base
8
+
9
+ desc "This generator installs resque-integration (#{Resque::Integration::VERSION}) assets"
10
+ source_root File.expand_path('../../../../../../vendor/assets', __FILE__)
11
+
12
+ def copy_resque_progressbar
13
+ say_status("copying", "resque-integration (#{Resque::Integration::VERSION})", :green)
14
+ copy_file "images/progressbar/white.gif", "public/images/progressbar/white.gif"
15
+ copy_file "javascripts/jquery.progressbar.js", "public/javascripts/jquery.js"
16
+ copy_file "stylesheets/jquery.progressbar.no_pipeline.css", "public/stylesheets/jquery.progressbar.css"
17
+ end
18
+ end
19
+ end
20
+ end
21
+ else
22
+ module Resque::Integration
23
+ module Generators
24
+ class InstallGenerator < ::Rails::Generators::Base
25
+ desc "Just show instructions so people will know what to do when mistakenly using generator for Rails 3.1 apps"
26
+
27
+ def do_nothing
28
+ say_status("deprecated", "You are using Rails 3.1 with the asset pipeline enabled, so this generator is not needed.")
29
+ say_status("", "The necessary files are already in your asset pipeline.")
30
+ say_status("", "Just add `//= require jquery.progressbar` to your app/assets/javascripts/application.js")
31
+ say_status("", "If you upgraded your app from Rails 3.0 and still have jquery.progressbar.js in your javascripts, be sure to remove them.")
32
+ say_status("", "If you do not want the asset pipeline enabled, you may turn it off in application.rb and re-run this generator.")
33
+ # ok, nothing
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+
3
+ module Resque
4
+ module Integration
5
+ # Extend your job with this class to see full backtrace in resque log
6
+ module Backtrace
7
+ def around_perform_backtrace(*)
8
+ yield
9
+ rescue => ex
10
+ $stderr.puts(_format_exception(ex))
11
+
12
+ raise
13
+ end
14
+
15
+ private
16
+ def _format_exception(exception)
17
+ bt = exception.backtrace.dup
18
+
19
+ "%s %s: %s (%s)\n%s" % [
20
+ Time.now.to_s,
21
+ bt.shift,
22
+ exception.message,
23
+ exception.class.to_s,
24
+ bt.map { |line| ' ' * 4 + line }.join("\n")
25
+ ]
26
+ end
27
+ end # module Backtrace
28
+ end # module Integration
29
+ end # module Resque