Browse files

Begin Listen integration

  • Loading branch information...
1 parent aeeba27 commit e47850f4d07a0ef153ef5550bc20a86c0200c80a Thibaud Guillaume-Gentil committed Mar 19, 2012
View
1 .gitignore
@@ -5,6 +5,7 @@ doc/*
.*.swp
*.bak
.bundle
+bundle
.yardoc
.rbx
.rvmrc
View
2 guard.gemspec
@@ -15,7 +15,7 @@ Gem::Specification.new do |s|
s.rubyforge_project = 'guard'
s.add_dependency 'thor', '~> 0.14.6'
- s.add_dependency 'listen', '>= 0.3.1'
+ s.add_dependency 'listen', '>= 0.3.3'
s.add_development_dependency 'bundler'
s.add_development_dependency 'rspec', '~> 2.8.0'
View
69 lib/guard.rb
@@ -1,4 +1,5 @@
require 'thread'
+require 'listen'
# Guard is the main module for all Guard related modules and classes.
# Also other Guard implementation should use this namespace.
@@ -10,7 +11,6 @@ module Guard
autoload :DslDescriber, 'guard/dsl_describer'
autoload :Group, 'guard/group'
autoload :Interactor, 'guard/interactor'
- autoload :Listener, 'guard/listener'
autoload :Watcher, 'guard/watcher'
autoload :Notifier, 'guard/notifier'
autoload :Hook, 'guard/hook'
@@ -79,32 +79,6 @@ def initialize_all_templates
guard_gem_names.each {|g| initialize_template(g) }
end
- # Initialize the Guard singleton:
- #
- # - Initialize the internal Guard state.
- # - Create the interactor when necessary for user interaction.
- # - Select and initialize the file change listener.
- #
- # @option options [Boolean] clear if auto clear the UI should be done
- # @option options [Boolean] notify if system notifications should be shown
- # @option options [Boolean] verbose if verbose output should be shown
- # @option options [Array<String>] group the list of groups to start
- # @option options [String] watchdir the director to watch
- # @option options [String] guardfile the path to the Guardfile
- # @option options [Boolean] watch_all_modifications watches all file modifications if true
- #
- def setup(options = {})
- @lock = Mutex.new
- @options = options
- @guards = []
- self.reset_groups
- @listener = Listener.select_and_init(options)
-
- UI.clear if @options[:clear]
- debug_command_execution if @options[:verbose]
-
- self
- end
# Smart accessor for retrieving a specific guard or several guards at once.
#
@@ -177,6 +151,35 @@ def reset_groups
@groups = [Group.new(:default)]
end
+ # Initialize the Guard singleton:
+ #
+ # - Initialize the internal Guard state.
+ # - Create the interactor when necessary for user interaction.
+ # - Select and initialize the file change listener.
+ #
+ # @option options [Boolean] clear if auto clear the UI should be done
+ # @option options [Boolean] notify if system notifications should be shown
+ # @option options [Boolean] verbose if verbose output should be shown
+ # @option options [Array<String>] group the list of groups to start
+ # @option options [String] watchdir the director to watch
+ # @option options [String] guardfile the path to the Guardfile
+ # @option options [Boolean] watch_all_modifications watches all file modifications if true
+ #
+ def setup(options = {})
+ @lock = Mutex.new
+ @options = options
+ @watchdir = (options[:watchdir] && File.expand_path(options[:watchdir])) || Dir.pwd
+ @guards = []
+ self.reset_groups
+
+ @listener = Listen.to(@watchdir, Hash.new(options))
+
+ UI.clear if @options[:clear]
+ debug_command_execution if @options[:verbose]
+
+ self
+ end
+
# Start Guard by evaluate the `Guardfile`, initialize the declared Guards
# and start the available file change listener.
# Main method for Guard that is called from the CLI when guard starts.
@@ -202,12 +205,13 @@ def start(options = {})
options[:notify] && ENV['GUARD_NOTIFY'] != 'false' ? Notifier.turn_on : Notifier.turn_off
- listener.on_change do |files|
- Dsl.reevaluate_guardfile if Watcher.match_guardfile?(files)
- listener.changed_files += files if Watcher.match_files?(guards, files)
+ callback = lambda do |modified, added, removed|
+ Dsl.reevaluate_guardfile if Watcher.match_guardfile?(modified)
+ run_on_change(modified) if Watcher.match_files?(guards, modified)
end
+ @listener = listener.change(&callback)
- UI.info "Guard is now watching at '#{ listener.directory }'"
+ UI.info "Guard is now watching at '#{ @watchdir }'"
run_on_guards do |guard|
run_supervised_task(guard, :start)
@@ -263,8 +267,7 @@ def run_all(scopes)
def pause
if listener.paused?
UI.info 'Un-paused files modification listening', :reset => true
- listener.clear_changed_files
- listener.run
+ listener.unpause
else
UI.info 'Paused files modification listening', :reset => true
listener.pause
View
376 lib/guard/listener.rb
@@ -1,376 +0,0 @@
-require 'rbconfig'
-require 'digest/sha1'
-
-module Guard
-
- autoload :Darwin, 'guard/listeners/darwin'
- autoload :Linux, 'guard/listeners/linux'
- autoload :Windows, 'guard/listeners/windows'
- autoload :Polling, 'guard/listeners/polling'
-
- # The Listener is the base class for all listener
- # implementations.
- #
- # @abstract
- #
- class Listener
-
- # Default paths that gets ignored by the listener
- DEFAULT_IGNORE_PATHS = %w[. .. .bundle .git log tmp vendor]
-
- attr_accessor :changed_files
- attr_reader :directory, :ignore_paths
-
- def paused?
- @paused
- end
-
- # Select the appropriate listener implementation for the
- # current OS and initializes it.
- #
- # @param [Hash] options the options for the listener
- # @option options [String] watchdir the directory to watch
- # @return [Guard::Listener] the chosen listener
- #
- def self.select_and_init(options = {})
- watchdir = options && options[:watchdir] && File.expand_path(options[:watchdir])
- watchdir = Dir.pwd unless watchdir
-
- no_vendor = options && options[:no_vendor] ? options[:no_vendor] : false
-
- if mac? && Darwin.usable?(no_vendor)
- Darwin.new(watchdir, options)
- elsif linux? && Linux.usable?(no_vendor)
- Linux.new(watchdir, options)
- elsif windows? && Windows.usable?(no_vendor)
- Windows.new(watchdir, options)
- else
- UI.info 'Using polling (Please help us to support your system better than that).'
- Polling.new(watchdir, options)
- end
- end
-
- # Initialize the listener.
- #
- # @param [String] directory the root directory to listen to
- # @option options [Boolean] relativize_paths use only relative paths
- # @option options [Boolean] watch_all_modifications to enable deleted and moved file listening.
- # @option options [Array<String>] ignore_paths the paths to ignore by the listener
- #
- def initialize(directory = Dir.pwd, options = {})
- @sha1_checksums_hash = {}
- @file_timestamp_hash = {}
- @changed_files = []
- @paused = false
-
- @directory = directory.to_s
-
- options = options.inject({}) { |h,(k,v)| h[k.to_sym] = v; h }
- @relativize_paths = options.fetch(:relativize_paths, true)
- @watch_all_modifications = options.fetch(:watch_all_modifications, false)
-
- @ignore_paths = DEFAULT_IGNORE_PATHS
- @ignore_paths |= options[:ignore_paths] if options[:ignore_paths]
-
- update_last_event
- start_reactor
- end
-
- # Start the listener thread.
- #
- def start_reactor
- return if ENV["GUARD_ENV"] == 'test'
-
- Thread.new do
- loop do
- if @changed_files != [] && !@paused
- changed_files = @changed_files.dup
- clear_changed_files
- ::Guard.run_on_change(changed_files)
- else
- sleep 0.1
- end
- end
- end
- end
-
- # Start watching the root directory.
- #
- def start
- watch(@directory)
- timestamp_files if watch_all_modifications?
- end
-
- # Stop listening for events.
- #
- def stop
- end
-
- # Pause the listener to ignore change events.
- #
- def pause
- @paused = true
- end
-
- # Unpause the listener to listen again to change events.
- #
- def run
- @paused = false
- end
-
- # Clear the list of changed files.
- #
- def clear_changed_files
- @changed_files.clear
- end
-
- # Store a listener callback.
- #
- # @param [Block] callback the callback to store
- #
- def on_change(&callback)
- @callback = callback
- end
-
- # Updates the timestamp of the last event.
- #
- def update_last_event
- @last_event = Time.now
- end
-
- # Get the modified files.
- #
- # If the `:watch_all_modifications` option is true, then moved and
- # deleted files are also reported, but prefixed by an exclamation point.
- #
- # @example Deleted or moved file
- # !/home/user/dir/file.rb
- #
- # @param [Array<String>] dirs the watched directories
- # @param [Hash] options the listener options
- # @option options [Symbol] all whether to files in sub directories
- # @return [Array<String>] paths of files that have been modified
- #
- def modified_files(dirs, options = {})
- last_event = @last_event
- files = []
-
- if watch_all_modifications?
- deleted_files = @file_timestamp_hash.collect do |path, ts|
- unless File.exists?(path)
- @sha1_checksums_hash.delete(path)
- @file_timestamp_hash.delete(path)
- "!#{path}"
- end
- end
- files.concat(deleted_files.compact)
- end
- update_last_event
- updated_files = potentially_modified_files(dirs, options).select do |path|
- file_modified?(path, last_event)
- end
- files.concat(updated_files)
-
- relativize_paths(files)
- end
-
- # Register a directory to watch.
- # Must be implemented by the subclasses.
- #
- # @param [String] directory the directory to watch
- #
- def watch(directory)
- raise NotImplementedError, "do whatever you want here, given the directory as only argument"
- end
-
- # Get all files that are in the watched directory.
- #
- # @return [Array<String>] the list of files
- #
- def all_files
- potentially_modified_files([@directory], :all => true)
- end
-
- # Scopes all given paths to the current directory.
- #
- # @param [Array<String>] paths the paths to change
- # @return [Array<String>] all paths now relative to the current dir
- #
- def relativize_paths(paths)
- return paths unless relativize_paths?
- paths.map do |path|
- path.gsub(%r{^(!)?#{ @directory }/},'\1')
- end
- end
-
- # Use paths relative to the current directory.
- #
- # @return [Boolean] whether to use relative or absolute paths
- #
- def relativize_paths?
- !!@relativize_paths
- end
-
- # test if the listener should also watch for deleted and
- # moved files
- #
- # @return [Boolean] whether to watch all file modifications or not
- #
- def watch_all_modifications?
- !!@watch_all_modifications
- end
-
- # Populate initial timestamp file hash to watch for deleted or moved files.
- #
- def timestamp_files
- all_files.each { |path| set_file_timestamp_hash(path) }
- end
-
- # Removes the ignored paths from the directory list.
- #
- # @param [Array<String>] dirs the directory to listen to
- # @param [Array<String>] ignore_paths the paths to ignore
- # @return children of the passed dirs that are not in the ignore_paths list
- #
- def exclude_ignored_paths(dirs, ignore_paths = self.ignore_paths)
- Dir.glob(dirs.map { |d| "#{d.sub(%r{/+$}, '')}/*" }, File::FNM_DOTMATCH).reject do |path|
- ignore_paths.include?(File.basename(path))
- end
- end
-
- private
-
- # Gets a list of files that are in the modified directories.
- #
- # @param [Array<String>] dirs the list of directories
- # @param [Hash] options the find file option
- # @option options [Symbol] all whether to files in sub directories
- #
- def potentially_modified_files(dirs, options = {})
- paths = exclude_ignored_paths(dirs)
-
- if options[:all]
- paths.inject([]) do |array, path|
- if File.file?(path)
- array << path
- else
- array += Dir.glob("#{ path }/**/*", File::FNM_DOTMATCH).select { |p| File.file?(p) }
- end
- array
- end
- else
- paths.select { |path| File.file?(path) }
- end
- end
-
- # Test if the file content has changed.
- #
- # Depending on the filesystem, mtime/ctime is probably only precise to the second, so round
- # both values down to the second for the comparison.
- #
- # ctime is used only on == comparison to always catches Rails 3.1 Assets pipelined on Mac OSX
- #
- # @param [String] path the file path
- # @param [Time] last_event the time of the last event
- # @return [Boolean] Whether the file content has changed or not.
- #
- def file_modified?(path, last_event)
- ctime = File.ctime(path).to_i
- mtime = File.mtime(path).to_i
-
- if [mtime, ctime].max == last_event.to_i
- file_content_modified?(path)
- elsif mtime > last_event.to_i
- set_sha1_checksums_hash(path)
- set_file_timestamp_hash(path) if watch_all_modifications?
- true
- elsif watch_all_modifications?
- ts = file_timestamp(path)
- if ts != @file_timestamp_hash[path]
- set_file_timestamp_hash(path, ts)
- true
- end
- else
- false
- end
- rescue
- false
- end
-
- # Tests if the file content has been modified by
- # comparing the SHA1 checksum.
- #
- # @param [String] path the file path
- #
- def file_content_modified?(path)
- checksum = sha1_checksum(path)
- if @sha1_checksums_hash[path] != checksum
- set_sha1_checksums_hash(path, checksum)
- true
- else
- false
- end
- end
-
- # Set save a files current timestamp
- #
- # @param [String] path the file path
- # @param [Integer] file_timestamp the files modified timestamp
- #
- def set_file_timestamp_hash(path, timestamp = nil)
- @file_timestamp_hash[path] = timestamp ? timestamp : file_timestamp(path)
- end
-
- # Set the current checksum of a file.
- #
- # @param [String] path the file path
- # @param [String] sha1_checksum the checksum of the file
- #
- def set_sha1_checksums_hash(path, checksum = nil)
- @sha1_checksums_hash[path] = checksum ? checksum : sha1_checksum(path)
- end
-
- # Gets a files modified timestamp
- #
- # @param [String] path the file path
- # @return [Integer] file modified timestamp
- #
- def file_timestamp(path)
- File.mtime(path).to_i
- end
-
- # Calculates the SHA1 checksum of a file.
- #
- # @param [String] path the path to the file
- # @return [String] the SHA1 checksum
- #
- def sha1_checksum(path)
- Digest::SHA1.file(path).to_s
- end
-
- # Test if the OS is Mac OS X.
- #
- # @return [Boolean] Whether the OS is Mac OS X
- #
- def self.mac?
- RbConfig::CONFIG['target_os'] =~ /darwin/i
- end
-
- # Test if the OS is Linux.
- #
- # @return [Boolean] Whether the OS is Linux
- #
- def self.linux?
- RbConfig::CONFIG['target_os'] =~ /linux/i
- end
-
- # Test if the OS is Windows.
- #
- # @return [Boolean] Whether the OS is Windows
- #
- def self.windows?
- RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
- end
-
- end
-end
View
62 lib/guard/listeners/darwin.rb
@@ -1,62 +0,0 @@
-module Guard
-
- # Listener implementation for Mac OS X `FSEvents`.
- #
- class Darwin < Listener
-
- # Initialize the Listener.
- #
- def initialize(*)
- super
- @fsevent = FSEvent.new
- end
-
- # Start the listener.
- #
- def start
- super
- worker.run
- end
-
- # Stop the listener.
- #
- def stop
- super
- worker.stop
- end
-
- # Check if the listener is usable on the current OS.
- #
- # @return [Boolean] whether usable or not
- #
- def self.usable?(no_vendor = false)
- return false unless RbConfig::CONFIG['target_os'] =~ /darwin(9|1\d)/i
-
- $LOAD_PATH << File.expand_path('../../../vendor/darwin/lib', __FILE__) unless no_vendor
- require 'rb-fsevent'
- true
- rescue LoadError
- false
- end
-
- private
-
- # Get the listener worker.
- #
- def worker
- @fsevent
- end
-
- # Watch the given directory for file changes.
- #
- # @param [String] directory the directory to watch
- #
- def watch(directory)
- worker.watch(directory) do |modified_dirs|
- files = modified_files(modified_dirs)
- @callback.call(files) unless files.empty?
- end
- end
-
- end
-end
View
93 lib/guard/listeners/linux.rb
@@ -1,93 +0,0 @@
-module Guard
-
- # Listener implementation for Linux `inotify`.
- #
- class Linux < Listener
-
- # Initialize the Listener.
- #
- def initialize(*)
- super
- @inotify = INotify::Notifier.new
- @files = []
- @latency = 0.5
- end
-
- # Start the listener.
- #
- def start
- @stop = false
- super
- watch_change unless watch_change?
- end
-
- # Stop the listener.
- #
- def stop
- super
- @stop = true
- end
-
- # Check if the listener is usable on the current OS.
- #
- # @return [Boolean] whether usable or not
- #
- def self.usable?(no_vendor = false)
- return false unless RbConfig::CONFIG['target_os'] =~ /linux/i
-
- $LOAD_PATH << File.expand_path('../../../vendor/linux/lib', __FILE__) unless no_vendor
- require 'rb-inotify'
- true
- rescue LoadError
- false
- end
-
- private
-
- # Get the listener worker.
- #
- def worker
- @inotify
- end
-
- # Watch the given directory for file changes.
- #
- # @param [String] directory the directory to watch
- #
- def watch(directory)
- worker.watch(directory, :recursive, :attrib, :create, :move_self, :close_write) do |event|
- unless event.name == "" # Event on root directory
- @files << event.absolute_name
- end
- end
- rescue Interrupt
- end
-
- # Test if inotify is watching for changes.
- #
- # @return [Boolean] whether inotify is active or not
- #
- def watch_change?
- !!(defined? @watch_change and @watch_change)
- end
-
- # Watch for file system changes.
- #
- def watch_change
- @watch_change = true
- until @stop
- if RbConfig::CONFIG['build'] =~ /java/ || IO.select([worker.to_io], [], [], @latency)
- break if @stop
-
- sleep(@latency)
- worker.process
-
- files = modified_files(@files.shift(@files.size).map { |f| File.dirname(f) }.uniq)
- @callback.call(files) unless files.empty?
- end
- end
- @watch_change = false
- end
-
- end
-end
View
55 lib/guard/listeners/polling.rb
@@ -1,55 +0,0 @@
-module Guard
-
- # Polling listener that works cross-platform and
- # has no dependencies. This is the listener that
- # uses the most CPU processing power and has higher
- # file IO that the other implementations.
- #
- class Polling < Listener
-
- # Initialize the Listener.
- #
- def initialize(*)
- super
- @latency = 1.5
- end
-
- # Start the listener.
- #
- def start
- @stop = false
- super
- watch_change
- end
-
- # Stop the listener.
- #
- def stop
- super
- @stop = true
- end
-
- # Watch the given directory for file changes.
- #
- # @param [String] directory the directory to watch
- #
- def watch(directory)
- @existing = all_files
- end
-
- private
-
- # Watch for file system changes.
- #
- def watch_change
- until @stop
- start = Time.now.to_f
- files = modified_files([@directory], :all => true)
- @callback.call(files) unless files.empty?
- nap_time = @latency - (Time.now.to_f - start)
- sleep(nap_time) if nap_time > 0
- end
- end
-
- end
-end
View
63 lib/guard/listeners/windows.rb
@@ -1,63 +0,0 @@
-module Guard
-
- # Listener implementation for Windows `fchange`.
- #
- class Windows < Listener
-
- # Initialize the Listener.
- #
- def initialize(*)
- super
- @fchange = FChange::Notifier.new
- end
-
- # Start the listener.
- #
- def start
- super
- worker.run
- end
-
- # Stop the listener.
- #
- def stop
- super
- worker.stop
- end
-
- # Check if the listener is usable on the current OS.
- #
- # @return [Boolean] whether usable or not
- #
- def self.usable?(no_vendor = false)
- return false unless RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
-
- $LOAD_PATH << File.expand_path('../../../vendor/windows/lib', __FILE__) unless no_vendor
- require 'rb-fchange'
- true
- rescue LoadError
- false
- end
-
- private
-
- # Watch the given directory for file changes.
- #
- # @param [String] directory the directory to watch
- #
- def watch(directory)
- worker.watch(directory, :all_events, :recursive) do |event|
- paths = [File.expand_path(event.watcher.path)]
- files = modified_files(paths, :all => true)
- @callback.call(files) unless files.empty?
- end
- end
-
- # Get the listener worker.
- #
- def worker
- @fchange
- end
-
- end
-end
View
434 spec/guard/listener_spec.rb
@@ -1,434 +0,0 @@
-require 'spec_helper'
-
-describe Guard::Listener do
-
- describe '.select_and_init' do
- before(:each) { @target_os = RbConfig::CONFIG['target_os'] }
- after(:each) { RbConfig::CONFIG['target_os'] = @target_os }
-
- it 'uses the Darwin listener on Mac OS X' do
- RbConfig::CONFIG['target_os'] = 'darwin10.4.0'
- Guard::Darwin.stub(:usable?).and_return(true)
- Guard::Darwin.should_receive(:new)
- described_class.select_and_init
- end
-
- it 'uses the Windows listener on Windows' do
- RbConfig::CONFIG['target_os'] = 'mingw'
- Guard::Windows.stub(:usable?).and_return(true)
- Guard::Windows.should_receive(:new)
- described_class.select_and_init
- end
-
- it 'uses the Linux listener on Linux' do
- RbConfig::CONFIG['target_os'] = 'linux'
- Guard::Linux.stub(:usable?).and_return(true)
- Guard::Linux.should_receive(:new)
- described_class.select_and_init
- end
-
- it 'forwards its options to the constructor' do
- described_class.stub!(:mac?).and_return(true)
- Guard::Darwin.stub!(:usable?).and_return(true)
-
- opts = { :foo => 23 }
- Guard::Darwin.should_receive(:new).with(anything(), opts).and_return(true)
- described_class.select_and_init(opts)
- end
-
- context 'with an explicit watch directory' do
- it 'uses the given working directory' do
- RbConfig::CONFIG['target_os'] = 'darwin10.4.0'
- Guard::Darwin.stub(:usable?).and_return(true)
- Guard::Darwin.should_receive(:new).with('/Users/mrx/projects/secret', { :watchdir => '/Users/mrx/projects/secret' })
- described_class.select_and_init({ :watchdir => '/Users/mrx/projects/secret' })
- end
- end
-
- context 'without an explicit watch directory' do
- it 'uses the current working directory' do
- RbConfig::CONFIG['target_os'] = 'darwin10.4.0'
- Guard::Darwin.stub(:usable?).and_return(true)
- Guard::Darwin.should_receive(:new).with(Dir.pwd, {})
- described_class.select_and_init
- end
- end
- end
-
- describe '#initialize' do
- context 'with a directory parameter' do
- it 'ensures the directory is a String' do
- listener = described_class.new(Pathname.new('/tmp'))
- listener.directory.should eql '/tmp'
- end
- end
-
- context 'without a directory parameter' do
- it 'takes the current working directory' do
- listener = described_class.new()
- listener.directory.should eql Dir.pwd.to_s
- end
- end
-
- context 'with the relativize_paths option' do
- it 'takes the passed option value from a string key' do
- listener = described_class.new('/tmp', { 'relativize_paths' => false })
- listener.relativize_paths?.should be_false
- end
-
- it 'takes the passed option value from a symbol key' do
- listener = described_class.new('/tmp', { :relativize_paths => false })
- listener.relativize_paths?.should be_false
- end
- end
-
- context 'without the relativize_paths option' do
- it 'sets it to true as default' do
- listener = described_class.new
- listener.relativize_paths?.should be_true
- end
- end
-
- context 'with the watch_all_modifications option' do
- it 'takes the passed option value from a string key' do
- listener = described_class.new('/tmp', { 'watch_all_modifications' => true })
- listener.watch_all_modifications?.should be_true
- end
-
- it 'takes the passed option value from a symbol key' do
- listener = described_class.new('/tmp', { :watch_all_modifications => true })
- listener.watch_all_modifications?.should be_true
- end
- end
-
- context 'without the watch_all_modifications option' do
- it 'sets it to false as default' do
- listener = described_class.new
- listener.watch_all_modifications?.should be_false
- end
- end
-
- context 'without the ignored_paths options' do
- it 'sets the default ignore paths' do
- listener = described_class.new
- listener.ignore_paths.should =~ %w[. .. .bundle .git log tmp vendor]
- end
- end
-
- context 'with the ignored_paths options' do
- it 'adds the paths to the default ignore paths' do
- listener = described_class.new('/tmp', { :ignore_paths => %w[.idea coverage] })
- listener.ignore_paths.should =~ %w[. .. .bundle .git log tmp vendor .idea coverage]
- end
- end
- end
-
- describe '#all_files' do
- subject { described_class.new(@fixture_path) }
-
- it 'should return all files' do
- subject.all_files.should =~
- Dir.glob("#{ @fixture_path }/**/*", File::FNM_DOTMATCH).select { |file| File.file?(file) }
- end
- end
-
- describe '#relativize_paths' do
- subject { described_class.new('/tmp') }
-
- let(:paths) { %w( /tmp/a /tmp/a/b /tmp/a.b/c.d ) }
-
- it 'should relativize paths to the configured directory' do
- subject.relativize_paths(paths).should =~ %w( a a/b a.b/c.d )
- end
-
- context 'when set to false' do
- subject { described_class.new('/tmp', :relativize_paths => false) }
-
- it 'can be disabled' do
- subject.relativize_paths(paths).should eql paths
- end
- end
- end
-
- describe '#update_last_event' do
- subject { described_class.new }
-
- it 'updates the last event to the current time' do
- time = Time.now
- subject.update_last_event
- subject.instance_variable_get(:@last_event).to_i.should >= time.to_i
- end
- end
-
- describe '#modified_files' do
- subject { described_class.new }
-
- let(:file) { fixture('folder1/newfile.rb') }
- let(:file1) { fixture('folder1/file1.txt') }
- let(:file2) { fixture('folder1/folder2/file2.txt') }
- let(:file3) { fixture('folder1/deletedfile1.txt') }
- let(:file4) { fixture('folder1/movedfile1.txt') }
- let(:file5) { fixture('folder1/folder2/movedfile1.txt') }
-
- before { listen_to subject }
-
- context 'for a new file' do
- before { FileUtils.rm(file) if File.exists?(file) }
- after { FileUtils.rm(file) }
-
- it 'catches the creation' do
- FileUtils.rm(file) if File.exists?(file)
- File.exists?(file).should be_false
-
- watch do
- FileUtils.touch(file)
- end
-
- subject.modified_files([fixture('folder1')], {}).should =~
- ['spec/fixtures/folder1/newfile.rb']
- end
- end
-
- context 'without the :all option' do
- it 'finds modified files only in the directory supplied' do
- watch do
- FileUtils.touch([file1, file2, file3])
- end
-
- subject.modified_files([fixture('folder1')], {}).should =~
- ['spec/fixtures/folder1/deletedfile1.txt', 'spec/fixtures/folder1/file1.txt']
- end
- end
-
- context 'with the :all options' do
- it 'finds modified files within subdirectories' do
- watch do
- FileUtils.touch([file1, file2, file3])
- end
-
- subject.modified_files([fixture('folder1')], { :all => true }).should =~
- ['spec/fixtures/folder1/deletedfile1.txt',
- 'spec/fixtures/folder1/file1.txt',
- 'spec/fixtures/folder1/folder2/file2.txt']
- end
- end
-
- context 'without updating the content' do
- it 'ignores the files for the second time' do
- watch do
- FileUtils.touch([file1, file2, file3])
- subject.modified_files([fixture('folder1')], {}).should =~
- ['spec/fixtures/folder1/deletedfile1.txt', 'spec/fixtures/folder1/file1.txt']
-
- subject.update_last_event
-
- FileUtils.touch([file1, file2, file3])
- subject.modified_files([fixture('folder1')], {}).should be_empty
- end
- end
- end
-
- context 'with content that has changed' do
- after { File.open(file1, 'w') { |f| f.write('') } }
-
- it 'identifies the files for the second time' do
- watch do
- FileUtils.touch([file1, file2, file3])
- subject.modified_files([fixture('folder1')], {}).should =~
- ['spec/fixtures/folder1/deletedfile1.txt', 'spec/fixtures/folder1/file1.txt']
-
- subject.update_last_event
-
- FileUtils.touch([file2, file3])
- File.open(file1, 'w') { |f| f.write('changed content') }
- subject.modified_files([fixture('folder1')], {}).should =~
- ['spec/fixtures/folder1/file1.txt']
- end
- end
- end
-
- context 'without the :watch_all_modifications option' do
- it 'defaults to false' do
- subject.watch_all_modifications?.should be_false
- end
-
- context 'for a deleted file' do
- after { FileUtils.touch(file3) }
-
- it 'does not catch the deletion' do
- File.exists?(file3).should be_true
-
- watch do
- FileUtils.rm(file3)
- end
-
- subject.modified_files([fixture('folder1')], {}).should eq []
- end
- end
-
- context 'for a moved file' do
- after { FileUtils.mv(file4, file1) }
-
- it 'does not catch the move' do
- File.exists?(file1).should be_true
- File.exists?(file4).should be_false
-
- watch do
- FileUtils.mv(file1, file4)
- end
-
- subject.modified_files([fixture('folder1')], {}).should eq []
- end
- end
- end
-
- context 'with the :watch_all_modifications option' do
- subject { described_class.new(Dir.pwd, :watch_all_modifications => true) }
-
- before do
- subject.timestamp_files
- subject.update_last_event
- end
-
- it 'should be true when set' do
- subject.watch_all_modifications?.should be_true
- end
-
- context 'for a new file then deleted then re-created and re-deleted' do
- after { FileUtils.touch(file1) }
-
- it 'catches all the events' do
- FileUtils.rm(file1) if File.exists?(file1)
- File.exists?(file1).should be_false
-
- watch do
- FileUtils.touch(file1)
- File.exists?(file1).should be_true
- subject.modified_files([fixture('folder1')], {}).should =~
- ['spec/fixtures/folder1/file1.txt']
-
- subject.update_last_event
-
- FileUtils.rm(file1)
- File.exists?(file1).should be_false
- subject.modified_files([fixture('folder1')], {}).should =~
- ['!spec/fixtures/folder1/file1.txt']
-
- subject.update_last_event
- sleep(sleep_time)
-
- FileUtils.touch(file1)
- File.exists?(file1).should be_true
- subject.modified_files([fixture('folder1')], {}).should =~
- ['spec/fixtures/folder1/file1.txt']
-
- subject.update_last_event
-
- FileUtils.rm(file1)
- File.exists?(file1).should be_false
- subject.modified_files([fixture('folder1')], {}).should =~
- ['!spec/fixtures/folder1/file1.txt']
- end
-
- end
- end
-
- context 'for a deleted file' do
- after { FileUtils.touch(file3) }
-
- it 'catches the deletion' do
- File.exists?(file3).should be_true
-
- watch do
- FileUtils.rm(file3)
- end
-
- subject.modified_files([fixture('folder1')], {}).should =~
- ['!spec/fixtures/folder1/deletedfile1.txt']
- end
- end
-
- context 'for a moved file' do
- after { FileUtils.mv(file4, file1) }
-
- it 'catches the move' do
- File.exists?(file1).should be_true
- File.exists?(file4).should be_false
-
- watch do
- FileUtils.mv(file1, file4)
- end
-
- subject.modified_files([fixture('folder1')], {}).should =~
- ['!spec/fixtures/folder1/file1.txt', 'spec/fixtures/folder1/movedfile1.txt']
- end
- end
- end
- end
-
- describe 'working directory' do
- context 'unspecified' do
- subject { described_class.new }
-
- it 'defaults to Dir.pwd' do
- subject.directory.should eql Dir.pwd
- end
-
- it 'can be not changed' do
- subject.should_not respond_to(:directory=)
- end
- end
-
- context 'specified as first argument to ::new' do
- let(:working_directory) { fixture('folder1') }
-
- subject { described_class.new working_directory }
-
- before { listen_to subject }
-
- it 'can be inspected' do
- subject.instance_variable_get(:@directory).should eql working_directory.to_s
- end
-
- it 'can be not changed' do
- subject.should_not respond_to(:directory=)
- end
-
- it 'will be used to watch' do
- subject.should_receive(:watch).with(working_directory.to_s)
- start
- stop
- end
- end
- end
-
- describe '#ignore_paths' do
- it 'defaults to the default ignore paths' do
- described_class.new.ignore_paths.should == Guard::Listener::DEFAULT_IGNORE_PATHS
- end
-
- it 'can be added to via :ignore_paths option' do
- listener = described_class.new 'path', :ignore_paths => ['foo', 'bar']
- listener.ignore_paths.should include('foo', 'bar')
- end
- end
-
- describe '#exclude_ignored_paths [<dirs>]' do
- let(:ignore_paths) { nil }
- subject { described_class.new(@fixture_path, { :ignore_paths => ignore_paths }) }
-
- it 'returns children of <dirs>' do
- subject.exclude_ignored_paths(['spec/fixtures']).should =~
- ['spec/fixtures/.dotfile', 'spec/fixtures/folder1', 'spec/fixtures/Guardfile']
- end
-
- describe 'when ignore_paths set to some of <dirs> children' do
- let(:ignore_paths) { ['Guardfile', '.dotfile'] }
-
- it 'excludes the ignored paths' do
- subject.exclude_ignored_paths(['spec/fixtures']).should =~ ['spec/fixtures/folder1']
- end
- end
- end
-
-end
View
27 spec/guard/listeners/darwin_spec.rb
@@ -1,27 +0,0 @@
-require 'spec_helper'
-require 'guard/listeners/darwin'
-
-describe Guard::Darwin do
-
- if windows?
- it "isn't usable on windows" do
- described_class.should_not be_usable
- end
- end
-
- if linux?
- it "isn't usable on linux" do
- described_class.should_not be_usable
- end
- end
-
- if mac? && Guard::Darwin.usable?
- it "is usable on 10.6" do
- described_class.should be_usable
- end
-
- it_should_behave_like "a listener that reacts to #on_change"
- it_should_behave_like "a listener scoped to a specific directory"
- end
-
-end
View
77 spec/guard/listeners/linux_spec.rb
@@ -1,77 +0,0 @@
-require 'spec_helper'
-require 'fileutils'
-require 'guard/listeners/linux'
-
-describe Guard::Linux do
-
- if mac?
- it "isn't usable on 10.6" do
- described_class.should_not be_usable
- end
- end
-
- if windows?
- it "isn't usable on windows" do
- described_class.should_not be_usable
- end
- end
-
- if linux? && Guard::Linux.usable?
- it "is usable on linux" do
- described_class.should be_usable
- end
-
- describe "#start", :long_running => true do
- before(:each) do
- @listener = Guard::Linux.new
- @listener.instance_variable_get(:@inotify).stub(:process)
- end
-
- it "calls watch_change on the first start" do
- @listener.should_receive(:watch_change)
- start
- end
-
- it "doesn't call watch_change on subsequent starts after a stop" do
- @listener.stub!(:stop)
- start
- stop
- @listener.should be_watch_change
- @listener.should_not_receive(:watch_change)
- start
- @listener.unstub!(:stop)
- stop
- @listener.should_not be_watch_change
- end
- end
-
- it_should_behave_like "a listener that reacts to #on_change"
- it_should_behave_like "a listener scoped to a specific directory"
-
- # Fun fact: FileUtils.touch seems not to be enough on Linux to trigger a modify event
-
- it "catches modified files with glib saving routine (like Vim, Emacs or Gedit)" do
- @listener = described_class.new
- record_results
- file = @fixture_path.join("folder1/file1.txt")
- File.exists?(file).should be_true
- start
- File.open(file, 'r+').close
- FileUtils.touch(file)
- stop
- results.should =~ ['spec/fixtures/folder1/file1.txt']
- end
-
- it "doesn't process a change when it is stopped" do
- @listener = described_class.new
- record_results
- file = @fixture_path.join("folder1/file1.txt")
- File.exists?(file).should be_true
- start
- @listener.instance_variable_get(:@inotify).should_not_receive(:process)
- stop
- File.open(file, 'w') {|f| f.write('') }
- end
- end
-
-end
View
9 spec/guard/listeners/polling_spec.rb
@@ -1,9 +0,0 @@
-require 'spec_helper'
-require 'guard/listeners/polling'
-
-describe Guard::Polling do
-
- it_should_behave_like "a listener that reacts to #on_change"
- it_should_behave_like "a listener scoped to a specific directory"
-
-end
View
27 spec/guard/listeners/windows_spec.rb
@@ -1,27 +0,0 @@
-require 'spec_helper'
-require 'guard/listeners/windows'
-
-describe Guard::Windows do
-
- if linux?
- it "isn't usable on linux" do
- described_class.should_not be_usable
- end
- end
-
- if mac?
- it "isn't usable on Mac" do
- described_class.should_not be_usable
- end
- end
-
- if windows?
- it "is usable on Windows 2000 and later" do
- described_class.should be_usable
- end
-
- it_should_behave_like "a listener that reacts to #on_change"
- it_should_behave_like "a listener scoped to a specific directory"
- end
-
-end
View
2 spec/guard_spec.rb
@@ -125,7 +125,7 @@
end
it "initializes the listener" do
- ::Guard.listener.should be_kind_of(Guard::Listener)
+ ::Guard.listener.should be_kind_of(Listen::Listener)
end
it "respect the watchdir option" do

0 comments on commit e47850f

Please sign in to comment.