Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added GrowlAgent for sending Growl notifications over GNTP #172

Merged
merged 4 commits into from
Mar 6, 2014
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ gem "rufus-scheduler", :require => false
gem 'json', '>= 1.7.7'
gem 'jsonpath'
gem 'twilio-ruby'
gem 'ruby-growl'

gem 'delayed_job'
gem 'delayed_job_active_record'#, "~> 0.3.3" # newer was giving a strange MySQL error
Expand Down
62 changes: 62 additions & 0 deletions app/models/agents/growl_agent.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
require 'ruby-growl'

module Agents
class GrowlAgent < Agent
attr_reader :growler

cannot_be_scheduled!
cannot_create_events!

description <<-MD
The GrowlAgent sends any events it receives to a Growl GNTP server immediately.

It is assumed that events have a `message` or `text` key, which will hold the body of the growl notification, and a `subject` key, which will have the headline of the Growl notification. You can use Event Formatting Agent if your event does not provide these keys.

Set `expected_receive_period_in_days` to the maximum amount of time that you'd expect to pass between Events being received by this Agent.
MD

def default_options
{
'growlserver' => 'localhost',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you be up for renaming these to growl_server, growl_password, growl_app_name, etc? That's more our style in this app.

'growlpassword' => '',
'growlappname' => 'HuginnGrowl',
'growlnotificationname' => 'Notification',
'expected_receive_period_in_days' => "2"
}
end

def working?
last_receive_at && last_receive_at > options['expected_receive_period_in_days'].to_i.days.ago && !recent_error_logs?
end

def validate_options
unless options['growlserver'].present? && options['expected_receive_period_in_days'].present?
errors.add(:base, "growlserver and expected_receive_period_in_days are required fields")
end
end

def register_growl
@growler = Growl.new options['growlserver'], options['growlappname'], "GNTP"
@growler.password = options['growlpassword']
@growler.add_notification options['growlnotificationname']
end

def notify_growl(subject, message)
@growler.notify(options['growlnotificationname'],subject,message)
end

def receive(incoming_events)
register_growl
incoming_events.each do |event|
message = (event.payload['message'] || event.payload['text']).to_s
subject = event.payload['subject'].to_s
if message.present? && subject.present?
log "Sending Growl notification '#{subject}': '#{message}' to #{options['growlserver']} with event #{event.id}"
notify_growl(subject,message)
else
log "Event #{event.id} not sent, message and subject expected"
end
end
end
end
end
116 changes: 116 additions & 0 deletions spec/models/agents/growl_agent_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
require 'spec_helper'

describe Agents::GrowlAgent do
before do
@checker = Agents::GrowlAgent.new(:name => 'a growl agent',
:options => { :growlserver => 'localhost',
:growlappname => 'HuginnGrowlApp',
:growlpassword => 'mypassword',
:growlnotificationname => 'Notification',
:expected_receive_period_in_days => '1' })
@checker.user = users(:bob)
@checker.save!

stub.any_instance_of(Growl).notify

@event = Event.new
@event.agent = agents(:bob_weather_agent)
@event.payload = { :subject => 'Weather Alert!', :message => 'Looks like its going to rain' }
@event.save!
end

describe "#working?" do
it "checks if events have been received within the expected receive period" do
@checker.should_not be_working # No events received
Agents::GrowlAgent.async_receive @checker.id, [@event.id]
@checker.reload.should be_working # Just received events
two_days_from_now = 2.days.from_now
stub(Time).now { two_days_from_now }
@checker.reload.should_not be_working # More time has passed than the expected receive period without any new events
end
end

describe "validation" do
before do
@checker.should be_valid
end

it "should validate presence of of growlserver" do
@checker.options[:growlserver] = ""
@checker.should_not be_valid
end

it "should validate presence of expected_receive_period_in_days" do
@checker.options[:expected_receive_period_in_days] = ""
@checker.should_not be_valid
end
end

describe "register_growl" do
it "should set the password for the Growl connection from the agent options" do
@checker.register_growl
@checker.growler.password.should eql(@checker.options[:growlpassword])
end

it "should add a notification to the Growl connection" do
any_instance_of(Growl) do |obj|
mock(obj).add_notification(@checker.options[:growlnotificationname])
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing here as I suggested below: make sure the block gets called.


@checker.register_growl
end
end

describe "notify_growl" do
before do
@checker.register_growl
end

it "should call Growl.notify with the correct notification name, subject, and message" do
message = "message"
subject = "subject"
any_instance_of(Growl) do |obj|
mock(obj).notify(@checker.options[:growlnotificationname],subject,message)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend also ensuring that this block gets called

called = false
any_instance_of(Growl) do |obj|
  called = true
  mock(obj).notify(@checker.options[:growlnotificationname], subject, message)
end
@checker.notify_growl(subject, message)
called.should be_true

end
@checker.notify_growl(subject,message)
end
end

describe "receive" do
def generate_events_array
events = []
(2..rand(7)).each do
events << @event
end
return events
end

it "should call register_growl once regardless of number of events received" do
mock.proxy(@checker).register_growl.once
@checker.receive(generate_events_array)
end

it "should call notify_growl one time for each event received" do
events = generate_events_array
events.each do |event|
mock.proxy(@checker).notify_growl(event.payload['subject'],event.payload['message'])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style: We always put spaces after commas.

end
@checker.receive(events)
end

it "should not call notify_growl if message or subject are missing" do
event_without_a_subject = Event.new
event_without_a_subject.agent = agents(:bob_weather_agent)
event_without_a_subject.payload = { :message => 'Looks like its going to rain' }
event_without_a_subject.save!

event_without_a_message = Event.new
event_without_a_message.agent = agents(:bob_weather_agent)
event_without_a_message.payload = { :subject => 'Weather Alert YO!' }
event_without_a_message.save!

mock.proxy(@checker).notify_growl.never
@checker.receive([event_without_a_subject,event_without_a_message])
end
end
end