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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3ddf02b693334196d4ea8e365bfdaa0959336836
4
- data.tar.gz: 9197bf207f0f242669cb049d39d6184135f96be4
3
+ metadata.gz: 8b18274511419a5544c45629e90002fa62e716ef
4
+ data.tar.gz: 00835b124deae21aa613dc9afb568d40fab14a85
5
5
  SHA512:
6
- metadata.gz: fd9773af6e30769ed6b6b794f39e1cad32ce3c68b7610c62ca7ebb905e059d8fb48d48925d70363ce05f211c3f479cdd333d5dc4aefda14cea1d13e64503f052
7
- data.tar.gz: 9f55de492294bf5c22c9eaeb27c538757fef8cae4e01022928d0fb7c9ab446ca84a8ef3e66ccc8f004561a78f9e4dfb3c6221b701e951a8076076ac26d239640
6
+ metadata.gz: ba9e5eecb9fefe67536e074b2dacdbe2dcf2b55f9013571670a74ebc1654f503e759658ea0adce124e026cab4a5e55bc415c1fe4eb89e4a6e628c4a29f2d0bb7
7
+ data.tar.gz: aa36fdabae4857d160e744be4198429f0e0b9ace7c8447c79934d0ae3a7a782aec9a12a0b2e0337381685036ebd7753d5d0849e4220ad95fec3266fb649dbc47
data/Gemfile CHANGED
@@ -2,4 +2,6 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
- gem "rails", ">= 4.2.0"
5
+ gem 'rails', '>= 4.2.0'
6
+ gem 'resque-status', '>= 0.5.0'
7
+ gem 'resque', '1.25.0'
@@ -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
- def perform(options, props)
8
- "Operationable::Runners::#{options[:type].capitalize}".constantize.call(options, props)
14
+ around_perform do |job, block|
15
+ safe_perform(job, block)
9
16
  end
10
17
 
11
- after_perform do
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
@@ -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
- class Persister
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
- # TODO: No sense, due to performance deterioration, better use postgres/mysql database
17
- # persist_operation
21
+ def persist?
22
+ true
18
23
  end
19
24
 
20
25
  def run
21
26
  end
22
27
 
23
- private
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 persist_operation
26
- @persist_operation ||= ::Operationable::Persister.persist(check_callbacks, user.id, props, operation_class_name)
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? ? :perform_now : :perform_later
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 : OperationJob.method(perform_method)).call(options(callback_method_name, queue), props)
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 options(callback_method_name, queue)
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(options, props)
22
- options[:callback_class_name].constantize.new(props).method(options[:callback_method_name]).call
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(options, props)
27
- # ::Operationable::Persister.around_call(options[:op_id], options[:callback_method_name], -> {
28
- # options[:callback_class_name].constantize.new(props).method(options[:callback_method_name]).call
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 : OperationJob.method(perform_method)).call(options, props)
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(options, props)
14
- instance = options[:callback_class_name].constantize.new(props)
15
- options[:callback_names].each { |method_name| instance.method(method_name).call }
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(options, props)
20
- # instance = options[:callback_class_name].constantize.new(props)
22
+ # def self.call(q_options, props)
23
+ # instance = q_options[:callback_class_name].constantize.new(props)
21
24
  #
22
- # options[:callback_names].each do |method_name|
23
- # ::Operationable::Persister.around_call(options[:op_id], method_name, -> {
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 options
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
@@ -1,3 +1,3 @@
1
1
  module Operationable
2
- VERSION = "0.2.6"
2
+ VERSION = "0.2.7"
3
3
  end
@@ -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.6
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-10 00:00:00.000000000 Z
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