boty 0.1.2 → 0.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: 232044d6e49a24255c5b5a554a6897ec473cdf1d
4
- data.tar.gz: fe980e1c3d03a64200a8d62f2ac8fa52f616aab8
3
+ metadata.gz: b9959fb9fd1218a64dc671442aef786311c03f10
4
+ data.tar.gz: bad2921130ae92b79ef775529861fb4c6a204cdf
5
5
  SHA512:
6
- metadata.gz: c3a0dac85b60abc7aa9c9e58dd2edc1942ced52d729c710ff22330d7d34526ba028c5a64a09b318fd9a715c2d468d4f0a53c1a751d1dfb44a34bbc844760ba59
7
- data.tar.gz: 5ab990e551d97057bab55722c61971eae5dd855b75af806ad24d136eb826b8d6c4ef98e20985be4f70845f0861b5476a5b9516ba3a2e16bed6137d2709c2d648
6
+ metadata.gz: 0052d938808382105c9f4b2e242ca357a01cb7e83c5eead10f05a3225b6a021cf08c24d0de7dfe7d69b3945015f1595ff20291dbc33d41b1e93b758965023fb5
7
+ data.tar.gz: 2b31a41cb5443d4693728b2a8689b321371424c7a90b84c058a721a545c2cdf17a1feb952e2404b0f302fb403bc13936e3b8f1a957e3a6d4bfc715d7318782d3
data/.gitignore CHANGED
@@ -15,3 +15,6 @@
15
15
  .env.local
16
16
  pkg
17
17
  /.rspec
18
+
19
+ /.yardoc
20
+ /docs/api
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- boty (0.1.2)
4
+ boty (0.2.0)
5
5
  eventmachine
6
6
  faraday
7
7
  faye-websocket
@@ -57,6 +57,7 @@ GEM
57
57
  websocket-driver (0.6.3)
58
58
  websocket-extensions (>= 0.1.0)
59
59
  websocket-extensions (0.1.2)
60
+ yard (0.8.7.6)
60
61
 
61
62
  PLATFORMS
62
63
  ruby
@@ -70,6 +71,7 @@ DEPENDENCIES
70
71
  rake (~> 10.0)
71
72
  rspec
72
73
  rubocop
74
+ yard
73
75
 
74
76
  BUNDLED WITH
75
77
  1.10.6
data/README.md CHANGED
@@ -1,5 +1,6 @@
1
1
  # Boty
2
2
  [<img src="https://travis-ci.org/ricardovaleriano/boty.svg?branch=master" />](https://travis-ci.org/ricardovaleriano/boty)
3
+ [<img src="http://img.shields.io/badge/yard-docs-blue.svg" />](http://www.rubydoc.info/github/ricardovaleriano/boty)
3
4
 
4
5
  `Boty` is a utilitary to create bots (at this time, specificaly Slack bots).
5
6
 
@@ -291,7 +292,7 @@ Before we start to study the "script" way of configuring `command` and
291
292
  `listener` binds, let's see how we can use ruby regexps to capture message
292
293
  parameters.
293
294
 
294
- ##### Regexes - a pattern that allow capture parameters within a message<a name="regexes" />
295
+ #### Regexes - a pattern that allow capture parameters within a message<a name="regexes" />
295
296
 
296
297
  What is a bot if it can't annoy people when we want it to? So let's teach our
297
298
  bot to send private messages to people in the Slack room.
@@ -316,6 +317,29 @@ _command_.
316
317
 
317
318
  And this is it! :tada:
318
319
 
320
+ ##### Commands, Listenners and aliases
321
+
322
+ Both *commands* and *listeners* can be aliased, which means that the same
323
+ `Action` can be triggered by different regexes:
324
+
325
+ ```ruby
326
+ command /hi/i, /hello/i do
327
+ say "Ohay #{user.name}! Hello there."
328
+ end
329
+ ```
330
+
331
+ valeriano 12:04 PM
332
+ @jeeba: hi
333
+
334
+ jeeba BOT 12:04 PM
335
+ Ohay valeriano! Hello there.
336
+
337
+ valeriano 12:05 PM
338
+ @jeeba: hello
339
+
340
+ jeeba BOT 12:05 PM
341
+ Ohay valeriano! Hello there.
342
+
319
343
  ### Adding custom scripts<a name="custom_scripts" />
320
344
 
321
345
  Now you already know what are [listeners](#listeners) and
data/Rakefile CHANGED
@@ -1,5 +1,10 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
+ require "yard"
3
4
 
4
5
  RSpec::Core::RakeTask.new(:spec)
5
6
  task default: :spec
7
+
8
+ YARD::Rake::YardocTask.new do |t|
9
+ t.options = ["-odocs/api"]
10
+ end
@@ -36,4 +36,5 @@ Gem::Specification.new do |spec|
36
36
  spec.add_development_dependency "dotenv"
37
37
  spec.add_development_dependency "fakefs"
38
38
  spec.add_development_dependency "rubocop"
39
+ spec.add_development_dependency "yard"
39
40
  end
@@ -1,28 +1,30 @@
1
- begin
2
- require "pp"
3
-
4
- require "dotenv"
5
- if defined? Dotenv
6
- Dotenv.load
7
- Dotenv.overload ".env.local"
8
- end
9
- rescue LoadError
10
- end
11
-
12
1
  require "faye/websocket"
13
2
  require "i18n"
14
3
  require "faraday"
4
+ require "dotenv"
5
+ require "pp"
15
6
 
16
7
  $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
17
8
 
9
+ Dotenv.load
10
+ Dotenv.overload ".env.local"
11
+
12
+ # Namespace for all Boty code.
18
13
  module Boty
14
+ # Public: Reloads the available locale configuration files and set the current
15
+ # language as the one passed in the paramenter `lang`.
16
+ #
17
+ # lang - String or Symbol representing the language that should be set for the
18
+ # current execution.
19
+ #
20
+ # Examples:
21
+ #
22
+ # Boty.locale = :en
23
+ #
24
+ # Returns nothing.
19
25
  def self.locale=(lang)
20
26
  Locale.reload lang
21
27
  end
22
-
23
- def self.locale
24
- I18n.locale ||= :en
25
- end
26
28
  end
27
29
 
28
30
  require "boty/version"
@@ -35,5 +37,6 @@ require "boty/script_loader"
35
37
  require "boty/dsl"
36
38
  require "boty/locale"
37
39
  require "boty/eventable"
40
+ require "boty/match_handler"
38
41
  require "boty/bot"
39
42
  require "boty/http"
@@ -1,32 +1,73 @@
1
1
  module Boty
2
- # Public: Wrap the idea of something that should happen when some regex is
3
- # matched.
2
+ # Public: Wrap the idea of something that should happen when a regex matches.
3
+ # Actions are the underlying mecanism for store and execute the blocks passed
4
+ # to `Bot#match` and `Bot#respond` invocations.
4
5
  #
6
+ # Maybe one of the more important things to remember here is that an Action is
7
+ # executed within the scope of a `Boty::DSL` instance already binded with the
8
+ # `Bot` of the current `Session` (via `#instance_exec`).
5
9
  #
10
+ # Examples:
11
+ #
12
+ # # some_script.rb
13
+ # desc "omg! whoo!"
14
+ # hear(/omg/i) do
15
+ # im "whoo!"
16
+ # end
17
+ #
18
+ # # Will generate an `Action` like this:
19
+ # action = Action.new(bot, /omg/i, "omg! whoo!") { im "whoo!" }
20
+ #
21
+ # # Then this can be executed, by `#execute`.
22
+ # action.execute "omg! this string triggers the hear block."
23
+ #
24
+ # # If the regex matches the parameter passed to execute, it'll invoke the
25
+ # # block passed to `#hear`.
6
26
  class Action
7
27
  include Boty::Logger
8
28
  attr_reader :regex, :desc, :action
9
29
 
30
+ # Public: Initialize an `Action` associated with a specific `Bot`.
31
+ #
32
+ # bot - A Bot that will react to the matches on the regex.
33
+ # regex - A Regexp to match against messages received in the bot session.
34
+ # action - A Proc that will be executed everytime the regex matches.
10
35
  def initialize(bot, regex, description, &action)
11
- if description
12
- description.regex = regex
13
- else
14
- description = ActionDescription.new(nil, regex: regex)
15
- end
16
-
17
36
  @dsl = DSL.new bot
18
37
  @regex = regex
19
- @desc = description
20
38
  @action = action
39
+
40
+ self.desc = description
21
41
  end
22
42
 
43
+ # Public: Check if a given Regexp and an optional block were the same used
44
+ # to build this Action.
45
+ #
46
+ # Examples:
47
+ #
48
+ # send_im = -> { im "whoo!" }
49
+ # action = Action.new(bot, /omg/i, "omg! whoo!", &send_im)
50
+ # action.this? /omg/i, send_im
51
+ # # => return true
52
+ #
53
+ # regex - A Regexp to be compared against `regex`.
54
+ # block - An optional Proc to be checked against `action`.
55
+ #
56
+ # Returns True or False.
23
57
  def this?(regex, block)
24
58
  same_regex = regex == self.regex
25
59
  block ? same_regex && block == action : same_regex
26
60
  end
27
61
 
62
+ # Public: Creates a `Regexp::Match` based on the internal `regex` against a
63
+ # Slack::Message.
64
+ # Then pass this match to be used as argument to the action per se.
65
+ #
66
+ # message - A Slack::Message object to be matched against the `regex`.
67
+ #
68
+ # Returns nothing.
28
69
  def execute(message)
29
- action_call message.match!(regex)
70
+ action_call regex.match(message.text)
30
71
  rescue => e
31
72
  logger.error e.message
32
73
  raise e
@@ -34,6 +75,43 @@ module Boty
34
75
 
35
76
  private
36
77
 
78
+ # Internal: Store the ActionDescription for this action (create a new for it
79
+ # if a nil value is the parameter).
80
+ #
81
+ # To make it possible to create handlers (that will became actions) without
82
+ # a description, this method can work with a nil parameter. If a nil value
83
+ # is passed, it creates an `ActionDescription` with only a regex (without a
84
+ # description text).
85
+ #
86
+ # Returns an ActionDescription with the regex associated with this Action.
87
+ def desc=(description)
88
+ if description
89
+ description.regex = regex
90
+ else
91
+ description = ActionDescription.new(nil, regex: regex)
92
+ end
93
+ @desc = description
94
+ end
95
+
96
+ # Internal: Effectively executes the action.
97
+ # The regex match created on the `#execute` method will be exploded, so the
98
+ # matches can be passed as arguments to the block.
99
+ # Important to remember: the block passed to `Bot#match` or `Bot#respond`
100
+ # will be executed within the scope of a `Boty::DSL`.
101
+ #
102
+ # Examples:
103
+ #
104
+ # # some_script.rb
105
+ # hear(/(omg)\s(lol)/i) do |first_match, second_match|
106
+ # im "whoo!"
107
+ # end
108
+ #
109
+ # # will cause an invocation like this:
110
+ # @dsl.instance_exec("omg", "lol") do |first_match, second_match|
111
+ # im "whoo!"
112
+ # end
113
+ #
114
+ # Returns nothing.
37
115
  def action_call(match)
38
116
  return unless match
39
117
  matches = Array(match)
@@ -1,4 +1,26 @@
1
1
  module Boty
2
+ # Public: contains de descriptive information that was associated with an
3
+ # action via the `Bot#desc` method.
4
+ #
5
+ # Besides that also keep tracks of the regex for the associated Action, it is
6
+ # used to describe the handler in case no command name and/or description was
7
+ # passed via `Bot#desc`.
8
+ #
9
+ # The `ActionDescription` object will be stored inside the `Action#desc`. A
10
+ # good usage example can be found in the `scripts/knows.rb` script.
11
+ #
12
+ # Examples:
13
+ #
14
+ # # scripts/omg.rb
15
+ # desc "X omg!", "Make the bot scream X omgs."
16
+ # hear(/(\d+ )?omg!/i) do |how_many|
17
+ # how_many.times do say "LOL!" end
18
+ # end
19
+ #
20
+ # # => generates an action with a description like this:
21
+ # ActionDescription.new "X omg!",
22
+ # description: "Make the bot scream X omgs."
23
+ # regex: /(\d+ )?omg!/i
2
24
  class ActionDescription
3
25
  attr_reader :command, :description
4
26
  attr_writer :regex
@@ -1,10 +1,11 @@
1
1
  module Boty
2
2
  class Bot
3
3
  include Boty::Eventable
4
+ include Boty::MatchHandler
4
5
  include Boty::Logger
5
6
  include Slack
6
7
 
7
- attr_reader :id, :name, :trigger_message, :brain
8
+ attr_reader :id, :name, :brain
8
9
 
9
10
  def initialize(bot_info)
10
11
  Locale.reload
@@ -12,42 +13,14 @@ module Boty
12
13
  @id = bot_info["id"]
13
14
  @name = bot_info["name"]
14
15
 
15
- @listeners ||= []
16
- @commands ||= []
17
16
  @brain ||= {}
18
17
 
19
18
  on :message, &method(:message_handler)
20
19
  ScriptLoader.new(self).load
21
20
  end
22
21
 
23
- def match(regex, &block)
24
- @listeners << create_action(regex, &block)
25
- end
26
-
27
- def respond(regex, &block)
28
- @commands << create_action(regex, &block)
29
- end
30
-
31
- def no_match(regex, &block)
32
- remove_action @listeners, regex, block
33
- end
34
-
35
- def no_respond(regex, &block)
36
- remove_action @commands, regex, block
37
- end
38
-
39
- def no(command)
40
- remove_action @listeners, command: command
41
- remove_action @commands, command: command
42
- end
43
-
44
- def desc(command, description = nil)
45
- @current_desc = ActionDescription.new command,
46
- description: description
47
- end
48
-
49
22
  def say(message, api_parameters = {})
50
- channel = (@trigger_message && @trigger_message.channel) || "#general"
23
+ channel = (trigger_message && trigger_message.channel) || "#general"
51
24
  options = { channel: channel }.merge api_parameters
52
25
  post_response = Slack.chat.post_message message, options
53
26
  logger.debug { "Post response: #{post_response}." }
@@ -63,41 +36,27 @@ module Boty
63
36
  end
64
37
  end
65
38
 
66
- # TODO: return an Action object instead of a hash
67
39
  def know_how
68
- actions = Array(@commands) + Array(@listeners)
40
+ actions = Array(commands) + Array(listeners)
69
41
  actions.sort_by { |action| action.desc.command || "_" }
70
42
  end
71
43
 
72
44
  private
73
45
 
74
- def remove_action(collection, regex = nil, block = nil, command: nil)
75
- collection.delete_if do |action|
76
- action.desc.command == command || action.this?(regex, block)
77
- end
78
- end
79
-
80
46
  def user_by_name(destiny, to)
81
47
  to ||= destiny
82
48
  if to
83
49
  Slack.users.by_name to
84
50
  else
85
- @trigger_message.user
51
+ trigger_message.user
86
52
  end
87
53
  end
88
54
 
89
- def create_action(regex, &block)
90
- regex = Regexp.new(regex) if regex.is_a? String
91
- Boty::Action.new(self, regex, @current_desc, &block).tap {
92
- @current_desc = nil
93
- }
94
- end
95
-
96
55
  def message_from_bot_itself?(data)
97
56
  data["user"] == id || data["user"] == name
98
57
  end
99
58
 
100
- def valid_mention?(data)
59
+ def command?(data)
101
60
  return false if message_from_bot_itself? data
102
61
 
103
62
  if /(<@#{id}|#{name}>)/ =~ data["text"]
@@ -111,17 +70,16 @@ module Boty
111
70
  unless data["text"]
112
71
  return logger.debug do "Non text message, just ignoring." end
113
72
  end
114
- actions = valid_mention?(data) ? @commands : @listeners
115
- execute_all Message.new(data), actions
73
+ execute data
116
74
  end
117
75
 
118
- def execute_all(message, actions)
119
- @trigger_message = message
120
- Array(actions).each do |action|
121
- action.execute @trigger_message
76
+ def execute(data)
77
+ message = Message.new data
78
+ if command?(data)
79
+ execute_commands message
80
+ else
81
+ execute_matches message
122
82
  end
123
- ensure
124
- @trigger_message = nil
125
83
  end
126
84
  end
127
85
  end
@@ -1,6 +1,7 @@
1
1
  module Boty
2
2
  module Eventable
3
3
  include Boty::Logger
4
+
4
5
  def events
5
6
  @events ||= {}
6
7
  end
@@ -0,0 +1,79 @@
1
+ module Boty
2
+ module MatchHandler
3
+ include Boty::Logger
4
+
5
+ attr_reader :trigger_message
6
+
7
+ def listeners
8
+ @listeners ||= []
9
+ end
10
+
11
+ def commands
12
+ @commands ||= []
13
+ end
14
+
15
+ def desc(command, description = nil)
16
+ @current_desc = ActionDescription.new command,
17
+ description: description
18
+ end
19
+
20
+ def match(*regexes, &block)
21
+ create_action_on_collection listeners, regexes, &block
22
+ end
23
+
24
+ def respond(*regexes, &block)
25
+ create_action_on_collection commands, regexes, &block
26
+ end
27
+
28
+ def no_match(regex, &block)
29
+ remove_action listeners, regex, block
30
+ end
31
+
32
+ def no_respond(regex, &block)
33
+ remove_action commands, regex, block
34
+ end
35
+
36
+ def no(command)
37
+ remove_action listeners, command: command
38
+ remove_action commands, command: command
39
+ end
40
+
41
+ def execute_matches(message)
42
+ execute_actions message, listeners
43
+ end
44
+
45
+ def execute_commands(message)
46
+ execute_actions message, commands
47
+ end
48
+
49
+ private
50
+
51
+ def create_action_on_collection(collection, regexes, &block)
52
+ regexes.each do |regex|
53
+ collection << create_action(regex, &block)
54
+ end
55
+ end
56
+
57
+ def execute_actions(message, actions)
58
+ @trigger_message = message
59
+ Array(actions).each do |action|
60
+ action.execute @trigger_message
61
+ end
62
+ ensure
63
+ @trigger_message = nil
64
+ end
65
+
66
+ def remove_action(collection, regex = nil, block = nil, command: nil)
67
+ collection.delete_if do |action|
68
+ action.desc.command == command || action.this?(regex, block)
69
+ end
70
+ end
71
+
72
+ def create_action(regex, &block)
73
+ regex = Regexp.new(regex) if regex.is_a? String
74
+ Boty::Action.new(self, regex, @current_desc, &block).tap {
75
+ @current_desc = nil
76
+ }
77
+ end
78
+ end
79
+ end
@@ -1,3 +1,3 @@
1
1
  module Boty
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -0,0 +1,23 @@
1
+ module Boty
2
+ RSpec.describe Action, :users do
3
+ let(:bot) { Bot.new("id" => "U1234", "name" => "boty") }
4
+ let(:event_data) {
5
+ {
6
+ "type" => "message",
7
+ "text" => "omg lol bbq"
8
+ }
9
+ }
10
+
11
+ subject(:action) {
12
+ Action.new(bot, /omg/, nil) do nil end
13
+ }
14
+
15
+ it "evals the block in the context of the bot" do
16
+ dsl = nil
17
+ bot.match(/omg/) do dsl = self end
18
+ bot.event event_data
19
+
20
+ expect(dsl.bot).to eq bot
21
+ end
22
+ end
23
+ end
@@ -1,6 +1,5 @@
1
1
  module Boty
2
2
  RSpec.describe Bot do
3
- include Boty
4
3
  subject(:bot) { described_class.new bot_info }
5
4
 
6
5
  let(:bot_info) do
@@ -10,105 +9,9 @@ module Boty
10
9
  }
11
10
  end
12
11
 
13
- describe "#on" do
14
- it "binds an event `'type'` to a block" do
15
- data = {
16
- "type" => "omg",
17
- "text" => "lol"
18
- }
19
-
20
- expect { |b|
21
- bot.on :omg, &b
22
- bot.event data
23
- }.to yield_with_args data
24
- end
25
- end
26
-
27
- describe "#off" do
28
- let(:data) do
29
- {
30
- "type" => "bbq",
31
- "text" => "omg. wtf."
32
- }
33
- end
34
-
35
- it "unbounds an event by type AND block, just if the block is the same" do
36
- @permanent_executed = false
37
- permanent_block = ->(_) do @permanent_executed = true end
38
- bot.on :bbq, &permanent_block
39
-
40
- @ephemeral_executed = false
41
- ephemeral_block = ->(_) do @ephemeral_executed = true end
42
- bot.on :bbq, &ephemeral_block
43
- bot.off :bbq, &ephemeral_block
44
-
45
- bot.event data
46
-
47
- expect(@permanent_executed).to eq true
48
- expect(@ephemeral_executed).to eq false
49
- end
50
-
51
- it "unbounds all events by type" do
52
- @first_block = @second_block = @third_block = false
53
- bot.on(:bbq) do |_| @first_block = true end
54
- bot.on(:bbq) do |_| @second_block = true end
55
- bot.on(:bbq) do |_| @third_block = true end
56
- bot.off(:bbq)
57
-
58
- bot.event data
59
-
60
- expect(@first_block).to eq false
61
- expect(@second_block).to eq false
62
- expect(@third_block).to eq false
63
- end
64
- end
65
-
66
- describe "#match", :users do
67
- let(:data) do
68
- {
69
- "type" => "message",
70
- "text" => "bbq omg lol"
71
- }
72
- end
73
-
74
- it "binds a regex to events of `type => message`" do
75
- message = nil
76
- bot.match(/omg/i) do
77
- message = self.message
78
- end
79
- bot.event data
80
-
81
- expect(message).to be_a Boty::Slack::Message
82
- expect(message.text).to eq "bbq omg lol"
83
- expect(message.match[0]).to eq "omg"
84
- end
85
-
86
- it "binds various actions to events of type message" do
87
- iterations = []
88
- bot.match(/omg/i) do iterations << "first" end
89
- bot.match(/omg/i) do iterations << "second" end
90
- bot.event data
91
-
92
- expect(iterations).to eq %w(first second)
93
- end
94
-
95
- it "passes the matched strings as parameters to block action" do
96
- rspec = self
97
- bot.match(/(bbq) omg (lol)/i) do |bbq, lol|
98
- rspec.expect(bbq).to rspec.eq "bbq"
99
- rspec.expect(lol).to rspec.eq "lol"
100
- end
101
-
102
- bot.event data
103
- end
104
-
105
- it "evals the block in the context of the bot" do
106
- dsl = nil
107
- bot.match(/omg/) do dsl = self end
108
- bot.event data
109
-
110
- expect(dsl.bot).to eq bot
111
- end
12
+ context "handling message events" do
13
+ # on message, check if the event is binded, or something...
14
+ # call execute_actions with a message object
112
15
  end
113
16
 
114
17
  describe "#respond", :users do
@@ -0,0 +1,57 @@
1
+ module Boty
2
+ RSpec.describe Boty::Eventable do
3
+ class Client
4
+ include Eventable
5
+ end
6
+
7
+ subject(:eventable) { Client.new }
8
+
9
+ describe "#on" do
10
+ it "binds an event `'type'` to a block" do
11
+ expect { |b|
12
+ eventable.on :omg, &b
13
+ eventable.event "type" => "omg", "other_field" => "lol"
14
+ }.to yield_with_args "type" => "omg", "other_field" => "lol"
15
+ end
16
+ end
17
+
18
+ describe "#off" do
19
+ let(:data) do
20
+ {
21
+ "type" => "bbq",
22
+ "text" => "omg. wtf."
23
+ }
24
+ end
25
+
26
+ it "unbounds an event by type AND block, just if the block is the same" do
27
+ @permanent_executed = false
28
+ permanent_block = ->(_) do @permanent_executed = true end
29
+ eventable.on :bbq, &permanent_block
30
+
31
+ @ephemeral_executed = false
32
+ ephemeral_block = ->(_) do @ephemeral_executed = true end
33
+ eventable.on :bbq, &ephemeral_block
34
+ eventable.off :bbq, &ephemeral_block
35
+
36
+ eventable.event data
37
+
38
+ expect(@permanent_executed).to eq true
39
+ expect(@ephemeral_executed).to eq false
40
+ end
41
+
42
+ it "unbounds all events by type" do
43
+ @first_block = @second_block = @third_block = false
44
+ eventable.on(:bbq) do |_| @first_block = true end
45
+ eventable.on(:bbq) do |_| @second_block = true end
46
+ eventable.on(:bbq) do |_| @third_block = true end
47
+ eventable.off(:bbq)
48
+
49
+ eventable.event data
50
+
51
+ expect(@first_block).to eq false
52
+ expect(@second_block).to eq false
53
+ expect(@third_block).to eq false
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,119 @@
1
+ module Boty
2
+ RSpec.describe MatchHandler do
3
+ class Handler
4
+ include MatchHandler
5
+ end
6
+
7
+ subject(:handler) { Handler.new }
8
+
9
+ describe "#match", :users do
10
+ it "binds a regex to be executed by an Action object" do
11
+ action_block = ->(*) { "omg" }
12
+ expect(Boty::Action).to receive(:new)
13
+ .with(handler, /omg/i, nil, &action_block)
14
+
15
+ handler.match(/omg/i, &action_block)
16
+ end
17
+
18
+ it "allows to bind more than one regex with the same action" do
19
+ counter = 0
20
+ action_block = ->(*) { counter += 1 }
21
+ handler.match(/omg/i, /lol/i, /bbq/i, &action_block)
22
+
23
+ handler.execute_matches OpenStruct.new(text: "omg")
24
+ handler.execute_matches OpenStruct.new(text: "lol")
25
+ handler.execute_matches OpenStruct.new(text: "bbq")
26
+
27
+ expect(counter).to eq 3
28
+ end
29
+
30
+ it "passes the matched strings as parameters to the block" do
31
+ first_match = nil
32
+ second_match = nil
33
+ handler.match(/(bbq) omg (lol)/i) do |bbq, lol|
34
+ first_match = bbq
35
+ second_match = lol
36
+ end
37
+ handler.execute_matches OpenStruct.new(text: "bbq omg lol")
38
+
39
+ expect(first_match).to eq "bbq"
40
+ expect(second_match).to eq "lol"
41
+ end
42
+ end
43
+
44
+ describe "#respond" do
45
+ it "allows to bind more than one regex with the same action" do
46
+ counter = 0
47
+ action_block = ->(*) { counter += 1 }
48
+ handler.respond(/omg/i, /lol/i, /bbq/i, &action_block)
49
+
50
+ handler.execute_commands OpenStruct.new(text: "omg")
51
+ handler.execute_commands OpenStruct.new(text: "lol")
52
+ handler.execute_commands OpenStruct.new(text: "bbq")
53
+
54
+ expect(counter).to eq 3
55
+ end
56
+ end
57
+
58
+ describe "#execute_matches" do
59
+ it "executes handler binded to the regex" do
60
+ executed = false
61
+ action_block = -> { executed = true }
62
+ handler.match(/omg/i, &action_block)
63
+ handler.execute_matches OpenStruct.new(text: "omg")
64
+
65
+ expect(executed).to eq true
66
+ end
67
+
68
+ it "repasses the text message holder as a parameter" do
69
+ message = nil
70
+ holder = OpenStruct.new(text: "omg")
71
+ handler.match(/omg/i) do message = self.message end
72
+ handler.execute_matches holder
73
+
74
+ expect(message).to be holder
75
+ end
76
+
77
+ it "executes all handlers when more than one are found" do
78
+ first_executed = false
79
+ first_block = -> { first_executed = true }
80
+ handler.match(/omg/i, &first_block)
81
+
82
+ second_executed = false
83
+ second_block = -> { second_executed = true }
84
+ handler.match(/omg/i, &second_block)
85
+
86
+ handler.execute_matches OpenStruct.new(text: "omg")
87
+
88
+ expect(first_executed).to eq true
89
+ expect(second_executed).to eq true
90
+ end
91
+ end
92
+
93
+ describe "#execute_commands" do
94
+ it "executes the command binded to the regex" do
95
+ executed = false
96
+ action_block = -> { executed = true }
97
+ handler.respond(/lol/i, &action_block)
98
+ handler.execute_commands OpenStruct.new(text: "lol")
99
+
100
+ expect(executed).to eq true
101
+ end
102
+
103
+ it "executes all commands when more than one are found" do
104
+ first_executed = false
105
+ first_block = -> { first_executed = true }
106
+ handler.respond(/lol/i, &first_block)
107
+
108
+ second_executed = false
109
+ second_block = -> { second_executed = true }
110
+ handler.respond(/lol/i, &second_block)
111
+
112
+ handler.execute_commands OpenStruct.new(text: "lol")
113
+
114
+ expect(first_executed).to eq true
115
+ expect(second_executed).to eq true
116
+ end
117
+ end
118
+ end
119
+ end
@@ -10,9 +10,8 @@ RSpec.describe "I18n on default scripts", :session do
10
10
  know_how = self.know_how
11
11
  end
12
12
 
13
- actions = know_how.reduce({}) { |hsh, action|
13
+ actions = know_how.each_with_object({}) { |action, hsh|
14
14
  hsh[action.desc.command] = action.desc.description
15
- hsh
16
15
  }
17
16
 
18
17
  expect(actions["knows"]).to eq "List all the commands known by this bot."
@@ -26,9 +25,8 @@ RSpec.describe "I18n on default scripts", :session do
26
25
  know_how = self.know_how
27
26
  end
28
27
 
29
- actions = know_how.reduce({}) { |hsh, action|
28
+ actions = know_how.each_with_object({}) { |action, hsh|
30
29
  hsh[action.desc.command] = action.desc.description
31
- hsh
32
30
  }
33
31
 
34
32
  expect(actions["knows"]).to eq "Lista os comandos conhecidos por esse bot."
@@ -4,7 +4,7 @@ RSpec.describe "script/knows", :session do
4
4
 
5
5
  bot.know_how.each { |action|
6
6
  next if action.desc.command == "knows" ||
7
- action.desc.command == "pug me"
7
+ action.desc.command == "pug me"
8
8
  bot.no action.desc.command
9
9
  }
10
10
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: boty
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ricardo Valeriano
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-12-18 00:00:00.000000000 Z
11
+ date: 2015-12-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: eventmachine
@@ -164,6 +164,20 @@ dependencies:
164
164
  - - ">="
165
165
  - !ruby/object:Gem::Version
166
166
  version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: yard
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
167
181
  description: |-
168
182
  Boty is intendted to be a framework for construction of
169
183
  automated Slack Bots for your needs.
@@ -204,6 +218,7 @@ files:
204
218
  - lib/boty/http.rb
205
219
  - lib/boty/locale.rb
206
220
  - lib/boty/logger.rb
221
+ - lib/boty/match_handler.rb
207
222
  - lib/boty/rspec.rb
208
223
  - lib/boty/script_loader.rb
209
224
  - lib/boty/session.rb
@@ -224,11 +239,14 @@ files:
224
239
  - script/knows.rb
225
240
  - script/pug.rb
226
241
  - script/remember.rb
242
+ - spec/boty/action_spec.rb
227
243
  - spec/boty/bot_spec.rb
228
244
  - spec/boty/cli_spec.rb
229
245
  - spec/boty/dsl_spec.rb
246
+ - spec/boty/eventable_spec.rb
230
247
  - spec/boty/http_spec.rb
231
248
  - spec/boty/logger_spec.rb
249
+ - spec/boty/match_handler_spec.rb
232
250
  - spec/boty/rspec_spec.rb
233
251
  - spec/boty/script_loader_spec.rb
234
252
  - spec/boty/session_spec.rb
@@ -293,3 +311,4 @@ signing_key:
293
311
  specification_version: 4
294
312
  summary: Boty is a pretty bot specially tailored for Slack.
295
313
  test_files: []
314
+ has_rdoc: