operationable 0.2.6 → 0.2.7
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 +4 -4
- data/Gemfile +3 -1
- data/lib/jobs/operation_job.rb +19 -4
- data/lib/models/operation.rb +16 -0
- data/lib/operationable.rb +8 -2
- data/lib/operationable/persister.rb +5 -1
- data/lib/operationable/runners/base.rb +27 -6
- data/lib/operationable/runners/separate.rb +10 -8
- data/lib/operationable/runners/serial.rb +12 -9
- data/lib/operationable/status.rb +145 -0
- data/lib/operationable/version.rb +1 -1
- data/operationable.gemspec +2 -0
- metadata +32 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8b18274511419a5544c45629e90002fa62e716ef
|
4
|
+
data.tar.gz: 00835b124deae21aa613dc9afb568d40fab14a85
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ba9e5eecb9fefe67536e074b2dacdbe2dcf2b55f9013571670a74ebc1654f503e759658ea0adce124e026cab4a5e55bc415c1fe4eb89e4a6e628c4a29f2d0bb7
|
7
|
+
data.tar.gz: aa36fdabae4857d160e744be4198429f0e0b9ace7c8447c79934d0ae3a7a782aec9a12a0b2e0337381685036ebd7753d5d0849e4220ad95fec3266fb649dbc47
|
data/Gemfile
CHANGED
data/lib/jobs/operation_job.rb
CHANGED
@@ -1,14 +1,29 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
class OperationJob < ActiveJob::Base
|
3
|
+
include Operationable::Status
|
4
|
+
# include Operationable::Persister
|
5
|
+
|
3
6
|
queue_as do
|
4
|
-
arguments.first[:queue]
|
7
|
+
arguments.first[:q_options][:queue]
|
8
|
+
end
|
9
|
+
|
10
|
+
after_enqueue do |job|
|
11
|
+
create_status_hash(job)
|
5
12
|
end
|
6
13
|
|
7
|
-
|
8
|
-
|
14
|
+
around_perform do |job, block|
|
15
|
+
safe_perform(job, block)
|
9
16
|
end
|
10
17
|
|
11
|
-
after_perform
|
18
|
+
after_perform :clear_runtime
|
19
|
+
|
20
|
+
def perform(q_options:, props:)
|
21
|
+
"Operationable::Runners::#{q_options[:type].capitalize}".constantize.call(q_options: q_options, props: props)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def clear_runtime
|
12
27
|
ActiveRecord::Base.clear_active_connections!
|
13
28
|
GC.start
|
14
29
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# == Schema Information
|
3
|
+
#
|
4
|
+
# Table name: operations
|
5
|
+
#
|
6
|
+
# id :integer not null, primary key
|
7
|
+
# name :string
|
8
|
+
# initiator_id :integer
|
9
|
+
# params :json
|
10
|
+
# callbacks :json
|
11
|
+
# created_at :datetime not null
|
12
|
+
# updated_at :datetime not null
|
13
|
+
#
|
14
|
+
|
15
|
+
class Operation < ActiveRecord::Base
|
16
|
+
end
|
data/lib/operationable.rb
CHANGED
@@ -1,20 +1,27 @@
|
|
1
1
|
require 'rails'
|
2
|
+
require 'active_job'
|
3
|
+
require 'active_record'
|
2
4
|
require 'active_support/dependencies'
|
5
|
+
require 'forwardable'
|
6
|
+
require 'resque'
|
7
|
+
require 'resque-status'
|
3
8
|
|
4
9
|
require "operationable/version"
|
5
|
-
|
6
10
|
require 'operationable/builder'
|
7
11
|
require 'operationable/callback'
|
8
12
|
require 'operationable/operation'
|
9
13
|
require 'operationable/serializer'
|
10
14
|
require 'operationable/specification'
|
11
15
|
require 'operationable/persister'
|
16
|
+
require 'operationable/status'
|
12
17
|
|
13
18
|
require 'operationable/runners/base'
|
14
19
|
require 'operationable/runners/serial'
|
15
20
|
require 'operationable/runners/separate'
|
16
21
|
|
17
22
|
require 'jobs/operation_job'
|
23
|
+
require 'models/operation'
|
24
|
+
|
18
25
|
require 'operations/create'
|
19
26
|
require 'operations/destroy'
|
20
27
|
require 'operations/update'
|
@@ -25,6 +32,5 @@ module Operationable
|
|
25
32
|
class Callback < ::Operationable::Callback; end
|
26
33
|
class Specification < ::Operationable::Specification; end
|
27
34
|
class Serializer < ::Operationable::Serializer; end
|
28
|
-
class Persister < ::Operationable::Persister; end
|
29
35
|
end
|
30
36
|
end
|
@@ -1,6 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module Operationable
|
3
|
-
|
3
|
+
module Persister
|
4
|
+
def persist_operation
|
5
|
+
@persist_operation ||= ::Operationable::Persister.persist(check_callbacks, user.id, props, operation_class_name)
|
6
|
+
end
|
7
|
+
|
4
8
|
class << self
|
5
9
|
def persist(callbacks, initiator_id, params, name)
|
6
10
|
::Operation.create(
|
@@ -12,20 +12,41 @@ module Operationable
|
|
12
12
|
@callbacks = []
|
13
13
|
|
14
14
|
initialize_callbacks
|
15
|
+
end
|
16
|
+
|
17
|
+
def check_status?
|
18
|
+
true
|
19
|
+
end
|
15
20
|
|
16
|
-
|
17
|
-
|
21
|
+
def persist?
|
22
|
+
true
|
18
23
|
end
|
19
24
|
|
20
25
|
def run
|
21
26
|
end
|
22
27
|
|
23
|
-
|
28
|
+
def ensure_enqueue
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
def job_method
|
33
|
+
"#{job_class}".constantize.method(perform_method)
|
34
|
+
end
|
35
|
+
|
36
|
+
def job_class
|
37
|
+
'OperationJob'
|
38
|
+
end
|
24
39
|
|
25
|
-
def
|
26
|
-
|
40
|
+
def job_sync_execute_method
|
41
|
+
:perform_now
|
27
42
|
end
|
28
43
|
|
44
|
+
def job_async_execute_method
|
45
|
+
:perform_later
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
29
50
|
def props
|
30
51
|
serializer_instance.serialize
|
31
52
|
end
|
@@ -87,7 +108,7 @@ module Operationable
|
|
87
108
|
end
|
88
109
|
|
89
110
|
def perform_method
|
90
|
-
sync? ?
|
111
|
+
sync? ? job_sync_execute_method : job_async_execute_method
|
91
112
|
end
|
92
113
|
|
93
114
|
def sync?
|
@@ -7,10 +7,13 @@ module Operationable
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def process(callback_method_name:, queue: nil)
|
10
|
-
(queue.blank? ? self.class :
|
10
|
+
(queue.blank? ? self.class : job_method).call(
|
11
|
+
q_options: q_options(callback_method_name, queue),
|
12
|
+
props: props
|
13
|
+
)
|
11
14
|
end
|
12
15
|
|
13
|
-
def
|
16
|
+
def q_options(callback_method_name, queue)
|
14
17
|
{ type: 'separate',
|
15
18
|
callback_class_name: callback_class_name,
|
16
19
|
callback_method_name: callback_method_name,
|
@@ -18,17 +21,16 @@ module Operationable
|
|
18
21
|
op_id: persist_operation.id }
|
19
22
|
end
|
20
23
|
|
21
|
-
def self.call(
|
22
|
-
|
24
|
+
def self.call(q_options:, props:)
|
25
|
+
q_options[:callback_class_name].constantize.new(props).method(q_options[:callback_method_name]).call
|
23
26
|
end
|
24
27
|
|
25
28
|
# TODO: No sense, due to performance deterioration, better use postgres/mysql database
|
26
|
-
# def self.call(
|
27
|
-
# ::Operationable::Persister.around_call(
|
28
|
-
#
|
29
|
+
# def self.call(q_options, props)
|
30
|
+
# ::Operationable::Persister.around_call(q_options[:op_id], q_options[:callback_method_name], -> {
|
31
|
+
# q_options[:callback_class_name].constantize.new(props).method(q_options[:callback_method_name]).call
|
29
32
|
# })
|
30
33
|
# end
|
31
|
-
|
32
34
|
end
|
33
35
|
end
|
34
36
|
end
|
@@ -7,26 +7,29 @@ module Operationable
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def process
|
10
|
-
(queue.blank? ? self.class :
|
10
|
+
(queue.blank? ? self.class : job_method).call(
|
11
|
+
q_options: q_options,
|
12
|
+
props: props
|
13
|
+
)
|
11
14
|
end
|
12
15
|
|
13
|
-
def self.call(
|
14
|
-
instance =
|
15
|
-
|
16
|
+
def self.call(q_options:, props:)
|
17
|
+
instance = q_options[:callback_class_name].constantize.new(props)
|
18
|
+
q_options[:callback_names].each { |method_name| instance.method(method_name).call }
|
16
19
|
end
|
17
20
|
|
18
21
|
# TODO: No sense, due to performance deterioration, better use postgres/mysql database
|
19
|
-
# def self.call(
|
20
|
-
# instance =
|
22
|
+
# def self.call(q_options, props)
|
23
|
+
# instance = q_options[:callback_class_name].constantize.new(props)
|
21
24
|
#
|
22
|
-
#
|
23
|
-
# ::Operationable::Persister.around_call(
|
25
|
+
# q_options[:callback_names].each do |method_name|
|
26
|
+
# ::Operationable::Persister.around_call(q_options[:op_id], method_name, -> {
|
24
27
|
# instance.method(method_name).call
|
25
28
|
# })
|
26
29
|
# end
|
27
30
|
# end
|
28
31
|
|
29
|
-
def
|
32
|
+
def q_options
|
30
33
|
{ type: 'serial',
|
31
34
|
callback_class_name: callback_class_name,
|
32
35
|
callback_names: callback_names,
|
@@ -0,0 +1,145 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# codebase has been taken from abandoned resque-status gem, that not compatible with ActiveJob and Rails now
|
3
|
+
module Operationable
|
4
|
+
module Status
|
5
|
+
extend Forwardable
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
STATUS_QUEUED = 'queued'
|
9
|
+
STATUS_WORKING = 'working'
|
10
|
+
STATUS_COMPLETED = 'completed'
|
11
|
+
STATUS_FAILED = 'failed'
|
12
|
+
STATUS_KILLED = 'killed'
|
13
|
+
STATUSES = [
|
14
|
+
STATUS_QUEUED,
|
15
|
+
STATUS_WORKING,
|
16
|
+
STATUS_COMPLETED,
|
17
|
+
STATUS_FAILED,
|
18
|
+
STATUS_KILLED
|
19
|
+
].freeze
|
20
|
+
|
21
|
+
autoload :Hash, 'resque/plugins/status/hash'
|
22
|
+
|
23
|
+
# The error class raised when a job is killed
|
24
|
+
class Killed < RuntimeError; end
|
25
|
+
class NotANumber < RuntimeError; end
|
26
|
+
|
27
|
+
attr_reader :uuid, :options
|
28
|
+
|
29
|
+
def create_status_hash(job)
|
30
|
+
Resque::Plugins::Status::Hash.create(self.job_id, arguments.first)
|
31
|
+
end
|
32
|
+
|
33
|
+
def uuid
|
34
|
+
self.job_id
|
35
|
+
end
|
36
|
+
|
37
|
+
def options
|
38
|
+
arguments.first
|
39
|
+
end
|
40
|
+
|
41
|
+
# Run by the Resque::Worker when processing this job. It wraps the <tt>perform</tt>
|
42
|
+
# method ensuring that the final status of the job is set regardless of error.
|
43
|
+
# If an error occurs within the job's work, it will set the status as failed and
|
44
|
+
# re-raise the error.
|
45
|
+
def safe_perform(job, block)
|
46
|
+
working
|
47
|
+
|
48
|
+
block.call
|
49
|
+
|
50
|
+
if status && status.failed?
|
51
|
+
on_failure(status.message) if respond_to?(:on_failure)
|
52
|
+
return
|
53
|
+
elsif status && !status.completed?
|
54
|
+
completed
|
55
|
+
end
|
56
|
+
on_success if respond_to?(:on_success)
|
57
|
+
rescue Killed
|
58
|
+
Resque::Plugins::Status::Hash.killed(uuid)
|
59
|
+
on_killed if respond_to?(:on_killed)
|
60
|
+
rescue => e
|
61
|
+
failed("The task failed because of an error: #{e}")
|
62
|
+
if respond_to?(:on_failure)
|
63
|
+
on_failure(e)
|
64
|
+
else
|
65
|
+
raise e
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Set the jobs status. Can take an array of strings or hashes that are merged
|
70
|
+
# (in order) into a final status hash.
|
71
|
+
def status=(new_status)
|
72
|
+
Resque::Plugins::Status::Hash.set(uuid, *new_status)
|
73
|
+
end
|
74
|
+
|
75
|
+
# get the Resque::Plugins::Status::Hash object for the current uuid
|
76
|
+
def status
|
77
|
+
Resque::Plugins::Status::Hash.get(uuid)
|
78
|
+
end
|
79
|
+
|
80
|
+
def name
|
81
|
+
"#{self.class.name}(#{options.inspect unless options.empty?})"
|
82
|
+
end
|
83
|
+
|
84
|
+
# Checks against the kill list if this specific job instance should be killed
|
85
|
+
# on the next iteration
|
86
|
+
def should_kill?
|
87
|
+
Resque::Plugins::Status::Hash.should_kill?(uuid)
|
88
|
+
end
|
89
|
+
|
90
|
+
# set the status of the job for the current itteration. <tt>num</tt> and
|
91
|
+
# <tt>total</tt> are passed to the status as well as any messages.
|
92
|
+
# This will kill the job if it has been added to the kill list with
|
93
|
+
# <tt>Resque::Plugins::Status::Hash.kill()</tt>
|
94
|
+
def at(num, total, *messages)
|
95
|
+
if total.to_f <= 0.0
|
96
|
+
raise(NotANumber, "Called at() with total=#{total} which is not a number")
|
97
|
+
end
|
98
|
+
tick({
|
99
|
+
'num' => num,
|
100
|
+
'total' => total
|
101
|
+
}, *messages)
|
102
|
+
end
|
103
|
+
|
104
|
+
def working
|
105
|
+
set_status({'status' => STATUS_WORKING})
|
106
|
+
end
|
107
|
+
|
108
|
+
# sets the status of the job for the current itteration. You should use
|
109
|
+
# the <tt>at</tt> method if you have actual numbers to track the iteration count.
|
110
|
+
# This will kill the job if it has been added to the kill list with
|
111
|
+
# <tt>Resque::Plugins::Status::Hash.kill()</tt>
|
112
|
+
def tick(*messages)
|
113
|
+
kill! if should_kill?
|
114
|
+
set_status({'status' => STATUS_WORKING}, *messages)
|
115
|
+
end
|
116
|
+
|
117
|
+
# set the status to 'failed' passing along any additional messages
|
118
|
+
def failed(*messages)
|
119
|
+
set_status({'status' => STATUS_FAILED}, *messages)
|
120
|
+
end
|
121
|
+
|
122
|
+
# set the status to 'completed' passing along any addional messages
|
123
|
+
def completed(*messages)
|
124
|
+
set_status({
|
125
|
+
'status' => STATUS_COMPLETED,
|
126
|
+
'message' => "Completed at #{Time.now}"
|
127
|
+
}, *messages)
|
128
|
+
end
|
129
|
+
|
130
|
+
# kill the current job, setting the status to 'killed' and raising <tt>Killed</tt>
|
131
|
+
def kill!
|
132
|
+
set_status({
|
133
|
+
'status' => STATUS_KILLED,
|
134
|
+
'message' => "Killed at #{Time.now}"
|
135
|
+
})
|
136
|
+
raise Killed
|
137
|
+
end
|
138
|
+
|
139
|
+
private
|
140
|
+
|
141
|
+
def set_status(*args)
|
142
|
+
self.status = [status, {'name' => self.name}, args].flatten
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
data/operationable.gemspec
CHANGED
@@ -23,6 +23,8 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.required_ruby_version = '>= 2.1.0'
|
24
24
|
|
25
25
|
spec.add_dependency 'activejob', '>= 4.2.0'
|
26
|
+
spec.add_dependency 'resque-status', '>= 0.5.0'
|
27
|
+
spec.add_dependency 'resque', '1.25.0'
|
26
28
|
|
27
29
|
spec.add_development_dependency "bundler", "~> 1.13"
|
28
30
|
spec.add_development_dependency "rake", "~> 10.0"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: operationable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kirill Suhodolov
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2017-03-
|
12
|
+
date: 2017-03-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activejob
|
@@ -25,6 +25,34 @@ dependencies:
|
|
25
25
|
- - ">="
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: 4.2.0
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: resque-status
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 0.5.0
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 0.5.0
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: resque
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - '='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: 1.25.0
|
49
|
+
type: :runtime
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - '='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: 1.25.0
|
28
56
|
- !ruby/object:Gem::Dependency
|
29
57
|
name: bundler
|
30
58
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,6 +114,7 @@ files:
|
|
86
114
|
- bin/console
|
87
115
|
- bin/setup
|
88
116
|
- lib/jobs/operation_job.rb
|
117
|
+
- lib/models/operation.rb
|
89
118
|
- lib/operationable.rb
|
90
119
|
- lib/operationable/builder.rb
|
91
120
|
- lib/operationable/callback.rb
|
@@ -96,6 +125,7 @@ files:
|
|
96
125
|
- lib/operationable/runners/serial.rb
|
97
126
|
- lib/operationable/serializer.rb
|
98
127
|
- lib/operationable/specification.rb
|
128
|
+
- lib/operationable/status.rb
|
99
129
|
- lib/operationable/version.rb
|
100
130
|
- lib/operations/create.rb
|
101
131
|
- lib/operations/destroy.rb
|