boty 0.1.2 → 0.2.0

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.
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: