active_job_metadata 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 843028f1e40916f667c6bae2fa228919eb0ed5d3
4
+ data.tar.gz: 068b6ec80c5f3560a907d3fa609fa4b6408482a5
5
+ SHA512:
6
+ metadata.gz: 2fe763b81691b7fa827e4dd41b2a37185a13d65205f4b9a03057d789600780f0925a50f21adf1004afce8b41071c1304c0e60284332e6f817cdeac6c9cc9a416
7
+ data.tar.gz: 25ef63615d7e34c1b91bca5d5baeb37e792ad729d4ae7a3275f7b29770d680bc84d10a5408c377c05a4a7a0ebe9ea6198eaa2c0393d55878056295f94ef1756e
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.3
4
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in active_job_metadata.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,128 @@
1
+ # ActiveJobMetadata
2
+
3
+ ActiveJobMetadata lets you store and retrieve metadata associated with ActiveJob jobs regardless of the queue processing library you use.
4
+
5
+ Primary use cases include:
6
+
7
+ - Providing job status clients
8
+ - Tracking job performance
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ ```ruby
15
+ gem 'active_job_metadata'
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ You can use as much or as little of ActiveJobMetadata as you need.
21
+
22
+ Use **Lifecyle** to track the current status of your job:
23
+
24
+ class MyJob < ActiveJob::Base
25
+ include ActiveJobMetadata::Lifecycle
26
+
27
+ def perform
28
+ #...
29
+ end
30
+ end
31
+
32
+ job = MyJob.new
33
+ job.enque
34
+ job.status #=> "enqueued"
35
+ ActiveJobMetadata.find(job.job_id) #=> {status: "enqueued"}
36
+
37
+ You can use this in RESTful controllers to let clients query for a job's status:
38
+
39
+ class JobsController
40
+ def create
41
+ job = MyJob.new
42
+ job.enque
43
+
44
+ render json: {id: job.job_id}
45
+ end
46
+
47
+ def show
48
+ metadata = ActiveJobMetadata.find(params[:id])
49
+
50
+ if metadata
51
+ render json: metadata
52
+ else
53
+ render json: {message: "Not Found"}, status: 404
54
+ end
55
+ end
56
+ end
57
+
58
+ Use the **Timing** module to track and report on execution times:
59
+
60
+ class TimedJob < ActiveJob::Base
61
+ include ActiveJobMetadata::Timing
62
+ after_peform :report_timing
63
+
64
+ def perform
65
+ #...
66
+ end
67
+
68
+ def report_timing
69
+ Rails.logger.info "Queue Duration: #{queue_duration}"
70
+ Rails.logger.info "Processing Duration: #{working_duration}"
71
+ Rails.logger.info "Total Duration: #{total_duration}"
72
+ end
73
+ end
74
+
75
+ Of course you could hook this up to [statsd](https://github.com/reinh/statsd), or any other place you collect metrics.
76
+
77
+ Use the **Metadata** module to track custom information. For instance a long running job might report the percent complete and the name of the last file it processed:
78
+
79
+ class LongRunningJob < ActiveJob::Base
80
+ include ActiveJobMetadata::Metadata
81
+ metadata :percent_complete, :current_file
82
+
83
+ def perform(files)
84
+ files.each_with_index do |file, i|
85
+ self.percent_complete = (files.length / i.to_f)
86
+ self.current_file = file
87
+
88
+ IO.open(file) do |io|
89
+ #...
90
+ end
91
+ end
92
+ end
93
+
94
+ end
95
+
96
+ Use this approach to enrich metadata you send back to your client.
97
+
98
+ ## Configuration
99
+
100
+ Although ActiveJobMetadata should mostly work out of the box in a development environment, you will probably want to customize the backing store. ActiveJobMetadata reads and writes to an [ActiveSupport::Cache::Store](http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html). If you use more than one web server, which is likely, you will probably want to use something like [Readthis](https://github.com/sorentwo/readthis) which provides a shared interface via Redis. If you want to use a different backend for ActiveJobMetadata than Rails, create an initializer with your custom store:
101
+
102
+ # initializers/active_job_metadata.rb
103
+
104
+ # Configure ActiveJobMetadata to use Readthis:
105
+ ActiveJobMetadata.store = Readthis::Cache.new(
106
+ expires_in: 2.days.to_i
107
+ )
108
+
109
+ Currently ActiveJobMetadata only uses the `read` and `write` methods, so you can easily store your metadata wherever all your servers can easily access it if you don't mind implementing something that quacks like a [ActiveSupport::Cache::Store](http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html).
110
+
111
+ ## Alternatives
112
+
113
+ It is always wise to consider your alternatives:
114
+
115
+ * [active_job_status](https://github.com/cdale77/active_job_status)
116
+ * [resque-status](https://github.com/quirkey/resque-status) for Resque
117
+ * [sidekiq-status](https://github.com/utgarda/sidekiq-status) for Sidekiq
118
+
119
+ ## Development
120
+
121
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
122
+
123
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
124
+
125
+ ## Contributing
126
+
127
+ Bug reports and pull requests are welcome on GitHub at https://github.com/adamsanderson/active_job_metadata.
128
+
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+
8
+ # Disable warnings to avoid circular load warnings in dependencies
9
+ t.warning = false
10
+
11
+ t.test_files = FileList['test/**/*_test.rb']
12
+ end
13
+
14
+ task :default => :test
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'active_job_metadata/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "active_job_metadata"
8
+ spec.version = ActiveJobMetadata::VERSION
9
+ spec.authors = ["adam sanderson"]
10
+ spec.email = ["netghost@gmail.com"]
11
+
12
+ spec.summary = "Store status and metadata for ActiveJobs"
13
+ spec.description = "ActiveJobMetadata lets you store and retrieve metadata associated with ActiveJob jobs regardless of the queue processing library you use."
14
+ spec.homepage = "https://github.com/adamsanderson/active_job_metadata"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "activejob", ">= 4.2", "< 5.1"
22
+ spec.add_dependency "activesupport", ">= 4.2", "< 5.1"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.10"
25
+ spec.add_development_dependency "rake", "~> 11.0"
26
+ spec.add_development_dependency "minitest", "~> 5.9"
27
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "active_job_metadata"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,54 @@
1
+ require "active_job_metadata/version"
2
+ require "active_support"
3
+
4
+ module ActiveJobMetadata
5
+
6
+ # Prefix used by ActiveJobMetadata for finding and setting metadata
7
+ # by job id.
8
+ mattr_accessor(:prefix) do
9
+ "ActiveJobMetadata"
10
+ end
11
+
12
+ # ActiveSupport::Cache::Store used to save metadata. By default this uses the Rails cache,
13
+ # though you may want to use a special store if metadata is likely to fall out of the cache.
14
+ #
15
+ # Consider adjusting the retention policies to be long enough to last the total lifetime of
16
+ # a job plus some time afterwards if client code may check it at a later date.
17
+ mattr_accessor(:store) do
18
+ Rails.cache if defined? Rails
19
+ end
20
+
21
+ # Same as find, but will raise an ActiveJobMetadata::MetadataNotFound exception if
22
+ # the job's metadata is not found.
23
+ def self.find!(job_id)
24
+ find(job_id) || raise(MetadataNotFound.new("Could not find metadata: #{job_id.inspect}"))
25
+ end
26
+
27
+ # Finds a job's metadata, otherwise returns nil. Note that depending on how your store is
28
+ # configured, metadata may not always be available. For instance, if your store is configured
29
+ # to expire data after one day, then that metadata will no longer be available.
30
+ def self.find(job_id)
31
+ metadata_key = self.key(job_id)
32
+ store.read(metadata_key)
33
+ end
34
+
35
+ # Writes metadata for a job directly to the cache. Typically you should use accessors defined
36
+ # via `metadata` on a class. See ActiveJobMetadata::Metdadata for more information.
37
+ def self.write(job_id, metadata)
38
+ metadata_key = self.key(job_id)
39
+ store.write(metadata_key, metadata)
40
+ end
41
+
42
+ # Generates a key for a given job id. You generally shouldn't need this.
43
+ def self.key(job_id)
44
+ "#{ActiveJobMetadata.prefix}-#{job_id}"
45
+ end
46
+
47
+ class MetadataNotFound < StandardError
48
+ end
49
+ end
50
+
51
+ require "active_job_metadata/metadata"
52
+ require "active_job_metadata/lifecycle"
53
+ require "active_job_metadata/timing"
54
+ require "active_job_metadata/all"
@@ -0,0 +1,7 @@
1
+ module ActiveJobMetadata::All
2
+ extend ActiveSupport::Concern
3
+
4
+ include ActiveJobMetadata::Metadata
5
+ include ActiveJobMetadata::Lifecycle
6
+ include ActiveJobMetadata::Timing
7
+ end
@@ -0,0 +1,19 @@
1
+ # Use Lifecyle to track the current status of your job.
2
+ #
3
+ module ActiveJobMetadata::Lifecycle
4
+ ENQUEUED = "enqueued"
5
+ RUNNING = "running"
6
+ DONE = "done"
7
+
8
+ extend ActiveSupport::Concern
9
+ include ActiveJobMetadata::Metadata
10
+
11
+ included do
12
+ metadata :status
13
+
14
+ after_enqueue { self.status = "enqueued" }
15
+ before_perform { self.status = "running" }
16
+ after_perform { self.status = "done" }
17
+ end
18
+
19
+ end
@@ -0,0 +1,58 @@
1
+ # Use Metadata to define custom metadata for your job.
2
+ #
3
+ module ActiveJobMetadata::Metadata
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ # No-op
8
+ end
9
+
10
+ class_methods do
11
+
12
+ # Defines metadata accessors for each specified attribute.
13
+ #
14
+ # class LongRunningJob < ActiveJob::Base
15
+ # include ActiveJobMetadata::Metadata
16
+ # metadata :percent_complete, :current_file
17
+ # end
18
+ #
19
+ # job = LongRunningJob.new
20
+ # job.percent_complete = 80
21
+ # job.metadata #=> {percent_complete: 80}
22
+ #
23
+ def metadata *names
24
+ names.each do |name|
25
+
26
+ define_method(name) do
27
+ self.metadata[name.to_sym]
28
+ end
29
+
30
+ define_method("#{name}=") do |value|
31
+ self.metadata[name.to_sym] = value
32
+ save_metadata
33
+ value
34
+ end
35
+
36
+ end
37
+ end
38
+ end
39
+
40
+ # Returns all metadata for the current job.
41
+ def metadata
42
+ @metadata ||= ActiveJobMetadata.find(job_id) || {}
43
+ end
44
+
45
+ # Merges data into the current job's metdata.
46
+ def metadata= hash
47
+ metadata.merge!(hash)
48
+ save_metadata
49
+ end
50
+
51
+ # Writes metadata to `ActiveJobMetadata.store`
52
+ def save_metadata
53
+ ActiveJobMetadata.write(job_id, metadata)
54
+
55
+ metadata
56
+ end
57
+
58
+ end
@@ -0,0 +1,44 @@
1
+ # Use Timing to track and report on execution times.
2
+ #
3
+ module ActiveJobMetadata::Timing
4
+ extend ActiveSupport::Concern
5
+ include ActiveJobMetadata::Metadata
6
+
7
+ included do
8
+ metadata :enqueued_at
9
+ metadata :started_at
10
+ metadata :done_at
11
+
12
+ after_enqueue { self.enqueued_at = Time.now }
13
+ before_perform { self.started_at = Time.now }
14
+ after_perform { self.done_at = Time.now }
15
+ end
16
+
17
+ # The queue duration is the time in seconds a job spent in the queue before
18
+ # it was processed. If it has not been enqueued or processing
19
+ # has not yet begun, it will return nil.
20
+ def queue_duration
21
+ return nil unless enqueued_at && started_at
22
+ started_at - enqueued_at
23
+ end
24
+
25
+ # The working duration is the time in seconds a job spent in the process
26
+ # method. If process has not yet been called, or process has not finished
27
+ # it will return nil.
28
+ def working_duration
29
+ return nil unless done_at && started_at
30
+ done_at - started_at
31
+ end
32
+
33
+ # The total duration is the sum of the queue and working duration. This
34
+ # gives a measure of how long it took from enqueuing the job to completing
35
+ # all processing.
36
+ def total_duration
37
+ q = queue_duration
38
+ w = working_duration
39
+ return nil unless q && w
40
+
41
+ queue_duration + working_duration
42
+ end
43
+
44
+ end
@@ -0,0 +1,3 @@
1
+ module ActiveJobMetadata
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,140 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: active_job_metadata
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - adam sanderson
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-09-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activejob
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '4.2'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '5.1'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '4.2'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '5.1'
33
+ - !ruby/object:Gem::Dependency
34
+ name: activesupport
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '4.2'
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: '5.1'
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '4.2'
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: '5.1'
53
+ - !ruby/object:Gem::Dependency
54
+ name: bundler
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '1.10'
60
+ type: :development
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: '1.10'
67
+ - !ruby/object:Gem::Dependency
68
+ name: rake
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '11.0'
74
+ type: :development
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - "~>"
79
+ - !ruby/object:Gem::Version
80
+ version: '11.0'
81
+ - !ruby/object:Gem::Dependency
82
+ name: minitest
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - "~>"
86
+ - !ruby/object:Gem::Version
87
+ version: '5.9'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - "~>"
93
+ - !ruby/object:Gem::Version
94
+ version: '5.9'
95
+ description: ActiveJobMetadata lets you store and retrieve metadata associated with
96
+ ActiveJob jobs regardless of the queue processing library you use.
97
+ email:
98
+ - netghost@gmail.com
99
+ executables: []
100
+ extensions: []
101
+ extra_rdoc_files: []
102
+ files:
103
+ - ".gitignore"
104
+ - ".travis.yml"
105
+ - Gemfile
106
+ - README.md
107
+ - Rakefile
108
+ - active_job_metadata.gemspec
109
+ - bin/console
110
+ - bin/setup
111
+ - lib/active_job_metadata.rb
112
+ - lib/active_job_metadata/all.rb
113
+ - lib/active_job_metadata/lifecycle.rb
114
+ - lib/active_job_metadata/metadata.rb
115
+ - lib/active_job_metadata/timing.rb
116
+ - lib/active_job_metadata/version.rb
117
+ homepage: https://github.com/adamsanderson/active_job_metadata
118
+ licenses: []
119
+ metadata: {}
120
+ post_install_message:
121
+ rdoc_options: []
122
+ require_paths:
123
+ - lib
124
+ required_ruby_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ required_rubygems_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ requirements: []
135
+ rubyforge_project:
136
+ rubygems_version: 2.4.5.1
137
+ signing_key:
138
+ specification_version: 4
139
+ summary: Store status and metadata for ActiveJobs
140
+ test_files: []