inst-jobs 1.0.1 → 1.0.2

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
  SHA256:
3
- metadata.gz: 2dad83034c6ca9ae9a7f7129581ecd36f64d1d51f93aa1126582ff2141068020
4
- data.tar.gz: 8b155f2ed6ce13b956d8383bc484ef19ed3b661502bf2aed734a4618befebf40
3
+ metadata.gz: 6d65c567b52e2baf26955d266c9fededc001deab65d871e6e60a42a905d552e8
4
+ data.tar.gz: b4d487088aba583188c90650a42a20726f84230e346c321b4b94ddc5c25fe36e
5
5
  SHA512:
6
- metadata.gz: 783af3d9f654e07a55a6be57691216a255bb548a720ae31d7c085ba0c70be20164a2a2c6a7b36767976468e034b5bafc6de427f577a05c2c70d0a6621301e86d
7
- data.tar.gz: cbc4ff85dea6bc2f12d8eb2cecb25cecae4dc47e5e5155e46c63ebdbc4884e15f43f41b69b8611c44cbb42f4539843ecffe8cf5e96cab852bf9ace06a7a6b6fa
6
+ metadata.gz: 9ade674610c5a1a4a04e4a6078b45c38358e7c7af79a417d5e6941d968e1565bc5e217405ad1ca427b373ee0d89bdbc28772f8ed52047394e8760fc09a79b60a
7
+ data.tar.gz: 216706e128397e84ba87d495e4b09d30eb58f35ab4821e8ef6f484d5f6226a959cab04a8bfdfa22916f19dad53e7ee01bbe1128bd61fb402aad29885ac81839f
@@ -7,32 +7,45 @@ end
7
7
  module Delayed
8
8
  module MessageSending
9
9
  class DelayProxy < BasicObject
10
- def initialize(object, synchronous: false, public_send: false, **enqueue_args)
10
+ def initialize(object, synchronous: false, sender: nil, **enqueue_args)
11
11
  @object = object
12
12
  @enqueue_args = enqueue_args
13
13
  @synchronous = synchronous
14
- @public_send = public_send
14
+ @sender = sender
15
15
  end
16
16
 
17
17
  def method_missing(method, *args, **kwargs)
18
+ # method doesn't exist? must be method_missing; assume private access
19
+ @sender = nil if !@sender.nil? &&
20
+ !@object.methods.include?(method) &&
21
+ !@object.protected_methods.include?(method) &&
22
+ !@object.private_methods.include?(method)
23
+
24
+ sender_is_object = @sender == @object
25
+ sender_is_class = @sender.is_a?(@object.class)
26
+
27
+ # even if the call is async, if the call is _going_ to generate an error, we make it synchronous
28
+ # so that the error is generated immediately, instead of waiting for it to fail in a job,
29
+ # which might go unnoticed
30
+ if !@sender.nil? && !@synchronous
31
+ @synchronous = true if !sender_is_object && @object.private_methods.include?(method)
32
+ @synchronous = true if !sender_is_class && @object.protected_methods.include?(method)
33
+ end
34
+
18
35
  if @synchronous
19
- if @public_send
20
- if kwargs.empty?
21
- return @object.public_send(method, *args)
22
- else
23
- return @object.public_send(method, *args, **kwargs)
24
- end
25
- else
36
+ if @sender.nil? || sender_is_object || sender_is_class && @object.protected_methods.include?(method)
26
37
  if kwargs.empty?
27
38
  return @object.send(method, *args)
28
39
  else
29
40
  return @object.send(method, *args, **kwargs)
30
41
  end
31
42
  end
32
- end
33
-
34
- if @public_send && @object.private_methods.include?(method)
35
- ::Kernel.raise ::NoMethodError.new("undefined method `#{method}' for #{@object}", method)
43
+
44
+ if kwargs.empty?
45
+ return @object.public_send(method, *args)
46
+ else
47
+ return @object.public_send(method, *args, **kwargs)
48
+ end
36
49
  end
37
50
 
38
51
  ignore_transaction = @enqueue_args.delete(:ignore_transaction)
@@ -50,7 +63,8 @@ module Delayed
50
63
  ::Delayed::Job.enqueue(::Delayed::PerformableMethod.new(@object, method,
51
64
  args: args, kwargs: kwargs,
52
65
  on_failure: on_failure,
53
- on_permanent_failure: on_permanent_failure),
66
+ on_permanent_failure: on_permanent_failure,
67
+ sender: @sender),
54
68
  **@enqueue_args)
55
69
  end
56
70
  return nil
@@ -61,14 +75,15 @@ module Delayed
61
75
  args: args,
62
76
  kwargs: kwargs,
63
77
  on_failure: on_failure,
64
- on_permanent_failure: on_permanent_failure),
78
+ on_permanent_failure: on_permanent_failure,
79
+ sender: @sender),
65
80
  **@enqueue_args)
66
81
  result = nil unless ignore_transaction
67
82
  result
68
83
  end
69
84
  end
70
85
 
71
- def delay(public_send: nil, **enqueue_args)
86
+ def delay(sender: nil, **enqueue_args)
72
87
  # support procs/methods as enqueue arguments
73
88
  enqueue_args.each do |k,v|
74
89
  if v.respond_to?(:call)
@@ -76,21 +91,15 @@ module Delayed
76
91
  end
77
92
  end
78
93
 
79
- public_send ||= __calculate_public_send_for_delay
94
+ sender ||= __calculate_sender_for_delay
80
95
 
81
- DelayProxy.new(self, public_send: public_send, **enqueue_args)
96
+ DelayProxy.new(self, sender: sender, **enqueue_args)
82
97
  end
83
98
 
84
- def __calculate_public_send_for_delay
99
+ def __calculate_sender_for_delay
85
100
  # enforce public send in dev and test, but not prod (since it uses
86
101
  # debug APIs, it's expensive)
87
- public_send = if ::Rails.env.test? || ::Rails.env.development?
88
- sender = self.sender(1)
89
- # if the caller isn't self, use public_send; i.e. enforce method visibility
90
- sender != self
91
- else
92
- false
93
- end
102
+ return sender(1) if ::Rails.env.test? || ::Rails.env.development?
94
103
  end
95
104
 
96
105
  module ClassMethods
@@ -114,7 +123,7 @@ module Delayed
114
123
  if synchronous
115
124
  super(*args, **kwargs)
116
125
  else
117
- delay(**enqueue_args).method_missing(method_name, *args, synchronous: true, **kwargs)
126
+ delay(sender: __calculate_sender_for_delay, **enqueue_args).method_missing(method_name, *args, synchronous: true, **kwargs)
118
127
  end
119
128
  end)
120
129
  end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Delayed
4
- class PerformableMethod < Struct.new(:object, :method, :args, :kwargs, :fail_cb, :permanent_fail_cb, :public_send)
5
- def initialize(object, method, args: [], kwargs: {}, on_failure: nil, on_permanent_failure: nil, public_send: true)
4
+ class PerformableMethod < Struct.new(:object, :method, :args, :kwargs, :fail_cb, :permanent_fail_cb, :sender)
5
+ def initialize(object, method, args: [], kwargs: {}, on_failure: nil, on_permanent_failure: nil, sender: nil)
6
6
  raise NoMethodError, "undefined method `#{method}' for #{object.inspect}" unless object.respond_to?(method, true)
7
7
 
8
8
  self.object = object
@@ -11,7 +11,13 @@ module Delayed
11
11
  self.method = method.to_sym
12
12
  self.fail_cb = on_failure
13
13
  self.permanent_fail_cb = on_permanent_failure
14
- self.public_send = public_send
14
+ self.sender = sender
15
+ begin
16
+ YAML.dump(sender)
17
+ rescue
18
+ # if for some reason you can't dump the sender, just drop it
19
+ self.sender = nil
20
+ end
15
21
  end
16
22
 
17
23
  def display_name
@@ -25,17 +31,21 @@ module Delayed
25
31
 
26
32
  def perform
27
33
  kwargs = self.kwargs || {}
28
- if public_send
34
+
35
+ sender_is_object = sender == object
36
+ sender_is_class = sender.is_a?(object.class)
37
+
38
+ if sender.nil? || sender_is_object || sender_is_class && object.protected_methods.include?(method)
29
39
  if kwargs.empty?
30
- object.public_send(method, *args)
40
+ object.send(method, *args)
31
41
  else
32
- object.public_send(method, *args, **kwargs)
42
+ object.send(method, *args, **kwargs)
33
43
  end
34
44
  else
35
45
  if kwargs.empty?
36
- object.send(method, *args)
46
+ object.public_send(method, *args)
37
47
  else
38
- object.send(method, *args, **kwargs)
48
+ object.public_send(method, *args, **kwargs)
39
49
  end
40
50
  end
41
51
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Delayed
4
- VERSION = "1.0.1"
4
+ VERSION = "1.0.2"
5
5
  end
@@ -8,21 +8,38 @@ RSpec.describe Delayed::MessageSending do
8
8
  allow(::Rails.env).to receive(:test?).and_return(true)
9
9
  end
10
10
 
11
- let(:klass) do
12
- Class.new do
11
+ before (:all) do
12
+ class SpecClass
13
13
  def call_private(**enqueue_args)
14
14
  delay(**enqueue_args).private_method
15
15
  end
16
16
 
17
+ def call_protected(**enqueue_args)
18
+ other = self.class.new
19
+ other.delay(**enqueue_args).protected_method
20
+ end
21
+
17
22
  private
18
23
 
19
24
  def private_method
20
25
  end
26
+
27
+ protected
28
+
29
+ def protected_method
30
+ end
21
31
  end
22
32
  end
23
33
 
34
+ after(:all) do
35
+ Object.send(:remove_const, :SpecClass)
36
+ end
37
+
38
+ let(:klass) { SpecClass }
39
+
24
40
  it "allows an object to send a private message to itself" do
25
- klass.new.call_private
41
+ job = klass.new.call_private(ignore_transaction: true)
42
+ job.invoke_job
26
43
  end
27
44
 
28
45
  it "allows an object to send a private message to itself synchronouosly" do
@@ -48,4 +65,37 @@ RSpec.describe Delayed::MessageSending do
48
65
  allow(::Rails.env).to receive(:development?).and_return(false)
49
66
  klass.new.delay(synchronous: true).private_method
50
67
  end
68
+
69
+ it "allows an object to send a protected message to itself" do
70
+ job = klass.new.call_protected(ignore_transaction: true)
71
+ job.invoke_job
72
+ end
73
+
74
+ it "allows an object to send a protected message to itself synchronouosly" do
75
+ klass.new.call_protected(synchronous: true)
76
+ end
77
+
78
+ it "warns about directly sending a protected message asynchronously" do
79
+ expect { klass.new.delay.protected_method }.to raise_error(NoMethodError)
80
+ end
81
+
82
+ it "warns about directly sending a protected message synchronusly" do
83
+ expect { klass.new.delay(synchronous: true).protected_method }.to raise_error(NoMethodError)
84
+ end
85
+
86
+ it "doesn't explode if you can't dump the sender" do
87
+ klass = Class.new do
88
+ def delay_something
89
+ Kernel.delay.sleep(1)
90
+ end
91
+
92
+ def encode_with(encoder)
93
+ raise "yaml encoding failed"
94
+ end
95
+ end
96
+
97
+ obj = klass.new
98
+ expect { YAML.dump(obj) }.to raise_error("yaml encoding failed")
99
+ expect { obj.delay_something }.not_to raise_error
100
+ end
51
101
  end
@@ -82,11 +82,11 @@ shared_examples_for 'random ruby objects' do
82
82
  obj = klass.new
83
83
  method = double()
84
84
 
85
- expect(Delayed::PerformableMethod).to receive(:new).with(obj, :test_method, args: [1,2,3], kwargs: {synchronous: true}, on_failure: nil, on_permanent_failure: nil).and_return(method)
85
+ expect(Delayed::PerformableMethod).to receive(:new).with(obj, :test_method, args: [1,2,3], kwargs: {synchronous: true}, on_failure: nil, on_permanent_failure: nil, sender: obj).and_return(method)
86
86
  expect(Delayed::Job).to receive(:enqueue).with(method, :enqueue_arg_1 => :thing)
87
87
  obj.test_method(1,2,3)
88
88
 
89
- expect(Delayed::PerformableMethod).to receive(:new).with(obj, :test_method, args: [4], kwargs: {:synchronous=>true}, on_failure: nil, on_permanent_failure: nil).and_return(method)
89
+ expect(Delayed::PerformableMethod).to receive(:new).with(obj, :test_method, args: [4], kwargs: {:synchronous=>true}, on_failure: nil, on_permanent_failure: nil, sender: obj).and_return(method)
90
90
  expect(Delayed::Job).to receive(:enqueue).with(method, :enqueue_arg_1 => :thing)
91
91
  obj.test_method(4)
92
92
 
@@ -35,6 +35,15 @@ RSpec.configure do |config|
35
35
  end
36
36
  end
37
37
 
38
+ module NoYamlDump
39
+ def encode_with(coder)
40
+ end
41
+ end
42
+ # example groups are often the sender, and if we try to serialize them,
43
+ # the resultant object is then encoded in the sender, and then we serialize
44
+ # again, and it just keeps getting bigger and bigger and bigger...
45
+ RSpec::Core::ExampleGroup.include(NoYamlDump)
46
+
38
47
  ENV['TEST_ENV_NUMBER'] ||= '1'
39
48
  ENV['TEST_DB_HOST'] ||= 'localhost'
40
49
  ENV['TEST_DB_DATABASE'] ||= "inst-jobs-test-#{ENV['TEST_ENV_NUMBER']}"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inst-jobs
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tobias Luetke
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2020-10-28 00:00:00.000000000 Z
12
+ date: 2020-10-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord