bg 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5b59fee4ea49e20b0f003c82d4e88cfe8944ba00
4
- data.tar.gz: 5e1d394c114e456a75d342f566777a79f855cf49
3
+ metadata.gz: 4324480197c976cb308ccd3ea1793b5620e01be2
4
+ data.tar.gz: 76fafd8fa06023ebb0beea60f5d9a264293e3647
5
5
  SHA512:
6
- metadata.gz: 0f98b49309738b49b5a2e91d361d7a1a4a7edde4547edade187374461d3be98d4c870820e4488ed57ae5e75b268cc673926b2a18cff7b7d43da41be3db645677
7
- data.tar.gz: e19f24f3030ca8f696859d0b051ca5225095044b978ea68bea7d1cff8e55a834c485393507e509684d354de6b3b130028eb3ef5b8398fb113cf7335ab3712470
6
+ metadata.gz: 02a6086051a13bc6a062fe1490664b4eaf1e5a94f238020423e531038cce1894a2203936bc0fc7f0a8166bc188d25038a3c57f45fbbc16be1fd3fa5c8f78cd54
7
+ data.tar.gz: 49a95bf68e2c28513012074449c522a3803ccf2d02087ec2993f68f7e0d84e4ef2d458326f89902b5cedc18c0a3322044e64feca9de6660433e1aaa9636dd62a
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- bg (0.0.4)
4
+ bg (0.0.5)
5
5
  activejob (>= 5.0)
6
6
  activerecord (>= 5.0)
7
7
  concurrent-ruby (>= 1.0)
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Lines of Code](http://img.shields.io/badge/lines_of_code-121-brightgreen.svg?style=flat)](http://blog.codinghorror.com/the-best-code-is-no-code-at-all/)
1
+ [![Lines of Code](http://img.shields.io/badge/lines_of_code-117-brightgreen.svg?style=flat)](http://blog.codinghorror.com/the-best-code-is-no-code-at-all/)
2
2
  [![Code Status](http://img.shields.io/codeclimate/github/hopsoft/bg.svg?style=flat)](https://codeclimate.com/github/hopsoft/bg)
3
3
  [![Dependency Status](http://img.shields.io/gemnasium/hopsoft/bg.svg?style=flat)](https://gemnasium.com/hopsoft/bg)
4
4
  [![Build Status](http://img.shields.io/travis/hopsoft/bg.svg?style=flat)](https://travis-ci.org/hopsoft/bg)
@@ -30,19 +30,25 @@ end
30
30
 
31
31
  ```ruby
32
32
  user = User.find(params[:id])
33
- user.do_hard_work # blocking in-process
34
- user.async.do_hard_work # non-blocking in-process
35
- user.defer.do_hard_work # non-blocking out-of-process background job
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
- ## Provisos
45
+ ## Deferrable
40
46
 
41
- Bg leverages [GlobalID::Identification](https://github.com/rails/globalid) to marshal ActiveRecord instances across thread & process boundaries.
42
- This means that state is not shared between the main process/thread with the process/thread actually executing the method.
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 with `Bg::Deferrable`.
45
- * __Do not__ pass unmarshallable types as arguments with `Bg::Deferrable`.
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
- user.name = "new value" # in memory changes will not be available in Bg invoked methods
68
+ # in memory changes will not be available in Bg::Deferrable invoked methods
69
+ user.name = "new value"
66
70
 
67
- user.async.do_hard_work do
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
- user.defer.do_hard_work :foo, Time.now # args won't marshal properly
79
+ ## Asyncable
72
80
 
73
- user.defer.do_hard_work do
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
+
@@ -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(global_id, delay: 0)
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
- @global_id = global_id
16
- @delay = delay.to_f
13
+ @object = object
14
+ @wait = wait.to_f
17
15
  end
18
16
 
19
17
  def invoke_method(name, *args)
20
- sleep delay if delay > 0
21
- ::ActiveRecord::Base.connection_pool.with_connection do
22
- global_id.find.send name, *args
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(delay: 0)
29
- ::Bg::Asyncable.new(self, delay: delay.to_f)
27
+ def async(wait: 0)
28
+ ::Bg::Asyncable.new(self, wait: wait.to_f)
30
29
  end
31
30
  end
32
31
 
33
- attr_reader :object, :delay
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
- @delay = delay.to_f
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.to_global_id, delay: delay)
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Bg
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
@@ -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
@@ -4,9 +4,9 @@ require_relative "../lib/bg"
4
4
  require_relative "backgroundable_object"
5
5
  require "minitest/autorun"
6
6
  require "purdytest"
7
- #require "pry"
8
- #require "pry-nav"
9
- #require "pry-stack_explorer"
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"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bg
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Hopkins