alloy-kicker 0.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 ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Eloy Duran <eloy.de.enige@gmail.com>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,20 @@
1
+ = kicker
2
+
3
+ A simple OS X CLI tool which uses FSEvents to run a given shell command.
4
+
5
+ Give it a path to a file or directory and a shell command to execute when any
6
+ changes occur:
7
+
8
+ Show all files whenever a change occurs in the current work directory:
9
+
10
+ $ kicker . "ls -l"
11
+
12
+ Run a Rake task whenever a given file is changed:
13
+
14
+ $ kicker guides/source/nested_model_forms.textile "ONLY=nested_model_forms rake guides && open -a Safari guides/output/nested_model_forms.html"
15
+
16
+ == Tests??
17
+
18
+ For now it's just a very simple bin script which uses the FSEvents abstraction
19
+ from Rucola, which is tested there. Once/if this will every be developed
20
+ further test cases will be added where appropriate.
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "kicker"
8
+ gem.summary = %Q{A simple OS X CLI tool which uses FSEvents to run a given shell command.}
9
+ gem.email = "eloy.de.enige@gmail.com"
10
+ gem.homepage = "http://github.com/alloy/kicker"
11
+ gem.authors = ["Eloy Duran"]
12
+ gem.executables << 'kicker'
13
+ gem.files = FileList['**/**']
14
+ gem.require_paths = ['vendor']
15
+ gem.has_rdoc = false
16
+ end
17
+ rescue LoadError
18
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
19
+ end
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 0
3
+ :major: 0
4
+ :minor: 1
data/bin/kicker ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path('../../vendor/rucola/fsevents', __FILE__)
4
+
5
+ path, command = ARGV
6
+
7
+ unless path && command
8
+ puts "Usage: #{$0} [PATH] [COMMAND]"
9
+ exit
10
+ else
11
+ unless File.exist?(path)
12
+ puts "The given path `#{path}' does not exist."
13
+ exit 1
14
+ end
15
+ end
16
+
17
+ path = File.expand_path(path)
18
+ file, path = path, File.dirname(path) unless File.directory?(path)
19
+
20
+ puts "Watching for changes on `#{file || path}'"
21
+
22
+ Rucola::FSEvents.start_watching(path) do |events|
23
+ unless file && !events.find { |e| e.last_modified_file == file }
24
+ system command
25
+ end
26
+ end
27
+
28
+ OSX::NSApplication.sharedApplication.run
data/kicker.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{kicker}
5
+ s.version = "0.1.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Eloy Duran"]
9
+ s.date = %q{2009-03-14}
10
+ s.default_executable = %q{kicker}
11
+ s.email = %q{eloy.de.enige@gmail.com}
12
+ s.executables = ["kicker"]
13
+ s.extra_rdoc_files = ["README.rdoc", "LICENSE"]
14
+ s.files = ["bin", "bin/kicker", "kicker.gemspec", "LICENSE", "pkg", "pkg/kicker-0.1.0.gem", "Rakefile", "README.rdoc", "vendor", "vendor/rucola", "vendor/rucola/fsevents.rb", "VERSION.yml"]
15
+ s.has_rdoc = true
16
+ s.homepage = %q{http://github.com/alloy/kicker}
17
+ s.rdoc_options = ["--inline-source", "--charset=UTF-8"]
18
+ s.require_paths = ["vendor"]
19
+ s.rubygems_version = %q{1.3.1}
20
+ s.summary = %q{A simple OS X CLI tool which uses FSEvents to run a given shell command.}
21
+
22
+ if s.respond_to? :specification_version then
23
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
24
+ s.specification_version = 2
25
+
26
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
27
+ else
28
+ end
29
+ else
30
+ end
31
+ end
@@ -0,0 +1,131 @@
1
+ require 'osx/cocoa'
2
+
3
+ OSX.require_framework '/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework'
4
+
5
+ module Rucola
6
+ class FSEvents
7
+ class FSEvent
8
+ attr_reader :fsevents_object
9
+ attr_reader :id
10
+ attr_reader :path
11
+ def initialize(fsevents_object, id, path)
12
+ @fsevents_object, @id, @path = fsevents_object, id, path
13
+ end
14
+
15
+ # Returns an array of the files/dirs in the path that the event occurred in.
16
+ # The files are sorted by the modification time, the first entry is the last modified file.
17
+ def files
18
+ Dir.glob("#{File.expand_path(path)}/*").sort_by {|f| File.mtime(f) }.reverse
19
+ end
20
+
21
+ # Returns the last modified file in the path that the event occurred in.
22
+ def last_modified_file
23
+ files.first
24
+ end
25
+ end
26
+
27
+ class StreamError < StandardError; end
28
+
29
+ attr_reader :paths
30
+ attr_reader :stream
31
+
32
+ attr_accessor :allocator
33
+ attr_accessor :context
34
+ attr_accessor :since
35
+ attr_accessor :latency
36
+ attr_accessor :flags
37
+
38
+ # Initializes a new FSEvents `watchdog` object and starts watching the directories you specify for events. The
39
+ # block is used as a handler for events, which are passed as the block's argument. This method is the easiest
40
+ # way to start watching some directories if you don't care about the details of setting up the event stream.
41
+ #
42
+ # Rucola::FSEvents.start_watching('/tmp') do |events|
43
+ # events.each { |event| log.debug("#{event.files.inspect} were changed.") }
44
+ # end
45
+ #
46
+ # Rucola::FSEvents.start_watching('/var/log/system.log', '/var/log/secure.log', :since => last_id, :latency => 5) do
47
+ # Growl.notify("Something was added to your log files!")
48
+ # end
49
+ #
50
+ # Note that the method also returns the FSEvents object. This enables you to control the event stream if you want to.
51
+ #
52
+ # fsevents = Rucola::FSEvents.start_watching('/Volumes') do |events|
53
+ # events.each { |event| Growl.notify("Volume changes: #{event.files.to_sentence}") }
54
+ # end
55
+ # fsevents.stop
56
+ def self.start_watching(*params, &block)
57
+ fsevents = new(*params, &block)
58
+ fsevents.create_stream
59
+ fsevents.start
60
+ fsevents
61
+ end
62
+
63
+ # Creates a new FSEvents `watchdog` object. You can specify a list of paths to watch and options to control the
64
+ # behaviour of the watchdog. The block you pass serves as a callback when an event is generated on one of the
65
+ # specified paths.
66
+ #
67
+ # fsevents = FSEvents.new('/etc/passwd') { Mailer.send_mail("Someone touched the password file!") }
68
+ # fsevents.create_stream
69
+ # fsevents.start
70
+ #
71
+ # fsevents = FSEvents.new('/home/upload', :since => UploadWatcher.last_event_id) do |events|
72
+ # events.each do |event|
73
+ # UploadWatcher.last_event_id = event.id
74
+ # event.files.each do |file|
75
+ # UploadWatcher.logfile.append("#{file} was changed")
76
+ # end
77
+ # end
78
+ # end
79
+ #
80
+ # *:since: The service will report events that have happened after the supplied event ID. Never use 0 because that
81
+ # will cause every fsevent since the "beginning of time" to be reported. Use OSX::KFSEventStreamEventIdSinceNow
82
+ # if you want to receive events that have happened after this call. (Default: OSX::KFSEventStreamEventIdSinceNow).
83
+ # You can find the ID's passed with :since in the events passed to your block.
84
+ # *:latency: Number of seconds to wait until an FSEvent is reported, this allows the service to bundle events. (Default: 0.0)
85
+ #
86
+ # Please refer to the Cocoa documentation for the rest of the options.
87
+ def initialize(*params, &block)
88
+ raise ArgumentError, 'No callback block was specified.' unless block_given?
89
+
90
+ options = params.last.kind_of?(Hash) ? params.pop : {}
91
+ @paths = params.flatten
92
+
93
+ paths.each { |path| raise ArgumentError, "The specified path (#{path}) does not exist." unless File.exist?(path) }
94
+
95
+ @allocator = options[:allocator] || OSX::KCFAllocatorDefault
96
+ @context = options[:context] || nil
97
+ @since = options[:since] || OSX::KFSEventStreamEventIdSinceNow
98
+ @latency = options[:latency] || 0.0
99
+ @flags = options[:flags] || 0
100
+ @stream = options[:stream] || nil
101
+
102
+ @user_callback = block
103
+ @callback = Proc.new do |stream, client_callback_info, number_of_events, paths_pointer, event_flags, event_ids|
104
+ paths_pointer.regard_as('*')
105
+ events = []
106
+ number_of_events.times {|i| events << Rucola::FSEvents::FSEvent.new(self, event_ids[i], paths_pointer[i]) }
107
+ @user_callback.call(events)
108
+ end
109
+ end
110
+
111
+ # Create the stream.
112
+ # Raises a Rucola::FSEvents::StreamError if the stream could not be created.
113
+ def create_stream
114
+ @stream = OSX.FSEventStreamCreate(@allocator, @callback, @context, @paths, @since, @latency, @flags)
115
+ raise(StreamError, 'Unable to create FSEvents stream.') unless @stream
116
+ OSX.FSEventStreamScheduleWithRunLoop(@stream, OSX.CFRunLoopGetCurrent, OSX::KCFRunLoopDefaultMode)
117
+ end
118
+
119
+ # Start the stream.
120
+ # Raises a Rucola::FSEvents::StreamError if the stream could not be started.
121
+ def start
122
+ raise(StreamError, 'Unable to start FSEvents stream.') unless OSX.FSEventStreamStart(@stream)
123
+ end
124
+
125
+ # Stop the stream.
126
+ # You can resume it by calling `start` again.
127
+ def stop
128
+ OSX.FSEventStreamStop(@stream)
129
+ end
130
+ end
131
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: alloy-kicker
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Eloy Duran
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-03-14 00:00:00 -07:00
13
+ default_executable: kicker
14
+ dependencies: []
15
+
16
+ description:
17
+ email: eloy.de.enige@gmail.com
18
+ executables:
19
+ - kicker
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ - LICENSE
25
+ files:
26
+ - bin
27
+ - bin/kicker
28
+ - kicker.gemspec
29
+ - LICENSE
30
+ - pkg
31
+ - pkg/kicker-0.1.0.gem
32
+ - Rakefile
33
+ - README.rdoc
34
+ - vendor
35
+ - vendor/rucola
36
+ - vendor/rucola/fsevents.rb
37
+ - VERSION.yml
38
+ has_rdoc: true
39
+ homepage: http://github.com/alloy/kicker
40
+ post_install_message:
41
+ rdoc_options:
42
+ - --inline-source
43
+ - --charset=UTF-8
44
+ require_paths:
45
+ - vendor
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ version:
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ requirements: []
59
+
60
+ rubyforge_project:
61
+ rubygems_version: 1.2.0
62
+ signing_key:
63
+ specification_version: 2
64
+ summary: A simple OS X CLI tool which uses FSEvents to run a given shell command.
65
+ test_files: []
66
+