jobler 0.0.1

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: b10be50515f5770dc9da54cedb9f700550c8a9bb
4
+ data.tar.gz: f274e293bd5c22da12da7ef1d2bcdc32de3c0ed7
5
+ SHA512:
6
+ metadata.gz: 43e45f56fdd80700e80e09a15e3c6b8ecf6857b3e5c2a4fde0037b0ef449fbd0ef2045680b86ebef9427204840e4041941d5c12b63c31d87d62b6d462d66974f
7
+ data.tar.gz: 56097d69f110d225413778b01c2d0b64c47c3b9505033de03acec159ea9099516c3ca152452cb591a8318dfa0f41f6ceb4836e467043a69cf53103459ce66c44
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2017 kaspernj
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,82 @@
1
+ # Jobler
2
+
3
+ Jobler helps you generate large reports or very large and slow pages through background jobs.
4
+
5
+ ## Install
6
+
7
+ Add it to your Gemfile and bundle it:
8
+
9
+ ```ruby
10
+ gem "jobler"
11
+ ```
12
+
13
+ Install the migrations and run them:
14
+ ```bash
15
+ rake railties:install:migrations
16
+ rake db:migrate
17
+ ```
18
+
19
+ Add it to your routes:
20
+
21
+ ```ruby
22
+ mount Jobler::Engine => "/jobler"
23
+ ```
24
+
25
+ Autoload the "Joblers" you are going to write through application.rb:
26
+
27
+ ```ruby
28
+ config.autoload_paths << Rails.root.join("app", "joblers")
29
+ ```
30
+
31
+ Add a ApplicationJobler to follow the ApplicationController and ApplicationRecord pattern:
32
+ ```ruby
33
+ class ApplicationJobler < Jobler::BaseJobler
34
+ end
35
+ ```
36
+
37
+ Jobler is going to queue its jobs through the ActiveJob queue called `:jobler`, so make sure a worker is listening to that queue.
38
+
39
+ ## Usage
40
+
41
+ Write a new Jobler located in "app/joblers":
42
+
43
+ ```ruby
44
+ class MyJobler < ApplicationJobler
45
+ # This method will be executed in the background
46
+ def execute!
47
+ my_temp_file = Tempfile.new
48
+
49
+ # Do some heavy lifting code wise...
50
+
51
+ create_result!(name: "my-file", temp_file: my_temp_file)
52
+ end
53
+
54
+ # This method will be called from the web when the execute is completed and successful
55
+ def result
56
+ Jobler::FileDownload.new(
57
+ file_name: "some-file.zip",
58
+ temp_file: temp_file_for_result(name: "my-file")
59
+ )
60
+ end
61
+ end
62
+ ```
63
+
64
+ You can do something like this in your controller:
65
+ ```ruby
66
+ class MyController < ApplicationController
67
+ def some_action
68
+ scheduler = Jobler::JobScheduler.create! jobler_type: "MyJobler", job_args: {
69
+ current_user_id: current_user.id,
70
+ query_parameters: request.query_parameters
71
+ }
72
+
73
+ redirect_to jobler.job_path(scheduler.job)
74
+ end
75
+ end
76
+ ```
77
+
78
+ This will show a wait page and them a complete page with a download link, once the job is completed.
79
+
80
+ ## License
81
+
82
+ This project rocks and uses MIT-LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ begin
2
+ require "bundler/setup"
3
+ rescue LoadError
4
+ puts "You must `gem install bundler` and `bundle install` to run rake tasks"
5
+ end
6
+
7
+ require "rdoc/task"
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = "rdoc"
11
+ rdoc.title = "Jobler"
12
+ rdoc.options << "--line-numbers"
13
+ rdoc.rdoc_files.include("README.rdoc")
14
+ rdoc.rdoc_files.include("lib/**/*.rb")
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
18
+ load "rails/tasks/engine.rake"
19
+
20
+ load "rails/tasks/statistics.rake"
21
+
22
+ if Rails.env.development? || Rails.env.test?
23
+ require "best_practice_project"
24
+ BestPracticeProject.load_tasks
25
+ end
26
+
27
+ Bundler::GemHelper.install_tasks
File without changes
@@ -0,0 +1,2 @@
1
+ //= require jquery
2
+ //= require jobler/jobs
@@ -0,0 +1,29 @@
1
+ function updateJobProgress() {
2
+ console.log("updateJobProgress()")
3
+ job = $(".job")
4
+
5
+ $.ajax({type: "GET", url: job.data("job-path"), complete: function(data) {
6
+ console.log(data.responseText)
7
+
8
+ result = $.parseJSON(data.responseText)
9
+
10
+ $(".job-progress").text((result.job.progress * 100).toFixed(2) + "%")
11
+
12
+ if (result.job.state == "completed") {
13
+ console.log("Job is completed - reload!")
14
+ location.reload()
15
+ }
16
+ }})
17
+
18
+ if (job.data("state") != "completed") {
19
+ setTimeout("updateJobProgress()", 5000)
20
+ }
21
+ }
22
+
23
+ $(document).ready(function() {
24
+ job = $(".job")
25
+
26
+ if (job.data("state") != "completed") {
27
+ updateJobProgress()
28
+ }
29
+ })
@@ -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 styles
10
+ * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
11
+ * file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,3 @@
1
+ class Jobler::ApplicationController < ActionController::Base
2
+ protect_from_forgery with: :exception
3
+ end
@@ -0,0 +1,13 @@
1
+ class Jobler::DownloadsController < Jobler::ApplicationController
2
+ def show
3
+ @job = Jobler::Job.find(params[:id])
4
+ @result = @job.jobler.result
5
+
6
+ if @result.is_a?(Jobler::FileDownload)
7
+ send_file @result.temp_file.path, disposition: "attachment", filename: @result.file_name
8
+ else
9
+ flash[:error] = "The result wasn't a file download"
10
+ redirect_to :back
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ class Jobler::JobsController < Jobler::ApplicationController
2
+ def show
3
+ @job = Jobler::Job.find(params[:id])
4
+ @result = @job.jobler.result if @job.completed?
5
+
6
+ respond_to do |format|
7
+ format.json do
8
+ render json: {
9
+ job: {
10
+ progress: @job.progress,
11
+ state: @job.state
12
+ }
13
+ }
14
+ end
15
+ format.html
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,2 @@
1
+ module Jobler::ApplicationHelper
2
+ end
@@ -0,0 +1,16 @@
1
+ class Jobler::Job < ActiveRecord::Base
2
+ has_many :results, class_name: "Jobler::Result", dependent: :destroy
3
+
4
+ validates :jobler_type, :state, presence: true
5
+
6
+ def jobler
7
+ user_jobler = jobler_type.constantize.new
8
+ user_jobler.instance_variable_set(:@args, YAML.load(parameters)) # rubocop:disable Security/YAMLLoad
9
+ user_jobler.instance_variable_set(:@job, self)
10
+ user_jobler
11
+ end
12
+
13
+ def completed?
14
+ state == "completed"
15
+ end
16
+ end
@@ -0,0 +1,5 @@
1
+ class Jobler::Result < ActiveRecord::Base
2
+ belongs_to :job, class_name: "Jobler::Job"
3
+
4
+ validates :job, :name, presence: true
5
+ end
@@ -0,0 +1,17 @@
1
+ <div class="job" data-id="<%= @job.id %>" data-path="<%= job_path(@job) %>" data-state="<%= @job.state %>"></div>
2
+
3
+ <% if @job.completed? %>
4
+ <h1>Completed</h1>
5
+
6
+ <% if @result.is_a?(Jobler::FileDownload) %>
7
+ <%= link_to "Download", download_path(@job) %>
8
+ <% end %>
9
+ <% else %>
10
+ <h1>Waiting</h1>
11
+
12
+ <div class="job-progress">
13
+ <% if @job.progress? %>
14
+ <%= number_with_precision @job.progress, precision: 2 %>%
15
+ <% end %>
16
+ </div>
17
+ <% end %>
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Jobler</title>
5
+ <%= stylesheet_link_tag "jobler/application", media: "all" %>
6
+ <%= javascript_include_tag "jobler/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body class="controller-<%= controller_name %> action-<%= action_name %>">
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
data/config/routes.rb ADDED
@@ -0,0 +1,4 @@
1
+ Jobler::Engine.routes.draw do
2
+ resources :downloads, only: :show
3
+ resources :jobs, only: :show
4
+ end
@@ -0,0 +1,13 @@
1
+ class CreateJobs < ActiveRecord::Migration
2
+ def change
3
+ create_table :jobler_jobs do |t|
4
+ t.string :jobler_type, null: false
5
+ t.string :state, default: "new", null: false
6
+ t.float :progress, default: 0.0, null: false
7
+ t.text :parameters
8
+ t.datetime :started_at
9
+ t.datetime :ended_at
10
+ t.timestamps null: false
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ class CreateJoblerResults < ActiveRecord::Migration
2
+ def change
3
+ create_table :jobler_results do |t|
4
+ t.belongs_to :job, index: true, null: false
5
+ t.string :name, index: true, null: false
6
+ t.binary :result, limit: 16.megabyte
7
+ t.timestamps null: false
8
+ end
9
+
10
+ add_foreign_key :jobler_results, :jobler_jobs, column: "job_id"
11
+ end
12
+ end
data/lib/jobler.rb ADDED
@@ -0,0 +1,11 @@
1
+ require "jobler/engine"
2
+
3
+ module Jobler
4
+ path = "#{File.dirname(__FILE__)}/jobler"
5
+
6
+ autoload :BaseJobler, "#{path}/base_jobler"
7
+ autoload :FileDownload, "#{path}/file_download"
8
+ autoload :JobScheduler, "#{path}/job_scheduler"
9
+ autoload :JobRunner, "#{path}/job_runner"
10
+ autoload :TemplateRenderer, "#{path}/template_renderer"
11
+ end
@@ -0,0 +1,56 @@
1
+ class Jobler::BaseJobler
2
+ attr_reader :args, :job
3
+
4
+ def create_result!(args)
5
+ temp_file = args.fetch(:temp_file)
6
+ temp_file.close unless temp_file.closed?
7
+
8
+ job.results.create!(
9
+ name: args.fetch(:name),
10
+ result: File.read(temp_file.path)
11
+ )
12
+ end
13
+
14
+ def execute!
15
+ raise NoMethodError, "You should define the 'execute!' method on #{self.class.name}"
16
+ end
17
+
18
+ def increment_progress!
19
+ @_progress_count ||= 0.0
20
+ @_progress_count += 1.0
21
+
22
+ new_progress = @_progress_count / @_progress_total
23
+
24
+ if @_current_progress.nil?
25
+ update = true
26
+ else
27
+ progress_difference = new_progress - @_current_progress
28
+ update = true if progress_difference > 0.01
29
+ end
30
+
31
+ if update
32
+ job.update_columns(progress: new_progress)
33
+ @_current_progress = new_progress
34
+ end
35
+ end
36
+
37
+ def progress_total(new_total)
38
+ @_progress_total = new_total.to_f
39
+ end
40
+
41
+ def result
42
+ raise NoMethodError, "You should define the 'result' method on #{self.class.name}"
43
+ end
44
+
45
+ def temp_file_for_result(args)
46
+ job_result = job.results.where(name: args.fetch(:name)).first
47
+
48
+ raise "No result by that name: #{args.fetch(:name)}" unless job_result
49
+
50
+ temp_file = Tempfile.new
51
+ temp_file.binmode
52
+ temp_file.write(job_result.result)
53
+ temp_file.close
54
+ temp_file
55
+ end
56
+ end
@@ -0,0 +1,3 @@
1
+ class Jobler::Engine < ::Rails::Engine
2
+ isolate_namespace Jobler
3
+ end
@@ -0,0 +1,8 @@
1
+ class Jobler::FileDownload
2
+ attr_reader :file_name, :temp_file
3
+
4
+ def initialize(args)
5
+ @file_name = args.fetch(:file_name)
6
+ @temp_file = args.fetch(:temp_file)
7
+ end
8
+ end
@@ -0,0 +1,16 @@
1
+ class Jobler::JobRunner < ActiveJob::Base
2
+ queue_as :jobler
3
+
4
+ def perform(job_id)
5
+ job = Jobler::Job.find(job_id)
6
+ job.update_attributes!(started_at: Time.zone.now, state: "started")
7
+
8
+ begin
9
+ job.jobler.execute!
10
+ job.update_attributes!(ended_at: Time.zone.now, progress: 1.0, state: "completed")
11
+ rescue Exception => e # rubocop:disable Lint/RescueException
12
+ job.update_attributes!(ended_at: Time.zone.now, state: "error")
13
+ raise e
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,23 @@
1
+ class Jobler::JobScheduler
2
+ attr_reader :job
3
+
4
+ def self.create!(args)
5
+ scheduler = Jobler::JobScheduler.new(args)
6
+ scheduler.create_job
7
+ scheduler.perform_job_later
8
+ scheduler
9
+ end
10
+
11
+ def initialize(args)
12
+ @jobler_type = args.fetch(:jobler_type)
13
+ @job_args = args[:job_args]
14
+ end
15
+
16
+ def create_job
17
+ @job = Jobler::Job.create!(jobler_type: @jobler_type, parameters: YAML.dump(@job_args))
18
+ end
19
+
20
+ def perform_job_later
21
+ Jobler::JobRunner.perform_later(job.id)
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ class TemplateRenderer
2
+ include RenderAnywhere
3
+ end
@@ -0,0 +1,3 @@
1
+ module Jobler
2
+ VERSION = "0.0.1".freeze
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :jobler do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jobler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - kaspernj
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-01-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 4.2.7.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 4.2.7.1
27
+ description: Generate pages or files in the background
28
+ email:
29
+ - kaspernj@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - MIT-LICENSE
35
+ - README.md
36
+ - Rakefile
37
+ - app/assets/javascripts/jobler.js
38
+ - app/assets/javascripts/jobler/application.js
39
+ - app/assets/javascripts/jobler/jobs.js
40
+ - app/assets/stylesheets/jobler/application.css
41
+ - app/controllers/jobler/application_controller.rb
42
+ - app/controllers/jobler/downloads_controller.rb
43
+ - app/controllers/jobler/jobs_controller.rb
44
+ - app/helpers/jobler/application_helper.rb
45
+ - app/models/jobler/job.rb
46
+ - app/models/jobler/result.rb
47
+ - app/views/jobler/jobs/show.html.erb
48
+ - app/views/layouts/jobler/application.html.erb
49
+ - config/routes.rb
50
+ - db/migrate/20170130220604_create_jobs.rb
51
+ - db/migrate/20170130220734_create_jobler_results.rb
52
+ - lib/jobler.rb
53
+ - lib/jobler/base_jobler.rb
54
+ - lib/jobler/engine.rb
55
+ - lib/jobler/file_download.rb
56
+ - lib/jobler/job_runner.rb
57
+ - lib/jobler/job_scheduler.rb
58
+ - lib/jobler/template_renderer.rb
59
+ - lib/jobler/version.rb
60
+ - lib/tasks/jobler_tasks.rake
61
+ homepage: https://www.github.com/kaspernj/jobler
62
+ licenses:
63
+ - MIT
64
+ metadata: {}
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubyforge_project:
81
+ rubygems_version: 2.6.8
82
+ signing_key:
83
+ specification_version: 4
84
+ summary: Generate pages or files in the background
85
+ test_files: []