Skip to content

Commit

Permalink
add cache helper module and ref reactions
Browse files Browse the repository at this point in the history
  • Loading branch information
Bartosz Kopiński committed Sep 20, 2013
1 parent c3ea2fb commit 1280161
Show file tree
Hide file tree
Showing 10 changed files with 127 additions and 55 deletions.
1 change: 1 addition & 0 deletions lib/hipbot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
require 'xmpp4r'
require 'xmpp4r/muc'

require 'hipbot/cache'
require 'hipbot/patches/hipchat_client'
require 'hipbot/patches/encoding'

Expand Down
23 changes: 23 additions & 0 deletions lib/hipbot/cache.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module Hipbot
module Cache
extend ActiveSupport::Concern

included do
extend ClassMethods
end

def _cache
@_cache ||= {}
end

module ClassMethods
def attr_cache *attributes, &block
attributes.each do |attr_name|
define_method(attr_name) do
_cache[attr_name] ||= block_given? ? instance_eval(&block) : []
end
end
end
end
end
end
6 changes: 4 additions & 2 deletions lib/hipbot/match.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module Hipbot
class Match < Struct.new(:reaction, :message)
include Cache

def matches?
matches_scope? && matches_place? && matches_regexp? && matches_sender? && matches_condition?
end
Expand All @@ -22,8 +24,8 @@ def matches_regexp?
reaction.anything? || regexp_match.present? || reaction.regexps.empty?
end

def regexp_match
@regexp_match ||= reaction.regexps.inject(nil) do |result, regexp|
attr_cache :regexp_match do
reaction.regexps.inject(nil) do |result, regexp|
break result if result
message_text.match(regexp)
end
Expand Down
39 changes: 13 additions & 26 deletions lib/hipbot/reactable.rb
Original file line number Diff line number Diff line change
@@ -1,51 +1,38 @@
module Hipbot
module Reactable
include Cache

attr_cache :reactions, :default_reactions, :options_stack
attr_cache :reaction_factory do
ReactionFactory.new(self)
end

def on *params, &block
scope *params do
reactions << to_reaction(params, block)
reactions << to_reaction(block)
end
end

def default *params, &block
scope *params do
default_reactions << to_reaction(params, block)
default_reactions << to_reaction(block)
end
end

def scope *params, &block
options_stack << reaction_factory.to_reaction_options(params)
options_stack << reaction_factory.get_reaction_options(params)
yield
options_stack.pop
end

def desc(text)
def desc text
reaction_factory.description(text)
end

def reactions
@reactions ||= []
end

def default_reactions
@default_reactions ||= []
end

protected

def to_reaction params, block
reaction_factory.build(params, block, scope_options)
end

def scope_options
options_stack.inject { |all, h| all.merge(h) }
end

def options_stack
@options_stack ||= []
end

def reaction_factory
@reaction_factory ||= ReactionFactory.new(self)
def to_reaction block
reaction_factory.build(options_stack, block)
end
end
end
10 changes: 6 additions & 4 deletions lib/hipbot/reaction.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module Hipbot
class Reaction < Struct.new(:plugin, :options, :block)
include Cache

def any_room?
options[:room] == true
end
Expand Down Expand Up @@ -56,12 +58,12 @@ def regexps
options[:regexps]
end

def rooms
replace_symbols options[:room], Hipbot.rooms
attr_cache :rooms do
replace_symbols(options[:room], Hipbot.rooms)
end

def users
replace_symbols options[:from], Hipbot.teams
attr_cache :users do
replace_symbols(options[:from], Hipbot.teams)
end

protected
Expand Down
35 changes: 24 additions & 11 deletions lib/hipbot/reaction_factory.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
module Hipbot
class ReactionFactory
attr_reader :reactable, :current_description
class ReactionFactory < Struct.new(:reactable)
attr_reader :current_description
private :reactable, :current_description

def initialize(reactable)
@reactable = reactable
end

def build(restrictions, block, scope_restrictions = {})
options = scope_restrictions.merge(to_reaction_options(restrictions))
def build(options_stack, block)
options = get_options(options_stack)
block ||= options.delete(:block)
@current_description = nil
Reaction.new(reactable, options, block)
end
Expand All @@ -17,9 +14,25 @@ def description(text)
@current_description = text
end

def to_reaction_options(array)
options = array.last.kind_of?(Hash) ? array.pop : {}
options.merge({ regexps: array, desc: current_description })
def get_reaction_options(params)
options = params.extract_options!
get_reaction_method_proc(params) do |block|
options[:block] = block
end
options[:regexps] = params if params.any?
options.merge(desc: current_description)
end

protected

def get_reaction_method_proc(params)
return unless params.last.kind_of?(Symbol)
method_name = params.pop
yield ->(*attributes){ plugin.send(method_name, *attributes) }
end

def get_options(stack)
stack.inject{ |all, h| all.deep_merge(h) } || {}
end
end
end
9 changes: 5 additions & 4 deletions lib/hipbot/room.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
module Hipbot
class Room
include Cache
include Reactable

attr_cache :users

def set_topic topic
Hipbot.set_topic(self, topic)
end
Expand All @@ -15,9 +20,5 @@ def invite users
def kick users
Hipbot.kick_from_room(self, users)
end

def users
@users ||= []
end
end
end
22 changes: 22 additions & 0 deletions spec/integration/hipbot_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,26 @@
subject.react(sender, room, '@robbot respond cool')
end
end

describe 'method reaction' do
it 'should reply to a method reaction defined in plugin' do
subject.expects(:send_to_room).with(room, 'parameter: empty')
subject.react(sender, room, '@robbot method reaction')
end

it 'should reply to a method reaction defined in plugin with parameters' do
subject.expects(:send_to_room).with(room, 'parameter: method param')
subject.react(sender, room, '@robbot method reaction method param')
end

it 'should reply to a scope method reaction defined in plugin' do
subject.expects(:send_to_room).with(room, 'scope method reaction')
subject.react(sender, room, '@robbot scope method reaction')
end

it 'should reply to a scope regexp with method reaction defined in plugin', focus: true do
subject.expects(:send_to_room).with(room, 'parameter: empty')
subject.react(sender, room, '@robbot scope regexp')
end
end
end
20 changes: 20 additions & 0 deletions spec/integration/my_hipbot.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,26 @@ class AwesomePlugin
on /respond awesome/ do
reply('awesome responded')
end

def method_reaction param = 'empty'
reply("parameter: #{param}")
end

on /^method reaction$/, :method_reaction
on /^method reaction (.*)$/, :method_reaction
on /^no block reaction$/

def scope_method_reaction
reply('scope method reaction')
end

scope :scope_method_reaction do
on /^scope method reaction$/
end

scope /^scope regexp$/ do
on :method_reaction
end
end

class CoolPlugin
Expand Down
17 changes: 9 additions & 8 deletions spec/unit/reaction_factory_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,21 @@
describe Hipbot::ReactionFactory do
subject { described_class.new(stub) }

let(:restrictions) { [] }
let(:params) { [] }
let(:block) { stub }

let(:reaction) { subject.build(restrictions, block) }
let(:options_stack) { [subject.get_reaction_options(params)] }
let(:reaction) { subject.build(options_stack, block) }

describe "taking a regexp" do
let(:restrictions) { [/.*/] }
let(:params) { [/.*/] }

it "builds a reaction with regexp" do
expect(reaction.regexps).to eq([/.*/])
end

describe "with additional options" do
let(:restrictions) { [/.*/, { from: 'wat' }] }
let(:params) { [/.*/, { from: 'wat' }] }

it "builds a reaction with proper options" do
expect(reaction.options).to eq(from: 'wat', regexps: [/.*/], desc: nil)
Expand All @@ -28,7 +29,7 @@
end

describe "taking multiple regexps and options" do
let(:restrictions) { [/.*/, /wat/, { from: 'wat' }] }
let(:params) { [/.*/, /wat/, { from: 'wat' }] }

it "builds a reaction with proper options" do
expect(reaction.options).to eq(from: 'wat', regexps: [/.*/, /wat/], desc: nil)
Expand All @@ -45,12 +46,12 @@
end

it "resets description after first built reaction" do
subject.build(restrictions, block)
subject.build(params, block)
expect(reaction.desc).to be_nil
end
end

it "applies optional scope restrictions as default" do
expect(subject.build(restrictions, block, { from: 'dave' }).options[:from]).to eq('dave')
it "applies optional scope params as default" do
expect(subject.build([{ from: 'dave' }], block).options[:from]).to eq('dave')
end
end

0 comments on commit 1280161

Please sign in to comment.