aaronp-meow 1.0.0 → 1.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/CHANGELOG.rdoc +8 -0
- data/Manifest.txt +2 -0
- data/README.rdoc +7 -1
- data/Rakefile +3 -1
- data/lib/meow.rb +78 -12
- data/meow.gemspec +1 -1
- data/test/test_meow.rb +33 -4
- data/vendor/hoe.rb +48 -21
- metadata +1 -1
data/CHANGELOG.rdoc
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
=== 1.1.0 / 2008-06-13
|
2
|
+
|
3
|
+
* 3 major enhancements
|
4
|
+
|
5
|
+
* Added click callbacks so that Meow can be notified of clicks.
|
6
|
+
* Added Meow.run() to block and wait for clicks
|
7
|
+
* Meow.import_image() returns an image suitable for icon use
|
8
|
+
|
1
9
|
=== 1.0.0 / 2008-06-05
|
2
10
|
|
3
11
|
* 1 major enhancement
|
data/Manifest.txt
CHANGED
data/README.rdoc
CHANGED
@@ -11,6 +11,12 @@ Send Growl notifications via Ruby.
|
|
11
11
|
meep = Meow.new('Meow Test')
|
12
12
|
meep.notify('Title', 'Description')
|
13
13
|
|
14
|
+
## Handle clicks
|
15
|
+
meep.notify('Click Me', 'Do it!') do
|
16
|
+
puts "I got clicked!"
|
17
|
+
end
|
18
|
+
Meow.start # Start blocks
|
19
|
+
|
14
20
|
== REQUIREMENTS:
|
15
21
|
|
16
22
|
* Growl: http://growl.info
|
@@ -28,7 +34,7 @@ Thanks to the Growl team! This code is heavily based on their ruby example.
|
|
28
34
|
|
29
35
|
(The MIT License)
|
30
36
|
|
31
|
-
Copyright (c) 2008 Aaron Patterson
|
37
|
+
Copyright (c) 2008 Aaron Patterson, Evan Phoenix, Eric Hodel
|
32
38
|
|
33
39
|
Permission is hereby granted, free of charge, to any person obtaining
|
34
40
|
a copy of this software and associated documentation files (the
|
data/Rakefile
CHANGED
data/lib/meow.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
require 'osx/cocoa'
|
2
|
+
require 'meow/notifier'
|
2
3
|
|
3
4
|
class Meow
|
4
|
-
VERSION = '1.
|
5
|
+
VERSION = '1.1.0'
|
5
6
|
PRIORITIES = { :very_low => -2,
|
6
7
|
:moderate => -1,
|
7
8
|
:normal => 0,
|
@@ -9,6 +10,18 @@ class Meow
|
|
9
10
|
:emergency => 2,
|
10
11
|
}
|
11
12
|
|
13
|
+
GROWL_IS_READY = "Lend Me Some Sugar; I Am Your Neighbor!"
|
14
|
+
GROWL_NOTIFICATION_CLICKED = "GrowlClicked!"
|
15
|
+
GROWL_NOTIFICATION_TIMED_OUT = "GrowlTimedOut!"
|
16
|
+
GROWL_KEY_CLICKED_CONTEXT = "ClickedContext"
|
17
|
+
|
18
|
+
# This sets up shared state properly that Cocoa uses.
|
19
|
+
@@application = OSX::NSApplication.sharedApplication
|
20
|
+
|
21
|
+
# Holds blocks waiting for clicks
|
22
|
+
@@callbacks = Notifier.new
|
23
|
+
@@callbacks.setup
|
24
|
+
|
12
25
|
class << self
|
13
26
|
###
|
14
27
|
# Send a message in one call.
|
@@ -18,6 +31,41 @@ class Meow
|
|
18
31
|
def notify(name, title, description, opts = {})
|
19
32
|
new(name).notify(title, description, opts)
|
20
33
|
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Convert +image+ to an NSImage that displays nicely in growl. If
|
37
|
+
# +image+ is a String, it's assumed to be the path to an image
|
38
|
+
# on disk and is loaded.
|
39
|
+
def import_image(image, size=128)
|
40
|
+
if image.kind_of? String
|
41
|
+
image = OSX::NSImage.alloc.initWithContentsOfFile image
|
42
|
+
end
|
43
|
+
|
44
|
+
return image if image.size.width.to_i == 128
|
45
|
+
|
46
|
+
new_image = OSX::NSImage.alloc.initWithSize(OSX.NSMakeSize(size, size))
|
47
|
+
new_image.lockFocus
|
48
|
+
image.drawInRect_fromRect_operation_fraction(
|
49
|
+
OSX.NSMakeRect(0, 0, size, size),
|
50
|
+
OSX.NSMakeRect(0, 0, image.size.width, image.size.height),
|
51
|
+
OSX::NSCompositeSourceOver, 1.0)
|
52
|
+
new_image.unlockFocus
|
53
|
+
|
54
|
+
return new_image
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# Call this if you have passed blocks to #notify. This blocks forever, but
|
59
|
+
# it's the only way to properly get events.
|
60
|
+
def run
|
61
|
+
OSX::NSApp.run
|
62
|
+
end
|
63
|
+
|
64
|
+
##
|
65
|
+
# Call this to cause Meow to stop running.
|
66
|
+
def stop
|
67
|
+
OSX::NSApp.stop(nil)
|
68
|
+
end
|
21
69
|
end
|
22
70
|
|
23
71
|
attr_accessor :name, :note_type, :icon
|
@@ -36,6 +84,17 @@ class Meow
|
|
36
84
|
@icon = icon
|
37
85
|
@note_type = note_type
|
38
86
|
@registered = []
|
87
|
+
|
88
|
+
@pid = OSX::NSProcessInfo.processInfo.processIdentifier
|
89
|
+
|
90
|
+
# The notification name to look for when someone clicks on a notify bubble.
|
91
|
+
@clicked_name = "#{name}-#{@pid}-GrowlClicked!"
|
92
|
+
|
93
|
+
notify_center = OSX::NSDistributedNotificationCenter.defaultCenter
|
94
|
+
notify_center.addObserver_selector_name_object_ @@callbacks,
|
95
|
+
"clicked:",
|
96
|
+
@clicked_name,
|
97
|
+
nil
|
39
98
|
end
|
40
99
|
|
41
100
|
###
|
@@ -44,6 +103,8 @@ class Meow
|
|
44
103
|
# * +title+ will be the title of the message.
|
45
104
|
# * +description+ is the description of the message
|
46
105
|
# * +opts+ is a hash of options.
|
106
|
+
# * +block+ is an optional block passed to +notify+. The block
|
107
|
+
# is called when someone clicks on the growl bubble.
|
47
108
|
#
|
48
109
|
# Possible values for +opts+ are:
|
49
110
|
# * :priority => Set the note priority
|
@@ -54,32 +115,36 @@ class Meow
|
|
54
115
|
#
|
55
116
|
# Example:
|
56
117
|
# note.notify('title', 'description', :priority => :very_low)
|
57
|
-
def notify(title, description, opts = {})
|
118
|
+
def notify(title, description, opts = {}, &block)
|
58
119
|
opts = {
|
59
120
|
:icon => icon,
|
60
121
|
:sticky => false,
|
61
122
|
:note_type => note_type,
|
123
|
+
:priority => 0
|
62
124
|
}.merge(opts)
|
63
125
|
|
64
126
|
register(opts[:note_type]) unless @registered.include?(opts[:note_type])
|
65
127
|
|
66
128
|
notification = {
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
129
|
+
:ApplicationName => name,
|
130
|
+
:ApplicationPID => @pid,
|
131
|
+
:NotificationName => opts[:note_type],
|
132
|
+
:NotificationTitle => title,
|
133
|
+
:NotificationDescription => description,
|
134
|
+
:NotificationIcon => opts[:icon].TIFFRepresentation(),
|
135
|
+
:NotificationPriority => opts[:priority].to_i
|
72
136
|
}
|
73
137
|
|
74
|
-
notification[
|
75
|
-
notification[
|
138
|
+
notification[:NotificationAppIcon] = opts[:app_icon].TIFFRepresentation if opts[:app_icon]
|
139
|
+
notification[:NotificationSticky] = OSX::NSNumber.numberWithBool_(true) if opts[:stick]
|
140
|
+
|
141
|
+
notify_center = OSX::NSDistributedNotificationCenter.defaultCenter
|
76
142
|
|
77
|
-
if
|
78
|
-
notification[
|
143
|
+
if block
|
144
|
+
notification[:NotificationClickContext] = @@callbacks.add(block)
|
79
145
|
end
|
80
146
|
|
81
147
|
d = OSX::NSDictionary.dictionaryWithDictionary_(notification)
|
82
|
-
notify_center = OSX::NSDistributedNotificationCenter.defaultCenter
|
83
148
|
notify_center.postNotificationName_object_userInfo_deliverImmediately_('GrowlNotification', nil, d, true)
|
84
149
|
end
|
85
150
|
|
@@ -100,4 +165,5 @@ class Meow
|
|
100
165
|
notify_center = OSX::NSDistributedNotificationCenter.defaultCenter
|
101
166
|
notify_center.postNotificationName_object_userInfo_deliverImmediately_('GrowlApplicationRegistrationNotification', nil, dictionary, true)
|
102
167
|
end
|
168
|
+
|
103
169
|
end
|
data/meow.gemspec
CHANGED
data/test/test_meow.rb
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), "helper"))
|
2
2
|
|
3
3
|
class MeowTest < Test::Unit::TestCase
|
4
|
+
ASSETS = File.expand_path(File.join(File.dirname(__FILE__), "assets"))
|
5
|
+
|
6
|
+
def my_method_name
|
7
|
+
/`(.*?)'/.match(caller.first)[1]
|
8
|
+
end
|
9
|
+
|
4
10
|
def test_initialize
|
5
11
|
meep = nil
|
6
12
|
assert_nothing_raised {
|
@@ -11,28 +17,51 @@ class MeowTest < Test::Unit::TestCase
|
|
11
17
|
|
12
18
|
def test_meow_has_static_method
|
13
19
|
assert_nothing_raised {
|
14
|
-
Meow.notify('Meow Test', 'Title',
|
20
|
+
Meow.notify('Meow Test', 'Title', my_method_name, :priority => :very_high)
|
15
21
|
}
|
16
22
|
end
|
17
23
|
|
18
24
|
def test_meow_can_notify_with_type
|
19
25
|
meep = Meow.new('Meow Test')
|
20
26
|
assert_nothing_raised {
|
21
|
-
meep.notify('Title',
|
27
|
+
meep.notify('Title', my_method_name, :type => 'Awesome')
|
22
28
|
}
|
23
29
|
end
|
24
30
|
|
25
31
|
def test_meow_can_notify_with_priority
|
26
32
|
meep = Meow.new('Meow Test')
|
27
33
|
assert_nothing_raised {
|
28
|
-
meep.notify('Title',
|
34
|
+
meep.notify('Title', my_method_name, :priority => :very_high)
|
29
35
|
}
|
30
36
|
end
|
31
37
|
|
32
38
|
def test_meow_can_notify_without_register
|
33
39
|
meep = Meow.new('Meow Test')
|
34
40
|
assert_nothing_raised {
|
35
|
-
meep.notify('Title',
|
41
|
+
meep.notify('Title', my_method_name)
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_import_image
|
46
|
+
icon = Meow.import_image(File.join(ASSETS, 'aaron.jpeg'))
|
47
|
+
assert_kind_of(OSX::NSImage, icon)
|
48
|
+
meep = Meow.new('Meow Test')
|
49
|
+
assert_nothing_raised {
|
50
|
+
meep.notify('Icon', my_method_name, :icon => icon)
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_clicks_work
|
55
|
+
$RUBYCOCOA_SUPPRESS_EXCEPTION_LOGGING = true
|
56
|
+
block_called = false
|
57
|
+
assert_raises(RuntimeError) {
|
58
|
+
meep = Meow.new('Meow Test')
|
59
|
+
meep.notify('Click Here', my_method_name) do
|
60
|
+
block_called = true
|
61
|
+
raise 'I do not know how to get run to stop blocking!'
|
62
|
+
end
|
63
|
+
Meow.run
|
36
64
|
}
|
65
|
+
assert block_called
|
37
66
|
end
|
38
67
|
end
|
data/vendor/hoe.rb
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'rake'
|
5
|
-
require 'rake/contrib/sshpublisher'
|
6
5
|
require 'rake/gempackagetask'
|
7
6
|
require 'rake/rdoctask'
|
8
7
|
require 'rake/testtask'
|
@@ -64,8 +63,8 @@ require 'yaml'
|
|
64
63
|
# exclude:: A regular expression of files to exclude from
|
65
64
|
# +check_manifest+.
|
66
65
|
# publish_on_announce:: Run +publish_docs+ when you run +release+.
|
67
|
-
# signing_key_file::
|
68
|
-
# signing_cert_file::
|
66
|
+
# signing_key_file:: Signs your gems with this private key.
|
67
|
+
# signing_cert_file:: Signs your gem with this certificate.
|
69
68
|
# blogs:: An array of hashes of blog settings.
|
70
69
|
#
|
71
70
|
# Run +config_hoe+ and see ~/.hoerc for examples.
|
@@ -116,7 +115,7 @@ require 'yaml'
|
|
116
115
|
#
|
117
116
|
|
118
117
|
class Hoe
|
119
|
-
VERSION = '1.5.
|
118
|
+
VERSION = '1.5.3'
|
120
119
|
|
121
120
|
ruby_prefix = Config::CONFIG['prefix']
|
122
121
|
sitelibdir = Config::CONFIG['sitelibdir']
|
@@ -214,6 +213,11 @@ class Hoe
|
|
214
213
|
|
215
214
|
attr_accessor :lib_files # :nodoc:
|
216
215
|
|
216
|
+
##
|
217
|
+
# Optional: Array of incompatible versions for multiruby filtering. Used as a regex.
|
218
|
+
|
219
|
+
attr_accessor :multiruby_skip
|
220
|
+
|
217
221
|
##
|
218
222
|
# *MANDATORY*: The name of the release.
|
219
223
|
|
@@ -229,6 +233,11 @@ class Hoe
|
|
229
233
|
|
230
234
|
attr_accessor :need_zip
|
231
235
|
|
236
|
+
##
|
237
|
+
# Optional: A post-install message to be displayed when gem is installed.
|
238
|
+
|
239
|
+
attr_accessor :post_install_message
|
240
|
+
|
232
241
|
##
|
233
242
|
# Optional: A regexp to match documentation files against the manifest.
|
234
243
|
|
@@ -310,6 +319,7 @@ class Hoe
|
|
310
319
|
self.description_sections = %w(description)
|
311
320
|
self.email = []
|
312
321
|
self.extra_deps = []
|
322
|
+
self.multiruby_skip = []
|
313
323
|
self.need_tar = true
|
314
324
|
self.need_zip = false
|
315
325
|
self.rdoc_pattern = /^(lib|bin|ext)|(txt|rdoc)$/
|
@@ -321,12 +331,18 @@ class Hoe
|
|
321
331
|
self.test_globs = ['test/**/test_*.rb']
|
322
332
|
self.readme = 'README.txt'
|
323
333
|
self.history = 'History.txt'
|
334
|
+
self.post_install_message = nil
|
324
335
|
|
325
336
|
yield self if block_given?
|
326
337
|
|
327
338
|
# Intuit values:
|
328
339
|
|
329
|
-
|
340
|
+
def missing name
|
341
|
+
warn "** #{name} is missing or in the wrong format for auto-intuiting."
|
342
|
+
warn " run `sow blah` and look at its text files"
|
343
|
+
end
|
344
|
+
|
345
|
+
readme = File.read(self.readme).split(/^(=+ .*)$/)[1..-1] rescue ''
|
330
346
|
unless readme.empty? then
|
331
347
|
sections = readme.map { |s|
|
332
348
|
s =~ /^=/ ? s.strip.downcase.chomp(':').split.last : s.strip
|
@@ -336,14 +352,20 @@ class Hoe
|
|
336
352
|
summ = desc.split(/\.\s+/).first(summary_sentences).join(". ")
|
337
353
|
|
338
354
|
self.description ||= desc
|
339
|
-
self.changes ||= File.read(self.history).split(/^(===.*)/)[1..2].join.strip
|
340
355
|
self.summary ||= summ
|
341
356
|
self.url ||= readme[1].gsub(/^\* /, '').split(/\n/).grep(/\S+/)
|
342
357
|
else
|
343
|
-
|
344
|
-
warn " run sow blah and look at it's text files"
|
358
|
+
missing 'README.txt'
|
345
359
|
end
|
346
360
|
|
361
|
+
self.changes ||= begin
|
362
|
+
h = File.read(self.history)
|
363
|
+
h.split(/^(===.*)/)[1..2].join.strip
|
364
|
+
rescue
|
365
|
+
missing 'History.txt'
|
366
|
+
''
|
367
|
+
end
|
368
|
+
|
347
369
|
%w(email author).each do |field|
|
348
370
|
value = self.send(field)
|
349
371
|
if value.nil? or value.empty? then
|
@@ -351,7 +373,7 @@ class Hoe
|
|
351
373
|
warn "Hoe #{field} value not set - Fix by 2008-04-01!"
|
352
374
|
self.send "#{field}=", "doofus"
|
353
375
|
else
|
354
|
-
abort "Hoe #{field} value not set"
|
376
|
+
abort "Hoe #{field} value not set. aborting"
|
355
377
|
end
|
356
378
|
end
|
357
379
|
end
|
@@ -455,6 +477,8 @@ class Hoe
|
|
455
477
|
s.extra_rdoc_files = s.files.grep(/txt$/)
|
456
478
|
s.has_rdoc = true
|
457
479
|
|
480
|
+
s.post_install_message = post_install_message
|
481
|
+
|
458
482
|
if test ?f, "test/test_all.rb" then
|
459
483
|
s.test_file = "test/test_all.rb"
|
460
484
|
else
|
@@ -471,13 +495,11 @@ class Hoe
|
|
471
495
|
if ENV['INLINE'] then
|
472
496
|
s.platform = ENV['FORCE_PLATFORM'] || Gem::Platform::CURRENT
|
473
497
|
# name of the extension is CamelCase
|
474
|
-
if name =~ /[A-Z]/
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
alternate_name = name.strip.split(/\s*_+\s*/).map! { |w| w.downcase.sub(/^./) { |c| c.upcase } }.join
|
480
|
-
end
|
498
|
+
alternate_name = if name =~ /[A-Z]/ then
|
499
|
+
name.gsub(/([A-Z])/, '_\1').downcase.sub(/^_/, '')
|
500
|
+
elsif name =~ /_/ then
|
501
|
+
name.capitalize.gsub(/_([a-z])/) { $1.upcase }
|
502
|
+
end
|
481
503
|
|
482
504
|
# Try collecting Inline extensions for +name+
|
483
505
|
if defined?(Inline) then
|
@@ -539,7 +561,7 @@ class Hoe
|
|
539
561
|
puts "rf.add_file #{rubyforge_name.inspect}, #{name.inspect}, release_id, \"#{pkg}.gem\""
|
540
562
|
end
|
541
563
|
|
542
|
-
rf = RubyForge.new
|
564
|
+
rf = RubyForge.new.configure
|
543
565
|
puts "Logging in"
|
544
566
|
rf.login
|
545
567
|
|
@@ -609,7 +631,7 @@ class Hoe
|
|
609
631
|
task :clean => [ :clobber_docs, :clobber_package ] do
|
610
632
|
clean_globs.each do |pattern|
|
611
633
|
files = Dir[pattern]
|
612
|
-
rm_rf files unless files.empty?
|
634
|
+
rm_rf files, :verbose => true unless files.empty?
|
613
635
|
end
|
614
636
|
end
|
615
637
|
|
@@ -688,7 +710,7 @@ class Hoe
|
|
688
710
|
require 'rubyforge'
|
689
711
|
subject, title, body, urls = announcement
|
690
712
|
|
691
|
-
rf = RubyForge.new
|
713
|
+
rf = RubyForge.new.configure
|
692
714
|
rf.login
|
693
715
|
rf.post_news(rubyforge_name, subject, "#{title}\n\n#{body}")
|
694
716
|
puts "Posted to rubyforge"
|
@@ -703,7 +725,8 @@ class Hoe
|
|
703
725
|
require 'find'
|
704
726
|
files = []
|
705
727
|
with_config do |config, _|
|
706
|
-
exclusions = config["exclude"]
|
728
|
+
exclusions = config["exclude"]
|
729
|
+
abort "exclude entry missing from .hoerc. Aborting." if exclusions.nil?
|
707
730
|
Find.find '.' do |path|
|
708
731
|
next unless File.file? path
|
709
732
|
next if path =~ exclusions
|
@@ -742,7 +765,7 @@ class Hoe
|
|
742
765
|
|
743
766
|
puts "Installed key and certificate."
|
744
767
|
|
745
|
-
rf = RubyForge.new
|
768
|
+
rf = RubyForge.new.configure
|
746
769
|
rf.login
|
747
770
|
|
748
771
|
cert_package = "#{rubyforge_name}-certificates"
|
@@ -788,7 +811,11 @@ class Hoe
|
|
788
811
|
tests.map! {|f| %Q(require "#{f}")}
|
789
812
|
"#{RUBY_FLAGS} -e '#{tests.join("; ")}' #{FILTER}"
|
790
813
|
end
|
814
|
+
|
815
|
+
excludes = multiruby_skip.join(":")
|
816
|
+
ENV['EXCLUDED_VERSIONS'] = excludes
|
791
817
|
cmd = "multiruby #{cmd}" if multi
|
818
|
+
|
792
819
|
send msg, cmd
|
793
820
|
end
|
794
821
|
|