bg 0.0.4 → 0.0.5
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.lock +1 -1
- data/README.md +45 -16
- data/lib/bg/asyncable.rb +15 -19
- data/lib/bg/deferrable.rb +6 -8
- data/lib/bg/version.rb +1 -1
- data/test/bg/deferred_method_call_test.rb +5 -0
- data/test/test_helper.rb +3 -3
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4324480197c976cb308ccd3ea1793b5620e01be2
|
4
|
+
data.tar.gz: 76fafd8fa06023ebb0beea60f5d9a264293e3647
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 02a6086051a13bc6a062fe1490664b4eaf1e5a94f238020423e531038cce1894a2203936bc0fc7f0a8166bc188d25038a3c57f45fbbc16be1fd3fa5c8f78cd54
|
7
|
+
data.tar.gz: 49a95bf68e2c28513012074449c522a3803ccf2d02087ec2993f68f7e0d84e4ef2d458326f89902b5cedc18c0a3322044e64feca9de6660433e1aaa9636dd62a
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
[](http://blog.codinghorror.com/the-best-code-is-no-code-at-all/)
|
2
2
|
[](https://codeclimate.com/github/hopsoft/bg)
|
3
3
|
[](https://gemnasium.com/hopsoft/bg)
|
4
4
|
[](https://travis-ci.org/hopsoft/bg)
|
@@ -30,19 +30,25 @@ end
|
|
30
30
|
|
31
31
|
```ruby
|
32
32
|
user = User.find(params[:id])
|
33
|
-
|
34
|
-
|
35
|
-
user.
|
33
|
+
|
34
|
+
# blocking in-process
|
35
|
+
user.do_hard_work
|
36
|
+
|
37
|
+
# non-blocking in-process separate thread
|
38
|
+
user.async.do_hard_work
|
39
|
+
|
40
|
+
# non-blocking out-of-process background job
|
41
|
+
user.defer.do_hard_work
|
36
42
|
user.defer(queue: :low, wait: 5.minutes).do_hard_work
|
37
43
|
```
|
38
44
|
|
39
|
-
##
|
45
|
+
## Deferrable
|
40
46
|
|
41
|
-
Bg leverages [GlobalID::Identification](https://github.com/rails/globalid) to marshal ActiveRecord instances across
|
42
|
-
This means that state is not shared between the main process
|
47
|
+
`Bg::Deferrable` leverages [GlobalID::Identification](https://github.com/rails/globalid) to marshal ActiveRecord instances across process boundaries.
|
48
|
+
This means that state is not shared between the main process & the process actually executing the method.
|
43
49
|
|
44
|
-
* __Do not__ depend on lexically scoped bindings when invoking methods
|
45
|
-
* __Do not__ pass unmarshallable types as arguments
|
50
|
+
* __Do not__ depend on lexically scoped bindings when invoking methods.
|
51
|
+
* __Do not__ pass unmarshallable types as arguments.
|
46
52
|
`Bg::Deferrable` will prepare arguments for enqueuing, but best practice is to follow
|
47
53
|
Sidekiq's [simple parameters](https://github.com/mperham/sidekiq/wiki/Best-Practices#1-make-your-job-parameters-small-and-simple) rule.
|
48
54
|
|
@@ -52,9 +58,6 @@ This means that state is not shared between the main process/thread with the pro
|
|
52
58
|
|
53
59
|
```ruby
|
54
60
|
user = User.find(params[:id])
|
55
|
-
user.update(name: "new value") # persisted changes will be available in Bg invoked methods
|
56
|
-
|
57
|
-
user.async.do_hard_work 1, true, "foo", :bar, Time.now
|
58
61
|
user.defer.do_hard_work 1, true, "foo"
|
59
62
|
```
|
60
63
|
|
@@ -62,15 +65,41 @@ user.defer.do_hard_work 1, true, "foo"
|
|
62
65
|
|
63
66
|
```ruby
|
64
67
|
user = User.find(params[:id])
|
65
|
-
|
68
|
+
# in memory changes will not be available in Bg::Deferrable invoked methods
|
69
|
+
user.name = "new value"
|
66
70
|
|
67
|
-
|
71
|
+
# args may not marshal properly
|
72
|
+
user.defer.do_hard_work :foo, Time.now, instance_of_complex_type
|
73
|
+
|
74
|
+
user.defer.do_hard_work do
|
68
75
|
# blocks are not supported
|
69
76
|
end
|
77
|
+
```
|
70
78
|
|
71
|
-
|
79
|
+
## Asyncable
|
72
80
|
|
73
|
-
|
81
|
+
`Bg::Asyncable` disallows invoking methods that take blocks as an argument.
|
82
|
+
|
83
|
+
* __Important:__ It's your responsibility to protect shared data between threads
|
84
|
+
|
85
|
+
### Examples
|
86
|
+
|
87
|
+
#### Good
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
user = User.find(params[:id])
|
91
|
+
user.name = "new value"
|
92
|
+
user.async.do_hard_work 1, true, "foo"
|
93
|
+
user.async.do_hard_work :foo, Time.now, instance_of_complex_type
|
94
|
+
```
|
95
|
+
|
96
|
+
#### Bad
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
user = User.find(params[:id])
|
100
|
+
|
101
|
+
user.async.do_hard_work do
|
74
102
|
# blocks are not supported
|
75
103
|
end
|
76
104
|
```
|
105
|
+
|
data/lib/bg/asyncable.rb
CHANGED
@@ -1,51 +1,47 @@
|
|
1
1
|
require "active_record"
|
2
2
|
require "concurrent"
|
3
|
-
require "globalid"
|
4
3
|
|
5
4
|
module Bg
|
6
5
|
class Asyncable
|
7
6
|
class Wrapper
|
8
7
|
include ::Concurrent::Async
|
9
|
-
attr_reader :global_id, :delay
|
10
8
|
|
11
|
-
def initialize(
|
9
|
+
def initialize(object, wait: 0)
|
12
10
|
# IMPORTANT: call super without any arguments
|
13
11
|
# https://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Async.html
|
14
12
|
super()
|
15
|
-
@
|
16
|
-
@
|
13
|
+
@object = object
|
14
|
+
@wait = wait.to_f
|
17
15
|
end
|
18
16
|
|
19
17
|
def invoke_method(name, *args)
|
20
|
-
sleep
|
21
|
-
::ActiveRecord::Base.
|
22
|
-
|
18
|
+
sleep @wait if @wait > 0
|
19
|
+
base = self.is_a?(::ActiveRecord::Base) ? self.class : ::ActiveRecord::Base
|
20
|
+
base.connection_pool.with_connection do
|
21
|
+
@object.send name, *args
|
23
22
|
end
|
24
23
|
end
|
25
24
|
end
|
26
25
|
|
27
26
|
module Behavior
|
28
|
-
def async(
|
29
|
-
::Bg::Asyncable.new(self,
|
27
|
+
def async(wait: 0)
|
28
|
+
::Bg::Asyncable.new(self, wait: wait.to_f)
|
30
29
|
end
|
31
30
|
end
|
32
31
|
|
33
|
-
|
34
|
-
|
35
|
-
def initialize(object, delay: 0)
|
36
|
-
raise ::ArgumentError unless object.is_a?(::GlobalID::Identification)
|
32
|
+
def initialize(object, wait: 0)
|
37
33
|
@object = object
|
38
|
-
@
|
34
|
+
@wait = wait.to_f
|
39
35
|
end
|
40
36
|
|
41
37
|
def method_missing(name, *args)
|
42
|
-
if object.respond_to? name
|
38
|
+
if @object.respond_to? name
|
43
39
|
raise ::ArgumentError.new("blocks are not supported") if block_given?
|
44
40
|
begin
|
45
|
-
wrapped = ::Bg::Asyncable::Wrapper.new(object
|
41
|
+
wrapped = ::Bg::Asyncable::Wrapper.new(@object, wait: @wait)
|
46
42
|
wrapped.async.invoke_method name, *args
|
47
43
|
rescue ::StandardError => e
|
48
|
-
raise ::ArgumentError.new("Failed to execute method asynchronously! <#{object.class.name}##{name}> #{e.message}")
|
44
|
+
raise ::ArgumentError.new("Failed to execute method asynchronously! <#{@object.class.name}##{name}> #{e.message}")
|
49
45
|
ensure
|
50
46
|
return
|
51
47
|
end
|
@@ -54,7 +50,7 @@ module Bg
|
|
54
50
|
end
|
55
51
|
|
56
52
|
def respond_to?(name)
|
57
|
-
return true if object.respond_to? name
|
53
|
+
return true if @object.respond_to? name
|
58
54
|
super
|
59
55
|
end
|
60
56
|
end
|
data/lib/bg/deferrable.rb
CHANGED
@@ -27,8 +27,6 @@ module Bg
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
attr_reader :object, :queue, :wait
|
31
|
-
|
32
30
|
def initialize(object, queue: :default, wait: 0)
|
33
31
|
raise ::ArgumentError unless object.is_a?(::GlobalID::Identification)
|
34
32
|
@object = object
|
@@ -37,14 +35,14 @@ module Bg
|
|
37
35
|
end
|
38
36
|
|
39
37
|
def method_missing(name, *args)
|
40
|
-
if object.respond_to? name
|
38
|
+
if @object.respond_to? name
|
41
39
|
raise ::ArgumentError.new("blocks are not supported") if block_given?
|
42
40
|
begin
|
43
|
-
queue_args = { queue: queue }
|
44
|
-
queue_args[:wait] = wait if wait > 0
|
45
|
-
job = ::Bg::DeferredMethodCallJob.set(**queue_args).perform_later object, name.to_s, *self.class.make_enqueable(args)
|
41
|
+
queue_args = { queue: @queue }
|
42
|
+
queue_args[:wait] = @wait if @wait > 0
|
43
|
+
job = ::Bg::DeferredMethodCallJob.set(**queue_args).perform_later @object, name.to_s, *self.class.make_enqueable(args)
|
46
44
|
rescue ::StandardError => e
|
47
|
-
raise ::ArgumentError.new("Failed to background method call! <#{object.class.name}##{name}> #{e.message}")
|
45
|
+
raise ::ArgumentError.new("Failed to background method call! <#{@object.class.name}##{name}> #{e.message}")
|
48
46
|
ensure
|
49
47
|
return job
|
50
48
|
end
|
@@ -53,7 +51,7 @@ module Bg
|
|
53
51
|
end
|
54
52
|
|
55
53
|
def respond_to?(name)
|
56
|
-
return true if object.respond_to? name
|
54
|
+
return true if @object.respond_to? name
|
57
55
|
super
|
58
56
|
end
|
59
57
|
end
|
data/lib/bg/version.rb
CHANGED
@@ -36,4 +36,9 @@ class Bg::DeferredMethodCallJobTest < ::ActiveJob::TestCase
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
+
test "#perform_now properly invokes the method" do
|
40
|
+
obj = ::Bg::BackgroundableObject.new(:example)
|
41
|
+
assert ::Bg::DeferredMethodCallJob.perform_now(obj, :update)
|
42
|
+
end
|
43
|
+
|
39
44
|
end
|
data/test/test_helper.rb
CHANGED
@@ -4,9 +4,9 @@ require_relative "../lib/bg"
|
|
4
4
|
require_relative "backgroundable_object"
|
5
5
|
require "minitest/autorun"
|
6
6
|
require "purdytest"
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
require "pry"
|
8
|
+
require "pry-nav"
|
9
|
+
require "pry-stack_explorer"
|
10
10
|
|
11
11
|
::ActiveSupport::TestCase.test_order = :random
|
12
12
|
::GlobalID.app = "test"
|