growlnotifier 1.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.
- data/LICENSE +2 -0
- data/Manifest.txt +11 -0
- data/README.txt +69 -0
- data/Rakefile +43 -0
- data/lib/growl.rb +161 -0
- data/lib/growl_helpers.rb +25 -0
- data/samples/growl_block_sample.rb +29 -0
- data/samples/growl_delegate_sample.rb +36 -0
- data/test/growl_helpers_test.rb +41 -0
- data/test/growl_test.rb +254 -0
- data/test/test_helper.rb +23 -0
- metadata +77 -0
data/LICENSE
ADDED
data/Manifest.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
= Growl Notifier
|
2
|
+
|
3
|
+
== Overview
|
4
|
+
|
5
|
+
Growl::Notifier is a class that allows your application to post notifications to the Growl daemon.
|
6
|
+
It can also receive clicked and timeout notifications as well as take blocks for clicked callback handlers.
|
7
|
+
|
8
|
+
This is an extraction and cleanup of the Growl code from LimeChat (http://github.com/psychs/limechat/tree/master) and later on extended for WebApp (http://github.com/alloy/webapp-app/tree/master).
|
9
|
+
|
10
|
+
== Requirements
|
11
|
+
|
12
|
+
* Mac OS X 10.4 or 10.5
|
13
|
+
* Ruby 1.8 (http://ruby-lang.org/)
|
14
|
+
* RubyCocoa (http://rubycocoa.sourceforge.net/)
|
15
|
+
|
16
|
+
== How to use Growl Notifier
|
17
|
+
|
18
|
+
A simple example:
|
19
|
+
|
20
|
+
require 'growl'
|
21
|
+
|
22
|
+
g = Growl::Notifier.sharedInstance
|
23
|
+
g.register('test_app', ['message_type'])
|
24
|
+
g.notify('message_type', 'title', 'desc')
|
25
|
+
|
26
|
+
How to receive clicked and timeout notifications in your application:
|
27
|
+
|
28
|
+
require 'growl'
|
29
|
+
|
30
|
+
class GrowlController < OSX::NSObject
|
31
|
+
def init
|
32
|
+
if super_init
|
33
|
+
@g = Growl::Notifier.sharedInstance
|
34
|
+
@g.delegate = self
|
35
|
+
@g.register('test_app', ['message_type'])
|
36
|
+
@g.notify('message_type', 'title', 'desc')
|
37
|
+
self
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def growlNotifierClicked_context(sender, context)
|
42
|
+
puts context
|
43
|
+
end
|
44
|
+
|
45
|
+
def growlNotifierTimedOut_context(sender, context)
|
46
|
+
puts context
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
Include the Growl module into your class to get access to a few convenience methods:
|
51
|
+
|
52
|
+
require 'growl'
|
53
|
+
|
54
|
+
class GrowlController < OSX::NSObject
|
55
|
+
include Growl
|
56
|
+
Growl::Notifier.sharedInstance.register('test_app', ['message_type'])
|
57
|
+
|
58
|
+
def init
|
59
|
+
if super_init
|
60
|
+
growl('message_type', 'title', 'desc')
|
61
|
+
self
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
== License
|
67
|
+
|
68
|
+
Copyright (c) 2007-2008 Satoshi Nakagawa <psychs@limechat.net>, Eloy Duran <e.duran@superalloy.nl>
|
69
|
+
You can redistribute it and/or modify it under the same terms as Ruby.
|
data/Rakefile
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/testtask'
|
3
|
+
|
4
|
+
require 'lib/growl.rb'
|
5
|
+
|
6
|
+
# Install the Hoe gem to be able to use the deploy tasks.
|
7
|
+
begin
|
8
|
+
require 'hoe'
|
9
|
+
|
10
|
+
class Hoe
|
11
|
+
def extra_deps
|
12
|
+
@extra_deps.reject do |x|
|
13
|
+
Array(x).first == 'hoe'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
Hoe.new('growlnotifier', Growl::Notifier::VERSION) do |p|
|
19
|
+
p.author = ["Satoshi Nakagawa", "Eloy Duran"]
|
20
|
+
p.description = "A ruby library which allows you to send Growl notifications."
|
21
|
+
p.email = ["psychs@limechat.net", "e.duran@superalloy.nl"]
|
22
|
+
p.summary = "Growl::Notifier is a OSX RubyCocoa class that allows your application to post notifications to the Growl daemon."
|
23
|
+
p.url = "http://growlnotifier.rubyforge.org/"
|
24
|
+
p.clean_globs = ['coverage'] # Remove this directory on "rake clean"
|
25
|
+
p.remote_rdoc_dir = '' # Release to root
|
26
|
+
end
|
27
|
+
rescue LoadError
|
28
|
+
end
|
29
|
+
|
30
|
+
task :default => :test
|
31
|
+
|
32
|
+
Rake::TestTask.new do |t|
|
33
|
+
t.libs << "test"
|
34
|
+
t.test_files = FileList['test/*_test.rb']
|
35
|
+
t.options = '-rs'
|
36
|
+
end
|
37
|
+
|
38
|
+
desc "Run code-coverage analysis using rcov"
|
39
|
+
task :coverage do
|
40
|
+
rm_rf "coverage"
|
41
|
+
sh "rcov test/*_test.rb --exclude=mocha,RubyCocoa.framework,osx,rcov"
|
42
|
+
sh "open coverage/index.html"
|
43
|
+
end
|
data/lib/growl.rb
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'osx/cocoa'
|
2
|
+
|
3
|
+
module Growl
|
4
|
+
class Notifier < OSX::NSObject
|
5
|
+
VERSION = '1.0'
|
6
|
+
|
7
|
+
GROWL_IS_READY = "Lend Me Some Sugar; I Am Your Neighbor!"
|
8
|
+
GROWL_NOTIFICATION_CLICKED = "GrowlClicked!"
|
9
|
+
GROWL_NOTIFICATION_TIMED_OUT = "GrowlTimedOut!"
|
10
|
+
GROWL_KEY_CLICKED_CONTEXT = "ClickedContext"
|
11
|
+
|
12
|
+
PRIORITIES = {
|
13
|
+
:emergency => 2,
|
14
|
+
:high => 1,
|
15
|
+
:normal => 0,
|
16
|
+
:moderate => -1,
|
17
|
+
:very_low => -2,
|
18
|
+
}
|
19
|
+
|
20
|
+
class << self
|
21
|
+
# Returns the singleton instance of Growl::Notifier with which you register and send your Growl notifications.
|
22
|
+
def sharedInstance
|
23
|
+
@sharedInstance ||= alloc.init
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_reader :application_name, :application_icon, :notifications, :default_notifications
|
28
|
+
attr_accessor :delegate
|
29
|
+
|
30
|
+
# Registers the applications metadata and the notifications, that your application might send, to Growl.
|
31
|
+
# The +default_notifications+ are notifications that will be enabled by default, the regular +notifications+ are
|
32
|
+
# optional and should be enabled by the user in the Growl system preferences.
|
33
|
+
#
|
34
|
+
# Register the applications name and the notifications that will be used.
|
35
|
+
# * +default_notifications+ defaults to the regular +notifications+.
|
36
|
+
# * +application_icon+ defaults to OSX::NSApplication.sharedApplication.applicationIconImage.
|
37
|
+
#
|
38
|
+
# Growl::Notifier.sharedInstance.register 'FoodApp', ['YourHamburgerIsReady', 'OhSomeoneElseAteIt']
|
39
|
+
#
|
40
|
+
# Register the applications name, the notifications plus the default notifications that will be used and the icon that's to be used in the Growl notifications.
|
41
|
+
#
|
42
|
+
# Growl::Notifier.sharedInstance.register 'FoodApp', ['YourHamburgerIsReady', 'OhSomeoneElseAteIt'], ['DefaultNotification], OSX::NSImage.imageNamed('GreasyHamburger')
|
43
|
+
def register(application_name, notifications, default_notifications = nil, application_icon = nil)
|
44
|
+
@application_name, @application_icon = application_name, (application_icon || OSX::NSApplication.sharedApplication.applicationIconImage)
|
45
|
+
@notifications, @default_notifications = notifications, (default_notifications || notifications)
|
46
|
+
@callbacks = {}
|
47
|
+
send_registration!
|
48
|
+
end
|
49
|
+
|
50
|
+
# Sends a Growl notification.
|
51
|
+
#
|
52
|
+
# * +notification_name+ : the name of one of the notifcations that your apllication registered with Growl. See register for more info.
|
53
|
+
# * +title+ : the title that should be used in the Growl notification.
|
54
|
+
# * +description+ : the body of the Grow notification.
|
55
|
+
# * +options+ : specifies a few optional options:
|
56
|
+
# * <tt>:sticky</tt> : indicates if the Grow notification should "stick" to the screen. Defaults to +false+.
|
57
|
+
# * <tt>:priority</tt> : sets the priority level of the Growl notification. Defaults to 0.
|
58
|
+
# * <tt>:icon</tt> : specifies the icon to be used in the Growl notification. Defaults to the registered +application_icon+, see register for more info.
|
59
|
+
#
|
60
|
+
# Simple example:
|
61
|
+
#
|
62
|
+
# name = 'YourHamburgerIsReady'
|
63
|
+
# title = 'Your hamburger is ready for consumption!'
|
64
|
+
# description = 'Please pick it up at isle 4.'
|
65
|
+
#
|
66
|
+
# Growl::Notifier.sharedInstance.notify(name, title, description)
|
67
|
+
#
|
68
|
+
# Example with optional options:
|
69
|
+
#
|
70
|
+
# Growl::Notifier.sharedInstance.notify(name, title, description, :sticky => true, :priority => 1, :icon => OSX::NSImage.imageNamed('SuperBigHamburger'))
|
71
|
+
#
|
72
|
+
# When you pass notify a block, that block will be used as the callback handler if the Growl notification was clicked. Eg:
|
73
|
+
#
|
74
|
+
# Growl::Notifier.sharedInstance.notify(name, title, description, :sticky => true) do
|
75
|
+
# user_clicked_notification_so_do_something!
|
76
|
+
# end
|
77
|
+
def notify(notification_name, title, description, options = {}, &callback)
|
78
|
+
dict = {
|
79
|
+
:ApplicationName => @application_name,
|
80
|
+
:ApplicationPID => pid,
|
81
|
+
:NotificationName => notification_name,
|
82
|
+
:NotificationTitle => title,
|
83
|
+
:NotificationDescription => description,
|
84
|
+
:NotificationPriority => PRIORITIES[options[:priority]] || options[:priority] || 0
|
85
|
+
}
|
86
|
+
dict[:NotificationIcon] = options[:icon].TIFFRepresentation if options[:icon]
|
87
|
+
dict[:NotificationSticky] = 1 if options[:sticky]
|
88
|
+
|
89
|
+
context = {}
|
90
|
+
context[:user_click_context] = options[:click_context] if options[:click_context]
|
91
|
+
if block_given?
|
92
|
+
@callbacks[callback.object_id] = callback
|
93
|
+
context[:callback_object_id] = callback.object_id.to_s
|
94
|
+
end
|
95
|
+
dict[:NotificationClickContext] = context unless context.empty?
|
96
|
+
|
97
|
+
notification_center.postNotificationName_object_userInfo_deliverImmediately(:GrowlNotification, nil, dict, true)
|
98
|
+
end
|
99
|
+
|
100
|
+
def onReady(notification)
|
101
|
+
send_registration!
|
102
|
+
end
|
103
|
+
|
104
|
+
def onClicked(notification)
|
105
|
+
user_context = nil
|
106
|
+
if context = notification.userInfo[GROWL_KEY_CLICKED_CONTEXT]
|
107
|
+
user_context = context[:user_click_context]
|
108
|
+
if callback_object_id = context[:callback_object_id]
|
109
|
+
@callbacks.delete(callback_object_id.to_i).call
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
@delegate.growlNotifierClicked_context(self, user_context) if @delegate && @delegate.respond_to?(:growlNotifierClicked_context)
|
114
|
+
end
|
115
|
+
|
116
|
+
def onTimeout(notification)
|
117
|
+
user_context = nil
|
118
|
+
if context = notification.userInfo[GROWL_KEY_CLICKED_CONTEXT]
|
119
|
+
@callbacks.delete(context[:callback_object_id].to_i) if context[:callback_object_id]
|
120
|
+
user_context = context[:user_click_context]
|
121
|
+
end
|
122
|
+
|
123
|
+
@delegate.growlNotifierTimedOut_context(self, user_context) if @delegate && @delegate.respond_to?(:growlNotifierTimedOut_context)
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
def pid
|
129
|
+
OSX::NSProcessInfo.processInfo.processIdentifier.to_i
|
130
|
+
end
|
131
|
+
|
132
|
+
def notification_center
|
133
|
+
OSX::NSDistributedNotificationCenter.defaultCenter
|
134
|
+
end
|
135
|
+
|
136
|
+
def send_registration!
|
137
|
+
add_observer 'onReady:', GROWL_IS_READY, false
|
138
|
+
add_observer 'onClicked:', GROWL_NOTIFICATION_CLICKED, true
|
139
|
+
add_observer 'onTimeout:', GROWL_NOTIFICATION_TIMED_OUT, true
|
140
|
+
|
141
|
+
dict = {
|
142
|
+
:ApplicationName => @application_name,
|
143
|
+
:ApplicationIcon => application_icon.TIFFRepresentation,
|
144
|
+
:AllNotifications => @notifications,
|
145
|
+
:DefaultNotifications => @default_notifications
|
146
|
+
}
|
147
|
+
|
148
|
+
notification_center.objc_send(
|
149
|
+
:postNotificationName, :GrowlApplicationRegistrationNotification,
|
150
|
+
:object, nil,
|
151
|
+
:userInfo, dict,
|
152
|
+
:deliverImmediately, true
|
153
|
+
)
|
154
|
+
end
|
155
|
+
|
156
|
+
def add_observer(selector, name, prepend_name_and_pid)
|
157
|
+
name = "#{@application_name}-#{pid}-#{name}" if prepend_name_and_pid
|
158
|
+
notification_center.addObserver_selector_name_object self, selector, name, nil
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.expand_path('../growl', __FILE__)
|
2
|
+
|
3
|
+
# Defines a few convenience methods that you can use in your class if you include the Growl module.
|
4
|
+
# Eg:
|
5
|
+
#
|
6
|
+
# class FoodReporter < OSX::NSObject
|
7
|
+
# include Growl
|
8
|
+
#
|
9
|
+
# def hamburger_time!
|
10
|
+
# growl 'YourHamburgerIsReady', 'Your hamburger is ready for consumption!', 'Please pick it up at isle 4.', :priority => 1 do
|
11
|
+
# throw_it_away_before_user_reaches_counter!
|
12
|
+
# end
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
module Growl
|
16
|
+
# Sends a Growl notification. See Growl::Notifier#notify for more info.
|
17
|
+
def growl(name, title, description, options = {}, &callback)
|
18
|
+
Growl::Notifier.sharedInstance.notify name, title, description, options, &callback
|
19
|
+
end
|
20
|
+
|
21
|
+
# Sends a sticky Growl notification. See Growl::Notifier#notify for more info.
|
22
|
+
def sticky_growl(name, title, description, options = {}, &callback)
|
23
|
+
growl name, title, description, options.merge!(:sticky => true), &callback
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'osx/cocoa'
|
2
|
+
require File.expand_path('../../lib/growl_helpers', __FILE__)
|
3
|
+
|
4
|
+
class GrowlController < OSX::NSObject
|
5
|
+
# Makes the #growl and #sticky_growl shortcut methods available.
|
6
|
+
include Growl
|
7
|
+
|
8
|
+
HELLO_TYPE = 'Hello message received'
|
9
|
+
Growl::Notifier.sharedInstance.register('GrowlSample', [HELLO_TYPE])
|
10
|
+
|
11
|
+
def init
|
12
|
+
if super_init
|
13
|
+
growl HELLO_TYPE, 'Not sticky', 'Hello world' do
|
14
|
+
puts "Clicked not sticky: #{ Time.now }"
|
15
|
+
OSX::NSApp.terminate(nil)
|
16
|
+
end
|
17
|
+
|
18
|
+
sticky_growl HELLO_TYPE, 'Sticky', 'Hello world' do
|
19
|
+
puts "Clicked sticky: #{ Time.now }"
|
20
|
+
OSX::NSApp.terminate(nil)
|
21
|
+
end
|
22
|
+
|
23
|
+
self
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
g = GrowlController.alloc.init
|
29
|
+
OSX::NSApp.run
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'osx/cocoa'
|
2
|
+
require File.expand_path('../../lib/growl', __FILE__)
|
3
|
+
|
4
|
+
class GrowlController < OSX::NSObject
|
5
|
+
HELLO_TYPE = 'Hello message received'
|
6
|
+
|
7
|
+
def init
|
8
|
+
if super_init
|
9
|
+
@g = Growl::Notifier.sharedInstance
|
10
|
+
@g.delegate = self
|
11
|
+
@g.register('GrowlSample', [HELLO_TYPE])
|
12
|
+
@g.notify(HELLO_TYPE, 'Sticky', 'Hello world', :sticky => true, :click_context => Time.now.to_s )
|
13
|
+
@g.notify(HELLO_TYPE, 'Timed out', 'Hello world', :click_context => Time.now.to_s )
|
14
|
+
@count = 2
|
15
|
+
self
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def growlNotifierClicked_context(sender, context)
|
20
|
+
puts "Clicked: #{context}"
|
21
|
+
checkCount
|
22
|
+
end
|
23
|
+
|
24
|
+
def growlNotifierTimedOut_context(sender, context)
|
25
|
+
puts "Timed out: #{context}"
|
26
|
+
checkCount
|
27
|
+
end
|
28
|
+
|
29
|
+
def checkCount
|
30
|
+
@count -= 1
|
31
|
+
OSX::NSApp.terminate(nil) if @count == 0
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
g = GrowlController.alloc.init
|
36
|
+
OSX::NSApp.run
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
require File.expand_path('../../lib/growl_helpers', __FILE__)
|
3
|
+
|
4
|
+
class Foo < OSX::NSObject
|
5
|
+
include Growl
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "Growl, when mixed into a class" do
|
9
|
+
before do
|
10
|
+
@name = 'YourHamburgerIsReady'
|
11
|
+
@title = 'Your hamburger is ready for consumption!'
|
12
|
+
@description = 'Please pick it up at isle 4.'
|
13
|
+
@options = { :sticky => true, :priority => 1 }
|
14
|
+
|
15
|
+
@object = Foo.alloc.init
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should define a #growl instance method" do
|
19
|
+
@object.should.respond_to :growl
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should call Growl::Notifier.sharedInstance.notify with the arguments passed to #growl" do
|
23
|
+
should_call_sticky_notify_with_priority_1
|
24
|
+
@object.growl @name, @title, @description, :sticky => true, :priority => 1
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should define a #sticky_growl instance method which is a shortcut method to send a sticky Growl notification" do
|
28
|
+
@object.should.respond_to :sticky_growl
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should call Growl::Notifier.sharedInstance.notify with the arguments passed to #sticky_growl and the sticky option set to true" do
|
32
|
+
should_call_sticky_notify_with_priority_1
|
33
|
+
@object.sticky_growl @name, @title, @description, :priority => 1
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def should_call_sticky_notify_with_priority_1
|
39
|
+
Growl::Notifier.sharedInstance.expects(:notify).with(@name, @title, @description, @options)
|
40
|
+
end
|
41
|
+
end
|
data/test/growl_test.rb
ADDED
@@ -0,0 +1,254 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
require File.expand_path('../../lib/growl', __FILE__)
|
3
|
+
|
4
|
+
describe 'Growl::Notifier' do
|
5
|
+
include GrowlNotifierSpecHelper
|
6
|
+
|
7
|
+
before do
|
8
|
+
set_variables!
|
9
|
+
@instance.stubs(:send_registration!)
|
10
|
+
@instance.register @name, @notifications
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should be a singleton class" do
|
14
|
+
@instance.should.be.instance_of Growl::Notifier
|
15
|
+
@instance.should.be Growl::Notifier.sharedInstance
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe 'Growl::Notifier#register' do
|
20
|
+
include GrowlNotifierSpecHelper
|
21
|
+
|
22
|
+
before do
|
23
|
+
set_variables!
|
24
|
+
@instance.stubs(:send_registration!)
|
25
|
+
@instance.register @name, @notifications
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should take the name of the application" do
|
29
|
+
@instance.application_name.should == @name
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should take an array of notifications that should be registered" do
|
33
|
+
@instance.notifications.should == @notifications
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should set the default notifications to the same as the notifications array if none is specified" do
|
37
|
+
@instance.default_notifications.should == @notifications
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should take an array of notifications that should be registered as the default notifications" do
|
41
|
+
@instance.register @name, @notifications, @default_notifications
|
42
|
+
@instance.default_notifications.should == @default_notifications
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should by default use the NSAppication's sharedApplication icon if none was specified" do
|
46
|
+
@instance.application_icon.should.be OSX::NSApplication.sharedApplication.applicationIconImage
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should take an optional application icon" do
|
50
|
+
@instance.register @name, @notifications, nil, @icon
|
51
|
+
@instance.application_icon.should.be @icon
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should register the configuration" do
|
55
|
+
@instance.expects(:send_registration!)
|
56
|
+
@instance.register @name, @notifications
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe 'Growl::Notifier#send_registration!' do
|
61
|
+
include GrowlNotifierSpecHelper
|
62
|
+
|
63
|
+
before do
|
64
|
+
set_variables!
|
65
|
+
@tiff = mock('TIFFRepresentation')
|
66
|
+
@icon.stubs(:TIFFRepresentation).returns(@tiff)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should register itself with Growl" do
|
70
|
+
pid = 54231
|
71
|
+
@instance.stubs(:pid).returns(pid)
|
72
|
+
|
73
|
+
@instance.expects(:add_observer).with('onReady:', "Lend Me Some Sugar; I Am Your Neighbor!", false)
|
74
|
+
@instance.expects(:add_observer).with('onClicked:', "GrowlClicked!", true)
|
75
|
+
@instance.expects(:add_observer).with('onTimeout:', "GrowlTimedOut!", true)
|
76
|
+
|
77
|
+
dict = {
|
78
|
+
:ApplicationName => @name,
|
79
|
+
:ApplicationIcon => @tiff,
|
80
|
+
:AllNotifications => @notifications,
|
81
|
+
:DefaultNotifications => @default_notifications
|
82
|
+
}
|
83
|
+
|
84
|
+
@center.expects(:objc_send).with(
|
85
|
+
:postNotificationName, :GrowlApplicationRegistrationNotification,
|
86
|
+
:object, nil,
|
87
|
+
:userInfo, dict,
|
88
|
+
:deliverImmediately, true
|
89
|
+
)
|
90
|
+
|
91
|
+
@instance.register @name, @notifications, @default_notifications, @icon
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "Growl::Notifier.sharedInstance" do
|
96
|
+
include GrowlNotifierSpecHelper
|
97
|
+
|
98
|
+
before do
|
99
|
+
set_variables!
|
100
|
+
|
101
|
+
@pid = 54231
|
102
|
+
OSX::NSProcessInfo.processInfo.stubs(:processIdentifier).returns(@pid)
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should return the applications PID" do
|
106
|
+
@instance.send(:pid).should.be @pid
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should be able to easily add observers to the NSDistributedNotificationCenter" do
|
110
|
+
@center.expects(:addObserver_selector_name_object).with(@instance, 'selector:', "name", nil)
|
111
|
+
@instance.send(:add_observer, 'selector:', 'name', false)
|
112
|
+
|
113
|
+
@center.expects(:addObserver_selector_name_object).with(@instance, 'selector:', "#{@name}-#{@pid}-name", nil)
|
114
|
+
@instance.send(:add_observer, 'selector:', 'name', true)
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should send a notification to Growl" do
|
118
|
+
another_icon = mock('Another icon')
|
119
|
+
another_icons_tiff_rep = mock('Another icons tiff representation')
|
120
|
+
another_icon.stubs(:TIFFRepresentation).returns(another_icons_tiff_rep)
|
121
|
+
|
122
|
+
dict = {
|
123
|
+
:ApplicationName => @name,
|
124
|
+
:ApplicationPID => @pid,
|
125
|
+
:NotificationName => @notifications.first,
|
126
|
+
:NotificationTitle => 'title',
|
127
|
+
:NotificationDescription => 'description',
|
128
|
+
:NotificationPriority => 1,
|
129
|
+
:NotificationIcon => another_icons_tiff_rep,
|
130
|
+
:NotificationSticky => 1,
|
131
|
+
:NotificationClickContext => { :user_click_context => 'foo' }
|
132
|
+
}
|
133
|
+
|
134
|
+
@center.expects(:postNotificationName_object_userInfo_deliverImmediately).with(:GrowlNotification, nil, dict, true)
|
135
|
+
|
136
|
+
@instance.notify(@notifications.first, 'title', 'description', :sticky => true, :priority => 1, :icon => another_icon, :click_context => 'foo')
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should not require all options to be specified when sending a notification to Growl" do
|
140
|
+
dict = {
|
141
|
+
:ApplicationName => @name,
|
142
|
+
:ApplicationPID => @pid,
|
143
|
+
:NotificationName => @notifications.first,
|
144
|
+
:NotificationTitle => 'title',
|
145
|
+
:NotificationDescription => 'description',
|
146
|
+
:NotificationPriority => 0
|
147
|
+
}
|
148
|
+
|
149
|
+
@center.expects(:postNotificationName_object_userInfo_deliverImmediately).with(:GrowlNotification, nil, dict, true)
|
150
|
+
@instance.notify(@notifications.first, 'title', 'description')
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should take a symbol instead of an integer to specify the priority level when sending a notification to Growl" do
|
154
|
+
priority_table = { :very_low => -2, :moderate => -1, :normal => 0, :high => 1, :emergency => 2 }
|
155
|
+
|
156
|
+
priority_table.each do |key, value|
|
157
|
+
@center.expects(:postNotificationName_object_userInfo_deliverImmediately).with do |name, object, info, immediately|
|
158
|
+
info[:NotificationPriority] == value
|
159
|
+
end
|
160
|
+
|
161
|
+
@instance.notify(@notifications.first, 'title', 'description', :priority => key)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should add a callback to the callbacks if a block is given to #notify" do
|
166
|
+
callback = proc { message_from_callback }
|
167
|
+
@center.expects(:postNotificationName_object_userInfo_deliverImmediately).with do |name, object, info, immediately|
|
168
|
+
info[:NotificationClickContext] = { :callback_object_id => callback.object_id, :user_click_context => 'foo' }
|
169
|
+
end
|
170
|
+
|
171
|
+
@instance.notify(@notifications.first, 'title', 'description', { :click_context => 'foo' }, &callback)
|
172
|
+
|
173
|
+
@instance.instance_variable_get(:@callbacks)[callback.object_id].should.be callback
|
174
|
+
end
|
175
|
+
|
176
|
+
it "should call a callback handler if the notification that it belongs to is clicked and then remove the callback" do
|
177
|
+
callback = nil
|
178
|
+
@instance.instance_eval do
|
179
|
+
callback = proc { message_from_callback }
|
180
|
+
end
|
181
|
+
@instance.notify(@notifications.first, 'title', 'description', &callback)
|
182
|
+
|
183
|
+
@instance.expects(:message_from_callback)
|
184
|
+
@instance.onClicked(stubbed_notification(:callback_object_id => callback.object_id.to_s.to_ns))
|
185
|
+
@instance.instance_variable_get(:@callbacks)[callback.object_id].should.be nil
|
186
|
+
end
|
187
|
+
|
188
|
+
it "should send a message to the delegate if a notification was clicked with the specified context" do
|
189
|
+
notification = stubbed_notification(:user_click_context => 'foo')
|
190
|
+
assign_delegate.expects(:growlNotifierClicked_context).with(@instance, 'foo')
|
191
|
+
@instance.onClicked(notification)
|
192
|
+
end
|
193
|
+
|
194
|
+
it "should send a message to the delegate if a notification was clicked with nil as the context if none was specified" do
|
195
|
+
notification = stubbed_notification
|
196
|
+
assign_delegate.expects(:growlNotifierClicked_context).with(@instance, nil)
|
197
|
+
@instance.onClicked(notification)
|
198
|
+
end
|
199
|
+
|
200
|
+
it "should not send a message to the delegate if a notification was clicked but the delegate doesn't respond to the delegate method" do
|
201
|
+
assign_delegate
|
202
|
+
@instance.onClicked(stubbed_notification)
|
203
|
+
end
|
204
|
+
|
205
|
+
it "should remove a callback handler if the notification that it belongs to times out" do
|
206
|
+
callback = proc {}
|
207
|
+
notification = stubbed_notification(:callback_object_id => callback.object_id.to_s.to_ns)
|
208
|
+
|
209
|
+
@instance.notify(@notifications.first, 'title', 'description', &callback)
|
210
|
+
@instance.delegate = nil
|
211
|
+
|
212
|
+
callback.expects(:call).times(0)
|
213
|
+
@instance.onTimeout(notification)
|
214
|
+
@instance.instance_variable_get(:@callbacks)[callback.object_id].should.be nil
|
215
|
+
end
|
216
|
+
|
217
|
+
it "should send a message to the delegate if a notification times out with the specified context" do
|
218
|
+
notification = stubbed_notification(:user_click_context => 'foo')
|
219
|
+
assign_delegate.expects(:growlNotifierTimedOut_context).with(@instance, 'foo')
|
220
|
+
|
221
|
+
@instance.onTimeout(notification)
|
222
|
+
end
|
223
|
+
|
224
|
+
it "should send a message to the delegate if a notification times out with nil as the context in none was specified" do
|
225
|
+
notification = stubbed_notification
|
226
|
+
assign_delegate.expects(:growlNotifierTimedOut_context).with(@instance, nil)
|
227
|
+
|
228
|
+
@instance.onTimeout(notification)
|
229
|
+
end
|
230
|
+
|
231
|
+
it "should not send a message to the delegate if a notification times out but the delegate doesn't respond to the delegate method" do
|
232
|
+
assign_delegate
|
233
|
+
@instance.onTimeout(stubbed_notification)
|
234
|
+
end
|
235
|
+
|
236
|
+
it "should resend the registration data to Growl if Growl was restarted for some reason" do
|
237
|
+
@instance.expects(:send_registration!)
|
238
|
+
@instance.onReady(nil)
|
239
|
+
end
|
240
|
+
|
241
|
+
private
|
242
|
+
|
243
|
+
def assign_delegate
|
244
|
+
delegate = mock('delegate')
|
245
|
+
@instance.delegate = delegate
|
246
|
+
delegate
|
247
|
+
end
|
248
|
+
|
249
|
+
def stubbed_notification(options = {})
|
250
|
+
notification = stub('timeout notification')
|
251
|
+
notification.stubs(:userInfo).returns("ClickedContext" => options.to_ns)
|
252
|
+
notification
|
253
|
+
end
|
254
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "test/unit"
|
3
|
+
require "test/spec"
|
4
|
+
require "mocha"
|
5
|
+
require 'osx/cocoa'
|
6
|
+
|
7
|
+
def OSX._ignore_ns_override; true; end
|
8
|
+
|
9
|
+
$: << File.expand_path('../../lib', __FILE__)
|
10
|
+
|
11
|
+
module GrowlNotifierSpecHelper
|
12
|
+
def set_variables!
|
13
|
+
@instance = Growl::Notifier.sharedInstance
|
14
|
+
@name = 'GrowlerApp'
|
15
|
+
@icon = mock('PrettyIcon')
|
16
|
+
@notifications = ['YourHamburgerIsReady', 'OhSomeoneElseAteIt']
|
17
|
+
@default_notifications = ['ADefaultNotification', *@notifications]
|
18
|
+
|
19
|
+
@center = mock('NSDistributedNotificationCenter')
|
20
|
+
@center.stubs(:postNotificationName_object_userInfo_deliverImmediately)
|
21
|
+
OSX::NSDistributedNotificationCenter.stubs(:defaultCenter).returns(@center)
|
22
|
+
end
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: growlnotifier
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "1.0"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Satoshi Nakagawa
|
8
|
+
- Eloy Duran
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2008-08-04 00:00:00 +09:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: hoe
|
18
|
+
type: :development
|
19
|
+
version_requirement:
|
20
|
+
version_requirements: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 1.7.0
|
25
|
+
version:
|
26
|
+
description: A ruby library which allows you to send Growl notifications.
|
27
|
+
email:
|
28
|
+
- psychs@limechat.net
|
29
|
+
- e.duran@superalloy.nl
|
30
|
+
executables: []
|
31
|
+
|
32
|
+
extensions: []
|
33
|
+
|
34
|
+
extra_rdoc_files:
|
35
|
+
- Manifest.txt
|
36
|
+
- README.txt
|
37
|
+
files:
|
38
|
+
- Manifest.txt
|
39
|
+
- README.txt
|
40
|
+
- Rakefile
|
41
|
+
- LICENSE
|
42
|
+
- lib/growl.rb
|
43
|
+
- lib/growl_helpers.rb
|
44
|
+
- samples/growl_block_sample.rb
|
45
|
+
- samples/growl_delegate_sample.rb
|
46
|
+
- test/growl_test.rb
|
47
|
+
- test/growl_helpers_test.rb
|
48
|
+
- test/test_helper.rb
|
49
|
+
has_rdoc: true
|
50
|
+
homepage: http://growlnotifier.rubyforge.org/
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options:
|
53
|
+
- --main
|
54
|
+
- README.txt
|
55
|
+
require_paths:
|
56
|
+
- lib
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
version:
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0"
|
68
|
+
version:
|
69
|
+
requirements: []
|
70
|
+
|
71
|
+
rubyforge_project: growlnotifier
|
72
|
+
rubygems_version: 1.2.0
|
73
|
+
signing_key:
|
74
|
+
specification_version: 2
|
75
|
+
summary: Growl::Notifier is a OSX RubyCocoa class that allows your application to post notifications to the Growl daemon.
|
76
|
+
test_files:
|
77
|
+
- test/test_helper.rb
|