зеркало из
https://github.com/glebtv/telegram-bot.git
synced 2026-09-04 02:05:52 +03:00
Сравнить коммиты
2 Коммитов
v0.9.0.alp
...
v0.9.0.alp
| Автор | SHA1 | Дата | |
|---|---|---|---|
|
|
2e405acfb1 | ||
|
|
b2eca2d37f |
@@ -30,12 +30,18 @@ module Telegram
|
|||||||
# client.send_message(message)
|
# client.send_message(message)
|
||||||
# client.async(false) { client.send_message(other_one) }
|
# client.async(false) { client.send_message(other_one) }
|
||||||
#
|
#
|
||||||
|
# `#async=` sets global value for all threads,
|
||||||
|
# while `#async(val, &block)` is thread-safe.
|
||||||
|
#
|
||||||
# It can be set with custom job class or classname. By default it defines
|
# It can be set with custom job class or classname. By default it defines
|
||||||
# job classes for every client class, inherited from ApplicationRecord, which
|
# job classes for every client class, inherited from ApplicationRecord, which
|
||||||
# can be accessed via `.default_async_job`. You can integrate it with any
|
# can be accessed via `.default_async_job`. You can integrate it with any
|
||||||
# other job provider by defining a class with `.perform_later(bot_id, *args)`
|
# other job provider by defining a class with `.perform_later(bot_id, *args)`
|
||||||
# method. See Async::Job for implemetation.
|
# method. See Async::Job for implemetation.
|
||||||
module Async
|
module Async
|
||||||
|
# Used to track missing key in a hash in local variable.
|
||||||
|
MISSING_VALUE = Object.new.freeze
|
||||||
|
|
||||||
module Job
|
module Job
|
||||||
class << self
|
class << self
|
||||||
def included(base)
|
def included(base)
|
||||||
@@ -78,6 +84,16 @@ module Telegram
|
|||||||
def prepare_async_args(*args)
|
def prepare_async_args(*args)
|
||||||
args
|
args
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns default_async_job if `true` is given,
|
||||||
|
# treats String as a constant name, or bypasses any other values.
|
||||||
|
def prepare_async_val(val)
|
||||||
|
case val
|
||||||
|
when true then default_async_job
|
||||||
|
when String then Object.const_get(val)
|
||||||
|
else val
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
@@ -91,38 +107,46 @@ module Telegram
|
|||||||
hash = hash.dup
|
hash = hash.dup
|
||||||
hash.each { |key, val| hash[key] = val.to_s if val.is_a?(Symbol) }
|
hash.each { |key, val| hash[key] = val.to_s if val.is_a?(Symbol) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Thread-local hash to store async config for every client.
|
||||||
|
def thread_store
|
||||||
|
Thread.current[:telegram_bot_async] ||= {}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_reader :id
|
attr_reader :id
|
||||||
|
|
||||||
def initialize(*, id: nil, async: nil, **options)
|
def initialize(*, id: nil, async: nil, **)
|
||||||
@id = id
|
@id = id
|
||||||
self.async = async
|
self.async = async
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sets `@async` to `self.class.default_async_job` if `true` is given
|
# Sets default async value for all threads.
|
||||||
# or uses given value.
|
# Uses `self.class.prepare_async_val` to prepare value.
|
||||||
# Pass custom job class to perform async calls with.
|
|
||||||
def async=(val)
|
def async=(val)
|
||||||
@async =
|
@async = self.class.prepare_async_val(val)
|
||||||
case val
|
|
||||||
when true then self.class.default_async_job
|
|
||||||
when String then const_get(val)
|
|
||||||
else val
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns value of `@async` if no block is given. Otherwise sets this value
|
# Sets async value in a thread-safe way for the block.
|
||||||
# for a block.
|
# Uses `self.class.prepare_async_val` to prepare value.
|
||||||
|
#
|
||||||
|
# If no block is given returns previously set value or the global one,
|
||||||
|
# set by #async=.
|
||||||
def async(val = true)
|
def async(val = true)
|
||||||
return @async unless block_given?
|
thread_key = object_id
|
||||||
|
thread_store = Async.thread_store
|
||||||
|
return thread_store.fetch(thread_key) { @async } unless block_given?
|
||||||
begin
|
begin
|
||||||
old_val = @async
|
old_val = thread_store.fetch(thread_key) { MISSING_VALUE }
|
||||||
self.async = val
|
thread_store[thread_key] = self.class.prepare_async_val(val)
|
||||||
yield
|
yield
|
||||||
ensure
|
ensure
|
||||||
@async = old_val
|
if MISSING_VALUE == old_val
|
||||||
|
thread_store.delete(thread_key)
|
||||||
|
else
|
||||||
|
thread_store[thread_key] = old_val
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
module Telegram
|
module Telegram
|
||||||
module Bot
|
module Bot
|
||||||
VERSION = '0.9.0.alpha1'.freeze
|
VERSION = '0.9.0.alpha2'.freeze
|
||||||
|
|
||||||
def self.gem_version
|
def self.gem_version
|
||||||
Gem::Version.new VERSION
|
Gem::Version.new VERSION
|
||||||
|
|||||||
@@ -34,6 +34,60 @@ RSpec.shared_examples 'async' do |request_args: -> {}|
|
|||||||
end.to_not change(instance, :async).from(described_class.default_async_job)
|
end.to_not change(instance, :async).from(described_class.default_async_job)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'in multi-threaded environment' do
|
||||||
|
it 'changes only in current thread' do
|
||||||
|
thread = nil
|
||||||
|
expect do
|
||||||
|
thread = Thread.new do
|
||||||
|
expect do
|
||||||
|
subject.call(false) do
|
||||||
|
expect(subject[]).to eq false
|
||||||
|
end
|
||||||
|
end.to_not change(instance, :async).from(described_class.default_async_job)
|
||||||
|
sleep 0.2
|
||||||
|
end
|
||||||
|
end.to_not change(instance, :async).from(described_class.default_async_job)
|
||||||
|
thread.join
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'uses default value after block' do
|
||||||
|
thread = Thread.new do
|
||||||
|
subject.call(false) {}
|
||||||
|
expect { sleep 0.2 }.to change(instance, :async).
|
||||||
|
from(described_class.default_async_job).to(nil)
|
||||||
|
end
|
||||||
|
sleep 0.1
|
||||||
|
instance.async = nil
|
||||||
|
thread.join
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#async=' do
|
||||||
|
subject { ->(val = new_val) { instance.async = val } }
|
||||||
|
let(:async) {}
|
||||||
|
|
||||||
|
context 'when true is given' do
|
||||||
|
let(:new_val) { true }
|
||||||
|
it { should change(instance, :async).to described_class.default_async_job }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when false is given' do
|
||||||
|
let(:new_val) { false }
|
||||||
|
it { should change(instance, :async).to false }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when string is given' do
|
||||||
|
let(:new_val) { 'Telegram::Bot::Client' }
|
||||||
|
it { should change(instance, :async).to Object.const_get(new_val) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'in multi-threaded environment' do
|
||||||
|
subject { ->(val = new_val) { Thread.new { instance.async = val }.join } }
|
||||||
|
let(:new_val) { true }
|
||||||
|
it { should change(instance, :async).to described_class.default_async_job }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.default_async_job' do
|
describe '.default_async_job' do
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user