active_job_metadata 0.1.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.
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: []