aaronp-meow 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -3,7 +3,9 @@ Manifest.txt
3
3
  README.rdoc
4
4
  Rakefile
5
5
  lib/meow.rb
6
+ lib/meow/notifier.rb
6
7
  meow.gemspec
8
+ test/assets/aaron.jpeg
7
9
  test/helper.rb
8
10
  test/test_meow.rb
9
11
  vendor/hoe.rb
@@ -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
@@ -2,7 +2,9 @@
2
2
 
3
3
  require 'rubygems'
4
4
  require './vendor/hoe.rb'
5
- require './lib/meow.rb'
5
+
6
+ $LOAD_PATH << File.join(File.dirname(__FILE__), 'lib')
7
+ require 'meow'
6
8
 
7
9
  Hoe.new('meow', Meow::VERSION) do |p|
8
10
  p.readme = 'README.rdoc'
@@ -1,7 +1,8 @@
1
1
  require 'osx/cocoa'
2
+ require 'meow/notifier'
2
3
 
3
4
  class Meow
4
- VERSION = '1.0.0'
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
- 'NotificationName' => opts[:note_type],
68
- 'ApplicationName' => name,
69
- 'NotificationTitle' => title,
70
- 'NotificationDescription' => description,
71
- 'NotificationIcon' => opts[:icon].TIFFRepresentation(),
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['NotificationAppIcon'] = opts[:app_icon].TIFFRepresentation if opts[:app_icon]
75
- notification['NotificationSticky'] = OSX::NSNumber.numberWithBool_(true) if opts[:stick]
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 opts[:priority]
78
- notification['NotificationPriority'] = OSX::NSNumber.numberWithInt_(PRIORITIES[opts[:priority]])
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
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = %q{meow}
3
- s.version = "1.0.0"
3
+ s.version = "1.1.0"
4
4
 
5
5
  s.specification_version = 2 if s.respond_to? :specification_version=
6
6
 
@@ -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', 'Description', :priority => :very_high)
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', 'Description', :type => 'Awesome')
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', 'Description', :priority => :very_high)
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', 'Description')
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
@@ -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:: Signs your gems with this private key.
68
- # signing_cert_file:: Signs your gem with this certificate.
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.1'
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
- readme = File.read(self.readme).split(/^(=+ .*)$/)[1..-1]
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
- warn "** README.txt is in the wrong format for auto-intuiting."
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
- # ClassName => class_name
476
- alternate_name = name.reverse.scan(%r/[A-Z]+|[^A-Z]*[A-Z]+?/).reverse.map { |word| word.reverse.downcase }.join('_')
477
- elsif name =~ /_/
478
- # class_name = ClassName
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"] || /tmp$|CVS|\.svn/
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
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aaronp-meow
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Patterson