slack-bot-manager 0.1.0pre1 → 0.1.0pre2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.gitmodules +0 -3
  4. data/.rubocop.yml +6 -0
  5. data/.rubocop_todo.yml +81 -0
  6. data/.travis.yml +26 -0
  7. data/CHANGELOG.md +1 -1
  8. data/Gemfile +6 -0
  9. data/README.md +62 -11
  10. data/Rakefile +17 -0
  11. data/examples/dm-bot/Gemfile +5 -0
  12. data/examples/dm-bot/Gemfile.lock +40 -0
  13. data/examples/dm-bot/README.md +23 -0
  14. data/examples/dm-bot/dm-bot.rb +98 -0
  15. data/examples/dm-bot/tokens.yml.sample +2 -0
  16. data/lib/slack-bot-manager/client/base.rb +50 -14
  17. data/lib/slack-bot-manager/client/commands.rb +5 -7
  18. data/lib/slack-bot-manager/config.rb +41 -13
  19. data/lib/slack-bot-manager/errors.rb +10 -13
  20. data/lib/slack-bot-manager/extend.rb +13 -2
  21. data/lib/slack-bot-manager/logger.rb +12 -8
  22. data/lib/slack-bot-manager/manager/base.rb +5 -3
  23. data/lib/slack-bot-manager/manager/connection.rb +88 -87
  24. data/lib/slack-bot-manager/manager/storage/dalli.rb +63 -0
  25. data/lib/slack-bot-manager/manager/storage/redis.rb +55 -0
  26. data/lib/slack-bot-manager/manager/storage.rb +6 -0
  27. data/lib/slack-bot-manager/manager/tokens.rb +26 -20
  28. data/lib/slack-bot-manager/version.rb +1 -1
  29. data/lib/slack-bot-manager.rb +2 -0
  30. data/slack-bot-manager.gemspec +10 -9
  31. data/spec/fixtures/slack-bot-manager/web/rtm_start.yml +59 -0
  32. data/spec/integration/client_spec.rb +62 -0
  33. data/spec/integration/manager_spec.rb +121 -0
  34. data/spec/slack-bot-manager/client/base_spec.rb +96 -0
  35. data/spec/slack-bot-manager/config_spec.rb +19 -0
  36. data/spec/slack-bot-manager/errors_spec.rb +5 -0
  37. data/spec/slack-bot-manager/extend_spec.rb +5 -0
  38. data/spec/slack-bot-manager/logger_spec.rb +5 -0
  39. data/spec/slack-bot-manager/version_spec.rb +7 -0
  40. data/spec/spec_helper.rb +10 -0
  41. data/spec/support/vcr.rb +8 -0
  42. metadata +153 -6
@@ -1,24 +1,23 @@
1
1
  module SlackBotManager
2
2
  module Config
3
-
4
3
  extend self
5
4
 
6
5
  MANAGER_ATTRIBUTES = [
7
6
  :tokens_key,
8
7
  :teams_key,
9
8
  :check_interval,
10
- :redis,
9
+ :storage_method,
10
+ :storage_options,
11
11
  :logger,
12
12
  :log_level,
13
13
  :verbose
14
- ]
14
+ ].freeze
15
15
 
16
16
  CLIENT_ATTRIBUTES = [
17
- :redis,
18
17
  :logger,
19
18
  :log_level,
20
19
  :verbose
21
- ]
20
+ ].freeze
22
21
 
23
22
  WEB_CLIENT_ATTRIBUTES = [
24
23
  :user_agent,
@@ -26,17 +25,28 @@ module SlackBotManager
26
25
  :ca_path,
27
26
  :ca_file,
28
27
  :endpoint
29
- ]
28
+ ].freeze
30
29
 
31
30
  RTM_CLIENT_ATTRIBUTES = [
32
31
  :websocket_ping,
33
32
  :websocket_proxy
34
- ]
35
-
36
- attr_accessor *Config::MANAGER_ATTRIBUTES
37
- attr_accessor *Config::CLIENT_ATTRIBUTES
38
- attr_accessor *Config::WEB_CLIENT_ATTRIBUTES
39
- attr_accessor *Config::RTM_CLIENT_ATTRIBUTES
33
+ ].freeze
34
+
35
+ RTM_CLIENT_METHODS = [
36
+ :url,
37
+ :team,
38
+ :self,
39
+ :users,
40
+ :channels,
41
+ :groups,
42
+ :ims,
43
+ :bots
44
+ ].freeze
45
+
46
+ attr_accessor(*Config::MANAGER_ATTRIBUTES)
47
+ attr_accessor(*Config::CLIENT_ATTRIBUTES)
48
+ attr_accessor(*Config::WEB_CLIENT_ATTRIBUTES)
49
+ attr_accessor(*Config::RTM_CLIENT_ATTRIBUTES)
40
50
 
41
51
  def reset
42
52
  # Slack web and realtime config options
@@ -46,7 +56,8 @@ module SlackBotManager
46
56
  self.tokens_key = 'tokens:statuses'
47
57
  self.teams_key = 'tokens:teams'
48
58
  self.check_interval = 5 # seconds
49
- self.redis = Redis.new
59
+ self.storage_method = method(:detect_storage_method)
60
+ self.storage_options = {}
50
61
  self.logger = defined?(Rails) ? Rails.logger : ::Logger.new(STDOUT)
51
62
  self.log_level = ::Logger::INFO
52
63
  self.logger.formatter = SlackBotManager::Logger::Formatter.new
@@ -72,6 +83,10 @@ module SlackBotManager
72
83
  end
73
84
  end
74
85
 
86
+ def storage_method
87
+ (val = @storage_method).respond_to?(:call) ? val.call : val
88
+ end
89
+
75
90
  def verbose=(val)
76
91
  @verbose = val
77
92
  self.log_level = val ? ::Logger::DEBUG : ::Logger::INFO
@@ -94,6 +109,19 @@ module SlackBotManager
94
109
  self.logger.formatter = formatter
95
110
  end
96
111
 
112
+ private
113
+
114
+ def detect_storage_method
115
+ [:Redis, :Dalli].each do |storage_method|
116
+ begin
117
+ return SlackBotManager::Storage.const_get(storage_method)
118
+ rescue LoadError, NameError
119
+ false
120
+ end
121
+ end
122
+
123
+ fail NoStorageMethod, 'Missing storage method. Add redis or dalli to your Gemfile.'
124
+ end
97
125
  end
98
126
 
99
127
  class << self
@@ -1,19 +1,17 @@
1
1
  module SlackBotManager
2
-
3
2
  class ConnectionClosed < StandardError; end
4
3
  class ConnectionRateLimited < StandardError; end
5
4
  class InvalidToken < StandardError; end
5
+ class NoStorageMethod < StandardError; end
6
6
  class TokenAlreadyConnected < StandardError; end
7
7
  class TokenNotConnected < StandardError; end
8
8
  class TokenRevoked < StandardError; end
9
9
 
10
-
11
10
  module Errors
12
-
13
11
  # Mapping of error classes to type
14
12
  CLASS_ERROR_TYPES = {
15
13
  token_revoked: [
16
- SlackBotManager::InvalidToken,
14
+ SlackBotManager::InvalidToken,
17
15
  SlackBotManager::TokenRevoked
18
16
  ],
19
17
  rate_limited: [
@@ -22,29 +20,28 @@ module SlackBotManager
22
20
  closed: [
23
21
  SlackBotManager::ConnectionClosed,
24
22
  Slack::RealTime::Client::ClientNotStartedError
25
- ],
26
- }
23
+ ]
24
+ }.freeze
27
25
 
28
26
  # Regexp mapping of error keywords to type
29
27
  STRING_ERROR_TYPES = {
30
28
  token_revoked: /token_revoked|account_inactive|invalid_auth/i,
31
29
  rate_limited: /rate_limit|status 429/i,
32
- closed: /closed/i,
33
- }
30
+ closed: /closed/i
31
+ }.freeze
34
32
 
35
33
  def determine_error_type(err)
36
34
  # Check known error types, unless string
37
- CLASS_ERROR_TYPES.each{|k,v| return k if v.include?(err) } unless err.is_a?(String)
35
+ CLASS_ERROR_TYPES.each { |k, v| return k if v.include?(err) } unless err.is_a?(String)
38
36
 
39
- # Check string matches, as we might get code responses or capture something inside it
40
- STRING_ERROR_TYPES.each{|k,v| return k if v.match(err.to_s) }
37
+ # Check string matches for code responses or capture something inside it
38
+ STRING_ERROR_TYPES.each { |k, v| return k if v.match(err.to_s) }
41
39
 
42
40
  :error
43
41
  end
44
42
 
45
- def on_error(err,data=nil)
43
+ def on_error(err, _ = nil)
46
44
  error(err)
47
45
  end
48
-
49
46
  end
50
47
  end
@@ -1,7 +1,6 @@
1
1
  # via https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/array/extract_options.rb
2
2
 
3
3
  # Add extract_options! used in ActiveSupport
4
-
5
4
  unless {}.respond_to?(:extractable_options?)
6
5
  class Hash
7
6
  def extractable_options?
@@ -16,4 +15,16 @@ unless [].respond_to?(:extract_options!)
16
15
  last.is_a?(Hash) && last.extractable_options? ? pop : {}
17
16
  end
18
17
  end
19
- end
18
+ end
19
+
20
+ # Allow removing methods from Slack::RealTime::Client
21
+ module Slack
22
+ module RealTime
23
+ class Client
24
+ def off(type)
25
+ type = type.to_s
26
+ callbacks.delete(type) if callbacks.key?(type)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,6 +1,5 @@
1
1
  module SlackBotManager
2
2
  module Logger
3
-
4
3
  def info(msg)
5
4
  logger.info(@id) { msg }
6
5
  end
@@ -17,28 +16,33 @@ module SlackBotManager
17
16
  logger.error(@id) { msg }
18
17
  end
19
18
 
20
-
21
19
  class Formatter
22
- SEVERITY_TO_COLOR_MAP = {'DEBUG'=>'0;37', 'INFO'=>'32', 'WARN'=>'33', 'ERROR'=>'31', 'FATAL'=>'31', 'UNKNOWN'=>'37'}
20
+ SEVERITY_TO_COLOR_MAP = {
21
+ 'DEBUG' => '0;37',
22
+ 'INFO' => '32',
23
+ 'WARN' => '33',
24
+ 'ERROR' => '31',
25
+ 'FATAL' => '31',
26
+ 'UNKNOWN' => '37'
27
+ }.freeze
23
28
 
24
29
  def call(severity, timeat, progname, message)
25
- formatted_severity = sprintf("%-5s",severity).strip
26
- formatted_time = timeat.strftime("%Y-%m-%d %H:%M:%S.") << timeat.usec.to_s[0..2].rjust(3)
30
+ formatted_severity = format('%-5s', severity).strip
31
+ formatted_time = timeat.strftime('%Y-%m-%d %H:%M:%S.') << timeat.usec.to_s[0..2].rjust(3)
27
32
  color = SEVERITY_TO_COLOR_MAP[severity]
28
33
 
29
34
  # Handle backtrace, if any
30
35
  msg = message.to_s
31
- message.backtrace.each{|n| msg << "\n #{n}"} if message.respond_to?(:backtrace)
36
+ message.backtrace.each { |n| msg << "\n #{n}" } if message.respond_to?(:backtrace)
32
37
 
33
38
  [
34
39
  "\033[0;37m#{formatted_time}\033[0m", # Formatted time
35
40
  "[\033[#{color}m#{formatted_severity}\033[0m]", # Level
36
- "[PID:#{$$}]", # PID
41
+ "[PID:#{$PID}]", # PID
37
42
  progname && progname != '' && "(#{progname})", # Progname (team ID), if exists
38
43
  msg.strip # Message
39
44
  ].compact.join(' ') + "\n"
40
45
  end
41
46
  end
42
-
43
47
  end
44
48
  end
@@ -1,12 +1,11 @@
1
1
  module SlackBotManager
2
2
  class Manager
3
-
4
3
  include Tokens
5
4
  include Connection
6
5
  include Errors
7
6
  include Logger
8
7
 
9
- attr_accessor :connections
8
+ attr_accessor :connections, :storage
10
9
  attr_accessor(*Config::MANAGER_ATTRIBUTES)
11
10
 
12
11
  def initialize(*args)
@@ -19,8 +18,12 @@ module SlackBotManager
19
18
  SlackBotManager::Config::MANAGER_ATTRIBUTES.each do |key|
20
19
  send("#{key}=", options[key] || SlackBotManager.config.send(key))
21
20
  end
21
+
22
+ # Set token storage method
23
+ @storage = storage_method.new(storage_options)
22
24
  end
23
25
 
26
+ # Include config helpers
24
27
  class << self
25
28
  def configure
26
29
  block_given? ? yield(config) : config
@@ -30,6 +33,5 @@ module SlackBotManager
30
33
  Config
31
34
  end
32
35
  end
33
-
34
36
  end
35
37
  end
@@ -1,133 +1,137 @@
1
1
  module SlackBotManager
2
2
  module Connection
3
-
4
3
  # Monitor our connections, while connections is {...}
5
4
  def monitor
6
- while connections do
5
+ while connections
7
6
  sleep 1 # Pause momentarily
8
7
 
9
8
  # On occasion, check our connection statuses
10
- if Time.now.to_i % check_interval == 0
11
- # Get tokens and connection statuses
12
- tokens_status, rtm_status = self.redis.hgetall(tokens_key), self.redis.hgetall(teams_key)
13
-
14
- # Manage connections
15
- connections.each do |cid,conn|
16
- id,_s = cid.split(':')
17
-
18
- # Remove/continue if connection is/will close or no longer connected
19
- if !conn
20
- warning("Removing: #{id} (Reason: rtm_not_connection)")
21
- destroy(cid: cid)
22
-
23
- elsif !conn.connected?
24
- warning("Removing: #{conn.id} (Reason: #{conn.status})")
25
- to_remove = ['token_revoked'].include?((conn.status || '').to_s)
26
- destroy(cid: cid, remove_token: to_remove)
27
-
28
- # Team is no longer valid, remove
29
- elsif !!tokens_status[conn.id].empty?
30
- warning("Removing: #{conn.id} (Reason: token_missing)")
31
- destroy(cid: cid, remove_token: true)
32
-
33
- elsif rtm_status[conn.id] == 'destroy'
34
- warning("Removing: #{conn.id} (Reason: token_destroy)")
35
- destroy(cid: cid)
36
-
37
- # Kill connection if token has changed, will re-create next block below
38
- elsif tokens_status[conn.id] != conn.token
39
- warning("Removing: #{conn.id} (Reason: new_token)")
40
- destroy(cid: cid)
41
-
42
- # Connection should be re-created unless it is active, will update next block below
43
- elsif rtm_status[conn.id] != 'active'
44
- warning("Restarting: #{conn.id} (Reason: #{rtm_status[conn.id]})")
45
- destroy(cid: cid)
46
- self.redis.hset(tokens_key, conn.id, tokens_status[conn.id])
47
- end
9
+ next unless Time.now.to_i % check_interval == 0
10
+
11
+ # Get tokens and connection statuses
12
+ tokens_status = storage.get_all(tokens_key)
13
+ rtm_status = storage.get_all(teams_key)
14
+
15
+ # Manage connections
16
+ connections.each do |cid, conn|
17
+ id, _s = cid.split(':')
18
+
19
+ # Remove/continue if connection is/will close or no longer connected
20
+ if !conn
21
+ warning("Removing: #{id} (Reason: rtm_not_connection)")
22
+ destroy(cid: cid)
23
+
24
+ elsif !conn.connected?
25
+ warning("Removing: #{conn.id} (Reason: #{conn.status})")
26
+ to_remove = ['token_revoked'].include?((conn.status || '').to_s)
27
+ destroy(cid: cid, remove_token: to_remove)
28
+
29
+ # Team is no longer valid, remove
30
+ elsif tokens_status[conn.id].nil? || tokens_status[conn.id].empty?
31
+ warning("Removing: #{conn.id} (Reason: token_missing)")
32
+ destroy(cid: cid, remove_token: true)
33
+
34
+ elsif rtm_status[conn.id] == 'destroy'
35
+ warning("Removing: #{conn.id} (Reason: token_destroy)")
36
+ destroy(cid: cid)
37
+
38
+ # Kill connection if token has changed, will re-create below
39
+ elsif tokens_status[conn.id] != conn.token
40
+ warning("Removing: #{conn.id} (Reason: new_token)")
41
+ destroy(cid: cid)
42
+
43
+ # Connection should re-create unless active, will update below
44
+ elsif rtm_status[conn.id] != 'active'
45
+ reason = rtm_status[conn.id] || '(unknown)'
46
+ warning("Restarting: #{conn.id} (Reason: #{reason})")
47
+ destroy(cid: cid)
48
+ storage.set(tokens_key, conn.id, tokens_status[conn.id])
48
49
  end
50
+ end
49
51
 
50
- # Give pause before any reconnects, as destroy methods might still be processing in their threads
51
- sleep 1
52
+ # Pause before reconnects, as destroy might be processing in threads
53
+ sleep 1
52
54
 
53
- # Check for new tokens / reconnections (reload keys since we might modify if bad). Kill and recreate
54
- tokens_status, rtm_status = self.redis.hgetall(tokens_key), self.redis.hgetall(teams_key)
55
- tokens_diff = (tokens_status.keys - rtm_status.keys) + (rtm_status.keys - tokens_status.keys)
55
+ # Check for new tokens / reconnections
56
+ # (reload keys since we might modify if bad). Kill and recreate
57
+ tokens_status = storage.get_all(tokens_key)
58
+ rtm_status = storage.get_all(teams_key)
59
+ tokens_diff = (tokens_status.keys - rtm_status.keys) + (rtm_status.keys - tokens_status.keys)
56
60
 
57
- unless !!tokens_diff.empty?
58
- tokens_diff.uniq.each do |id|
59
- warning("Starting: #{id}")
60
- destroy(id: id)
61
- create(id, tokens_status[id])
62
- end
61
+ unless tokens_diff.empty?
62
+ tokens_diff.uniq.each do |id|
63
+ warning("Starting: #{id}")
64
+ destroy(id: id)
65
+ create(id, tokens_status[id])
63
66
  end
64
-
65
- info("Active Connections: [#{connections.count}]")
66
67
  end
68
+
69
+ info("Active Connections: [#{connections.count}]")
67
70
  end
68
71
  end
69
72
 
70
73
  # Create websocket connections for active tokens
71
74
  def start
72
75
  # Clear RTM connections
73
- self.redis.del(teams_key)
76
+ storage.delete_all(teams_key)
74
77
 
75
78
  # Start a new connection for each team
76
- self.redis.hgetall(tokens_key).each do |id,token|
77
- create(id,token)
79
+ storage.get_all(tokens_key).each do |id, token|
80
+ create(id, token)
78
81
  end
79
82
  end
80
83
 
81
84
  # Remove all connections from app, will disconnect in monitor loop
82
85
  def stop
83
86
  # Thread wrapped to ensure no lock issues on shutdown
84
- thr = Thread.new {
85
- conns = self.redis.hgetall(teams_key)
86
- self.redis.pipelined do
87
- conns.each{|k,v| self.redis.hset(teams_key, k, 'destroy') }
87
+ thr = Thread.new do
88
+ conns = storage.get_all(teams_key)
89
+ storage.pipeline do
90
+ conns.each { |k, _| storage.set(teams_key, k, 'destroy') }
88
91
  end
89
- info("Stopped.")
90
- }
92
+ info('Stopped.')
93
+ end
91
94
  thr.join
92
95
  end
93
96
 
94
- # Issue restart status on all RTM connections, will re-connect in monitor loop
97
+ # Issue restart status on all RTM connections
98
+ # will re-connect in monitor loop
95
99
  def restart
96
- conns = self.redis.hgetall(teams_key)
97
- self.redis.pipelined do
98
- conns.each{|k,v| self.redis.hset(teams_key, k, 'restart') }
100
+ conns = storage.get_all(teams_key)
101
+ storage.pipeline do
102
+ conns.each { |k, _| storage.set(teams_key, k, 'restart') }
99
103
  end
100
104
  end
101
105
 
102
106
  # Get status of current connections
103
107
  def status
104
- info("Active connections: [#{self.redis.hgetall(teams_key).count}]")
108
+ info("Active connections: [#{storage.get_all(teams_key).count}]")
105
109
  end
106
110
 
107
-
108
- protected
111
+ protected
109
112
 
110
113
  # Find the connection based on id and has active connection
111
114
  def find_connection(id)
112
- connections.each do |cid,conn|
115
+ connections.each do |_, conn|
113
116
  return (conn.connected? ? conn : false) if conn && conn.id == id
114
117
  end
115
118
  false
116
119
  end
117
120
 
118
121
  # Create new connection if not exist
119
- def create(id,token)
120
- raise SlackBotManager::TokenAlreadyConnected if find_connection(id)
122
+ def create(id, token)
123
+ fail SlackBotManager::TokenAlreadyConnected if find_connection(id)
121
124
 
122
125
  # Create connection
123
- conn = SlackBotManager::Client.new(id, token)
126
+ conn = SlackBotManager::Client.new(token)
127
+ conn.connect
124
128
 
125
- # Add to connections using a uniq token, as we might have connection closing and opening with same id
129
+ # Add to connections using a uniq token
126
130
  if conn
127
- cid = [id,Time.now.to_i].join(':')
131
+ cid = [id, Time.now.to_i].join(':')
128
132
  connections[cid] = conn
129
133
  info("Connected: #{id} (Connection: #{cid})")
130
- self.redis.hset(teams_key, id, 'active')
134
+ storage.set(teams_key, id, 'active')
131
135
  end
132
136
  rescue => err
133
137
  on_error(err)
@@ -139,29 +143,26 @@ module SlackBotManager
139
143
 
140
144
  # Get connection or search for connection with cid
141
145
  if options[:cid]
142
- conn, cid = connections[options[:cid]], options[:cid]
146
+ conn = connections[options[:cid]]
147
+ cid = options[:cid]
143
148
  elsif options[:id]
144
149
  conn, cid = find_connection(options[:id])
145
150
  end
146
151
  return false unless conn && cid
147
152
 
148
- # Kill connection, remove from connection keys, and delete from connections list
153
+ # Kill connection, remove from keys, and delete from list
149
154
  begin
150
- thr = Thread.new {
151
- self.redis.hdel(teams_key, conn.id) rescue nil
152
-
153
- if options[:remove_token]
154
- self.redis.hdel(tokens_key, conn.id) rescue nil
155
- end
156
- }
155
+ thr = Thread.new do
156
+ storage.delete(teams_key, conn.id) rescue nil
157
+ storage.delete(tokens_key, conn.id) rescue nil if options[:remove_token]
158
+ end
157
159
  thr.join
158
160
  connections.delete(cid)
159
- rescue => err
161
+ rescue
160
162
  nil
161
163
  end
162
164
  rescue => err
163
165
  on_error(err)
164
166
  end
165
-
166
167
  end
167
168
  end
@@ -0,0 +1,63 @@
1
+ require 'dalli'
2
+
3
+ module SlackBotManager
4
+ module Storage
5
+ class Dalli
6
+ attr_accessor :connection
7
+
8
+ def initialize(options)
9
+ if options.is_a?(Dalli)
10
+ @connecton = options
11
+ else
12
+ servers = options.delete(:servers)
13
+ @connection = ::Dalli::Client.new(servers, options)
14
+ end
15
+ end
16
+
17
+ def pipeline(&block)
18
+ yield block
19
+ end
20
+
21
+ def get_all(type)
22
+ connection.get(type) || {}
23
+ end
24
+
25
+ def get(type, key)
26
+ connection.get(type).try(key)
27
+ end
28
+
29
+ def set(type, key, val)
30
+ obj = get_all(type).merge(key => val)
31
+ connection.set(type, obj)
32
+ end
33
+
34
+ def multiset(type, *args)
35
+ vals = args.extract_options!
36
+ obj = get_all(type).merge(vals)
37
+ connection.set(type, obj)
38
+ end
39
+
40
+ def delete(type, key)
41
+ obj = get_all(type)
42
+ obj.delete(key)
43
+ connection.set(type, obj)
44
+ end
45
+
46
+ def delete_all(type)
47
+ connection.delete(type)
48
+ end
49
+
50
+ # def expire(key, len)
51
+ # connection.expire(key, len)
52
+ # end
53
+
54
+ # def exists(type)
55
+ # connection.exists(key)
56
+ # end
57
+
58
+ # def incrby(type, key, incr=1)
59
+ # # TODO
60
+ # end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,55 @@
1
+ require 'redis'
2
+
3
+ module SlackBotManager
4
+ module Storage
5
+ class Redis
6
+ attr_accessor :connection
7
+
8
+ def initialize(options)
9
+ @connection = options.is_a?(Redis) ? options : ::Redis.new(options)
10
+ end
11
+
12
+ def pipeline(&block)
13
+ connection.pipelined do
14
+ yield block
15
+ end
16
+ end
17
+
18
+ def get_all(type)
19
+ connection.hgetall(type)
20
+ end
21
+
22
+ def get(type, key)
23
+ connection.get(type, key)
24
+ end
25
+
26
+ def set(type, key, val)
27
+ connection.hset(type, key, val)
28
+ end
29
+
30
+ def multiset(type, *args)
31
+ connection.hmset(type, *args)
32
+ end
33
+
34
+ def delete(type, key)
35
+ connection.hdel(type, key)
36
+ end
37
+
38
+ def delete_all(type)
39
+ connection.del(type)
40
+ end
41
+
42
+ # def expire(key, len)
43
+ # connection.expire(key, len)
44
+ # end
45
+
46
+ # def exists(type)
47
+ # connection.exists(key)
48
+ # end
49
+
50
+ # def incrby(type, key, incr=1)
51
+ # # TODO
52
+ # end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,6 @@
1
+ module SlackBotManager
2
+ module Storage
3
+ autoload :Redis, 'slack-bot-manager/manager/storage/redis'
4
+ autoload :Dalli, 'slack-bot-manager/manager/storage/dalli'
5
+ end
6
+ end