slack-bot-manager 0.1.0pre1 → 0.1.0pre2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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