career 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +174 -0
- data/Rakefile +5 -0
- data/app/assets/config/career_manifest.js +1 -0
- data/app/assets/stylesheets/career/application.css +15 -0
- data/app/controllers/career/application_controller.rb +4 -0
- data/app/controllers/career/tasks_controller.rb +13 -0
- data/app/helpers/career/application_helper.rb +4 -0
- data/app/jobs/career/application_job.rb +4 -0
- data/app/mailers/career/application_mailer.rb +6 -0
- data/app/models/career/application_record.rb +5 -0
- data/app/models/career/task.rb +62 -0
- data/app/models/career/task_log_entry.rb +11 -0
- data/app/views/career/tasks/index.html.erb +28 -0
- data/app/views/career/tasks/show.html.erb +16 -0
- data/app/views/layouts/career/application.html.erb +15 -0
- data/config/routes.rb +3 -0
- data/db/migrate/20210423134400_create_career_tasks.rb +17 -0
- data/db/migrate/20210423182534_create_career_task_log_entries.rb +11 -0
- data/lib/career.rb +6 -0
- data/lib/career/engine.rb +11 -0
- data/lib/career/version.rb +3 -0
- data/lib/tasks/career_tasks.rake +4 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/config/manifest.js +2 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
- data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
- data/spec/dummy/app/controllers/application_controller.rb +2 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/javascript/packs/application.js +15 -0
- data/spec/dummy/app/jobs/application_job.rb +7 -0
- data/spec/dummy/app/mailers/application_mailer.rb +4 -0
- data/spec/dummy/app/models/application_record.rb +3 -0
- data/spec/dummy/app/views/layouts/application.html.erb +15 -0
- data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
- data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +33 -0
- data/spec/dummy/config.ru +6 -0
- data/spec/dummy/config/application.rb +38 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/cable.yml +10 -0
- data/spec/dummy/config/database.yml +19 -0
- data/spec/dummy/config/database.yml.tmpl +19 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +76 -0
- data/spec/dummy/config/environments/production.rb +120 -0
- data/spec/dummy/config/environments/test.rb +59 -0
- data/spec/dummy/config/initializers/application_controller_renderer.rb +8 -0
- data/spec/dummy/config/initializers/assets.rb +12 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +8 -0
- data/spec/dummy/config/initializers/content_security_policy.rb +28 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +6 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/permissions_policy.rb +11 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +33 -0
- data/spec/dummy/config/puma.rb +43 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/config/storage.yml +34 -0
- data/spec/dummy/db/migrate/20210423141758_create_career_tasks.career.rb +18 -0
- data/spec/dummy/db/migrate/20210423182737_create_career_task_log_entries.career.rb +12 -0
- data/spec/dummy/db/schema.rb +41 -0
- data/spec/dummy/log/development.log +198 -0
- data/spec/dummy/log/test.log +306 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
- data/spec/dummy/public/apple-touch-icon.png +0 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/tmp/development_secret.txt +1 -0
- data/spec/factories/career/task_log_entries.rb +7 -0
- data/spec/factories/career/tasks.rb +9 -0
- data/spec/models/career/task_log_entry_spec.rb +17 -0
- data/spec/models/career/task_spec.rb +25 -0
- data/spec/rails_helper.rb +70 -0
- data/spec/spec_helper.rb +96 -0
- data/spec/support/factory_bot.rb +3 -0
- metadata +251 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3c571636304becef9f1dd5988f5606d88e8b608ce706772d3d8841a263fd8a80
|
4
|
+
data.tar.gz: 291e46357b476a6ddb3beac09d39c3995dc6b0f8a832c2631e56480009889a86
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7ad2d7798f5e3e39b65584e38b650b35d5dc824b6c40bc4b418062ac0bd07a7a39ade83af2a5628b5af84df6b053ea2523dbd510ddefbf63e2736a662e1ca3b8
|
7
|
+
data.tar.gz: 042a6eb56e0e0b4dcde9862f0a50d6b8f74be5cf25816e5b026d2a7babbcc45e2bc27f28f72468145aa25747c8e9692e117bd1bde1f6a6d8609ef0e289a0255c
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2021 Casey Li
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
# Career
|
2
|
+
|
3
|
+
It's more than just a job, it's a career.
|
4
|
+
|
5
|
+
Career provides persistent data to supplement your background jobs providing insight into things like status, percent complete, started at, and stopped at. Combined with ActionCable, it can provide a powerful UI for your background jobs.
|
6
|
+
|
7
|
+
**Currently, we only support [Resque](https://github.com/resque/resque).**
|
8
|
+
|
9
|
+
## Usage
|
10
|
+
|
11
|
+
### Tasks
|
12
|
+
|
13
|
+
The main model that Career introduces is tasks in the form of `Career::Task`.
|
14
|
+
|
15
|
+
1. Create a task:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
task = Career::Task.create(status: 'scheduled', description: 'My task', class_name: 'MyBackgroundJob')
|
19
|
+
```
|
20
|
+
|
21
|
+
2. Enqueue the task:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
task.enqueue
|
25
|
+
```
|
26
|
+
|
27
|
+
### Writing jobs for Tasks
|
28
|
+
|
29
|
+
`Career::Task#enqueue` always passes in its own ID as the first argument to your background jobs. So, to make your background jobs compatible with `Career::Task`, you'll want the first argument to be `task_id`.
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
require "resque/errors"
|
33
|
+
|
34
|
+
class TestJob
|
35
|
+
@queue = :default_queue
|
36
|
+
|
37
|
+
def self.perform(task_id, my_param_1, my_param_2)
|
38
|
+
# Load Task
|
39
|
+
task = Career::Task.find(task_id)
|
40
|
+
|
41
|
+
task.log "Started my job..."
|
42
|
+
|
43
|
+
# Do stuff
|
44
|
+
|
45
|
+
task.log "...finished this part of the job..."
|
46
|
+
task.update_percent_complete(50)
|
47
|
+
|
48
|
+
# Do Stuff
|
49
|
+
|
50
|
+
task.log "...complete"
|
51
|
+
task.update_percent_complete(100)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
```
|
55
|
+
|
56
|
+
### Using Tasks in your jobs
|
57
|
+
|
58
|
+
Once you has access to your task in your job, you can start updating the task within your job with methods like
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
require "resque/errors"
|
62
|
+
|
63
|
+
class TestJob
|
64
|
+
@queue = :default_queue
|
65
|
+
|
66
|
+
def self.perform(task_id, my_param_1, my_param_2)
|
67
|
+
# Load Task
|
68
|
+
task = Career::Task.find(task_id)
|
69
|
+
|
70
|
+
# Log messages
|
71
|
+
task.log "...info message...", "info"
|
72
|
+
task.log "...successful message...", "success"
|
73
|
+
task.log "...error message...", "error"
|
74
|
+
|
75
|
+
# Update percent
|
76
|
+
task.update_percent_complete(50)
|
77
|
+
task.update_percent_complete(100)
|
78
|
+
|
79
|
+
# Update status
|
80
|
+
task.update_status('scheduled')
|
81
|
+
task.update_status('started') # which also sets the started_at timestamp
|
82
|
+
task.update_status('failed')
|
83
|
+
task.update_status('interrupted')
|
84
|
+
task.update_status('canceled')
|
85
|
+
task.update_status('complete') # which also sets the completed_at timestamp
|
86
|
+
end
|
87
|
+
end
|
88
|
+
```
|
89
|
+
|
90
|
+
|
91
|
+
|
92
|
+
## Installation
|
93
|
+
Add this line to your application's Gemfile:
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
gem 'career'
|
97
|
+
```
|
98
|
+
|
99
|
+
And then execute:
|
100
|
+
```bash
|
101
|
+
$ bundle
|
102
|
+
```
|
103
|
+
|
104
|
+
Or install it yourself as:
|
105
|
+
```bash
|
106
|
+
$ gem install career
|
107
|
+
```
|
108
|
+
|
109
|
+
### Run Migrations
|
110
|
+
|
111
|
+
This gem comes packaged with some database tables so be sure to run your migrations:
|
112
|
+
|
113
|
+
```
|
114
|
+
rails career:install:migrations
|
115
|
+
rails db:migrate
|
116
|
+
```
|
117
|
+
|
118
|
+
### Setup CSS
|
119
|
+
|
120
|
+
Add the following line to your Manifest if you're using Rails 6
|
121
|
+
|
122
|
+
```
|
123
|
+
# app/assets/config/manifest.js
|
124
|
+
//= link career/application.css
|
125
|
+
```
|
126
|
+
|
127
|
+
### Want a free UI?
|
128
|
+
|
129
|
+
If you want to use our built-in UI, then add this to your `config/routes.rb`:
|
130
|
+
|
131
|
+
```
|
132
|
+
Rails.application.routes.draw do
|
133
|
+
...
|
134
|
+
mount Career::Engine => "/career"
|
135
|
+
...
|
136
|
+
end
|
137
|
+
```
|
138
|
+
|
139
|
+
## Contributing
|
140
|
+
Contribution directions go here.
|
141
|
+
|
142
|
+
### Development
|
143
|
+
|
144
|
+
#### Testing
|
145
|
+
|
146
|
+
To run rspec, be sure to
|
147
|
+
|
148
|
+
1. Setup your dummy database
|
149
|
+
|
150
|
+
```
|
151
|
+
cd spec/dummy
|
152
|
+
cp config/database.yml.tmpl config/database.yml
|
153
|
+
```
|
154
|
+
|
155
|
+
Then edit the `config/database.yml` for your database and run `rails db:create`
|
156
|
+
|
157
|
+
2. Copy engine migrations
|
158
|
+
|
159
|
+
The main engine migrations are managed in the top level directory and need to be copied down to `spec`:
|
160
|
+
|
161
|
+
```
|
162
|
+
cd spec/dummy
|
163
|
+
rails career:install:migrations
|
164
|
+
rails db:migrate
|
165
|
+
```
|
166
|
+
|
167
|
+
*Note: We use `ActiveRecord::Migrator.migrations_paths = './dummy/db/migrate'` to deal with the new timestamp that gets created when copying migrations.*
|
168
|
+
|
169
|
+
3. Run rspec
|
170
|
+
|
171
|
+
At this point, you should be able to run `rspec` from the root folder.
|
172
|
+
|
173
|
+
## License
|
174
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
//= link_directory ../stylesheets/career .css
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
6
|
+
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
11
|
+
* It is generally better to create a new file per style scope.
|
12
|
+
*
|
13
|
+
*= require_tree .
|
14
|
+
*= require_self
|
15
|
+
*/
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Career
|
2
|
+
class Task < ApplicationRecord
|
3
|
+
has_many :task_log_entries, dependent: :destroy
|
4
|
+
belongs_to :user, optional: true
|
5
|
+
|
6
|
+
VALID_STATUSES = ['scheduled', 'started', 'complete', 'failed', 'interrupted', 'canceled']
|
7
|
+
|
8
|
+
serialize :params, Array
|
9
|
+
|
10
|
+
validates :description, :class_name, :percent_complete, presence: true
|
11
|
+
validates :status, presence: true, inclusion: { in: VALID_STATUSES, message: "%{value} is not a valid status" }
|
12
|
+
|
13
|
+
scope :reverse_chronological, -> { order(created_at: :desc) }
|
14
|
+
|
15
|
+
VALID_STATUSES.each do |method_name|
|
16
|
+
define_method "#{method_name}?" do
|
17
|
+
status == method_name
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def enqueue
|
22
|
+
Resque.enqueue(class_name.constantize, id, *params)
|
23
|
+
end
|
24
|
+
|
25
|
+
def enqueue_at(scheduled_date)
|
26
|
+
Resque.enqueue_at(scheduled_date, class_name.constantize, id, *params)
|
27
|
+
end
|
28
|
+
|
29
|
+
def log(content, entry_type='info', data=nil)
|
30
|
+
task_log_entry = task_log_entries.create(content: content, entry_type: entry_type, data: data)
|
31
|
+
|
32
|
+
# TODO: Implement
|
33
|
+
#TaskLogEntriesChannel.broadcast_to(self, task_log_entry)
|
34
|
+
end
|
35
|
+
|
36
|
+
def set_status(status)
|
37
|
+
updated_attributes = { status: status }
|
38
|
+
|
39
|
+
if status == 'started'
|
40
|
+
updated_attributes[:percent_complete] = 0
|
41
|
+
updated_attributes[:started_at] = Time.now
|
42
|
+
end
|
43
|
+
|
44
|
+
if status == 'complete'
|
45
|
+
updated_attributes[:percent_complete] = 100
|
46
|
+
updated_attributes[:completed_at] = Time.now
|
47
|
+
end
|
48
|
+
|
49
|
+
self.update(updated_attributes)
|
50
|
+
|
51
|
+
# TODO: Implement
|
52
|
+
#TasksChannel.broadcast_to(self, self)
|
53
|
+
end
|
54
|
+
|
55
|
+
def update_percent_complete(percent_complete)
|
56
|
+
self.update(percent_complete: percent_complete)
|
57
|
+
|
58
|
+
# TODO: Implement
|
59
|
+
#TasksChannel.broadcast_to(self, self)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
<h1>Tasks</h1>
|
2
|
+
|
3
|
+
<table>
|
4
|
+
<thead>
|
5
|
+
<tr>
|
6
|
+
<th>ID</th>
|
7
|
+
<th>Description</th>
|
8
|
+
<th>Status</th>
|
9
|
+
<th>Started at</th>
|
10
|
+
<th>Completed at</th>
|
11
|
+
<th>% complete</th>
|
12
|
+
<th></th>
|
13
|
+
</tr>
|
14
|
+
</thead>
|
15
|
+
<tbody>
|
16
|
+
<% @tasks.each do |task| %>
|
17
|
+
<tr>
|
18
|
+
<td><%= task.id %></td>
|
19
|
+
<td><%= task.description %></td>
|
20
|
+
<td><%= task.status %></td>
|
21
|
+
<td><%= task.started_at %></td>
|
22
|
+
<td><%= task.completed_at %></td>
|
23
|
+
<td><%= task.percent_complete %></td>
|
24
|
+
<td><%= link_to 'View', task %></td>
|
25
|
+
</tr>
|
26
|
+
<% end %>
|
27
|
+
</tbody>
|
28
|
+
</table>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<h1>Task <%= @task.id %></h1>
|
2
|
+
|
3
|
+
<%= @task.description %><br />
|
4
|
+
<%= @task.status %><br />
|
5
|
+
<%= @task.started_at %><br />
|
6
|
+
<%= @task.completed_at %><br />
|
7
|
+
<%= @task.percent_complete %><br />
|
8
|
+
|
9
|
+
<h2>Log</h2>
|
10
|
+
<div>
|
11
|
+
<% @task.task_log_entries.chronological.each do |task_log_entry| %>
|
12
|
+
<div>
|
13
|
+
<%= "#{[task_log_entry.created_at]} - #{task_log_entry.content}" %>
|
14
|
+
</div>
|
15
|
+
<% end %>
|
16
|
+
</div>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
class CreateCareerTasks < ActiveRecord::Migration[6.1]
|
2
|
+
def change
|
3
|
+
create_table :career_tasks do |t|
|
4
|
+
t.string :status
|
5
|
+
t.float :percent_complete
|
6
|
+
t.string :description
|
7
|
+
t.string :class_name
|
8
|
+
t.datetime :started_at
|
9
|
+
t.datetime :completed_at
|
10
|
+
t.integer :user_id
|
11
|
+
t.text :params
|
12
|
+
t.datetime :scheduled_at
|
13
|
+
|
14
|
+
t.timestamps
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|