fanforce-app-worker 1.0.0

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1b22704a3d47459c5e47cf934525d3a0c7414aab
4
+ data.tar.gz: 841222ac9823186acad69b8ea3e80d0fb8cf7eef
5
+ SHA512:
6
+ metadata.gz: c6a5bf7e16db9836358bec430ac3fb263e397771973228aa2985fb0694e5c6355a20dd840eed353f43d08715db6f2adf56a541a5afa26c2c57ffb6cfefd4aa9c
7
+ data.tar.gz: 0d725b85abf7cfb875e5cf8594dabd9527c5e3e9aa2b464ab339914212dd457278116580d9e7e9f1f674f8c82737041cebc1f67266314a967a465811466f99f0
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
19
+ .DS_Store
20
+ .idea/
21
+ .idea/*
22
+ Gemfile.lock
@@ -0,0 +1 @@
1
+ 2.0.0-p576
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ --load ./.yardopts.rb
3
+ --markup-provider=redcarpet
4
+ --markup=markdown
5
+ --readme README.md
@@ -0,0 +1 @@
1
+ require 'redcarpet/compat'
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
4
+ group :development, :test do
5
+ gem 'rake'
6
+ gem 'minitest'
7
+ gem 'rack-test'
8
+ gem 'simplecov', :require => false
9
+ gem 'yard'
10
+ gem 'redcarpet'
11
+ gem 'github-markup', :require => true
12
+ end
@@ -0,0 +1,4 @@
1
+ fanforce-app-worker
2
+ ==
3
+
4
+ Fanforce worker used by apps to connect with iron_mg and iron_worker_ng
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env rake
2
+ require 'rubygems'
3
+ require 'bundler/setup'
4
+ require 'fileutils'
5
+ require 'logger'
6
+ require 'rake/testtask'
7
+ require 'active_support/all'
8
+ require 'bundler/gem_tasks'
9
+
10
+ #########################################################################################################
11
+
12
+ # Load ENV vars
13
+ if FileTest.exist?('.powenv')
14
+ File.open('.powenv', 'rb') do |powenv|
15
+ contents = powenv.read
16
+
17
+ lines = contents.gsub('export ', '').split(/\n\r?/).reject{|line| line.blank?}
18
+ lines.each do |line|
19
+ keyValue = line.split('=', 2)
20
+ next unless keyValue.count == 2
21
+ ENV[keyValue.first] = keyValue.last.gsub("'",'').gsub('"','')
22
+ end
23
+ end
24
+ end if !ENV['RACK_ENV'] or ENV['RACK_ENV'] == 'development'
25
+
26
+ Rake::TestTask.new do |t|
27
+ t.libs.push 'lib'
28
+ t.libs.push 'test'
29
+ t.pattern = 'test/**/*_test.rb'
30
+ t.verbose = false
31
+ end
32
+
33
+ #########################################################################################################
34
+
35
+ Rake::TaskManager.record_task_metadata = true
36
+
37
+ task :default do
38
+ puts "\nAVAILABLE TASKS:"
39
+ Rake.application.options.show_tasks = :tasks
40
+ Rake.application.options.full_description = false
41
+ Rake.application.options.show_task_pattern = //
42
+ Rake.application.display_tasks_and_comments
43
+ puts "\n"
44
+ end
45
+
46
+ task :environment do
47
+
48
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'fanforce/app_worker/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = 'fanforce-app-worker'
8
+ gem.version = Fanforce::AppWorker::VERSION
9
+ gem.date = Time.now.utc.strftime('%Y-%m-%d')
10
+ gem.summary = %q{Worker library to help Fanforce apps with background processing}
11
+
12
+ gem.authors = ['Caleb Clark']
13
+ gem.email = ['cclark@fanforce.com']
14
+ gem.homepage = 'http://github.com/fanforce/gem-app-worker'
15
+
16
+ gem.files = `git ls-files`.split($/)
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ['lib']
19
+
20
+ gem.add_runtime_dependency 'iron_mq', '~> 5.0.1'
21
+ gem.add_runtime_dependency 'iron_cache', '~> 1.4.2'
22
+ gem.add_runtime_dependency 'uuid', '~> 2.3.7'
23
+ gem.add_runtime_dependency 'activesupport', '~> 4.1.6'
24
+ gem.add_runtime_dependency 'redis', '~> 3.1.0'
25
+ gem.add_runtime_dependency 'multi_json', '>= 1.7.2'
26
+
27
+ gem.add_runtime_dependency 'fanforce-base', '~> 1.0'
28
+ gem.add_runtime_dependency 'fanforce-api', '~> 1.0'
29
+ end
@@ -0,0 +1,10 @@
1
+ require 'logger'
2
+ require 'multi_json'
3
+ require 'active_support/all'
4
+
5
+ require_relative 'app_worker/version'
6
+ require_relative 'app_worker/utils'
7
+ require_relative 'app_worker/worker'
8
+ require_relative 'app_worker/errors'
9
+ require_relative 'app_worker/runner'
10
+
@@ -0,0 +1,200 @@
1
+ require 'redis'
2
+ require 'uuid'
3
+
4
+ class Fanforce::AppWorker::Errors
5
+ include Fanforce::AppWorker::Utils
6
+
7
+ @@redis = nil
8
+
9
+ def self.redis
10
+ @@redis ||= Redis.new(url: Fanforce::AppWorker.redis_url_errorlog)
11
+ end
12
+
13
+ def self.find(queue_id, error_id)
14
+ Fanforce::AppWorker::Error.new(queue_id, error_id)
15
+ end
16
+
17
+ def self.list(queue_id, from_num=0, to_num=100, reverse=false)
18
+ error_ids = redis.lrange("ERRORS:#{queue_id}", from_num, to_num)
19
+ error_ids.reverse! if reverse
20
+ Fanforce::AppWorker::ErrorList.new(queue_id, error_ids)
21
+ end
22
+
23
+ def self.list_summaries(queue_id, from_num=0, to_num=100, reverse=false)
24
+ error_ids = redis.lrange("ERRORS:#{queue_id}", from_num, to_num)
25
+ error_ids.reverse! if reverse
26
+ Fanforce::AppWorker::ErrorList.new(queue_id, error_ids).summaries
27
+ end
28
+
29
+ def self.delete_all(queue_id, error_ids)
30
+ Fanforce::AppWorker::ErrorList.new(queue_id, error_ids).delete
31
+ end
32
+
33
+ def self.retry_all(queue_id, error_ids)
34
+ Fanforce::AppWorker::ErrorList.new(queue_id, error_ids).retry
35
+ end
36
+
37
+ def self.add(queue_id, e, job_data, worker_env)
38
+ error_id = UUID.generate
39
+ error = {
40
+ error_id: error_id,
41
+ class: Fanforce::AppWorker,
42
+ exception: e.class.name,
43
+ http_code: (e.code if e.respond_to?(:code)),
44
+ message: e.message,
45
+ backtrace: e.backtrace.to_json,
46
+ errored_at: Time.now,
47
+ queue_id: queue_id,
48
+ raw_json: job_data[:params].to_json,
49
+ env_vars: worker_env.to_json,
50
+ retries: job_data[:retries],
51
+ curl_command: (e.curl_command if e.respond_to?(:curl_command)),
52
+ iron_token: Fanforce::AppWorker.iron_token,
53
+ iron_project_id: Fanforce::AppWorker.iron_project_id
54
+ }
55
+ redis.multi do
56
+ redis.rpush("ERRORS:#{queue_id}", error_id)
57
+ redis.hmset("ERRORS:#{queue_id}:#{error_id}", error.flatten)
58
+ end
59
+ rescue => e
60
+ log.fatal '-----------------------------------------------------'
61
+ log.fatal 'APPWORKER ERRORED WHILE RECOVERING FROM JOB ERROR:'
62
+ log.fatal '-----------------------------------------------------'
63
+ error.each {|k,v| log.fatal "#{k}: #{v}" }
64
+ log.fatal '-----------------------------------------------------'
65
+ raise
66
+ end
67
+
68
+ end
69
+
70
+ ###################################################################################################################
71
+
72
+ class Fanforce::AppWorker::Error
73
+
74
+ def initialize(queue_id, error_id)
75
+ @queue_id = queue_id
76
+ @error_id = error_id
77
+ end
78
+
79
+ def delete
80
+ self.class.delete(@queue_id, @error_id)
81
+ return nil
82
+ end
83
+
84
+ def details
85
+ v = self.class.get_details(@queue_id, @error_id)
86
+ self.class.format_details(v)
87
+ end
88
+
89
+ def summary
90
+ v = self.class.get_summary(@queue_id, @error_id)
91
+ self.class.format_summary(v)
92
+ end
93
+
94
+ def retry
95
+ v = Error.get_all(@queue_id, @error_id)
96
+ Error.retry(@queue_id, v)
97
+ end
98
+
99
+ ###################################################################################################################
100
+
101
+ SUMMARY_FIELDS = [:error_id, :exception, :message, :errored_at, :raw_json, :retries]
102
+ DETAIL_FIELDS = [:error_id, :exception, :http_code, :message, :backtrace, :errored_at, :raw_json, :env_vars, :retries, :curl_command]
103
+
104
+ def self.redis; Fanforce::AppWorker::Errors.redis end
105
+
106
+ def self.delete(queue_id, error_id)
107
+ redis.lrem("ERRORS:#{queue_id}", 0, error_id)
108
+ redis.del("ERRORS:#{queue_id}:#{error_id}")
109
+ end
110
+
111
+ def self.get_summary(queue_id, error_id)
112
+ redis.hmget("ERRORS:#{queue_id}:#{error_id}", *SUMMARY_FIELDS)
113
+ end
114
+
115
+ def self.format_summary(summary)
116
+ format(Hash[SUMMARY_FIELDS.zip(summary)])
117
+ end
118
+
119
+ def self.format(error)
120
+ error.each do |k,v|
121
+ error[k] = case k
122
+ when :backtrace then MultiJson.load(v) rescue []
123
+ when :env_vars then MultiJson.load(v) rescue {}
124
+ when :retries then v.to_i
125
+ else v
126
+ end
127
+ end
128
+ end
129
+
130
+ def self.get_details(queue_id, error_id)
131
+ redis.hmget("ERRORS:#{queue_id}:#{error_id}", *DETAIL_FIELDS)
132
+ end
133
+
134
+ def self.format_details(details)
135
+ format(Hash[DETAIL_FIELDS.zip(details)])
136
+ end
137
+
138
+ def self.get_all(queue_id, error_id)
139
+ redis.hgetall("ERRORS:#{queue_id}:#{error_id}")
140
+ end
141
+
142
+ def self.retry(queue_id, raw_error)
143
+ error = format(raw_error.symbolize_keys)
144
+ payload = MultiJson.load(error[:raw_json], symbolize_keys: true) rescue {}
145
+ queue_id = queue_id.gsub(":#{ENV['DEVELOPER_EMAIL']}", '') if ENV['DEVELOPER_EMAIL']
146
+ Fanforce::AppWorker.set_config(iron_token: error[:iron_token], iron_project_id: error[:iron_project_id])
147
+ Fanforce::AppWorker.enqueue(queue_id, payload, :retries => error[:retries] + 1)
148
+ delete(queue_id, error[:error_id])
149
+ end
150
+
151
+ end
152
+
153
+ ###################################################################################################################
154
+
155
+ class Fanforce::AppWorker::ErrorList
156
+ Error = Fanforce::AppWorker::Error
157
+
158
+ def initialize(queue_id, error_ids)
159
+ @queue_id = queue_id
160
+ @error_ids = error_ids
161
+ end
162
+
163
+ def redis; Fanforce::AppWorker::Errors.redis end
164
+
165
+ def summaries
166
+ redis_responses = []
167
+ redis.multi do
168
+ @error_ids.each do |error_id|
169
+ redis_responses << Error.get_summary(@queue_id, error_id)
170
+ end
171
+ end
172
+ redis_responses.map do |redis_response|
173
+ Error.format_summary(redis_response.value)
174
+ end
175
+ end
176
+
177
+ def retry
178
+ redis_responses = []
179
+ redis.multi do
180
+ @error_ids.each do |error_id|
181
+ redis_responses << Error.get_all(@queue_id, error_id)
182
+ end
183
+ end
184
+ redis.multi do
185
+ redis_responses.map do |redis_response|
186
+ Error.retry(@queue_id, redis_response.value)
187
+ end
188
+ end
189
+ end
190
+
191
+ def delete
192
+ redis.multi do
193
+ @error_ids.each do |error_id|
194
+ Error.delete(@queue_id, error_id)
195
+ end
196
+ end
197
+ return nil
198
+ end
199
+
200
+ end
@@ -0,0 +1,116 @@
1
+ require 'iron_mq'
2
+ require 'fanforce/api'
3
+ require 'timeout'
4
+
5
+ class Fanforce::AppWorker::Runner
6
+ include Fanforce::AppWorker::Utils
7
+
8
+ MAX_EXECUTION_TIME = 3300
9
+ class Timeout < RuntimeError; end
10
+
11
+ def initialize(worker_data, min_execution_time=300, &code_block)
12
+ raise "min_execution_time was set to #{min_execution_time}, which is #{min_execution_time - MAX_EXECUTION_TIME} seconds too long" if min_execution_time > MAX_EXECUTION_TIME
13
+ log.debug '------------------------------------------------------------------------------------'
14
+ log.debug 'LOADING WORKER ENV'
15
+
16
+ @queue_id = worker_data['queue_id'] || (raise 'worker_data must contain queue_id')
17
+ @worker_env = worker_data['env_vars'] || {}
18
+
19
+ @min_execution_time = min_execution_time
20
+ @code_block = code_block
21
+
22
+ load_env_from_server
23
+ load_env_from_worker(@worker_env)
24
+ ENV.each {|k,v| log.debug "#{k} = #{v}" }
25
+
26
+ load_jobs
27
+ end
28
+
29
+ def load_jobs
30
+ log.debug '------------------------------------------------------------------------------------'
31
+ log.debug 'PROCESSING JOBS...'
32
+ log.debug '------------------------------------------------------------------------------------'
33
+ job_num = 0
34
+ job_data = nil
35
+ while job_has_enough_time_to_run and (job = Fanforce::AppWorker.iron_mq.queue(iron_queue_id(@queue_id)).get(timeout: 3600)) do
36
+ log.debug "- JOB #{job_num+=1}: #{job.body}"
37
+ timeout(worker_time_remaining, Timeout) do
38
+ job_data = nil
39
+ job_data = MultiJson.load(job.body, symbolize_keys: true)
40
+ run_job(job, job_data, &@code_block)
41
+ end
42
+ delete_job
43
+ log.debug '------------------------------------------------------------------------------------'
44
+ end
45
+ delete_job
46
+ log.debug 'WINDING DOWN WORKER!'
47
+
48
+ rescue Exception => e
49
+ handle_job_loading_error(e, job, job_data)
50
+ end
51
+
52
+ def load_env_from_server
53
+ if File.exists?('.developmentenv.rb')
54
+ require '.developmentenv'
55
+ elsif File.exists?('.stagingenv.rb')
56
+ require '.stagingenv'
57
+ elsif File.exists?('.productionenv.rb')
58
+ require '.productionenv'
59
+ end
60
+ end
61
+
62
+ def load_env_from_worker(vars)
63
+ vars.each {|k,v| ENV[k.to_s]=v }
64
+ end
65
+
66
+ def run_job(job, job_data, &code_block)
67
+ @current_job = job
68
+ @current_params = job_data[:params]
69
+ @current_retries = job_data[:retries]
70
+
71
+ code_block.call(job_data[:params].clone, retries: job_data[:retries], queue_id: @queue_id)
72
+ delete_job(job)
73
+
74
+ rescue Exception => e
75
+ handle_job_error(e, job, job_data)
76
+ end
77
+
78
+ def handle_job_loading_error(e, job, job_data)
79
+ raise($!, "#{$!}: THERE IS NO JOB", $!.backtrace) if job.nil?
80
+
81
+ delete_job(job)
82
+ log.debug 'REMOVED JOB FROM QUEUE, BUT COULD NOT SAVE TO ERROR CACHE...'
83
+ raise($!, "#{$!}: #{job_data.to_json}", $!.backtrace)
84
+ end
85
+
86
+ def handle_job_error(e, job, job_data)
87
+ raise($!, "#{$!}: THERE IS NO JOB", $!.backtrace) if job.nil?
88
+
89
+ delete_job(job)
90
+ require_relative 'errors'
91
+ log.debug 'REMOVED JOB FROM QUEUE, AND SAVING TO ERROR CACHE...'
92
+ Fanforce::AppWorker::Errors.add(@queue_id, e, job_data, @worker_env)
93
+ end
94
+
95
+ def worker_time_remaining
96
+ time_since_load = Time.now - Fanforce::AppWorker::LOADED_AT
97
+ MAX_EXECUTION_TIME - time_since_load
98
+ end
99
+
100
+ def job_has_enough_time_to_run
101
+ time_since_load = Time.now - Fanforce::AppWorker::LOADED_AT
102
+ return false if time_since_load > MAX_EXECUTION_TIME
103
+ return false if worker_time_remaining < @min_execution_time
104
+ return true
105
+ end
106
+
107
+ def delete_job(job=nil)
108
+ return if job.nil? and @current_job.nil?
109
+ (job || @current_job).delete
110
+ rescue Exception => e
111
+ log.debug "Job could not be deleted: #{e.message}"
112
+ ensure
113
+ @current_job = nil
114
+ end
115
+
116
+ end
@@ -0,0 +1,13 @@
1
+ module Fanforce::AppWorker::Utils
2
+ extend self
3
+ def self.included(base) base.extend(self) end
4
+
5
+ def log
6
+ Fanforce::AppWorker.log
7
+ end
8
+
9
+ def iron_queue_id(iron_queue_id)
10
+ (ENV['RACK_ENV'] == 'development' && ENV['DEVELOPER_EMAIL']) ? "#{iron_queue_id}:#{ENV['DEVELOPER_EMAIL']}" : iron_queue_id
11
+ end
12
+
13
+ end
@@ -0,0 +1,5 @@
1
+ class Fanforce
2
+ class AppWorker
3
+ VERSION = '1.0.0'
4
+ end
5
+ end
@@ -0,0 +1,50 @@
1
+ class Fanforce::AppWorker
2
+
3
+ LOADED_AT = Time.now
4
+
5
+ @@iron = {}
6
+ @@redis = {}
7
+ @@log = Logger.new($stdout)
8
+
9
+ def self.iron_token
10
+ @@iron[:token] || ENV['IRON_TOKEN']
11
+ end
12
+
13
+ def self.iron_project_id
14
+ @@iron[:project_id] || ENV['IRON_PROJECT_ID']
15
+ end
16
+
17
+ def self.iron_mq
18
+ require 'iron_mq'
19
+ @@iron[:mq] ||= IronMQ::Client.new(:token => @@iron[:token], :project_id => @@iron[:project_id])
20
+ end
21
+
22
+ def self.redis_url_errorlog
23
+ @@redis[:url_errorlog] || ENV['REDIS_URL_ERRORLOG'] || (raise 'No REDIS_URL_ERRORLOG found in ENV')
24
+ end
25
+
26
+ def self.set_config(obj)
27
+ @@iron[:token] = obj[:iron_token] if obj[:iron_token]
28
+ @@iron[:project_id] = obj[:iron_project_id] if obj[:iron_project_id]
29
+ @@redis[:url_errorlog] = obj[:redis_url_errorlog] if obj[:redis_url_errorlog]
30
+ end
31
+
32
+ def self.log
33
+ @@log
34
+ end
35
+
36
+ ##########################################################################################
37
+
38
+ def self.enqueue(queue_id, params, options={})
39
+ raise 'Params being sent to the queue must be a Hash' if !params.is_a?(Hash)
40
+
41
+ queue_id = Utils.iron_queue_id(queue_id)
42
+ retries = (options[:retries].present?) ? options.delete(:retries) : 0
43
+ iron_mq.queue(queue_id).post({params: params, retries: retries}.to_json, options)
44
+ end
45
+
46
+ def self.run(worker_data, min_execution_time=300, &code_block)
47
+ Runner.new(worker_data, min_execution_time, &code_block)
48
+ end
49
+
50
+ end
@@ -0,0 +1,27 @@
1
+ require 'test_helper'
2
+
3
+ describe Fanforce::AppWorker do
4
+
5
+ before do
6
+ clean_dbs
7
+ end
8
+
9
+ it 'should enqueue a job' do
10
+ Fanforce::AppWorker.enqueue('test', {name: 'caleb'})
11
+ assert Fanforce::AppWorker.iron_mq.queue('test').size == 1
12
+ end
13
+
14
+ it 'should run an enqueued job' do
15
+ Fanforce::AppWorker.enqueue('test', {name: 'caleb'})
16
+ processed_job = false
17
+
18
+ Fanforce::AppWorker.run({'queue_id' => 'test'}) do |params|
19
+ processed_job = true
20
+ assert params[:name] == 'caleb'
21
+ end
22
+
23
+ assert processed_job == true
24
+ assert Fanforce::AppWorker.iron_mq.queue('test').size == 0
25
+ end
26
+
27
+ end
@@ -0,0 +1,32 @@
1
+ require 'test_helper'
2
+
3
+ describe Fanforce::AppWorker::Errors do
4
+
5
+ before do
6
+ clean_dbs
7
+ end
8
+
9
+ it 'should save error to redis' do
10
+ Fanforce::AppWorker.enqueue('test', {name: 'caleb'})
11
+ ran_job = false
12
+
13
+ Fanforce::AppWorker.run({'queue_id' => 'test'}) do |params|
14
+ ran_job = true
15
+ raise 'test'
16
+ end
17
+ assert ran_job == true
18
+ assert Fanforce::AppWorker::Errors.list_summaries('test').size == 1
19
+ assert Fanforce::AppWorker.iron_mq.queue('test').size == 0
20
+ end
21
+
22
+ it 'should correctly retry error' do
23
+ Fanforce::AppWorker.enqueue('test', {name: 'caleb'})
24
+
25
+ Fanforce::AppWorker.run({'queue_id' => 'test'}) do |params|
26
+ raise 'test'
27
+ end
28
+ assert Fanforce::AppWorker::Errors.list('test').retry
29
+ assert Fanforce::AppWorker.iron_mq.queue('test').size == 1
30
+ end
31
+
32
+ end
@@ -0,0 +1,22 @@
1
+ require 'rubygems'
2
+ require 'minitest/autorun'
3
+ require 'minitest/spec'
4
+ require 'rack/test'
5
+
6
+ ENV['RACK_ENV'] = 'test'
7
+
8
+ require 'fanforce/app_worker'
9
+
10
+ # Clean databases before each test case
11
+ def clean_dbs
12
+ Fanforce::AppWorker.set_config(
13
+ iron_token: 'TF0YNz8na2H_reJ7_EoRnQAeKHM',
14
+ iron_project_id: '53725dfe0db3ef0005000001',
15
+ redis_url_errorlog: 'redis://localhost:6379/5'
16
+ )
17
+ Fanforce::AppWorker.iron_mq.queues.all.each do |queue|
18
+ queue.delete_queue
19
+ end
20
+ Fanforce::AppWorker::Errors.redis.flushdb
21
+ Fanforce::AppWorker.log.level = Logger::FATAL
22
+ end
metadata ADDED
@@ -0,0 +1,176 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fanforce-app-worker
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Caleb Clark
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-11-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: iron_mq
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 5.0.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 5.0.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: iron_cache
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 1.4.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 1.4.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: uuid
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 2.3.7
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 2.3.7
55
+ - !ruby/object:Gem::Dependency
56
+ name: activesupport
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 4.1.6
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 4.1.6
69
+ - !ruby/object:Gem::Dependency
70
+ name: redis
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: 3.1.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: 3.1.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: multi_json
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: 1.7.2
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: 1.7.2
97
+ - !ruby/object:Gem::Dependency
98
+ name: fanforce-base
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ version: '1.0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: '1.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: fanforce-api
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ version: '1.0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ~>
123
+ - !ruby/object:Gem::Version
124
+ version: '1.0'
125
+ description:
126
+ email:
127
+ - cclark@fanforce.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - .gitignore
133
+ - .ruby-version
134
+ - .yardopts
135
+ - .yardopts.rb
136
+ - Gemfile
137
+ - README.md
138
+ - Rakefile
139
+ - fanforce-app-worker.gemspec
140
+ - lib/fanforce/app_worker.rb
141
+ - lib/fanforce/app_worker/errors.rb
142
+ - lib/fanforce/app_worker/runner.rb
143
+ - lib/fanforce/app_worker/utils.rb
144
+ - lib/fanforce/app_worker/version.rb
145
+ - lib/fanforce/app_worker/worker.rb
146
+ - test/controllers/enqueue_test.rb
147
+ - test/controllers/error_test.rb
148
+ - test/test_helper.rb
149
+ homepage: http://github.com/fanforce/gem-app-worker
150
+ licenses: []
151
+ metadata: {}
152
+ post_install_message:
153
+ rdoc_options: []
154
+ require_paths:
155
+ - lib
156
+ required_ruby_version: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - '>='
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ required_rubygems_version: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - '>='
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ requirements: []
167
+ rubyforge_project:
168
+ rubygems_version: 2.0.14
169
+ signing_key:
170
+ specification_version: 4
171
+ summary: Worker library to help Fanforce apps with background processing
172
+ test_files:
173
+ - test/controllers/enqueue_test.rb
174
+ - test/controllers/error_test.rb
175
+ - test/test_helper.rb
176
+ has_rdoc: