jobly 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
+ SHA256:
3
+ metadata.gz: 8ad67b7e96a1ee27e3034c812d05026f922b72585d70d0c8085f9173f8e70d04
4
+ data.tar.gz: 9614bb05c9da5bf17a9e2d25e3aafdd859bf811828f71c6a511557dc926985ae
5
+ SHA512:
6
+ metadata.gz: c891c3bbff7c1c37180fdc186b22f63a50730f978125546e48a2231ebeab630bb27312016c3d47fa39afcc48dd79bb0e59bb96c1abafebbc16804f77eec8da42
7
+ data.tar.gz: f021ecf23d579f53fc713b4657ca2613b429da6e7ff2e503e21bf9edb3d4b9f18bf48362349deafd0e1fab36324d51f029ea2d4e9f8dc5d38b43912ce63dce42
data/README.md ADDED
@@ -0,0 +1,76 @@
1
+ Jobly
2
+ ==================================================
3
+
4
+ [![Gem Version](https://badge.fury.io/rb/jobly.svg)](https://badge.fury.io/rb/jobly)
5
+ [![Build Status](https://travis-ci.com/DannyBen/jobly.svg?branch=master)](https://travis-ci.com/DannyBen/jobly)
6
+
7
+ ---
8
+
9
+ Job server toolbelt with API, CLI, Web UI and a Sidekiq heart.
10
+
11
+ ---
12
+
13
+ Installation
14
+ --------------------------------------------------
15
+
16
+ $ gem install jobly
17
+
18
+
19
+ What's in the Box
20
+ --------------------------------------------------
21
+
22
+ Jobly is a command line utility that wraps several goodies around the
23
+ sidekiq backgronud jobs system. It includes the following components:
24
+
25
+ - **Command line interface** - for starting the server, starting the worker,
26
+ and for running jobs.
27
+ - **Web API** - for executing jobs.
28
+ - **Web Dashboard** - including job progress and status.
29
+
30
+ Getting Started
31
+ --------------------------------------------------
32
+
33
+ TODO
34
+
35
+
36
+ Usage
37
+ --------------------------------------------------
38
+
39
+ ### Server
40
+
41
+ To start the server run `jobly server` and open <http://localhost:3000/>
42
+ in your browser.
43
+
44
+ This will start a webserver with two primary entrypoints:
45
+
46
+ - `/` (root) - a dashboard for your background job processes.
47
+ - `/do/:job?param=value` - an API for executing jobs
48
+
49
+
50
+ ### Worker
51
+
52
+ To start processing jobs, run `jobly worker`
53
+
54
+
55
+ ### Running jobs from the command line
56
+
57
+ There are three ways to run a job from the command line:
58
+
59
+ Run the job locally, without going through any of the background job
60
+ processing chain:
61
+
62
+ ```ruby
63
+ jobly run JobName param:value
64
+ ```
65
+
66
+ Run the job locally, but wait for a worker to process it.
67
+
68
+ ```ruby
69
+ jobly run --later JobName param:value
70
+ ```
71
+
72
+ Send a job through the API (either localhost or remote).
73
+
74
+ ```ruby
75
+ jobly send JobName param:value
76
+ ```
data/bin/jobly ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ require 'colsole'
3
+ require 'jobly'
4
+ require 'jobly/boot'
5
+
6
+ include Colsole
7
+
8
+ router = Jobly::CLI.router
9
+
10
+ begin
11
+ exit router.run ARGV
12
+ rescue => e
13
+ puts e.backtrace.reverse if ENV['DEBUG']
14
+ say! "!txtred!#{e.class}: #{e.message}"
15
+ exit 1
16
+ end
data/lib/jobly.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'requires'
2
+ require 'byebug' if ENV['BYEBUG']
3
+
4
+ requires 'jobly/refinements'
5
+ require 'jobly/exceptions'
6
+ require 'jobly/sidekiq'
7
+ require 'jobly/module_functions'
8
+ require 'jobly/cli'
9
+ require 'jobly/job'
10
+ require 'jobly/jobs'
11
+ require 'jobly/server'
data/lib/jobly/boot.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'jobly'
2
+
3
+ Jobly.load_custom_config
4
+ Jobly::Jobs.load_all
5
+ Jobly::SidekiqBoot.configure
data/lib/jobly/cli.rb ADDED
@@ -0,0 +1,23 @@
1
+ require 'mister_bin'
2
+ requires 'commands/base', 'commands'
3
+
4
+ module Jobly
5
+
6
+ # Command line interface router. This is called by bin/jobly.
7
+ class CLI
8
+ def self.router
9
+ router = MisterBin::Runner.new version: VERSION,
10
+ header: "Jobly",
11
+ footer: "Run !txtpur!jobly COMMAND --help!txtrst! for more information"
12
+
13
+ router.route 'server', to: Commands::ServerCmd
14
+ router.route 'worker', to: Commands::WorkerCmd
15
+ router.route 'send', to: Commands::SendCmd
16
+ router.route 'run', to: Commands::RunCmd
17
+ router.route 'config', to: Commands::ConfigCmd
18
+
19
+ router
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,11 @@
1
+ require 'mister_bin'
2
+ require 'colsole'
3
+ require 'lp'
4
+
5
+ module Jobly
6
+ module Commands
7
+ class Base < MisterBin::Command
8
+ include Colsole
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,34 @@
1
+ require 'pathname'
2
+
3
+ module Jobly
4
+ module Commands
5
+ class ConfigCmd < Base
6
+ summary "Show configuration options"
7
+ usage "jobly config"
8
+ usage "jobly config (-h|--help)"
9
+
10
+ def run
11
+ line "custom config file", short_config_path, Jobly.custom_config?
12
+ Jobly.options.each do |key, value|
13
+ if key.to_s.end_with? '_path'
14
+ line key, value, !Dir.exist?(value)
15
+ else
16
+ line key, value
17
+ end
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def short_config_path
24
+ Jobly.config_file.sub "#{Dir.pwd}", '.'
25
+ end
26
+
27
+ def line(key, value, attention=false)
28
+ color = attention ? '!txtred!' : '!txtgrn!'
29
+ say "#{key.to_s.rjust 20} #{color}#{value.to_s.strip}"
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,38 @@
1
+ module Jobly
2
+ module Commands
3
+ class RunCmd < Base
4
+ using ArgumentConverters
5
+ using ConvertToTyped
6
+
7
+ summary "Run a job locally"
8
+ usage "jobly run [--later] JOB [PARAMS...]"
9
+ usage "jobly run (-h|--help)"
10
+ option "-l, --later", "Schedule the job to be executed later by a worker instead of running it immediately"
11
+ param "JOB", "Job name"
12
+ param "PARAMS", "Parameters to pass to the job as key:value"
13
+ example "jobly run Greet name:Bob"
14
+ example "jobly run --later Greet name:Bob"
15
+ example "jobly run Deploy env:stage branch:master"
16
+
17
+ def run
18
+ job = args['JOB']
19
+ params = args['PARAMS'].to_params.convert_to_typed
20
+ job_class = Jobs.get_class! job
21
+
22
+ if args['--later']
23
+ say "!txtgrn!Scheduling"
24
+ if params.empty?
25
+ job_class.perform_async
26
+ else
27
+ job_class.perform_async params
28
+ end
29
+
30
+ else
31
+ say "!txtgrn!Running"
32
+ job_class.new.perform params
33
+ end
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,38 @@
1
+ require 'http'
2
+
3
+ module Jobly
4
+ module Commands
5
+ class SendCmd < Base
6
+ using ArgumentConverters
7
+
8
+ summary "Send a job to the API"
9
+ usage "jobly send JOB [PARAMS...]"
10
+ usage "jobly send (-h|--help)"
11
+ param "JOB", "Job name"
12
+ param "PARAMS", "Parameters to pass to the job as key:value"
13
+ example "jobly send Greet name:Bob"
14
+ example "jobly send Deploy env:stage branch:master"
15
+
16
+ def run
17
+ job = args['JOB']
18
+ params = args['PARAMS'].to_params
19
+ url = "#{Jobly.api_base}/#{job}"
20
+
21
+ response = if params.empty?
22
+ HTTP.get url
23
+ else
24
+ HTTP.get url, params: params
25
+ end
26
+
27
+ if response.code != 200
28
+ say "!txtred!#{response.code}"
29
+ else
30
+ say "!txtgrn!200 OK"
31
+ end
32
+
33
+ lp response.parse
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,22 @@
1
+ module Jobly
2
+ module Commands
3
+ class ServerCmd < Base
4
+ summary "Start the server"
5
+ usage "jobly server [--port NUMBER]"
6
+ usage "jobly server (-h|--help)"
7
+ option "-p, --port NUMBER", "Set the port number [default: 3000]"
8
+
9
+ def run
10
+ port = args['--port']
11
+ say "Starting server"
12
+ exec "rackup --env #{Jobly.environment} --port #{port} --host 0.0.0.0 #{rackup_file}"
13
+ end
14
+
15
+ private
16
+
17
+ def rackup_file
18
+ @rackup_file ||= File.expand_path '../config.ru', __dir__
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ module Jobly
2
+ module Commands
3
+ class WorkerCmd < Base
4
+ summary "Start a job worker"
5
+ usage "jobly worker [--concurrency COUNT]"
6
+ usage "jobly worker (-h|--help)"
7
+ option "-c, --concurrency COUNT", "Number of parallel jobs [default: 4]"
8
+
9
+ def run
10
+ concurrency = args['--concurrency']
11
+ say "Starting sidekiq"
12
+ exec "sidekiq --environment #{Jobly.environment} --concurrency #{concurrency} --require #{boot_file}"
13
+ end
14
+
15
+ private
16
+
17
+ def boot_file
18
+ @boot_file ||= File.expand_path '../boot.rb', __dir__
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,10 @@
1
+ require 'puma'
2
+ require 'jobly'
3
+ require 'jobly/boot'
4
+
5
+ mounts = {
6
+ '/' => Sidekiq::Web,
7
+ '/do' => Jobly::Server
8
+ }
9
+
10
+ run Rack::URLMap.new mounts
@@ -0,0 +1,4 @@
1
+ module Jobly
2
+ class Error < StandardError; end
3
+ class JobNotFound < Error; end
4
+ end
data/lib/jobly/job.rb ADDED
@@ -0,0 +1,16 @@
1
+ module Jobly
2
+ class Job
3
+ include Sidekiq::Worker
4
+ include Sidekiq::Status::Worker
5
+ sidekiq_options retry: 5
6
+
7
+ def perform(params={})
8
+ params = params.to_h.transform_keys(&:to_sym)
9
+ params.empty? ? execute : execute(params)
10
+ end
11
+
12
+ def execute(params={})
13
+ raise NotImplementedError
14
+ end
15
+ end
16
+ end
data/lib/jobly/jobs.rb ADDED
@@ -0,0 +1,27 @@
1
+ module Jobly
2
+ module Jobs
3
+ def self.get_class(job)
4
+ Object.const_get namespaced_job(job) rescue nil
5
+ end
6
+
7
+ def self.get_class!(job)
8
+ Object.const_get namespaced_job(job)
9
+ rescue NameError
10
+ raise JobNotFound, job
11
+ end
12
+
13
+ def self.load_all
14
+ Dir["#{Jobly.full_app_path}/**/*.rb"].each { |file| require file }
15
+ Dir["#{Jobly.full_jobs_path}/**/*.rb"].each { |file| require file }
16
+ end
17
+
18
+ def self.namespaced_job(job)
19
+ if Jobly.jobs_namespace
20
+ "#{Jobly.jobs_namespace}::#{job}"
21
+ else
22
+ job
23
+ end
24
+ end
25
+ end
26
+ end
27
+
@@ -0,0 +1,65 @@
1
+ module Jobly
2
+ def self.configure
3
+ yield self
4
+ end
5
+
6
+ def self.load_custom_config
7
+ require config_file if File.exist? config_file
8
+ end
9
+
10
+ def self.default_options
11
+ {
12
+ environment: ENV['JOBLY_ENVIRONMENT'] || 'development',
13
+ api_base: ENV['JOBLY_API_URL'] || 'http://localhost:3000/do',
14
+ app_path: ENV['JOBLY_APP_PATH'] || 'app',
15
+ jobs_path: ENV['JOBLY_JOBS_PATH'] || "jobs",
16
+ config_path: ENV['JOBLY_CONFIG_PATH'] || "config",
17
+ redis_url: ENV['JOBLY_REDIS_URL'] || "redis://localhost:6379/0",
18
+ status_expiration: 30,
19
+ jobs_namespace: nil,
20
+ logger: nil,
21
+ }
22
+ end
23
+
24
+ def self.method_missing(method, args=nil, &_block)
25
+ key = method.to_s
26
+ assign = key[-1] == "="
27
+ key = key.chomp('=') if assign
28
+ key = key.to_sym
29
+
30
+ if options.has_key? key
31
+ assign ? options[key] = args : options[key]
32
+ else
33
+ super
34
+ end
35
+ end
36
+
37
+ def self.respond_to_missing?(method, include_private=false)
38
+ key = method.to_s.chomp('=').to_sym
39
+ options.has_key?(key) ? true : super
40
+ end
41
+
42
+ def self.options
43
+ @options ||= default_options.dup
44
+ end
45
+
46
+ def self.full_app_path
47
+ File.expand_path app_path
48
+ end
49
+
50
+ def self.full_jobs_path
51
+ File.expand_path jobs_path
52
+ end
53
+
54
+ def self.full_config_path
55
+ File.expand_path config_path
56
+ end
57
+
58
+ def self.config_file
59
+ File.expand_path 'jobly.rb', self.config_path
60
+ end
61
+
62
+ def self.custom_config?
63
+ File.exist? config_file
64
+ end
65
+ end
@@ -0,0 +1,9 @@
1
+ module Jobly
2
+ module ArgumentConverters
3
+ refine Array do
4
+ def to_params
5
+ map { |pair| pair.split ':' }.to_h.transform_keys(&:to_sym)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,25 @@
1
+ module Jobly
2
+ module ConvertToTyped
3
+ refine Hash do
4
+ def convert_to_typed
5
+ transform_values do |element|
6
+ element.is_a?(String) ? element.convert_to_typed : element
7
+ end
8
+ end
9
+ end
10
+
11
+ refine Array do
12
+ def convert_to_typed
13
+ map do |element|
14
+ element.is_a?(String) ? element.convert_to_typed : element
15
+ end
16
+ end
17
+ end
18
+
19
+ refine String do
20
+ def convert_to_typed
21
+ Integer self rescue self
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,77 @@
1
+ require 'jobly'
2
+ require 'sinatra/base'
3
+ require 'sinatra/reloader'
4
+ require 'sinatra/custom_logger'
5
+
6
+ module Jobly
7
+
8
+ class Server < Sinatra::Application
9
+ helpers Sinatra::CustomLogger
10
+ using ConvertToTyped
11
+
12
+ configure :development, :production do
13
+ set :server, :puma
14
+ set :logger, Jobly.logger if Jobly.logger
15
+ end
16
+
17
+ configure :development do
18
+ register Sinatra::Reloader
19
+ end
20
+
21
+ before do
22
+ content_type 'application/json'
23
+ end
24
+
25
+ get '/' do
26
+ {
27
+ version: VERSION,
28
+ message: %Q["I'm gonna live till I die" - Frank Sinatra]
29
+ }.to_json
30
+ end
31
+
32
+ get '/:job' do
33
+ job = params.delete :job
34
+ add_job job, params
35
+ end
36
+
37
+ post '/:job' do
38
+ job = params.delete :job
39
+ add_job job, params
40
+ end
41
+
42
+ private
43
+
44
+ def add_job(job, args={})
45
+ job_class = Jobs.get_class job
46
+
47
+ if !job_class
48
+ response = {
49
+ status: 'error',
50
+ message: 'No such job',
51
+ job: job,
52
+ params: args
53
+ }
54
+
55
+ status 404
56
+ logger.debug "[jobly server] No such job (#{job})"
57
+ return response.to_json
58
+ end
59
+
60
+ args = args.convert_to_typed
61
+ if args.empty?
62
+ job_class.perform_async
63
+ else
64
+ job_class.perform_async args
65
+ end
66
+
67
+ response = {
68
+ status: 'received',
69
+ job: job,
70
+ params: args,
71
+ }
72
+
73
+ logger.debug "[jobly server] Job received (#{job})"
74
+ response.to_json
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,26 @@
1
+ require 'sidekiq'
2
+ require 'sidekiq/web'
3
+ require 'sidekiq-status'
4
+ require 'sidekiq-status/web'
5
+
6
+ module Jobly
7
+ module SidekiqBoot
8
+ def self.configure
9
+ Sidekiq.configure_client do |config|
10
+ config.redis = { url: Jobly.redis_url }
11
+ Sidekiq::Status.configure_client_middleware config, expiration: 60 * 60
12
+ end
13
+
14
+ Sidekiq.configure_server do |config|
15
+ # :nocov:
16
+ config.redis = { url: Jobly.redis_url }
17
+
18
+ Sidekiq::Status.configure_server_middleware config, expiration: Jobly.status_expiration * 60
19
+ Sidekiq::Status.configure_client_middleware config, expiration: Jobly.status_expiration * 60
20
+ # :nocov:
21
+ end
22
+
23
+ Sidekiq::Logging.logger = Jobly.logger if Jobly.logger
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,3 @@
1
+ module Jobly
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,218 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jobly
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Danny Ben Shitrit
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-03-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mister_bin
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: colsole
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.5'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: puma
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.12'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.12'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rack
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: requires
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.1'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.1'
83
+ - !ruby/object:Gem::Dependency
84
+ name: sidekiq
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '5.2'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '5.2'
97
+ - !ruby/object:Gem::Dependency
98
+ name: sidekiq-status
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.1'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.1'
111
+ - !ruby/object:Gem::Dependency
112
+ name: sinatra
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '2.0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '2.0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: sinatra-contrib
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '2.0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '2.0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: http
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '4.0'
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '4.0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: lp
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '0.1'
160
+ type: :runtime
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '0.1'
167
+ description: Execute background jobs and build tasks on this sidekiq-based job server
168
+ email: db@dannyben.com
169
+ executables:
170
+ - jobly
171
+ extensions: []
172
+ extra_rdoc_files: []
173
+ files:
174
+ - README.md
175
+ - bin/jobly
176
+ - lib/jobly.rb
177
+ - lib/jobly/boot.rb
178
+ - lib/jobly/cli.rb
179
+ - lib/jobly/commands/base.rb
180
+ - lib/jobly/commands/config.rb
181
+ - lib/jobly/commands/run.rb
182
+ - lib/jobly/commands/send.rb
183
+ - lib/jobly/commands/server.rb
184
+ - lib/jobly/commands/worker.rb
185
+ - lib/jobly/config.ru
186
+ - lib/jobly/exceptions.rb
187
+ - lib/jobly/job.rb
188
+ - lib/jobly/jobs.rb
189
+ - lib/jobly/module_functions.rb
190
+ - lib/jobly/refinements/argument_converters.rb
191
+ - lib/jobly/refinements/convert_to_typed.rb
192
+ - lib/jobly/server.rb
193
+ - lib/jobly/sidekiq.rb
194
+ - lib/jobly/version.rb
195
+ homepage: https://github.com/dannyben/jobly
196
+ licenses:
197
+ - MIT
198
+ metadata: {}
199
+ post_install_message:
200
+ rdoc_options: []
201
+ require_paths:
202
+ - lib
203
+ required_ruby_version: !ruby/object:Gem::Requirement
204
+ requirements:
205
+ - - ">="
206
+ - !ruby/object:Gem::Version
207
+ version: 2.4.0
208
+ required_rubygems_version: !ruby/object:Gem::Requirement
209
+ requirements:
210
+ - - ">="
211
+ - !ruby/object:Gem::Version
212
+ version: '0'
213
+ requirements: []
214
+ rubygems_version: 3.0.3
215
+ signing_key:
216
+ specification_version: 4
217
+ summary: Compact job server with API, CLI, Web UI and a Sidekiq heart
218
+ test_files: []