resque-integration 3.4.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.
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