mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-05-12 20:48:11 +08:00
Recently, the `lock` step from our Ruby service framework has been extended to be able to work with keys from the global context when the key isn’t present in the `params`. The implementation is flawed because we usually get a contract object in the `params` key of the context, and currently we call `public_send(key)` on it. This actually raises an error when the key doesn’t exist on the contract. The fix is rather simple, we just use `try` instead, that way if the method doesn’t exist, it returns `nil`, allowing us to get the key from the global context as expected.
48 lines
1 KiB
Ruby
48 lines
1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Service
|
|
module Base
|
|
# @!visibility private
|
|
class LockStep < Step
|
|
include StepsHelpers
|
|
|
|
attr_reader :steps, :keys
|
|
|
|
def initialize(*keys, &block)
|
|
super(keys.join(":"))
|
|
@keys = keys
|
|
@steps = []
|
|
instance_exec(&block)
|
|
end
|
|
|
|
def run_step
|
|
success =
|
|
begin
|
|
DistributedMutex.synchronize(lock_name) do
|
|
steps.each { |step| step.call(instance, context) }
|
|
:success
|
|
end
|
|
rescue Discourse::ReadOnly
|
|
:read_only
|
|
end
|
|
|
|
if success != :success
|
|
context[result_key].fail(lock_not_acquired: true)
|
|
context.fail!
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def lock_name
|
|
[
|
|
context.__service_class__.to_s.underscore,
|
|
*keys.flat_map do |key|
|
|
value = context[:params].try(key) || context[key]
|
|
[key, value.try(:id) || value]
|
|
end,
|
|
].join(":")
|
|
end
|
|
end
|
|
end
|
|
end
|