rita 0.1.0 → 5.0.0.alpha.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +19 -0
  3. data/.rubocop.yml +51 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +16 -0
  6. data/CONTRIBUTING.md +18 -0
  7. data/Gemfile +5 -0
  8. data/{LICENSE.txt → LICENSE} +1 -3
  9. data/README.md +17 -24
  10. data/Rakefile +5 -5
  11. data/bin/lita +7 -0
  12. data/lib/lita/adapter.rb +147 -0
  13. data/lib/lita/adapters/shell.rb +126 -0
  14. data/lib/lita/adapters/test.rb +62 -0
  15. data/lib/lita/authorization.rb +112 -0
  16. data/lib/lita/callback.rb +39 -0
  17. data/lib/lita/cli.rb +218 -0
  18. data/lib/lita/configurable.rb +47 -0
  19. data/lib/lita/configuration_builder.rb +247 -0
  20. data/lib/lita/configuration_validator.rb +95 -0
  21. data/lib/lita/default_configuration.rb +122 -0
  22. data/lib/lita/errors.rb +25 -0
  23. data/lib/lita/handler/chat_router.rb +141 -0
  24. data/lib/lita/handler/common.rb +208 -0
  25. data/lib/lita/handler/event_router.rb +84 -0
  26. data/lib/lita/handler/http_router.rb +31 -0
  27. data/lib/lita/handler.rb +15 -0
  28. data/lib/lita/handlers/authorization.rb +129 -0
  29. data/lib/lita/handlers/help.rb +171 -0
  30. data/lib/lita/handlers/info.rb +66 -0
  31. data/lib/lita/handlers/room.rb +36 -0
  32. data/lib/lita/handlers/users.rb +37 -0
  33. data/lib/lita/http_callback.rb +46 -0
  34. data/lib/lita/http_route.rb +83 -0
  35. data/lib/lita/logger.rb +43 -0
  36. data/lib/lita/message.rb +124 -0
  37. data/lib/lita/middleware_registry.rb +39 -0
  38. data/lib/lita/namespace.rb +29 -0
  39. data/lib/lita/plugin_builder.rb +43 -0
  40. data/lib/lita/rack_app.rb +100 -0
  41. data/lib/lita/registry.rb +164 -0
  42. data/lib/lita/response.rb +65 -0
  43. data/lib/lita/robot.rb +273 -0
  44. data/lib/lita/room.rb +119 -0
  45. data/lib/lita/route_validator.rb +82 -0
  46. data/lib/lita/rspec/handler.rb +127 -0
  47. data/lib/lita/rspec/matchers/chat_route_matcher.rb +53 -0
  48. data/lib/lita/rspec/matchers/event_route_matcher.rb +29 -0
  49. data/lib/lita/rspec/matchers/http_route_matcher.rb +34 -0
  50. data/lib/lita/rspec.rb +48 -0
  51. data/lib/lita/source.rb +81 -0
  52. data/lib/lita/store.rb +23 -0
  53. data/lib/lita/target.rb +3 -0
  54. data/lib/lita/template.rb +71 -0
  55. data/lib/lita/template_resolver.rb +52 -0
  56. data/lib/lita/timer.rb +49 -0
  57. data/lib/lita/user.rb +157 -0
  58. data/lib/lita/util.rb +31 -0
  59. data/lib/lita/version.rb +6 -0
  60. data/lib/lita.rb +166 -0
  61. data/rita.gemspec +50 -0
  62. data/spec/lita/adapter_spec.rb +54 -0
  63. data/spec/lita/adapters/shell_spec.rb +99 -0
  64. data/spec/lita/authorization_spec.rb +122 -0
  65. data/spec/lita/configuration_builder_spec.rb +247 -0
  66. data/spec/lita/configuration_validator_spec.rb +114 -0
  67. data/spec/lita/default_configuration_spec.rb +242 -0
  68. data/spec/lita/handler/chat_router_spec.rb +236 -0
  69. data/spec/lita/handler/common_spec.rb +289 -0
  70. data/spec/lita/handler/event_router_spec.rb +121 -0
  71. data/spec/lita/handler/http_router_spec.rb +155 -0
  72. data/spec/lita/handler_spec.rb +62 -0
  73. data/spec/lita/handlers/authorization_spec.rb +111 -0
  74. data/spec/lita/handlers/help_spec.rb +124 -0
  75. data/spec/lita/handlers/info_spec.rb +67 -0
  76. data/spec/lita/handlers/room_spec.rb +24 -0
  77. data/spec/lita/handlers/users_spec.rb +35 -0
  78. data/spec/lita/logger_spec.rb +28 -0
  79. data/spec/lita/message_spec.rb +178 -0
  80. data/spec/lita/plugin_builder_spec.rb +41 -0
  81. data/spec/lita/response_spec.rb +62 -0
  82. data/spec/lita/robot_spec.rb +285 -0
  83. data/spec/lita/room_spec.rb +136 -0
  84. data/spec/lita/rspec/handler_spec.rb +33 -0
  85. data/spec/lita/rspec_spec.rb +162 -0
  86. data/spec/lita/source_spec.rb +68 -0
  87. data/spec/lita/store_spec.rb +23 -0
  88. data/spec/lita/template_resolver_spec.rb +42 -0
  89. data/spec/lita/template_spec.rb +52 -0
  90. data/spec/lita/timer_spec.rb +32 -0
  91. data/spec/lita/user_spec.rb +167 -0
  92. data/spec/lita/util_spec.rb +18 -0
  93. data/spec/lita_spec.rb +227 -0
  94. data/spec/spec_helper.rb +35 -0
  95. data/spec/templates/basic.erb +1 -0
  96. data/spec/templates/basic.irc.erb +1 -0
  97. data/spec/templates/helpers.erb +1 -0
  98. data/spec/templates/interpolated.erb +1 -0
  99. data/templates/locales/en.yml +137 -0
  100. data/templates/plugin/Gemfile +5 -0
  101. data/templates/plugin/README.tt +29 -0
  102. data/templates/plugin/Rakefile +8 -0
  103. data/templates/plugin/gemspec.tt +27 -0
  104. data/templates/plugin/gitignore +18 -0
  105. data/templates/plugin/lib/lita/plugin_type/plugin.tt +19 -0
  106. data/templates/plugin/lib/plugin.tt +16 -0
  107. data/templates/plugin/locales/en.yml.tt +4 -0
  108. data/templates/plugin/spec/lita/plugin_type/plugin_spec.tt +6 -0
  109. data/templates/plugin/spec/spec_helper.tt +8 -0
  110. data/templates/plugin/templates/gitkeep +0 -0
  111. data/templates/robot/Gemfile +5 -0
  112. data/templates/robot/lita_config.rb +28 -0
  113. metadata +386 -21
  114. data/.standard.yml +0 -3
  115. data/CHANGELOG.md +0 -5
  116. data/CODE_OF_CONDUCT.md +0 -132
  117. data/lib/rita/version.rb +0 -5
  118. data/lib/rita.rb +0 -8
  119. data/sig/rita.rbs +0 -4
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "configuration_builder"
4
+ require_relative "middleware_registry"
5
+
6
+ module Lita
7
+ # Builds the configuration object that is stored in each {Registry}.
8
+ # @since 4.0.0
9
+ # @api private
10
+ class DefaultConfiguration
11
+ # Valid levels for Lita's logger.
12
+ LOG_LEVELS = %w[debug info warn error fatal].freeze
13
+
14
+ # The default log formatter.
15
+ #
16
+ # This is specified as a constant instead of inline for +config.robot.log_formatter+ so it can
17
+ # be used by {Lita.logger} for early logging without accessing it via the config attribute and
18
+ # generating the default config too early, before plugins have been registered.
19
+ DEFAULT_LOG_FORMATTER = lambda { |severity, datetime, _progname, msg|
20
+ "[#{datetime.utc}] #{severity}: #{msg}\n"
21
+ }
22
+
23
+ # A {Registry} to extract configuration for plugins from.
24
+ # @return [Registry] The registry.
25
+ attr_reader :registry
26
+
27
+ # The top-level {ConfigurationBuilder} attribute.
28
+ # @return [Configuration] The root attribute.
29
+ attr_reader :root
30
+
31
+ # @param registry [Registry] The registry to build a default configuration object from.
32
+ def initialize(registry)
33
+ @registry = registry
34
+ @root = ConfigurationBuilder.new
35
+
36
+ adapters_config
37
+ handlers_config
38
+ http_config
39
+ redis_config
40
+ robot_config
41
+ end
42
+
43
+ # Processes the {ConfigurationBuilder} object to return a {Configuration}.
44
+ # @return [Configuration] The built configuration object.
45
+ def build
46
+ root.build
47
+ end
48
+
49
+ private
50
+
51
+ # Builds config.adapters
52
+ def adapters_config
53
+ adapters = registry.adapters
54
+
55
+ root.config :adapters do
56
+ adapters.each do |key, adapter|
57
+ combine(key, adapter.configuration_builder)
58
+ end
59
+ end
60
+ end
61
+
62
+ # Builds config.handlers
63
+ def handlers_config
64
+ handlers = registry.handlers
65
+
66
+ root.config :handlers do
67
+ handlers.each do |handler|
68
+ if handler.configuration_builder.children?
69
+ combine(handler.namespace, handler.configuration_builder)
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ # Builds config.http
76
+ def http_config
77
+ root.config :http do
78
+ config :host, type: String, default: "0.0.0.0"
79
+ config :port, type: [Integer, String], default: 8080
80
+ config :min_threads, type: [Integer, String], default: 0
81
+ config :max_threads, type: [Integer, String], default: 16
82
+ config :middleware, type: MiddlewareRegistry, default: MiddlewareRegistry.new
83
+ end
84
+ end
85
+
86
+ # Builds config.redis
87
+ def redis_config
88
+ root.config :redis, type: Hash, default: {}
89
+ end
90
+
91
+ # Builds config.robot
92
+ def robot_config
93
+ root.config :robot do
94
+ config :name, type: String, default: "Lita"
95
+ config :mention_name, type: String
96
+ config :alias, type: String
97
+ config :adapter, types: [String, Symbol], default: :shell
98
+ config :locale, types: [String, Symbol], default: nil
99
+ config :default_locale, types: [String, Symbol], default: nil
100
+ config :redis_namespace, type: String, default: Lita.test_mode? ? "lita.test" : "lita"
101
+ config :log_level, types: [String, Symbol], default: :info do
102
+ validate do |value|
103
+ unless LOG_LEVELS.include?(value.to_s.downcase.strip)
104
+ "must be one of: #{LOG_LEVELS.join(", ")}"
105
+ end
106
+ end
107
+ end
108
+ config :log_formatter, default: DEFAULT_LOG_FORMATTER do
109
+ validate do |value|
110
+ "must respond to #call" unless value.respond_to?(:call)
111
+ end
112
+ end
113
+ config :admins
114
+ config :error_handler, default: ->(_error, _metadata) {} do
115
+ validate do |value|
116
+ "must respond to #call" unless value.respond_to?(:call)
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lita
4
+ # The root exception class that all Lita-specific exceptions inherit from.
5
+ # @since 4.0.0
6
+ class Error < StandardError; end
7
+
8
+ # An exception raised when a custom validation is set on a configuration attribute that is
9
+ # violated by the default value of the attribute.
10
+ # @since 4.0.0
11
+ class ValidationError < Error; end
12
+
13
+ # An exception raised when Lita can't connect to Redis in test mode.
14
+ # @since 4.0.3
15
+ class RedisError < Error; end
16
+
17
+ # An exception raised when attempting to resolve a template that doesn't exist.
18
+ # @since 4.2.0
19
+ class MissingTemplateError < Error; end
20
+
21
+ # An exception raised when a handler attempts to render a template without having set its
22
+ # template root.
23
+ # @since 4.2.0
24
+ class MissingTemplateRootError < Error; end
25
+ end
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "i18n"
4
+
5
+ require_relative "../callback"
6
+ require_relative "../response"
7
+ require_relative "../route_validator"
8
+ require_relative "common"
9
+
10
+ module Lita
11
+ class Handler
12
+ # A handler mixin that provides the methods necessary for responding to chat messages.
13
+ # @since 4.0.0
14
+ module ChatRouter
15
+ # Includes common handler methods in any class that includes {ChatRouter}.
16
+ def self.extended(klass)
17
+ klass.include(Common)
18
+ end
19
+
20
+ # A Struct representing a chat route defined by a handler.
21
+ Route = Struct.new(
22
+ :pattern,
23
+ :callback,
24
+ :command,
25
+ :required_groups,
26
+ :help,
27
+ :extensions
28
+ )
29
+
30
+ class Route
31
+ alias command? command
32
+ end
33
+
34
+ # @overload route(pattern, method_name, **options)
35
+ # Creates a chat route.
36
+ # @param pattern [Regexp] A regular expression to match incoming messages against.
37
+ # @param method_name [Symbol, String] The name of the instance method to trigger.
38
+ # @param command [Boolean] Whether or not the message must be directed at the robot.
39
+ # @param restrict_to [Array<Symbol, String>, nil] An optional list of authorization
40
+ # groups the user must be in to trigger the route.
41
+ # @param help [Hash] An optional map of example invocations to descriptions.
42
+ # @param options [Hash] Aribtrary additional data that can be used by Lita extensions.
43
+ # @return [void]
44
+ # @overload route(pattern, **options)
45
+ # Creates a chat route.
46
+ # @param pattern [Regexp] A regular expression to match incoming messages against.
47
+ # @param command [Boolean] Whether or not the message must be directed at the robot.
48
+ # @param restrict_to [Array<Symbol, String>, nil] An optional list of authorization
49
+ # groups the user must be in to trigger the route.
50
+ # @param help [Hash] An optional map of example invocations to descriptions.
51
+ # @param options [Hash] Aribtrary additional data that can be used by Lita extensions.
52
+ # @yield The body of the route's callback.
53
+ # @return [void]
54
+ # @since 4.0.0
55
+ def route(pattern, method_name = nil, **options, &block)
56
+ options = default_route_options.merge(options)
57
+ options[:restrict_to] = options[:restrict_to].nil? ? nil : Array(options[:restrict_to])
58
+ routes << Route.new(
59
+ pattern,
60
+ Callback.new(method_name || block),
61
+ options.delete(:command),
62
+ options.delete(:restrict_to),
63
+ options.delete(:help),
64
+ options
65
+ )
66
+ end
67
+
68
+ # A list of chat routes defined by the handler.
69
+ # @return [Array<Handler::Route>]
70
+ def routes
71
+ @routes ||= []
72
+ end
73
+
74
+ # The main entry point for the handler at runtime. Checks if the message
75
+ # matches any of the routes and invokes the route's method if it does.
76
+ # Called by {Robot#receive}.
77
+ # @param robot [Robot] The currently running robot.
78
+ # @param message [Message] The incoming message.
79
+ # @return [Boolean] Whether or not the message matched any routes.
80
+ def dispatch(robot, message)
81
+ routes.map do |route|
82
+ next unless route_applies?(route, message, robot)
83
+
84
+ log_dispatch(robot, route)
85
+
86
+ robot.run_concurrently { dispatch_to_route(route, robot, message) }
87
+
88
+ true
89
+ end.any?
90
+ end
91
+
92
+ # Dispatch directly to a {Route}, ignoring route conditions.
93
+ # @param route [Route] The route to invoke.
94
+ # @param robot [Robot] The currently running robot.
95
+ # @param message [Message] The incoming message.
96
+ # @return [void]
97
+ # @since 3.3.0
98
+ def dispatch_to_route(route, robot, message)
99
+ response = Response.new(message, route.pattern)
100
+ robot.hooks[:trigger_route].each { |hook| hook.call(response: response, route: route) }
101
+ handler = new(robot)
102
+ route.callback.call(handler, response)
103
+ robot.hooks[:post_route].each { |hook| hook.call(response: response, route: route) }
104
+ robot.trigger(
105
+ :message_dispatched,
106
+ handler: self,
107
+ route: route,
108
+ message: message,
109
+ robot: robot
110
+ )
111
+ rescue StandardError => e
112
+ log_error(robot, e, message: message)
113
+ end
114
+
115
+ private
116
+
117
+ # The default options for every chat route.
118
+ def default_route_options
119
+ {
120
+ command: false,
121
+ restrict_to: nil,
122
+ help: {}
123
+ }
124
+ end
125
+
126
+ # Determines whether or not an incoming messages should trigger a route.
127
+ def route_applies?(route, message, robot)
128
+ RouteValidator.new(self, route, message, robot).call
129
+ end
130
+
131
+ # Logs the dispatch of message.
132
+ def log_dispatch(robot, route)
133
+ robot.logger.debug I18n.t(
134
+ "lita.handler.dispatch",
135
+ handler: name,
136
+ method: route.callback.method_name || "(block)"
137
+ )
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,208 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "faraday"
4
+ require "i18n"
5
+ require "redis-namespace"
6
+
7
+ require_relative "../configurable"
8
+ require_relative "../configuration_builder"
9
+ require_relative "../errors"
10
+ require_relative "../namespace"
11
+ require_relative "../template"
12
+ require_relative "../template_resolver"
13
+ require_relative "../timer"
14
+ require_relative "../version"
15
+
16
+ module Lita
17
+ class Handler
18
+ # Methods included in any class that includes at least one type of router.
19
+ # @since 4.0.0
20
+ module Common
21
+ # Adds common functionality to the class and initializes the handler's configuration builder.
22
+ def self.included(klass)
23
+ klass.extend(ClassMethods)
24
+ klass.extend(Configurable)
25
+ klass.extend(Namespace)
26
+ klass.configuration_builder = ConfigurationBuilder.new
27
+ end
28
+
29
+ # Common class-level methods for all handlers.
30
+ module ClassMethods
31
+ # Gets (and optionally sets) the directory where the handler's templates are stored.
32
+ # @param path [String] If provided, sets the template root to this value.
33
+ # @return [String] The template root path.
34
+ # @raise [MissingTemplateRootError] If accessed without setting a value first.
35
+ def template_root(path = nil)
36
+ @template_root = path if path
37
+
38
+ if defined?(@template_root)
39
+ @template_root
40
+ else
41
+ raise MissingTemplateRootError
42
+ end
43
+ end
44
+
45
+ # Returns the translation for a key, automatically namespaced to the handler.
46
+ # @param key [String] The key of the translation.
47
+ # @param hash [Hash] An optional hash of values to be interpolated in the string.
48
+ # @return [String] The translated string.
49
+ # @since 3.0.0
50
+ def translate(key, hash = {})
51
+ I18n.translate("lita.handlers.#{namespace}.#{key}", **hash)
52
+ end
53
+
54
+ alias t translate
55
+
56
+ # Logs an error raised by a plugin.
57
+ def log_error(robot, error, metadata = {})
58
+ error_handler = robot.config.robot.error_handler
59
+
60
+ if error_handler.arity == 2
61
+ error_handler.call(error, metadata.merge(robot: robot))
62
+ else
63
+ error_handler.call(error)
64
+ end
65
+
66
+ robot.logger.error I18n.t(
67
+ "lita.handler.exception",
68
+ handler: name,
69
+ message: error.message,
70
+ backtrace: error.backtrace.join("\n")
71
+ )
72
+
73
+ raise error if Lita.test_mode?
74
+ end
75
+ end
76
+
77
+ # A Redis::Namespace scoped to the handler.
78
+ # @return [Redis::Namespace]
79
+ attr_reader :redis
80
+
81
+ # The running {Robot} instance.
82
+ # @return [Robot]
83
+ attr_reader :robot
84
+
85
+ # @param robot [Robot] The currently running robot.
86
+ def initialize(robot)
87
+ @robot = robot
88
+ @redis = Redis::Namespace.new(redis_namespace, redis: robot.redis)
89
+ end
90
+
91
+ # Invokes the given block after the given number of seconds.
92
+ # @param interval [Integer] The number of seconds to wait before invoking the block.
93
+ # @yieldparam timer [Timer] The current {Timer} instance.
94
+ # @return [void]
95
+ # @since 3.0.0
96
+ def after(interval, &block)
97
+ robot.run_concurrently { Timer.new(interval: interval, &block).start }
98
+ end
99
+
100
+ # The handler's configuration object.
101
+ # @return [Configuration, Config] The handler's configuration object.
102
+ # @since 3.2.0
103
+ def config
104
+ if robot.config.handlers.respond_to?(self.class.namespace)
105
+ robot.config.handlers.public_send(self.class.namespace)
106
+ end
107
+ end
108
+
109
+ # Invokes the given block repeatedly, waiting the given number of seconds between each
110
+ # invocation.
111
+ # @param interval [Integer] The number of seconds to wait before each invocation of the block.
112
+ # @yieldparam timer [Timer] The current {Timer} instance.
113
+ # @return [void]
114
+ # @note The block should call {Timer#stop} at a terminating condition to avoid infinite
115
+ # recursion.
116
+ # @since 3.0.0
117
+ def every(interval, &block)
118
+ robot.run_concurrently { Timer.new(interval: interval, recurring: true, &block).start }
119
+ end
120
+
121
+ # Creates a new +Faraday::Connection+ for making HTTP requests.
122
+ # @param options [Hash] A set of options passed on to Faraday.
123
+ # @yield [builder] A Faraday builder object for adding middleware.
124
+ # @return [Faraday::Connection] The new connection object.
125
+ def http(options = {}, &block)
126
+ options = default_faraday_options.merge(options)
127
+
128
+ if block
129
+ Faraday::Connection.new(nil, options, &block)
130
+ else
131
+ Faraday::Connection.new(nil, options)
132
+ end
133
+ end
134
+
135
+ # The +Logger+ for the currently running robot.
136
+ # @return [Logger] The Lita logger.
137
+ # @since 3.2.0
138
+ def log
139
+ robot.logger
140
+ end
141
+
142
+ # Render an ERB template to a string, with any provided variables made available to the
143
+ # template.
144
+ # @param template_name [String] The name of the ERB template file.
145
+ # @param variables [Hash] An optional hash of variables to make available within the
146
+ # template. Hash keys become instance variable names with the hash values as the instance
147
+ # variables' values.
148
+ # @return [String] The rendered template.
149
+ # @since 4.2.0
150
+ def render_template(template_name, variables = {})
151
+ Template.from_file(file_for_template(template_name)).render(variables)
152
+ end
153
+
154
+ # Renders an ERB template to a string, like {#render_template}, but also takes an array of
155
+ # modules with helper methods to be made available to the template.
156
+ # @param template_name [String] The name of the ERB template file.
157
+ # @param helpers [Array<Module>] An array of modules whose methods should be added to the
158
+ # template evaluation context.
159
+ # @param variables [Hash] An optional hash of variables to make available within the
160
+ # template. Hash keys become instance variable names with the hash values as the instance
161
+ # variables' values.
162
+ # @return [String] The rendered template.
163
+ # @since 4.5.0
164
+ def render_template_with_helpers(template_name, helpers, variables = {})
165
+ template = Template.from_file(file_for_template(template_name))
166
+ helpers.each { |helper| template.add_helper(helper) }
167
+ template.render(variables)
168
+ end
169
+
170
+ # A hash-like object that can be used to persist data between instances of the current
171
+ # handler. Useful for storing objects that are expensive to create and that can be safely
172
+ # reused between instances.
173
+ # @return [Store] The handler's shared store.
174
+ # @since 5.0.0
175
+ def store
176
+ robot.store[self.class.namespace]
177
+ end
178
+
179
+ # @see .translate
180
+ def translate(*args)
181
+ self.class.translate(*args)
182
+ end
183
+
184
+ alias t translate
185
+
186
+ private
187
+
188
+ # Default options for new Faraday connections. Sets the user agent to the
189
+ # current version of Lita.
190
+ def default_faraday_options
191
+ { headers: { "User-Agent" => "Lita v#{VERSION}" } }
192
+ end
193
+
194
+ def file_for_template(template_name)
195
+ TemplateResolver.new(
196
+ self.class.template_root,
197
+ template_name,
198
+ robot.config.robot.adapter
199
+ ).resolve
200
+ end
201
+
202
+ # The handler's namespace for Redis.
203
+ def redis_namespace
204
+ "handlers:#{self.class.namespace}"
205
+ end
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../callback"
4
+ require_relative "common"
5
+
6
+ module Lita
7
+ class Handler
8
+ # A handler mixin that provides the methods necessary for handling events.
9
+ # @since 4.0.0
10
+ module EventRouter
11
+ # Includes common handler methods in any class that includes {EventRouter}.
12
+ def self.extended(klass)
13
+ klass.include(Common)
14
+ end
15
+
16
+ # @overload on(event_name, method_name)
17
+ # Registers an event subscription. When an event is triggered with
18
+ # {#trigger}, a new instance of the handler will be created and the
19
+ # instance method name supplied to {#on} will be invoked with a payload
20
+ # (a hash of arbitrary keys and values).
21
+ # @param event_name [String, Symbol] The name of the event to subscribe to.
22
+ # @param method_name [String, Symbol] The name of the instance method on
23
+ # the handler that should be invoked when the event is triggered.
24
+ # @return [void]
25
+ # @overload on(event_name, callable)
26
+ # Registers an event subscription. When an event is triggered with
27
+ # {#trigger}, a new instance of the handler will be created and the
28
+ # callable object supplied to {#on} will be evaluated within the context of the new
29
+ # handler instance, and passed a payload (a hash of arbitrary keys and values).
30
+ # @param event_name [String, Symbol] The name of the event to subscribe to.
31
+ # @param callable [#call] A callable object to serve as the event callback.
32
+ # @return [void]
33
+ # @since 4.0.0
34
+ # @overload on(event_name)
35
+ # Registers an event subscription. When an event is triggered with
36
+ # {#trigger}, a new instance of the handler will be created and the
37
+ # block supplied to {#on} will be evaluated within the context of the new
38
+ # handler instance, and passed a payload (a hash of arbitrary keys and values).
39
+ # @param event_name [String, Symbol] The name of the event to subscribe to.
40
+ # @yield The body of the event callback.
41
+ # @return [void]
42
+ # @since 4.0.0
43
+ def on(event_name, method_name_or_callable = nil, &block)
44
+ event_subscriptions[normalize_event(event_name)] << Callback.new(
45
+ method_name_or_callable || block
46
+ )
47
+ end
48
+
49
+ # Returns an array of all callbacks registered for the named event.
50
+ # @param event_name [String, Symbol] The name of the event to return callbacks for.
51
+ # @return [Array] The array of callbacks.
52
+ # @since 4.0.0
53
+ def event_subscriptions_for(event_name)
54
+ event_subscriptions[normalize_event(event_name)]
55
+ end
56
+
57
+ # Triggers an event, invoking methods previously registered with {#on} and
58
+ # passing them a payload hash with any arbitrary data.
59
+ # @param robot [Robot] The currently running robot instance.
60
+ # @param event_name [String, Symbol], The name of the event to trigger.
61
+ # @param payload [Hash] An optional hash of arbitrary data.
62
+ # @return [Boolean] Whether or not the event triggered any callbacks.
63
+ def trigger(robot, event_name, payload = {})
64
+ event_subscriptions_for(event_name).map do |callback|
65
+ callback.call(new(robot), payload)
66
+ rescue StandardError => e
67
+ log_error(robot, e, payload: payload)
68
+ end.any?
69
+ end
70
+
71
+ private
72
+
73
+ # A hash of arrays used to store event subscriptions registered with {#on}.
74
+ def event_subscriptions
75
+ @event_subscriptions ||= Hash.new { |h, k| h[k] = [] }
76
+ end
77
+
78
+ # Normalize the event name, ignoring casing and spaces.
79
+ def normalize_event(event_name)
80
+ event_name.to_s.downcase.strip.to_sym
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../http_route"
4
+ require_relative "common"
5
+
6
+ module Lita
7
+ class Handler
8
+ # A handler mixin that provides the methods necessary for handling incoming HTTP requests.
9
+ # @since 4.0.0
10
+ module HTTPRouter
11
+ # Includes common handler methods in any class that includes {HTTPRouter}.
12
+ def self.extended(klass)
13
+ klass.include(Common)
14
+ end
15
+
16
+ # Creates a new {HTTPRoute} which is used to define an HTTP route
17
+ # for the built-in web server.
18
+ # @see HTTPRoute
19
+ # @return [HTTPRoute] The new {HTTPRoute}.
20
+ def http
21
+ HTTPRoute.new(self)
22
+ end
23
+
24
+ # An array of all HTTP routes defined for the handler.
25
+ # @return [Array<HTTPRoute>] The array of routes.
26
+ def http_routes
27
+ @http_routes ||= []
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "handler/chat_router"
4
+ require_relative "handler/http_router"
5
+ require_relative "handler/event_router"
6
+
7
+ module Lita
8
+ # Base class for objects that add new behavior to Lita. {Handler} is simply a class with all
9
+ # types of routers mixed in.
10
+ class Handler
11
+ extend ChatRouter
12
+ extend HTTPRouter
13
+ extend EventRouter
14
+ end
15
+ end