lita 3.1.0 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b317185fd4bf51c66e01de3aef18547d27d154ef
4
- data.tar.gz: 0ee25a799c8b033fb82e0e58fe3654ebaf974eea
3
+ metadata.gz: 68d6c55cacef1663c210df90ff03f764bca2c3b3
4
+ data.tar.gz: 4696fa739de19bb0a0245fd855666fe6a5622f31
5
5
  SHA512:
6
- metadata.gz: 23c70beb7798a316f725225c17a0b75f8e0fdd2d104e9d9f4c611ae6c423300810adb1c1a9805629287a0db1d27b60071779bd4bf7ecebfbef302a3fde3623f3
7
- data.tar.gz: 0498b930d44e0057058a25df985b8117d97de953f76ed89c414c7902482f889db1333b4376665aab01409f60fbcc55ae6fb3159594432cbfac0944b4bd8c9168
6
+ metadata.gz: 7701c02aa161e08559b50e1133ea1227cc79d53f4278a772ef274b090fceb9a9bad22a317263f067adfe036a336fc6d3ba6d0a546b014387e12f49cf46d52ec7
7
+ data.tar.gz: b4b212cee5587adaf06f0ad8944715b9653a678f041e18d71258bbc4d52864f5cf575c3fb010161325153a42d6bde5271f722202383c5a3ca48b035e2685d204
@@ -18,10 +18,14 @@ LineLength:
18
18
  Max: 100
19
19
  MethodLength:
20
20
  Enabled: false
21
+ RegexpLiteral:
22
+ Enabled: false
21
23
  RescueException:
22
24
  Enabled: false
23
25
  SignalException:
24
26
  Enabled: false
27
+ SpecialGlobalVars:
28
+ Enabled: false
25
29
  StringLiterals:
26
30
  EnforcedStyle: double_quotes
27
31
  TrailingComma:
data/README.md CHANGED
@@ -5,8 +5,6 @@
5
5
  [![Code Climate](https://codeclimate.com/github/jimmycuadra/lita.png)](https://codeclimate.com/github/jimmycuadra/lita)
6
6
  [![Coverage Status](https://coveralls.io/repos/jimmycuadra/lita/badge.png)](https://coveralls.io/r/jimmycuadra/lita)
7
7
 
8
- ![Lita](http://f.cl.ly/items/0c271a2P3k2V180B1R0X/lita.jpg)
9
-
10
8
  **Lita** is a chat bot written in [Ruby](https://www.ruby-lang.org/) with persistent storage provided by [Redis](http://redis.io/). It uses a plugin system to connect to different chat services and to provide new behavior. The plugin system uses the familiar tools of the Ruby ecosystem: [RubyGems](https://rubygems.org/) and [Bundler](http://gembundler.com/).
11
9
 
12
10
  Automate your business and have fun with your very own robot companion.
@@ -26,27 +26,6 @@ module Lita
26
26
  @adapters ||= {}
27
27
  end
28
28
 
29
- # Adds an adapter to the global registry under the provided key.
30
- # @param key [String, Symbol] The key that identifies the adapter.
31
- # @param adapter [Lita::Adapter] The adapter class.
32
- # @return [void]
33
- def register_adapter(key, adapter)
34
- adapters[key.to_sym] = adapter
35
- end
36
-
37
- # The global registry of handlers.
38
- # @return [Set] The set of handlers.
39
- def handlers
40
- @handlers ||= Set.new
41
- end
42
-
43
- # Adds a handler to the global registry.
44
- # @param handler [Lita::Handler] The handler class.
45
- # @return [void]
46
- def register_handler(handler)
47
- handlers << handler
48
- end
49
-
50
29
  # The global configuration object. Provides user settings for the robot.
51
30
  # @return [Lita::Config] The Lita configuration object.
52
31
  def config
@@ -61,11 +40,17 @@ module Lita
61
40
  yield config
62
41
  end
63
42
 
64
- # Clears the global configuration object. The next call to {Lita.config}
65
- # will create a fresh config object.
66
- # @return [void]
67
- def clear_config
68
- @config = nil
43
+ # The global registry of handlers.
44
+ # @return [Set] The set of handlers.
45
+ def handlers
46
+ @handlers ||= Set.new
47
+ end
48
+
49
+ # The global registry of hook handler objects.
50
+ # @return [Hash] A hash mapping hook names to sets of objects that handle them.
51
+ # @since 3.2.0
52
+ def hooks
53
+ @hooks ||= Hash.new { |h, k| h[k] = Set.new }
69
54
  end
70
55
 
71
56
  # The global Logger object.
@@ -83,10 +68,72 @@ module Lita
83
68
  end
84
69
  end
85
70
 
71
+ # Adds an adapter to the global registry under the provided key.
72
+ # @param key [String, Symbol] The key that identifies the adapter.
73
+ # @param adapter [Lita::Adapter] The adapter class.
74
+ # @return [void]
75
+ def register_adapter(key, adapter)
76
+ adapters[key.to_sym] = adapter
77
+ end
78
+
79
+ # Adds a handler to the global registry.
80
+ # @param handler [Lita::Handler] The handler class.
81
+ # @return [void]
82
+ def register_handler(handler)
83
+ handlers << handler
84
+ end
85
+
86
+ # Adds a hook handler object to the global registry for the given hook.
87
+ # @return [void]
88
+ # @since 3.2.0
89
+ def register_hook(name, hook)
90
+ hooks[name.to_s.downcase.strip.to_sym] << hook
91
+ end
92
+
93
+ # Clears the global configuration object and the global adapter, handler, and hook registries.
94
+ # @return [void]
95
+ # @since 3.2.0
96
+ def reset
97
+ reset_adapters
98
+ reset_config
99
+ reset_handlers
100
+ reset_hooks
101
+ end
102
+
103
+ # Resets the global adapter registry, removing all registered adapters.
104
+ # @return [void]
105
+ # @since 3.2.0
106
+ def reset_adapters
107
+ @adapters = nil
108
+ end
109
+
110
+ # Resets the global configuration object. The next call to {Lita.config}
111
+ # will create a fresh config object.
112
+ # @return [void]
113
+ def reset_config
114
+ @config = nil
115
+ end
116
+ alias_method :clear_config, :reset_config
117
+
118
+ # Resets the global handler registry, removing all registered handlers.
119
+ # @return [void]
120
+ # @since 3.2.0
121
+ def reset_handlers
122
+ @handlers = nil
123
+ end
124
+
125
+ # Resets the global hooks registry, removing all registered hook handlers.
126
+ # @return [void]
127
+ # @since 3.2.0
128
+ def reset_hooks
129
+ @hooks = nil
130
+ end
131
+
86
132
  # Loads user configuration and starts the robot.
87
133
  # @param config_path [String] The path to the user configuration file.
88
134
  # @return [void]
89
135
  def run(config_path = nil)
136
+ hooks[:before_run].each { |hook| hook.call(config_path: config_path) }
90
137
  Config.load_user_config(config_path)
91
138
  Lita.config.finalize
92
139
  self.locale = Lita.config.robot.locale
@@ -112,6 +159,7 @@ require_relative "lita/robot"
112
159
  require_relative "lita/adapter"
113
160
  require_relative "lita/adapters/shell"
114
161
  require_relative "lita/handler"
162
+ require_relative "lita/route_validator"
115
163
  require_relative "lita/handlers/authorization"
116
164
  require_relative "lita/handlers/help"
117
165
  require_relative "lita/handlers/info"
@@ -89,7 +89,7 @@ module Lita
89
89
  # @return [void]
90
90
  # @abstract This should be implemented by the adapter.
91
91
  [:join, :part, :run, :send_messages, :set_topic, :shut_down].each do |method|
92
- define_method(method) do |*args|
92
+ define_method(method) do
93
93
  Lita.logger.warn(I18n.t("lita.adapter.method_not_implemented", method: method))
94
94
  end
95
95
  end
@@ -20,7 +20,7 @@ module Lita
20
20
  # shell environment.
21
21
  # @param strings [Array<String>] An array of strings to output.
22
22
  # @return [void]
23
- def send_messages(target, strings)
23
+ def send_messages(_target, strings)
24
24
  strings = Array(strings)
25
25
  strings.reject! { |string| string.empty? }
26
26
  unless RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ || !$stdout.tty?
@@ -99,6 +99,14 @@ module Lita
99
99
  generate_templates(generate_config(name, "handler"))
100
100
  end
101
101
 
102
+ desc "extension NAME", "Generates a new Lita extension"
103
+ # Generates a new Lita extension.
104
+ # @param name [String] The name for the new extension.
105
+ # @return [void]
106
+ def extension(name)
107
+ generate_templates(generate_config(name, "extension"))
108
+ end
109
+
102
110
  desc "version", "Outputs the current version of Lita"
103
111
  # Outputs the current version of Lita.
104
112
  # @return [void]
@@ -17,7 +17,8 @@ module Lita
17
17
  :method_name,
18
18
  :command,
19
19
  :required_groups,
20
- :help
20
+ :help,
21
+ :extensions
21
22
  )
22
23
  alias_method :command?, :command
23
24
  end
@@ -32,10 +33,19 @@ module Lita
32
33
  # @param restrict_to [Array<Symbol, String>, nil] A list of authorization
33
34
  # groups the user must be in to trigger the route.
34
35
  # @param help [Hash] A map of example invocations to descriptions.
36
+ # @param extensions [Hash] Aribtrary additional data that can be used by Lita extensions.
35
37
  # @return [void]
36
- def route(pattern, method, command: false, restrict_to: nil, help: {})
37
- groups = restrict_to.nil? ? nil : Array(restrict_to)
38
- routes << Route.new(pattern, method, command, groups, help)
38
+ def route(pattern, method, **options)
39
+ options = default_route_options.merge(options)
40
+ options[:restrict_to] = options[:restrict_to].nil? ? nil : Array(options[:restrict_to])
41
+ routes << Route.new(
42
+ pattern,
43
+ method,
44
+ options.delete(:command),
45
+ options.delete(:restrict_to),
46
+ options.delete(:help),
47
+ options
48
+ )
39
49
  end
40
50
 
41
51
  # A list of chat routes defined by the handler.
@@ -57,10 +67,9 @@ module Lita
57
67
  log_dispatch(route)
58
68
 
59
69
  begin
60
- new(robot).public_send(
61
- route.method_name,
62
- Response.new(message, route.pattern)
63
- )
70
+ response = Response.new(message, route.pattern)
71
+ Lita.hooks[:trigger_route].each { |hook| hook.call(response: response, route: route) }
72
+ new(robot).public_send(route.method_name, response)
64
73
  rescue Exception => e
65
74
  log_dispatch_error(e)
66
75
  raise e if rspec_loaded?
@@ -133,6 +142,14 @@ module Lita
133
142
 
134
143
  private
135
144
 
145
+ def default_route_options
146
+ {
147
+ command: false,
148
+ restrict_to: nil,
149
+ help: {}
150
+ }
151
+ end
152
+
136
153
  # A hash of arrays used to store event subscriptions registered with {on}.
137
154
  def event_subscriptions
138
155
  @event_subscriptions ||= Hash.new { |h, k| h[k] = [] }
@@ -140,19 +157,7 @@ module Lita
140
157
 
141
158
  # Determines whether or not an incoming messages should trigger a route.
142
159
  def route_applies?(route, message, robot)
143
- # Message must be a command if the route requires a command
144
- return if route.command? && !message.command?
145
-
146
- # Messages from self should be ignored to prevent infinite loops
147
- return if message.user.name == robot.name
148
-
149
- # Message must match the pattern
150
- return unless route.pattern === message.body
151
-
152
- # User must be in auth group if route is restricted
153
- return unless authorized?(message.user, route.required_groups)
154
-
155
- true
160
+ RouteValidator.new(route, message, robot).call
156
161
  end
157
162
 
158
163
  # Checks if RSpec is loaded. If so, assume we are testing and let handler
@@ -161,13 +166,6 @@ module Lita
161
166
  defined?(::RSpec)
162
167
  end
163
168
 
164
- # Checks if the user is authorized to at least one of the given groups.
165
- def authorized?(user, required_groups)
166
- required_groups.nil? || required_groups.any? do |group|
167
- Authorization.user_in_group?(user, group)
168
- end
169
- end
170
-
171
169
  # Logs the dispatch of message.
172
170
  def log_dispatch(route)
173
171
  Lita.logger.debug I18n.t(
@@ -206,6 +204,13 @@ module Lita
206
204
  Thread.new { Timer.new(interval: interval, &block).start }
207
205
  end
208
206
 
207
+ # The handler's config object.
208
+ # @return [Lita::Config] The handler's config object.
209
+ # @since 3.2.0
210
+ def config
211
+ Lita.config.handlers[self.class.namespace]
212
+ end
213
+
209
214
  # Invokes the given block repeatedly, waiting the given number of seconds between each
210
215
  # invocation.
211
216
  # @param interval [Integer] The number of seconds to wait before each invocation of the block.
@@ -227,6 +232,13 @@ module Lita
227
232
  Faraday::Connection.new(nil, options, &block)
228
233
  end
229
234
 
235
+ # The Lita logger.
236
+ # @return [Lita::Logger] The Lita logger.
237
+ # @since 3.2.0
238
+ def log
239
+ Lita.logger
240
+ end
241
+
230
242
  # @see .translate
231
243
  def translate(*args)
232
244
  self.class.translate(*args)
@@ -88,7 +88,7 @@ module Lita
88
88
 
89
89
  def valid_group?(response, identifier)
90
90
  unless identifier && @group
91
- response.reply "#{t('format')}: #{robot.name} auth add USER GROUP"
91
+ response.reply "#{t("format")}: #{robot.name} auth add USER GROUP"
92
92
  return
93
93
  end
94
94
 
@@ -21,7 +21,7 @@ module Lita
21
21
  # @param request [Rack::Request] The HTTP request.
22
22
  # @param response [Rack::Response] The HTTP response.
23
23
  # @return [void]
24
- def web(request, response)
24
+ def web(_request, response)
25
25
  response.headers["Content-Type"] = "application/json"
26
26
  json = MultiJson.dump(
27
27
  lita_version: Lita::VERSION,
@@ -9,7 +9,7 @@ module Lita
9
9
  def get_logger(level)
10
10
  logger = ::Logger.new(STDERR)
11
11
  logger.level = get_level_constant(level)
12
- logger.formatter = proc do |severity, datetime, progname, msg|
12
+ logger.formatter = proc do |severity, datetime, _progname, msg|
13
13
  "[#{datetime.utc}] #{severity}: #{msg}\n"
14
14
  end
15
15
  logger
@@ -8,6 +8,11 @@ module Lita
8
8
  # @return [Lita::Message] The message.
9
9
  attr_accessor :message
10
10
 
11
+ # A hash of arbitrary data that can be populated by Lita extensions.
12
+ # @return [Hash] The extensions data.
13
+ # @since 3.2.0
14
+ attr_accessor :extensions
15
+
11
16
  # The pattern the incoming message matched.
12
17
  # @return [Regexp] The pattern.
13
18
  attr_accessor :pattern
@@ -29,6 +34,7 @@ module Lita
29
34
  # @param pattern [Regexp] The pattern the incoming message matched.
30
35
  def initialize(message, pattern)
31
36
  self.message = message
37
+ self.extensions = {}
32
38
  self.pattern = pattern
33
39
  end
34
40
 
@@ -0,0 +1,63 @@
1
+ module Lita
2
+ # Determines if an incoming message should trigger a route.
3
+ # @api private
4
+ class RouteValidator
5
+ # The incoming message.
6
+ attr_reader :message
7
+
8
+ # The currently running robot.
9
+ attr_reader :robot
10
+
11
+ # The route being checked.
12
+ attr_reader :route
13
+
14
+ def initialize(route, message, robot)
15
+ @route = route
16
+ @message = message
17
+ @robot = robot
18
+ end
19
+
20
+ # Returns a boolean indicating whether or not the route should be triggered.
21
+ # @return [Boolean] Whether or not the route should be triggered.
22
+ def call
23
+ return unless passes_route_hooks?(route, message, robot)
24
+ return unless command_satisfied?(route, message)
25
+ return if from_self?(message, robot)
26
+ return unless matches_pattern?(route, message)
27
+ return unless authorized?(message.user, route.required_groups)
28
+
29
+ true
30
+ end
31
+
32
+ private
33
+
34
+ # Message must be a command if the route requires a command
35
+ def command_satisfied?(route, message)
36
+ !route.command? || message.command?
37
+ end
38
+
39
+ # Messages from self should be ignored to prevent infinite loops
40
+ def from_self?(message, robot)
41
+ message.user.name == robot.name
42
+ end
43
+
44
+ # Message must match the pattern
45
+ def matches_pattern?(route, message)
46
+ route.pattern === message.body
47
+ end
48
+
49
+ # Allow custom route hooks to reject the route
50
+ def passes_route_hooks?(route, message, robot)
51
+ Lita.hooks[:validate_route].all? do |hook|
52
+ hook.call(route: route, message: message, robot: robot)
53
+ end
54
+ end
55
+
56
+ # User must be in auth group if route is restricted.
57
+ def authorized?(user, required_groups)
58
+ required_groups.nil? || required_groups.any? do |group|
59
+ Authorization.user_in_group?(user, group)
60
+ end
61
+ end
62
+ end
63
+ end
@@ -43,7 +43,7 @@ module Lita
43
43
  base.class_eval do
44
44
  before do
45
45
  [:send_messages, :send_message].each do |message|
46
- allow(robot).to receive(message) do |target, *strings|
46
+ allow(robot).to receive(message) do |_target, *strings|
47
47
  replies.concat(strings)
48
48
  end
49
49
  end
@@ -22,7 +22,7 @@ module Lita
22
22
  word = camel_cased_word.to_s.dup
23
23
  word.gsub!("::", "/")
24
24
  word.gsub!(/(?:([A-Za-z\d])|^)(#{ACRONYM_REGEX})(?=\b|[^a-z])/) do
25
- "#{Regexp.last_match[1]}#{Regexp.last_match[1] && '_'}#{Regexp.last_match[2].downcase}"
25
+ "#{Regexp.last_match[1]}#{Regexp.last_match[1] && "_"}#{Regexp.last_match[2].downcase}"
26
26
  end
27
27
  word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
28
28
  word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
@@ -1,4 +1,4 @@
1
1
  module Lita
2
2
  # The current version of Lita.
3
- VERSION = "3.1.0"
3
+ VERSION = "3.2.0"
4
4
  end
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.version = Lita::VERSION
9
9
  spec.authors = ["Jimmy Cuadra"]
10
10
  spec.email = ["jimmy@jimmycuadra.com"]
11
- spec.description = %q{A multi-service chat bot with extendable behavior.}
12
- spec.summary = %q{A multi-service chat bot with extendable behavior.}
11
+ spec.description = %q(A multi-service chat bot with extendable behavior.)
12
+ spec.summary = %q(A multi-service chat bot with extendable behavior.)
13
13
  spec.homepage = "https://github.com/jimmycuadra/lita"
14
14
  spec.license = "MIT"
15
15
 
@@ -36,5 +36,5 @@ Gem::Specification.new do |spec|
36
36
  spec.add_development_dependency "simplecov"
37
37
  spec.add_development_dependency "coveralls"
38
38
  spec.add_development_dependency "pry"
39
- spec.add_development_dependency "rubocop"
39
+ spec.add_development_dependency "rubocop", "~> 0.21.0"
40
40
  end
@@ -12,38 +12,70 @@ describe Lita::Handler, lita: true do
12
12
 
13
13
  let(:queue) { Queue.new }
14
14
 
15
+ let(:guard_hook) do
16
+ Class.new do
17
+ def self.call(payload)
18
+ if payload[:route].extensions[:guard]
19
+ payload[:message].body.include?("code word")
20
+ else
21
+ true
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ let(:response_hook) do
28
+ Class.new do
29
+ def self.call(payload)
30
+ payload[:response].extensions[:data] = payload[:route].extensions[:data]
31
+ end
32
+ end
33
+ end
34
+
15
35
  let(:handler_class) do
16
36
  Class.new(described_class) do
17
37
  route(/\w{3}/, :foo)
18
38
  route(/\w{4}/, :blah, command: true)
19
39
  route(/secret/, :secret, restrict_to: :admins)
20
40
  route(/danger/, :danger)
41
+ route(/guard/, :guard, guard: true)
42
+ route(/trigger route hook/, :trigger_route_hook, data: :foo)
21
43
 
22
44
  on :connected, :greet
23
45
  on :some_hook, :test_payload
24
46
 
25
- def foo(response)
47
+ def self.default_config(config)
48
+ config.foo = "bar"
26
49
  end
27
50
 
28
- def blah(response)
51
+ def foo(_response)
29
52
  end
30
53
 
31
- def secret(response)
54
+ def blah(_response)
32
55
  end
33
56
 
34
- def danger(response)
57
+ def secret(_response)
58
+ end
59
+
60
+ def danger(_response)
35
61
  raise "The developer of this handler's got a bug in their code!"
36
62
  end
37
63
 
64
+ def guard(_response)
65
+ end
66
+
67
+ def trigger_route_hook(_response)
68
+ end
69
+
38
70
  def greet(payload)
39
71
  robot.send_message("Hi, #{payload[:name]}! Lita has started!")
40
72
  end
41
73
 
42
- def after_test(response, queue)
43
- after(2) { |timer| queue.push("Waited 2 seconds!") }
74
+ def after_test(_response, queue)
75
+ after(2) { queue.push("Waited 2 seconds!") }
44
76
  end
45
77
 
46
- def every_test(response, queue)
78
+ def every_test(_response, queue)
47
79
  array = [1, 2, 3]
48
80
 
49
81
  every(2) do |timer|
@@ -138,6 +170,36 @@ describe Lita::Handler, lita: true do
138
170
  allow(message).to receive(:body).and_return("#{robot.name}: danger")
139
171
  expect { handler_class.dispatch(robot, message) }.to raise_error
140
172
  end
173
+
174
+ context "with a custom validate_route hook" do
175
+ before { Lita.register_hook(:validate_route, guard_hook) }
176
+ after { Lita.reset_hooks }
177
+
178
+ it "matches if the hook returns true" do
179
+ allow(message).to receive(:body).and_return("guard code word")
180
+ expect_any_instance_of(handler_class).to receive(:guard)
181
+ handler_class.dispatch(robot, message)
182
+ end
183
+
184
+ it "does not match if the hook returns false" do
185
+ allow(message).to receive(:body).and_return("guard")
186
+ expect_any_instance_of(handler_class).not_to receive(:guard)
187
+ handler_class.dispatch(robot, message)
188
+ end
189
+ end
190
+
191
+ context "with a custom trigger_route hook" do
192
+ before { Lita.register_hook(:trigger_route, response_hook) }
193
+ after { Lita.reset_hooks }
194
+
195
+ it "adds data to the response's extensions" do
196
+ allow(message).to receive(:body).and_return("trigger route hook")
197
+ allow_any_instance_of(handler_class).to receive(:trigger_route_hook) do |_robot, response|
198
+ expect(response.extensions[:data]).to eq(:foo)
199
+ end
200
+ handler_class.dispatch(robot, message)
201
+ end
202
+ end
141
203
  end
142
204
 
143
205
  describe ".namespace" do
@@ -171,6 +233,19 @@ describe Lita::Handler, lita: true do
171
233
  end
172
234
  end
173
235
 
236
+ describe "#config" do
237
+ before { Lita.register_handler(handler_class) }
238
+ subject { handler_class.new(robot) }
239
+
240
+ it "returns a Lita config" do
241
+ expect(subject.config).to be_a(Lita::Config)
242
+ end
243
+
244
+ it "contains the handler's config settings" do
245
+ expect(subject.config.foo).to eq("bar")
246
+ end
247
+ end
248
+
174
249
  describe "#http" do
175
250
  it "returns a Faraday connection" do
176
251
  expect(subject.http).to be_a(Faraday::Connection)
@@ -194,6 +269,12 @@ describe Lita::Handler, lita: true do
194
269
  end
195
270
  end
196
271
 
272
+ describe "#log" do
273
+ it "returns the Lita logger" do
274
+ expect(subject.log).to eq(Lita.logger)
275
+ end
276
+ end
277
+
197
278
  describe "timer methods" do
198
279
  let(:response) { instance_double("Lita::Response") }
199
280
 
@@ -10,7 +10,7 @@ describe Lita::RackApp do
10
10
  http.get ":var/otherwise/identical/path", :constraint, var: /\d+/
11
11
  http.get ":var/otherwise/identical/path", :no_constraint
12
12
 
13
- def web(request, response)
13
+ def web(_request, response)
14
14
  response.write("it worked")
15
15
  end
16
16
 
@@ -24,11 +24,11 @@ describe Lita::RackApp do
24
24
  response.write(segments.join("/"))
25
25
  end
26
26
 
27
- def constraint(request, response)
27
+ def constraint(_request, response)
28
28
  response.write("constraint")
29
29
  end
30
30
 
31
- def no_constraint(request, response)
31
+ def no_constraint(_request, response)
32
32
  response.write("no constraint")
33
33
  end
34
34
 
@@ -28,4 +28,12 @@ describe Lita::Response do
28
28
  subject.match_data
29
29
  end
30
30
  end
31
+
32
+ describe "#extensions" do
33
+ it "can be populated with arbitrary data" do
34
+ subject.extensions[:foo] = :bar
35
+
36
+ expect(subject.extensions[:foo]).to eq(:bar)
37
+ end
38
+ end
31
39
  end
@@ -17,13 +17,13 @@ handler_class = Class.new(Lita::Handler) do
17
17
  response.reply "bongo", "wongo"
18
18
  end
19
19
 
20
- def restricted(response)
20
+ def restricted(_response)
21
21
  end
22
22
 
23
- def web(request, response)
23
+ def web(_request, _response)
24
24
  end
25
25
 
26
- def greet(payload)
26
+ def greet(_payload)
27
27
  end
28
28
 
29
29
  def self.name
@@ -1,11 +1,20 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Lita do
4
+ before { described_class.register_adapter(:shell, Lita::Adapters::Shell) }
5
+
4
6
  it "memoizes a Config" do
5
7
  expect(described_class.config).to be_a(Lita::Config)
6
8
  expect(described_class.config).to eql(described_class.config)
7
9
  end
8
10
 
11
+ it "keeps track of registered hooks" do
12
+ hook = double("hook")
13
+ described_class.register_hook("Foo ", hook)
14
+ described_class.register_hook(:foO, hook)
15
+ expect(described_class.hooks[:foo]).to eq(Set.new([hook]))
16
+ end
17
+
9
18
  describe ".configure" do
10
19
  it "yields the Config object" do
11
20
  described_class.configure { |c| c.robot.name = "Not Lita" }
@@ -57,12 +66,48 @@ describe Lita do
57
66
  end
58
67
  end
59
68
 
69
+ describe ".reset" do
70
+ it "clears the config" do
71
+ described_class.config.robot.name = "Foo"
72
+ described_class.reset
73
+ expect(described_class.config.robot.name).to eq("Lita")
74
+ end
75
+
76
+ it "clears adapters" do
77
+ described_class.register_adapter(:foo, double)
78
+ described_class.reset
79
+ expect(described_class.adapters).to be_empty
80
+ end
81
+
82
+ it "clears handlers" do
83
+ described_class.register_handler(double)
84
+ described_class.reset
85
+ expect(described_class.handlers).to be_empty
86
+ end
87
+
88
+ it "clears hooks" do
89
+ described_class.register_hook(:foo, double)
90
+ described_class.reset
91
+ expect(described_class.hooks).to be_empty
92
+ end
93
+ end
94
+
60
95
  describe ".run" do
61
- before { Lita.config }
96
+ let(:hook) { double("Hook") }
97
+
98
+ before do
99
+ allow_any_instance_of(Lita::Robot).to receive(:run)
100
+ end
62
101
 
63
102
  it "runs a new Robot" do
64
103
  expect_any_instance_of(Lita::Robot).to receive(:run)
65
104
  described_class.run
66
105
  end
106
+
107
+ it "calls before_run hooks" do
108
+ described_class.register_hook(:before_run, hook)
109
+ expect(hook).to receive(:call).with(config_path: "path/to/config")
110
+ described_class.run("path/to/config")
111
+ end
67
112
  end
68
113
  end
@@ -14,8 +14,8 @@ require "lita/rspec"
14
14
  RSpec.configure do |config|
15
15
  config.mock_with :rspec do |mocks_config|
16
16
  mocks_config.verify_doubled_constant_names = true
17
- # Enable config option this when a new rspec-mocks beta including this patch is released:
18
- # https://github.com/rspec/rspec-mocks/pull/466
17
+ # Enable config option when a new rspec-mocks beta including this patch is released:
18
+ # https://github.com/rspec/rspec-mocks/pull/615
19
19
  #
20
20
  # mocks_config.verify_partial_doubles = true
21
21
  end
@@ -4,16 +4,26 @@ TODO: Add a description of the plugin.
4
4
 
5
5
  ## Installation
6
6
 
7
+ <%- if config[:plugin_type] == "extension" -%>
8
+ Add <%= config[:gem_name] %> to your Lita plugin's gemspec:
9
+
10
+ ``` ruby
11
+ spec.add_runtime_dependency "<%= config[:gem_name] %>"
12
+ ```
13
+ <%- else -%>
7
14
  Add <%= config[:gem_name] %> to your Lita instance's Gemfile:
8
15
 
9
16
  ``` ruby
10
17
  gem "<%= config[:gem_name] %>"
11
18
  ```
19
+ <%- end -%>
12
20
 
21
+ <%- unless config[:plugin_type] == "extension" %>
13
22
  ## Configuration
14
23
 
15
24
  TODO: Describe any configuration attributes the plugin exposes.
16
25
 
26
+ <%- end -%>
17
27
  ## Usage
18
28
 
19
29
  TODO: Describe the plugin's features and how to use them.
@@ -1,12 +1,16 @@
1
1
  module Lita
2
2
  module <%= config[:constant_namespace] %>
3
- class <%= config[:constant_name] %> < <%= config[:plugin_type].capitalize %>
3
+ class <%= config[:constant_name] %><% unless config[:plugin_type] == "extension" %> < <%= config[:plugin_type].capitalize %><% end %>
4
4
  end
5
5
 
6
6
  <%- if config[:plugin_type] == "adapter" -%>
7
7
  Lita.register_adapter(:<%= config[:name] %>, <%= config[:constant_name] %>)
8
- <%- else -%>
8
+ <%- elsif config[:plugin_type] == "handler" -%>
9
9
  Lita.register_handler(<%= config[:constant_name] %>)
10
+ <%- else -%>
11
+ # If your extension needs to register with a Lita hook, uncomment the
12
+ # following line and change the hook name to the appropriate value:
13
+ # Lita.register_hook(:hook_name, <%= config[:constant_name] %>)
10
14
  <%- end -%>
11
15
  end
12
16
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lita
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jimmy Cuadra
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-11 00:00:00.000000000 Z
11
+ date: 2014-05-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -224,16 +224,16 @@ dependencies:
224
224
  name: rubocop
225
225
  requirement: !ruby/object:Gem::Requirement
226
226
  requirements:
227
- - - ">="
227
+ - - "~>"
228
228
  - !ruby/object:Gem::Version
229
- version: '0'
229
+ version: 0.21.0
230
230
  type: :development
231
231
  prerelease: false
232
232
  version_requirements: !ruby/object:Gem::Requirement
233
233
  requirements:
234
- - - ">="
234
+ - - "~>"
235
235
  - !ruby/object:Gem::Version
236
- version: '0'
236
+ version: 0.21.0
237
237
  description: A multi-service chat bot with extendable behavior.
238
238
  email:
239
239
  - jimmy@jimmycuadra.com
@@ -270,6 +270,7 @@ files:
270
270
  - lib/lita/rack_app.rb
271
271
  - lib/lita/response.rb
272
272
  - lib/lita/robot.rb
273
+ - lib/lita/route_validator.rb
273
274
  - lib/lita/rspec.rb
274
275
  - lib/lita/rspec/handler.rb
275
276
  - lib/lita/rspec/matchers/event_subscription_matcher.rb