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

Ability to use type-castion with telegram-bot-types

Этот коммит содержится в:
Max Melentiev
2016-02-23 14:11:55 +06:00
родитель 00a2e3ba8d
Коммит a1adf17df3
24 изменённых файлов: 392 добавлений и 200 удалений

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

@@ -6,6 +6,8 @@ group :development do
gem 'pry', '~> 0.10.1' gem 'pry', '~> 0.10.1'
gem 'pry-byebug', '~> 3.2.0' gem 'pry-byebug', '~> 3.2.0'
gem 'telegram-bot-types', '~> 0.2.0'
gem 'rspec', '~> 3.3.0' gem 'rspec', '~> 3.3.0'
gem 'rspec-its', '~> 1.1.0' gem 'rspec-its', '~> 1.1.0'

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

@@ -5,13 +5,13 @@
[![Build Status](https://travis-ci.org/printercu/telegram-bot.svg)](https://travis-ci.org/printercu/telegram-bot) [![Build Status](https://travis-ci.org/printercu/telegram-bot.svg)](https://travis-ci.org/printercu/telegram-bot)
Tools for developing bot for Telegram. Best used with Rails, but can be be used in Tools for developing bot for Telegram. Best used with Rails, but can be be used in
standalone app. Supposed to be used in webhook-mode in production, and poller mode standalone app. Supposed to be used in webhook-mode in production, and poller-mode
in development, but you can use poller in production if you want. in development, but you can use poller in production if you want.
Package contains: Package contains:
- Ligthweight client to bot API (with fast and thread-safe - Ligthweight client for bot API (with fast and thread-safe
[httpclient](https://github.com/nahi/httpclient) is under the hood.) [httpclient](https://github.com/nahi/httpclient) under the hood).
- Controller with message parser. Allows to write separate methods for each command. - Controller with message parser. Allows to write separate methods for each command.
- Middleware and routes helpers for production env. - Middleware and routes helpers for production env.
- Poller with automatic source-reloader for development env. - Poller with automatic source-reloader for development env.
@@ -62,17 +62,33 @@ telegram:
From now clients will be accessible with `Telegram.bots[:chat]` or `Telegram.bots[:auction]`. From now clients will be accessible with `Telegram.bots[:chat]` or `Telegram.bots[:auction]`.
Single bot can be accessed with `Telegram.bot` or `Telegram.bots[:default]`. Single bot can be accessed with `Telegram.bot` or `Telegram.bots[:default]`.
You can create clients manually with `Telegram::Bot.new(token, username)`. You can create clients manually with `Telegram::Bot::Client.new(token, username)`.
Username is optional and used only to parse commands with mentions. Username is optional and used only to parse commands with mentions.
Client has all available methods in underscored style There is `request(path_suffix, body)` method to perform any query.
And there are also shortcuts for available queries in underscored style
(`answer_inline_query` instead of `answerInlineQuery`). (`answer_inline_query` instead of `answerInlineQuery`).
All this methods just post given params to specific URL. All this methods just post given params to specific URL.
```ruby ```ruby
bot.request(:getMe) or bot.get_me
bot.request(:getupdates, offset: 1) or bot.get_updates(offset: 1)
bot.send_message chat_id: chat_id, text: 'Test' bot.send_message chat_id: chat_id, text: 'Test'
``` ```
By default client will return parsed json responses. You can enable
response typecasting to virtus models using `telegram-bot-types` gem:
```ruby
# Add to your gemfile:
gem 'telegram-bot-types', '~> x.x.x'
# Enable typecasting:
Telegram::Bot::Client.typed_response!
# or for single instance:
bot.extend Telegram::Bot::Client::TypedResponse
bot.get_me.class # => Telegram::Bot::Types::User
```
### Controller ### Controller
```ruby ```ruby
@@ -121,6 +137,19 @@ class Telegram::WebhookController < Telegram::Bot::UpdatesController
end end
``` ```
You can enable typecasting of `update` with `telegram-bot-types` by including
`Telegram::Bot::UpdatesPoller::TypedUpdate`:
```ruby
class Telegram::WebhookController < Telegram::Bot::UpdatesController
include Telegram::Bot::UpdatesPoller::TypedUpdate
def message(message)
message.class # => Telegram::Bot::Types::Message
end
end
```
### Routes ### Routes
Use `telegram_webhooks` helper to add routes. It will create routes for bots Use `telegram_webhooks` helper to add routes. It will create routes for bots

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

@@ -1,97 +1,16 @@
require 'httpclient' require 'telegram/bot/config_methods'
require 'json'
require 'active_support/core_ext/string/inflections'
require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/array/wrap'
require 'telegram/bottable'
module Telegram module Telegram
extend Bottable extend Bot::ConfigMethods
class Bot module Bot
class Error < StandardError; end class Error < StandardError; end
class NotFound < Error; end class NotFound < Error; end
autoload :Client, 'telegram/bot/client'
autoload :Middleware, 'telegram/bot/middleware' autoload :Middleware, 'telegram/bot/middleware'
autoload :UpdatesController, 'telegram/bot/updates_controller' autoload :UpdatesController, 'telegram/bot/updates_controller'
autoload :UpdatesPoller, 'telegram/bot/updates_poller' autoload :UpdatesPoller, 'telegram/bot/updates_poller'
URL_TEMPLATE = 'https://api.telegram.org/bot%s/'.freeze
class << self
# Accepts different options to initialize bot.
def wrap(input)
case input
when self then input
when Array then input.map(&method(__callee__))
when Hash then
input = input.stringify_keys
new input['token'], input['username']
else
new(input)
end
end
end
attr_reader :client, :token, :username, :base_uri
def initialize(token, username = nil)
@client = HTTPClient.new
@token = token
@username = username
@base_uri = format URL_TEMPLATE, token
end
def debug!(dev = STDOUT)
client.debug_dev = dev
end
def debug_off!
client.debug_dev = nil
end
def request(action, data = {})
res = http_request("#{base_uri}#{action}", data)
status = res.status
return JSON.parse(res.body) if 300 > status
result = JSON.parse(res.body) rescue nil # rubocop:disable RescueModifier
err_msg = "#{res.reason}: #{result && result['description'] || '-'}"
# NotFound is raised only for valid responses from Telegram
raise NotFound, err_msg if 404 == status && result
raise Error, err_msg
end
%w(
answerInlineQuery
forwardMessage
getFile
getMe
getUpdates
getUserProfilePhotos
sendAudio
sendChatAction
sendDocument
sendLocation
sendMessage
sendPhoto
sendSticker
sendVideo
sendVoice
setWebhook
).each do |method|
define_method(method.underscore) { |*args| request(method, *args) }
end
# Endpoint for low-level request. For easy host highjacking & instrumentation.
# Params are not used directly but kept for instrumentation purpose.
# You probably don't want to use this method directly.
def http_request(uri, body)
client.post(uri, body)
end
def inspect
"#<Telegram::Bot##{object_id}(#{@username})>"
end
end end
end end

94
lib/telegram/bot/client.rb Обычный файл
Просмотреть файл

@@ -0,0 +1,94 @@
require 'json'
require 'httpclient'
require 'active_support/core_ext/string/inflections'
require 'active_support/core_ext/hash/keys'
module Telegram
module Bot
class Client
autoload :TypedResponse, 'telegram/bot/client/typed_response'
URL_TEMPLATE = 'https://api.telegram.org/bot%s/'.freeze
class << self
# Accepts different options to initialize bot.
def wrap(input)
case input
when self then input
when Array then input.map(&method(__callee__))
when Hash then
input = input.stringify_keys
new input['token'], input['username']
else
new(input)
end
end
# Prepend TypedResponse module.
def typed_response!
prepend TypedResponse
end
end
attr_reader :client, :token, :username, :base_uri
def initialize(token, username = nil)
@client = HTTPClient.new
@token = token
@username = username
@base_uri = format URL_TEMPLATE, token
end
def debug!(dev = STDOUT)
client.debug_dev = dev
end
def debug_off!
client.debug_dev = nil
end
def request(action, data = {})
res = http_request("#{base_uri}#{action}", data)
status = res.status
return JSON.parse(res.body) if 300 > status
result = JSON.parse(res.body) rescue nil # rubocop:disable RescueModifier
err_msg = "#{res.reason}: #{result && result['description'] || '-'}"
# NotFound is raised only for valid responses from Telegram
raise NotFound, err_msg if 404 == status && result
raise Error, err_msg
end
%w(
answerInlineQuery
forwardMessage
getFile
getMe
getUpdates
getUserProfilePhotos
sendAudio
sendChatAction
sendDocument
sendLocation
sendMessage
sendPhoto
sendSticker
sendVideo
sendVoice
setWebhook
).each do |method|
define_method(method.underscore) { |*args| request(method, *args) }
end
# Endpoint for low-level request. For easy host highjacking & instrumentation.
# Params are not used directly but kept for instrumentation purpose.
# You probably don't want to use this method directly.
def http_request(uri, body)
client.post(uri, body)
end
def inspect
"#<Telegram::Bot::Client##{object_id}(#{@username})>"
end
end
end
end

38
lib/telegram/bot/client/typed_response.rb Обычный файл
Просмотреть файл

@@ -0,0 +1,38 @@
module Telegram
module Bot
class Client
# Actions with type-casted results. Install `telegram-bot-types` gem first.
module TypedResponse
{
getFile: :File,
getMe: :User,
getUpdates: [:Update],
getUserProfilePhotos: :UserProfilePhotos,
forwardMessage: :Message,
sendAudio: :Message,
sendDocument: :Message,
sendLocation: :Message,
sendMessage: :Message,
sendPhoto: :Message,
sendSticker: :Message,
sendVideo: :Message,
sendVoice: :Message,
}.each do |method, type|
next unless type
if type.is_a?(Array)
type_class = Types.const_get(type.first)
define_method(method.to_s.underscore) do |*args|
request(method, *args)['result'].map { |x| type_class.new(x) }
end
else
type_class = Types.const_get(type)
define_method(method.to_s.underscore) do |*args|
type_class.new request(method, *args)['result']
end
end
end
end
end
end
end

46
lib/telegram/bot/config_methods.rb Обычный файл
Просмотреть файл

@@ -0,0 +1,46 @@
require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/hash/transform_values'
module Telegram
module Bot
module ConfigMethods
# Overwrite config.
attr_writer :bots_config
# Keep this setting here, so we can avoid loading Bot::UpdatesPoller
# when polling is disabled.
attr_writer :bot_poller_mode
# It just tells routes helpers whether to add routed bots to
# Bot::UpdatesPoller, so their config will be available by bot key in
# Bot::UpdatesPoller.start.
def bot_poller_mode?
return @bot_poller_mode if defined?(@bot_poller_mode)
Rails.env.development? if defined?(Rails)
end
# Hash of bots made with bots_config.
def bots
@bots ||= bots_config.transform_values(&Client.method(:wrap))
end
# Default bot.
def bot
@bot ||= bots[:default]
end
# Returns config for .bots method. By default uses `telegram['bots']` section
# from `secrets.yml` merging `telegram['bot']` at `:default` key.
#
# Can be overwritten with .bots_config=
def bots_config
return @bots_config if @bots_config
telegram_config = Rails.application.secrets[:telegram]
(telegram_config['bots'] || {}).symbolize_keys.tap do |config|
default = telegram_config['bot']
config[:default] = default if default
end
end
end
end
end

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

@@ -3,7 +3,7 @@ require 'action_dispatch/http/mime_type'
require 'action_dispatch/middleware/params_parser' require 'action_dispatch/middleware/params_parser'
module Telegram module Telegram
class Bot module Bot
class Middleware class Middleware
attr_reader :bot, :controller attr_reader :bot, :controller

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

@@ -1,7 +1,7 @@
require 'telegram/bot/routes_helper' require 'telegram/bot/routes_helper'
module Telegram module Telegram
class Bot module Bot
class Railtie < Rails::Railtie class Railtie < Rails::Railtie
config.telegram_updates_controller = ActiveSupport::OrderedOptions.new config.telegram_updates_controller = ActiveSupport::OrderedOptions.new

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

@@ -1,7 +1,8 @@
require 'telegram/bot' require 'telegram/bot'
require 'active_support/core_ext/array/wrap'
module Telegram module Telegram
class Bot module Bot
module RoutesHelper module RoutesHelper
class << self class << self
# Returns route name for given bot. Result depends on `Telegram.bots`. # Returns route name for given bot. Result depends on `Telegram.bots`.
@@ -42,7 +43,7 @@ module Telegram
controllers = Hash[bots.map { |x| [x, controllers] }] controllers = Hash[bots.map { |x| [x, controllers] }]
end end
controllers.each do |bot, controller| controllers.each do |bot, controller|
bot = Bot.wrap(bot) bot = Client.wrap(bot)
controller, bot_options = controller if controller.is_a?(Array) controller, bot_options = controller if controller.is_a?(Array)
params = { params = {
to: Middleware.new(bot, controller), to: Middleware.new(bot, controller),

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

@@ -2,8 +2,10 @@ require 'abstract_controller'
require 'active_support/callbacks' require 'active_support/callbacks'
module Telegram module Telegram
class Bot module Bot
class UpdatesController < AbstractController::Base class UpdatesController < AbstractController::Base
abstract!
include AbstractController::Callbacks include AbstractController::Callbacks
include AbstractController::Translation include AbstractController::Translation
@@ -11,6 +13,8 @@ module Telegram
require 'telegram/bot/updates_controller/instrumentation' require 'telegram/bot/updates_controller/instrumentation'
prepend Instrumentation prepend Instrumentation
autoload :TypedUpdate, 'telegram/bot/updates_controller/typed_update'
PAYLOAD_TYPES = %w( PAYLOAD_TYPES = %w(
message message
inline_query inline_query
@@ -18,7 +22,6 @@ module Telegram
).freeze ).freeze
CMD_REGEX = %r{\A/([a-z\d_]{,31})(@(\S+))?(\s|$)}i CMD_REGEX = %r{\A/([a-z\d_]{,31})(@(\S+))?(\s|$)}i
CONFLICT_CMD_REGEX = Regexp.new("^(#{PAYLOAD_TYPES.join('|')}|\\d)") CONFLICT_CMD_REGEX = Regexp.new("^(#{PAYLOAD_TYPES.join('|')}|\\d)")
abstract!
class << self class << self
def dispatch(*args) def dispatch(*args)

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

@@ -1,5 +1,5 @@
module Telegram module Telegram
class Bot module Bot
class UpdatesController class UpdatesController
# Most methods are taken from ActionController::Instrumentation, # Most methods are taken from ActionController::Instrumentation,
# some are slightly modified. # some are slightly modified.

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

@@ -1,7 +1,7 @@
require 'active_support/log_subscriber' require 'active_support/log_subscriber'
module Telegram module Telegram
class Bot module Bot
class UpdatesController class UpdatesController
class LogSubscriber < ActiveSupport::LogSubscriber class LogSubscriber < ActiveSupport::LogSubscriber
def start_processing(event) def start_processing(event)

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

@@ -0,0 +1,8 @@
RSpec.shared_context 'telegram/bot/updates_controller' do
let(:instance) { described_class.new(bot, update) }
let(:update) { {payload_type => payload} }
let(:payload_type) { 'some_type' }
let(:payload) { double(:payload) }
let(:bot) { double(username: bot_name) }
let(:bot_name) { 'bot' }
end

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

@@ -0,0 +1,14 @@
module Telegram
module Bot
class UpdatesController
# Include this module to type cast update to Virtus model
# using `telegram-bot-types` gem (install this gem first).
module TypedUpdate
def initialize(bot, update)
update = Types::Update.new(update) if update && !update.is_a?(Types::Update)
super
end
end
end
end
end

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

@@ -1,5 +1,5 @@
module Telegram module Telegram
class Bot module Bot
# Supposed to be used in development environments only. # Supposed to be used in development environments only.
class UpdatesPoller class UpdatesPoller
class << self class << self
@@ -15,7 +15,7 @@ module Telegram
end end
def start(bot_id, controller = nil) def start(bot_id, controller = nil)
bot = bot_id.is_a?(Symbol) ? Telegram.bots[bot_id] : Bot.wrap(bot_id) bot = bot_id.is_a?(Symbol) ? Telegram.bots[bot_id] : Client.wrap(bot_id)
instance = controller ? new(bot, controller) : instances[bot] instance = controller ? new(bot, controller) : instances[bot]
raise "Poller not found for #{bot_id.inspect}" unless instance raise "Poller not found for #{bot_id.inspect}" unless instance
instance.start instance.start

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

@@ -1,5 +1,5 @@
module Telegram module Telegram
class Bot module Bot
VERSION = '0.3.0'.freeze VERSION = '0.3.0'.freeze
def self.gem_version def self.gem_version

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

@@ -1,41 +0,0 @@
module Telegram
module Bottable
# Overwrite config.
attr_writer :bots_config
# Keep this setting here, so we can avoid loading Bot::UpdatesPoller
# when polling is disabled.
attr_writer :bot_poller_mode
# It just tells routes helpers whether to add routed bots to
# Bot::UpdatesPoller, so their config will be available by bot key in
# Bot::UpdatesPoller.start.
def bot_poller_mode?
return @bot_poller_mode if defined?(@bot_poller_mode)
Rails.env.development? if defined?(Rails)
end
# Hash of bots made with bots_config.
def bots
@bots ||= bots_config.transform_values(&Bot.method(:wrap))
end
# Default bot.
def bot
@bot ||= bots[:default]
end
# Returns config for .bots method. By default uses `telegram['bots']` section
# from `secrets.yml` merging `telegram['bot']` at `:default` key.
#
# Can be overwritten with .bots_config=
def bots_config
return @bots_config if @bots_config
telegram_config = Rails.application.secrets[:telegram]
(telegram_config['bots'] || {}).symbolize_keys.tap do |config|
default = telegram_config['bot']
config[:default] = default if default
end
end
end
end

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

@@ -14,6 +14,8 @@ GEM_ROOT = Pathname.new File.expand_path('../..', __FILE__)
$LOAD_PATH.unshift GEM_ROOT.join('lib') $LOAD_PATH.unshift GEM_ROOT.join('lib')
require 'telegram/bot' require 'telegram/bot'
require 'telegram/bot/updates_controller/rspec_helpers'
require 'telegram/bot/types'
RSpec.configure do |config| RSpec.configure do |config|
config.expect_with :rspec do |expectations| config.expect_with :rspec do |expectations|

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

@@ -0,0 +1,32 @@
RSpec.describe Telegram::Bot::Client::TypedResponse do
let(:bot) { Telegram::Bot::Client.new('token').tap { |x| x.extend described_class } }
describe '#get_me' do
subject { bot.get_me }
before { expect(bot).to receive(:request).with(:getMe) { response } }
let(:response) { {'ok' => true, 'result' => {'id' => user_id}} }
let(:user_id) { 123 }
it { should be_instance_of Telegram::Bot::Types::User }
its(:id) { should eq user_id }
context 'on error' do
let(:response) { raise Telegram::Bot::Error }
it { expect { subject }.to raise_error Telegram::Bot::Error }
end
end
describe '#get_updates' do
subject { bot.get_updates }
before { expect(bot).to receive(:request).with(:getUpdates) { response } }
let(:response) { {'ok' => true, 'result' => [{'update_id' => update_id}]} }
let(:update_id) { 123 }
it { should be_instance_of Array }
its(:first) { should be_instance_of Telegram::Bot::Types::Update }
its('first.update_id') { should eq update_id }
context 'on error' do
let(:response) { raise Telegram::Bot::Error }
it { expect { subject }.to raise_error Telegram::Bot::Error }
end
end
end

47
spec/telegram/bot/client_spec.rb Обычный файл
Просмотреть файл

@@ -0,0 +1,47 @@
RSpec.describe Telegram::Bot::Client do
describe '.wrap' do
subject { described_class.wrap(input) }
let(:result) { double(:result) }
let(:token) { 'token' }
let(:username) { 'username' }
let(:instance) { described_class.new 'token' }
context 'when input is a string' do
let(:input) { token }
it 'treats string as token' do
expect(described_class).to receive(:new).with(token) { result }
should eq result
end
end
context 'when input is a hash' do
let(:input) { {token: token, username: username, ignore: :ignore} }
it 'extracts token & username' do
expect(described_class).to receive(:new).with(token, username) { result }
should eq result
end
end
context 'when input is an instance of described_class' do
let!(:input) { instance }
it 'returns input' do
expect(described_class).to_not receive(:new)
should eq input
end
end
context 'when input is an array' do
let!(:input) { ['other_token', instance, token: token, username: username] }
let(:result_2) { double(:result_2) }
it 'calls wrap for every element' do
expect(described_class).to receive(:new).with('other_token') { result }
expect(described_class).to receive(:new).with(token, username) { result_2 }
should eq [result, instance, result_2]
end
end
end
end

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

@@ -1,10 +1,14 @@
require 'telegram/bot/routes_helper' require 'telegram/bot/routes_helper'
RSpec.describe Telegram::Bot::RoutesHelper do RSpec.describe Telegram::Bot::RoutesHelper do
let(:bot) { Telegram::Bot.new('bot_token') } let(:bot) { create_bot('bot_token') }
let(:other_bot) { Telegram::Bot.new('other_token') } let(:other_bot) { create_bot('other_token') }
let(:bots) { {default: bot, other: other_bot} } let(:bots) { {default: bot, other: other_bot} }
def create_bot(*args)
Telegram::Bot::Client.new(*args)
end
describe '.route_name_for_bot' do describe '.route_name_for_bot' do
subject { described_class.route_name_for_bot(input) } subject { described_class.route_name_for_bot(input) }
before { expect(Telegram).to receive(:bots) { bots } } before { expect(Telegram).to receive(:bots) { bots } }
@@ -82,7 +86,7 @@ RSpec.describe Telegram::Bot::RoutesHelper do
it 'creates routes for every bot and its controller' do it 'creates routes for every bot and its controller' do
assert_routes [bot, controller, 'default_telegram_webhook', option: :val], assert_routes [bot, controller, 'default_telegram_webhook', option: :val],
[ [
Telegram::Bot.new('custom_token'), create_bot('custom_token'),
other_controller, other_controller,
:custom_route, :custom_route,
option: :other_val, option: :other_val,
@@ -101,12 +105,12 @@ RSpec.describe Telegram::Bot::RoutesHelper do
it 'creates routes for every created bot and controller' do it 'creates routes for every created bot and controller' do
assert_routes [ assert_routes [
Telegram::Bot.new('custom_token'), create_bot('custom_token'),
controller, controller,
'telegram_webhook', 'telegram_webhook',
option: :val, option: :val,
], [ ], [
Telegram::Bot.new(bot.token, 'new_name'), create_bot(bot.token, 'new_name'),
controller, controller,
'telegram_webhook', 'telegram_webhook',
option: :val, option: :val,

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

@@ -0,0 +1,27 @@
RSpec.describe Telegram::Bot::UpdatesController::TypedUpdate do
include_context 'telegram/bot/updates_controller'
let(:instance) { controller_class.new(bot, update) }
let(:controller_class) do
described_class = self.described_class
Class.new(Telegram::Bot::UpdatesController) do
include described_class
end
end
context 'when `update` is a virtus model' do
subject { instance }
%w(
message
inline_query
chosen_inline_result
).each do |type|
context "with #{type}" do
type_class = Telegram::Bot::Types.const_get(type.camelize)
let(:payload_type) { type }
let(:payload) { {} }
its(:payload_type) { should eq payload_type }
its(:payload) { should be_instance_of type_class }
end
end
end
end

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

@@ -1,10 +1,5 @@
RSpec.describe Telegram::Bot::UpdatesController do RSpec.describe Telegram::Bot::UpdatesController do
let(:instance) { described_class.new(bot, update) } include_context 'telegram/bot/updates_controller'
let(:update) { {payload_type => payload} }
let(:payload_type) { 'some_type' }
let(:payload) { double(:payload) }
let(:bot) { double(username: bot_name) }
let(:bot_name) { 'bot' }
let(:other_bot_name) { 'other_bot' } let(:other_bot_name) { 'other_bot' }
describe '.action_for_command' do describe '.action_for_command' do
@@ -117,6 +112,24 @@ RSpec.describe Telegram::Bot::UpdatesController do
end end
end end
context 'when `update` is a virtus model' do
subject { instance }
let(:update) { Telegram::Bot::Types::Update.new(super()) }
%w(
message
inline_query
chosen_inline_result
).each do |type|
context "with #{type}" do
type_class = Telegram::Bot::Types.const_get(type.camelize)
let(:payload_type) { type }
let(:payload) { {} }
its(:payload_type) { should eq payload_type }
its(:payload) { should be_instance_of type_class }
end
end
end
describe '#bot_username' do describe '#bot_username' do
subject { instance.bot_username } subject { instance.bot_username }

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

@@ -1,51 +1,5 @@
RSpec.describe Telegram::Bot do RSpec.describe Telegram::Bot do
it 'has a version number' do it 'has a version number' do
expect(Telegram::Bot::VERSION).not_to be nil expect(described_class::VERSION).not_to be nil
end
describe '.wrap' do
subject { described_class.wrap(input) }
let(:result) { double(:result) }
let(:token) { 'token' }
let(:username) { 'username' }
let(:instance) { described_class.new 'token' }
context 'when input is a string' do
let(:input) { token }
it 'treats string as token' do
expect(described_class).to receive(:new).with(token) { result }
should eq result
end
end
context 'when input is a hash' do
let(:input) { {token: token, username: username, ignore: :ignore} }
it 'extracts token & username' do
expect(described_class).to receive(:new).with(token, username) { result }
should eq result
end
end
context 'when input is an instance of described_class' do
let!(:input) { instance }
it 'returns input' do
expect(described_class).to_not receive(:new)
should eq input
end
end
context 'when input is an array' do
let!(:input) { ['other_token', instance, token: token, username: username] }
let(:result_2) { double(:result_2) }
it 'calls wrap for every element' do
expect(described_class).to receive(:new).with('other_token') { result }
expect(described_class).to receive(:new).with(token, username) { result_2 }
should eq [result, instance, result_2]
end
end
end end
end end