├── README ├── CHANGELOG ├── lib ├── fs_event.rb └── fs_event │ └── stream.rb ├── MIT-LICENSE ├── Rakefile └── doc └── inspiration.rb /README: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/fs_event.rb: -------------------------------------------------------------------------------- 1 | require 'fs_event/stream' 2 | -------------------------------------------------------------------------------- /lib/fs_event/stream.rb: -------------------------------------------------------------------------------- 1 | # Pull in the Ruby Cocoa Framework. 2 | require 'osx/foundation' 3 | OSX.require_framework '/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework' 4 | 5 | module FSEvent 6 | class Stream 7 | def initialize(paths, &block) 8 | @stream = OSX::FSEventStreamCreate( 9 | OSX::KCFAllocatorDefault, 10 | block, 11 | nil, 12 | Array[*paths], 13 | OSX::KFSEventStreamEventIdSinceNow, 14 | 1.0, # latency 15 | 0 # flags 16 | ) 17 | end 18 | 19 | def schedule 20 | OSX::FSEventStreamScheduleWithRunLoop(@stream, OSX::CFRunLoopGetCurrent(), OSX::KCFRunLoopDefaultMode) 21 | end 22 | 23 | def start 24 | OSX::FSEventStreamStart(@stream) 25 | end 26 | 27 | def stop 28 | OSX::FSEventStreamStop(@stream) 29 | end 30 | 31 | def invalidate 32 | OSX::FSEventStreamInvalidate(@stream) 33 | end 34 | 35 | def release 36 | OSX::FSEventStreamRelease(@stream) 37 | @stream = nil 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2007 Rubaidh Ltd. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'rake' 3 | require 'rake/clean' 4 | require 'rake/gempackagetask' 5 | require 'rake/rdoctask' 6 | 7 | require 'spec/rake/spectask' 8 | 9 | desc "Compiles and tests the build" 10 | task :default => [ :spec ] 11 | 12 | CLEAN.include [ 13 | "*.gem", 'pkg', 'doc/coverage', 'doc/rdoc' 14 | ] 15 | 16 | Gem::manage_gems 17 | SPEC = Gem::Specification.new do |s| 18 | # Stuff I might want to tweak. 19 | s.summary = "A Ruby interface to the Mac OS X FSEvent library." 20 | s.version = "0.1" 21 | 22 | # Usual constants 23 | s.name = File.basename(File.dirname(File.expand_path(__FILE__))) 24 | s.author = "Graeme Mathieson" 25 | s.email = "mathie@rubaidh.com" 26 | s.homepage = "http://www.rubaidh.com/fsevents" 27 | s.platform = Gem::Platform::RUBY 28 | s.description = s.summary 29 | s.files = (%w(CHANGELOG Rakefile README) + 30 | FileList["{bin,doc,lib,spec}/**/*"].to_a).delete_if do |f| 31 | f =~ /^\._/ || 32 | f =~ /doc\/(rdoc|coverage)/ || 33 | f =~ /\.(so|bundle)$/ 34 | end 35 | s.require_path = "lib" 36 | s.bindir = 'bin' 37 | s.test_files = FileList["spec/*.rb"].to_a 38 | 39 | # Documentation 40 | s.has_rdoc = true 41 | s.extra_rdoc_files = ['README', 'CHANGELOG'] 42 | end 43 | 44 | Spec::Rake::SpecTask.new do |spec| 45 | spec.spec_files = FileList['spec/**/*_spec.rb'] 46 | spec.rcov = true 47 | spec.rcov_dir = "doc/coverage" 48 | end 49 | 50 | Rake::RDocTask.new do |rdoc| 51 | rdoc.rdoc_dir = 'doc/rdoc' 52 | rdoc.options += ['--line-numbers', '--inline-source'] 53 | rdoc.main = 'README' 54 | rdoc.rdoc_files.add [SPEC.extra_rdoc_files, 'lib/**/*.rb'].flatten 55 | end 56 | 57 | Rake::GemPackageTask.new(SPEC) 58 | -------------------------------------------------------------------------------- /doc/inspiration.rb: -------------------------------------------------------------------------------- 1 | class Autotest 2 | def run 3 | hook :run 4 | reset 5 | 6 | run_tests 7 | 8 | require 'osx/foundation' 9 | OSX.require_framework '/System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework' 10 | 11 | callback = proc do |stream, ctx, numEvents, paths, marks, eventIDs| 12 | paths.regard_as('*') 13 | rpaths = [] 14 | 15 | numEvents.times { |i| rpaths << paths[i] } 16 | 17 | run_tests_in_paths(*rpaths) 18 | end 19 | 20 | allocator = OSX::KCFAllocatorDefault 21 | context = nil 22 | path = [Dir.pwd] 23 | sinceWhen = OSX::KFSEventStreamEventIdSinceNow 24 | latency = 1.0 25 | flags = 0 26 | 27 | stream = OSX::FSEventStreamCreate(allocator, callback, context, path, sinceWhen, latency, flags) 28 | unless stream 29 | puts "Failed to create stream" 30 | exit 31 | end 32 | 33 | OSX::FSEventStreamScheduleWithRunLoop(stream, OSX::CFRunLoopGetCurrent(), OSX::KCFRunLoopDefaultMode) 34 | unless OSX::FSEventStreamStart(stream) 35 | puts "Failed to start stream" 36 | exit 37 | end 38 | 39 | OSX::CFRunLoopRun() 40 | rescue Interrupt 41 | OSX::FSEventStreamStop(stream) 42 | OSX::FSEventStreamInvalidate(stream) 43 | OSX::FSEventStreamRelease(stream) 44 | end 45 | 46 | def find_files_in_paths(*paths) 47 | current_dir = Dir.pwd.length + 1 48 | result = {} 49 | paths.each do |path| 50 | # Largely copied from autotest 51 | Find.find path do |f| 52 | Find.prune if @exceptions and f =~ @exceptions and test ?d, f 53 | next if test ?d, f 54 | next if f =~ /(swp|~|rej|orig)$/ 55 | next if f =~ /\/\.?#/ 56 | 57 | filename = f[current_dir..-1] 58 | result[filename] = File.stat(filename).mtime rescue next 59 | end 60 | end 61 | return result 62 | end 63 | 64 | def run_tests_in_paths(*paths) 65 | find_files_to_test(find_files_in_paths(*paths)) 66 | return if @files_to_test.empty? 67 | # Copied from autotest 68 | cmd = make_test_cmd @files_to_test 69 | 70 | hook :run_command 71 | puts cmd 72 | 73 | old_sync = $stdout.sync 74 | $stdout.sync = true 75 | @results = [] 76 | line = [] 77 | begin 78 | open("| #{cmd}", "r") do |f| 79 | until f.eof? do 80 | c = f.getc 81 | putc c 82 | line << c 83 | if c == ?\n then 84 | @results << line.pack("c*") 85 | line.clear 86 | end 87 | end 88 | end 89 | ensure 90 | $stdout.sync = old_sync 91 | end 92 | hook :ran_command 93 | @results = @results.join 94 | 95 | handle_results(@results) 96 | end 97 | end --------------------------------------------------------------------------------