inst-jobs 1.0.1 → 1.0.2

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
  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