1
0
зеркало из https://github.com/glebtv/telegram-bot.git synced 2026-09-05 02:35:50 +03:00

Make #session raise error when store is not configured

Add more explanation on sessions in readme
Этот коммит содержится в:
Max Melentiev
2017-10-06 11:05:25 +03:00
родитель df7039326d
Коммит ef4f3caa5f
3 изменённых файлов: 85 добавлений и 12 удалений

Просмотреть файл

@@ -199,12 +199,33 @@ end
#### Session #### Session
There is support for sessions using `ActiveSupport::Cache` stores. This API is very close to ActiveController's session API, but works different
under the hood. Cookies can not be used to store session id or
whole session (like CookieStore does). So it uses key-value store and `session_key`
method to build identifier from update.
Store can be one of numerous `ActiveSupport::Cache` stores.
While `:file_store` is suitable for development and single-server deployments
without heavy load, it doesn't scale well. Key-value databases with persistance
like Redis are more appropriate for production use.
```ruby ```ruby
# configure store in env files: # In rails app store can be configured in env files:
config.telegram_updates_controller.session_store = :redis_store, {expires_in: 1.month} config.telegram_updates_controller.session_store = :redis_store, {expires_in: 1.month}
# In other app it can be done for all controllers with:
Telegram::Bot::UpdatesController.session_store = :redis_store, {expires_in: 1.month}
# or for specific one:
OneOfUpdatesController.session_store = :redis_store, {expires_in: 1.month}
```
Default session id is made from bot's username and `(from || chat)['id']`.
It means that session will be the same for updates from user in every chat,
and different for every user in the same group chat.
To change this behavior you can override `session_key` method, or even
define multiple sessions in single controller. For details see `Session` module.
```ruby
class Telegram::WebhookController < Telegram::Bot::UpdatesController class Telegram::WebhookController < Telegram::Bot::UpdatesController
include Telegram::Bot::UpdatesController::Session include Telegram::Bot::UpdatesController::Session
# or just shortcut: # or just shortcut:
@@ -222,12 +243,16 @@ class Telegram::WebhookController < Telegram::Bot::UpdatesController
end end
private private
# By default it uses bot's username and user's id as a session key.
# Chat's id is used only when `from` field is empty. # In this case session will persist for user only in specific chat.
# Override `session_key` method to change this behavior. # Same user in other chat will have different session.
def session_key def session_key
# In this case session will persist for user only in specific chat: "#{bot.username}:#{chat['id']}:#{from['id']}" if chat && from
"#{bot.username}:#{chat['id']}:#{from['id']}" end
# This session will be the same for all updates in chat.
def chat_session
@_chat_session ||= self.class.build_session(chat && "#{bot.username}:#{chat['id']}")
end end
end end
``` ```

Просмотреть файл

@@ -8,19 +8,26 @@ module Telegram
module Session module Session
extend ActiveSupport::Concern extend ActiveSupport::Concern
module ClassMethods
# Builds session with given key and optional store (default to session_store).
# This way it's easier to define multiple custom sessions,
# ex. one for group chat and one for user.
def build_session(key, store = session_store)
raise 'session_store is not configured' unless store
key ? SessionHash.new(store, key) : NullSessionHash.new
end
end
def process_action(*) def process_action(*)
super super
ensure ensure
session.commit session.commit if @_session
end end
protected protected
def session def session
@_session ||= begin @_session ||= self.class.build_session(session_key)
key = session_key
key ? SessionHash.new(self.class.session_store, key) : NullSessionHash.new
end
end end
def session_key def session_key

Просмотреть файл

@@ -54,4 +54,45 @@ RSpec.describe Telegram::Bot::UpdatesController::Session do
end end
end end
end end
describe '.build_session' do
subject { controller_class.build_session(key, *args) }
let(:key) {}
let(:args) { [] }
it { expect { subject }.to raise_error(/session_store is not configured/) }
shared_examples 'NullSessionHash when key is not present' do |store_proc|
it { should be_instance_of(described_class::NullSessionHash) }
context 'and key is present' do
let(:key) { :test_key }
it 'is valid SessionHash' do
expect(subject).to be_instance_of(described_class::SessionHash)
expect(subject.id).to eq key
expect(subject.instance_variable_get(:@store)).to be(instance_exec(&store_proc))
end
end
end
context 'when store configured' do
before { controller_class.session_store = nil }
include_examples 'NullSessionHash when key is not present',
-> { controller_class.session_store }
end
context 'when store is given' do
let(:args) { [double(:store)] }
include_examples 'NullSessionHash when key is not present', -> { args[0] }
end
end
describe '.session_store=' do
subject { ->(val) { controller_class.session_store = val } }
it 'casts to AS::Cache' do
expect { subject[:null_store] }.to change(controller_class, :session_store).
to(instance_of(ActiveSupport::Cache::NullStore))
expect { subject[nil] }.to change(controller_class, :session_store).
to(instance_of(ActiveSupport::Cache::MemoryStore))
end
end
end end