ctt-background-jobs 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/.gitignore +18 -0
  2. data/.rspec +1 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/CTT-Background-Jobs.gemspec +37 -0
  6. data/Gemfile +6 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +25 -0
  9. data/Rakefile +29 -0
  10. data/lib/background_jobs/configuration.rb +27 -0
  11. data/lib/background_jobs/job.rb +17 -0
  12. data/lib/background_jobs/job_factory.rb +9 -0
  13. data/lib/background_jobs/job_id_generator.rb +15 -0
  14. data/lib/background_jobs/job_notifier.rb +31 -0
  15. data/lib/background_jobs/job_queue_factory.rb +36 -0
  16. data/lib/background_jobs/job_registry.rb +65 -0
  17. data/lib/background_jobs/queue_service.rb +17 -0
  18. data/lib/background_jobs/strategies/direct_call/job_queue.rb +11 -0
  19. data/lib/background_jobs/strategies/sidekiq/job_adapter.rb +21 -0
  20. data/lib/background_jobs/strategies/sidekiq/job_attributes_adapter.rb +141 -0
  21. data/lib/background_jobs/strategies/sidekiq/job_queue.rb +42 -0
  22. data/lib/background_jobs/strategies/sidekiq/setup.rb +31 -0
  23. data/lib/version.rb +3 -0
  24. data/spec/fast_helper.rb +11 -0
  25. data/spec/integration/sidekiq_spec.rb +34 -0
  26. data/spec/lib/background_jobs/configuration_spec.rb +13 -0
  27. data/spec/lib/background_jobs/job_factory_fast_spec.rb +24 -0
  28. data/spec/lib/background_jobs/job_fast_spec.rb +38 -0
  29. data/spec/lib/background_jobs/job_id_generator_fast_spec.rb +23 -0
  30. data/spec/lib/background_jobs/job_notififer_spec.rb +9 -0
  31. data/spec/lib/background_jobs/job_queue_factory_fast_spec.rb +58 -0
  32. data/spec/lib/background_jobs/queue_service_fast_spec.rb +36 -0
  33. data/spec/lib/background_jobs/strategies/direct_call/job_queue_spec.rb +27 -0
  34. data/spec/lib/background_jobs/strategies/sidekiq/job_adapter_spec.rb +30 -0
  35. data/spec/lib/background_jobs/strategies/sidekiq/job_attributes_adapter_spec.rb +129 -0
  36. data/spec/lib/background_jobs/strategies/sidekiq/job_queue_spec.rb +63 -0
  37. data/spec/lib/background_jobs/torquebox_jobs_queue_fast_spec.rb +31 -0
  38. data/spec/support/job_queue.rb +7 -0
  39. metadata +281 -0
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ background-jobs.sublime-*
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ ctt-background-jobs
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-1.9.3-p194
@@ -0,0 +1,37 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ctt-background-jobs"
8
+ spec.version = BackgroundJobs::VERSION
9
+ spec.authors = ["CTT Innovations"]
10
+ spec.email = ["info@cttinnovations.com"]
11
+ spec.description = 'Common CTT caching tools.'
12
+ spec.summary = 'CTT Caching.'
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'ctt-notifications', "~> 0.1"
22
+ spec.add_dependency 'require_relative', '1.0.2'
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.3"
25
+ spec.add_development_dependency 'delayed_job_active_record', '0.4.2'
26
+ spec.add_development_dependency 'imitate', '0.2.0'
27
+ spec.add_development_dependency 'mocha', '0.13.3'
28
+ spec.add_development_dependency "rake"
29
+ spec.add_development_dependency 'rspec', '2.13.0'
30
+ spec.add_development_dependency 'shoulda', '3.4.0'
31
+ spec.add_development_dependency 'sidekiq'
32
+ spec.add_development_dependency 'timecop', '0.6.1'
33
+
34
+ if RUBY_ENGINE == 'jruby'
35
+ spec.add_development_dependency 'torquebox', '2.3.0'
36
+ end
37
+ end
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+ source 'http://gems.github.com'
3
+ source 'http://gemfile:NLjoiningcare2013@gems.cttinnovations.net'
4
+
5
+ # Specify your gem's dependencies in ctt-background-jobs.gemspec
6
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Petr Janda
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # CTT::BackgroundJobs
2
+
3
+ ## Installation
4
+
5
+ Add this line to your application's Gemfile:
6
+
7
+ gem 'ctt-background-jobs'
8
+
9
+ And then execute:
10
+
11
+ $ bundle
12
+
13
+ Or install it yourself as:
14
+
15
+ $ gem install ctt-background-jobs
16
+
17
+ ## Usage
18
+
19
+ ## Contributing
20
+
21
+ 1. Fork it
22
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
23
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
24
+ 4. Push to the branch (`git push origin my-new-feature`)
25
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ task :release_private do
4
+ require 'version'
5
+
6
+ version = BackgroundJobs::VERSION
7
+
8
+ puts '** Commiting the VERSION file'
9
+ system 'git add lib/version.rb'
10
+ system "git commit -m 'v#{version}'"
11
+
12
+ puts '** Tagging the git version'
13
+ system "git tag v#{version}"
14
+
15
+ puts '** Pushing tag to remote'
16
+ system "git push origin v#{version}"
17
+
18
+
19
+ puts '** Pushing to remote'
20
+ system 'git push origin master'
21
+
22
+ puts '** Building the gem'
23
+ system 'gem build ctt-background-jobs.gemspec'
24
+
25
+ puts '** Publishing the gem'
26
+ system "gem inabox ctt-background-jobs-#{version}.gem"
27
+
28
+ puts '** Relax. All done.'
29
+ end
@@ -0,0 +1,27 @@
1
+ require 'singleton'
2
+
3
+ module BackgroundJobs
4
+ def self.configure
5
+ yield Configuration.instance
6
+
7
+ strategy = BackgroundJobs.config.strategy
8
+
9
+ case strategy
10
+ when 'sidekiq'
11
+ options = BackgroundJobs.config.strategy_options
12
+
13
+ require 'background_jobs/strategies/sidekiq/setup'
14
+ BackgroundJobs::Sidekiq.setup options
15
+ end
16
+ end
17
+
18
+ def self.config
19
+ Configuration.instance
20
+ end
21
+
22
+ class Configuration
23
+ include Singleton
24
+
25
+ attr_accessor :strategy, :strategy_options
26
+ end
27
+ end
@@ -0,0 +1,17 @@
1
+ module BackgroundJobs
2
+ class Job
3
+ attr_writer :id
4
+
5
+ def perform
6
+ execute
7
+ end
8
+
9
+ def execute
10
+ raise NotImplementedError
11
+ end
12
+
13
+ def error(job, exception)
14
+ Airbrake.notify exception
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ require 'background_jobs/job_registry'
2
+
3
+ module BackgroundJobs
4
+ class JobFactory
5
+ def self.build(job_name, attributes = [])
6
+ JobRegistry.instance.get_class!(job_name).new(*attributes)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ require 'digest'
2
+
3
+ module BackgroundJobs
4
+ module JobIdGenerator
5
+ def self.included(base)
6
+ base.extend ClassMethods
7
+ end
8
+
9
+ module ClassMethods
10
+ def generate_job_id
11
+ Digest::SHA256.hexdigest("#{Time.now.to_f}-#{rand(0)}")
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,31 @@
1
+ require 'i18n'
2
+ require 'notification/notifier_service'
3
+ require 'notification/channel_name_service'
4
+
5
+ module BackgroundJobs
6
+ module JobNotifier
7
+ def error(job, exception)
8
+ super(job, exception)
9
+
10
+ notify_error(:message => I18n.t(:flash_background_error))
11
+ end
12
+
13
+ private
14
+
15
+ def notify_error(data)
16
+ notify('error', data)
17
+ end
18
+
19
+ def notify_success(data)
20
+ notify('success', data)
21
+ end
22
+
23
+ def notify(event, data)
24
+ Notification::NotifierService.notify(channel_name, event, data)
25
+ end
26
+
27
+ def channel_name
28
+ @channel_name ||= Notification::ChannelNameService.private_channel_name(@id)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,36 @@
1
+ require_relative 'configuration'
2
+
3
+ module BackgroundJobs
4
+ class UnknownBackgroundJobsQueueStrategy < RuntimeError
5
+ def initialize(strategy_name)
6
+ @strategy_name = strategy_name
7
+ end
8
+
9
+ def to_s
10
+ "Unknown strategy '#{@strategy_name}' doesnt exist!"
11
+ end
12
+ end
13
+
14
+ class JobQueueFactory
15
+ def self.build
16
+ to_class(Configuration.instance.strategy)
17
+ end
18
+
19
+ private
20
+
21
+ def self.to_class(strategy_name)
22
+ case strategy_name
23
+ when 'sidekiq'
24
+ require 'background_jobs/strategies/sidekiq/job_queue'
25
+ Sidekiq::JobQueue.new
26
+
27
+ when 'direct_call'
28
+ require 'background_jobs/strategies/direct_call/job_queue'
29
+ DirectCall::JobQueue.new
30
+
31
+ else
32
+ raise UnknownBackgroundJobsQueueStrategy.new(strategy_name)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,65 @@
1
+ require 'singleton'
2
+
3
+ module BackgroundJobs
4
+ class UnknownJob < RuntimeError
5
+ def initialize(job_name)
6
+ @job_name = job_name
7
+ end
8
+
9
+ def to_s
10
+ "Unknown job '#{@job_name}'!"
11
+ end
12
+ end
13
+
14
+ class JobMetadata
15
+ attr_reader :name, :type, :clazz, :options
16
+
17
+ def initialize(name, type, clazz, options = nil)
18
+ @name, @type, @clazz, @options = name, type, clazz, options
19
+ end
20
+ end
21
+
22
+ class JobRegistry
23
+ include Singleton
24
+
25
+ attr_writer :register_callback
26
+
27
+ def register_job(job_metadata)
28
+ jobs[job_metadata.name] = job_metadata
29
+
30
+ @register_callback.call(job_metadata) if @register_callback
31
+ end
32
+
33
+ def get_class!(job_name)
34
+ raise UnknownJob.new(job_name) unless job_metadata = get(job_name)
35
+
36
+ job_metadata.clazz
37
+ end
38
+
39
+ def get_type(job_name)
40
+ raise UnknownJob.new(job_name) unless job_metadata = get(job_name)
41
+
42
+ job_metadata.type
43
+ end
44
+
45
+ private
46
+
47
+ def get(job_name)
48
+ jobs[job_name]
49
+ end
50
+
51
+ def jobs
52
+ @jobs ||= {}
53
+ end
54
+ end
55
+
56
+ def self.register_job(job_name, job_type, job_class, options = {})
57
+ job_registry.register_job JobMetadata.new(job_name, job_type, job_class, options)
58
+ end
59
+
60
+ private
61
+
62
+ def self.job_registry
63
+ JobRegistry.instance
64
+ end
65
+ end
@@ -0,0 +1,17 @@
1
+ require 'background_jobs/job_id_generator'
2
+ require 'background_jobs/job_queue_factory'
3
+
4
+ module BackgroundJobs
5
+ class QueueService
6
+ include JobIdGenerator
7
+
8
+ def self.enqueue(job_type, attributes = [], options = {})
9
+ job_id = generate_job_id
10
+
11
+ job_queue = JobQueueFactory.build
12
+ job_queue.enqueue(job_type, job_id, attributes, options)
13
+
14
+ job_id
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ require 'background_jobs/job_factory'
2
+
3
+ module BackgroundJobs
4
+ module DirectCall
5
+ class JobQueue
6
+ def enqueue(job_type, job_id, attrs, options = {})
7
+ JobFactory.build(job_type, attrs).execute
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,21 @@
1
+ require 'sidekiq'
2
+ require 'background_jobs/job_factory'
3
+ require 'background_jobs/strategies/sidekiq/job_attributes_adapter'
4
+
5
+ module BackgroundJobs
6
+ module Sidekiq
7
+ class JobAdapter
8
+ include ::Sidekiq::Worker
9
+
10
+ def perform(*attrs)
11
+ job_type = attrs.shift
12
+ job_id = attrs.shift
13
+ attrs = JobAttributesAdapter.new(attrs).decode
14
+
15
+ job = JobFactory.build(job_type.to_sym, attrs)
16
+ job.id = job_id.to_i
17
+ job.execute
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,141 @@
1
+ module BackgroundJobs
2
+ module Sidekiq
3
+ class JobAttributesAdapter
4
+ def initialize(attributes)
5
+ @attributes = Attributes.new(attributes)
6
+ end
7
+
8
+ def encode
9
+ @attributes.encode
10
+ end
11
+
12
+ def decode
13
+ @attributes.decode
14
+ end
15
+ end
16
+
17
+ class Attributes < Array
18
+ def initialize(array)
19
+ super array.map {|attr| create_attribute(attr) }
20
+ end
21
+
22
+ def encode
23
+ map(&:encode)
24
+ end
25
+
26
+ def decode
27
+ map(&:decode)
28
+ end
29
+
30
+ private
31
+
32
+ def create_attribute(attr)
33
+ AttributeFactory.build(attr)
34
+ end
35
+ end
36
+
37
+ class AttributeFactory
38
+ def self.build(value)
39
+ return HashAttribute.new(value) if value.kind_of?(Hash)
40
+ return ArrayAttribute.new(value) if value.kind_of?(Array)
41
+ return Attribute.new(value) unless value.kind_of?(String)
42
+ StringAttribute.new(value)
43
+ end
44
+ end
45
+
46
+ class Attribute
47
+ def initialize(value)
48
+ @value = value
49
+ end
50
+
51
+ def decode
52
+ @value
53
+ end
54
+
55
+ def encode
56
+ case @value.class.name
57
+ when 'Date'
58
+ "date:#{@value.to_s}"
59
+
60
+ when 'Hash'
61
+ HashAttribute.new(@value).encode
62
+
63
+ when 'Array'
64
+ ArrayAttribute.new(@value).encode
65
+
66
+ when 'Range'
67
+ "range:[#{Attribute.new(@value.first).encode},#{Attribute.new(@value.last).encode}]"
68
+
69
+ else
70
+ @value
71
+ end
72
+ end
73
+ end
74
+
75
+ class HashAttribute < Hash
76
+ def initialize(value)
77
+ @value = value
78
+ end
79
+
80
+ def encode
81
+ @value.keys.each do |key|
82
+ value = @value.delete(key)
83
+ @value[key.to_s] = AttributeFactory.build(value).encode
84
+ end
85
+
86
+ @value
87
+ end
88
+
89
+ def decode
90
+ @value.keys.each do |key|
91
+ @value[key.to_sym] = AttributeFactory.build(@value.delete(key)).decode
92
+ end
93
+
94
+ @value
95
+ end
96
+ end
97
+
98
+ class ArrayAttribute < Array
99
+ def decode
100
+ map {|i| AttributeFactory.build(i).decode }
101
+ end
102
+
103
+ def encode
104
+ map {|i| AttributeFactory.build(i).encode }
105
+ end
106
+ end
107
+
108
+ class StringAttribute < String
109
+ def decode
110
+ if number?
111
+ return to_i
112
+ end
113
+
114
+ if date?
115
+ return Date.parse(gsub('date:', ''))
116
+ end
117
+
118
+ if range?
119
+ matches = match(/range:\[(.*),(.*)\]/)
120
+ return AttributeFactory.build(matches[1]).decode..AttributeFactory.build(matches[2]).decode
121
+ end
122
+
123
+ self
124
+ end
125
+
126
+ private
127
+
128
+ def number?
129
+ match(/^\d*$/)
130
+ end
131
+
132
+ def date?
133
+ match(/^date:.*$/)
134
+ end
135
+
136
+ def range?
137
+ match(/^range:\[(.*),(.*)\]$/)
138
+ end
139
+ end
140
+ end
141
+ end