listen-2.4.0/0000755000004100000410000000000012261232670013044 5ustar www-datawww-datalisten-2.4.0/Rakefile0000644000004100000410000000016112261232670014507 0ustar www-datawww-datarequire 'bundler/gem_tasks' require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) task default: :spec listen-2.4.0/Gemfile0000644000004100000410000000053712261232670014344 0ustar www-datawww-datasource 'https://rubygems.org' gemspec require 'rbconfig' gem 'wdm', '>= 0.1.0' if RbConfig::CONFIG['target_os'] =~ /mswin|mingw|cygwin/i gem 'rb-kqueue', '>= 0.2' if RbConfig::CONFIG['target_os'] =~ /freebsd/i group :tool do gem 'yard', require: false gem 'guard-rspec', require: false end group :test do gem 'coveralls', require: false end listen-2.4.0/.rspec0000644000004100000410000000003712261232670014161 0ustar www-datawww-data--color --format documentation listen-2.4.0/LICENSE.txt0000644000004100000410000000207112261232670014667 0ustar www-datawww-dataCopyright (c) 2013 Thibaud Guillaume-Gentil MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. listen-2.4.0/spec/0000755000004100000410000000000012261232670013776 5ustar www-datawww-datalisten-2.4.0/spec/spec_helper.rb0000644000004100000410000000155612261232670016623 0ustar www-datawww-datarequire 'rubygems' require 'listen' def ci?; ENV['CI'] end if ci? require 'coveralls' Coveralls.wear! end Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f } # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration RSpec.configure do |config| config.color_enabled = true config.order = :random config.filter_run focus: true config.treat_symbols_as_metadata_keys_with_true_values = true config.run_all_when_everything_filtered = true config.fail_fast = !ci? config.expect_with :rspec do |c| c.syntax = :expect end end require 'rspec/retry' RSpec.configure do |config| config.default_retry_count = ci? ? 3 : 1 end require 'celluloid/rspec' Thread.abort_on_exception = true Celluloid.logger.level = Logger::ERROR RSpec.configuration.before(:each) do Listen.stopping = false Celluloid.shutdown Celluloid.boot end listen-2.4.0/spec/lib/0000755000004100000410000000000012261232670014544 5ustar www-datawww-datalisten-2.4.0/spec/lib/listen/0000755000004100000410000000000012261232670016042 5ustar www-datawww-datalisten-2.4.0/spec/lib/listen/listener_spec.rb0000644000004100000410000001725312261232670021236 0ustar www-datawww-datarequire 'spec_helper' describe Listen::Listener do let(:listener) { Listen::Listener.new(options) } let(:options) { {} } let(:registry) { double(Celluloid::Registry, :[]= => true) } let(:supervisor) { double(Celluloid::SupervisionGroup, add: true, pool: true) } let(:record) { double(Listen::Record, terminate: true, build: true) } let(:silencer) { double(Listen::Silencer, terminate: true) } let(:adapter) { double(Listen::Adapter::Base) } let(:change_pool) { double(Listen::Change, terminate: true) } let(:change_pool_async) { double('ChangePoolAsync') } before { Celluloid::Registry.stub(:new) { registry } Celluloid::SupervisionGroup.stub(:run!) { supervisor } registry.stub(:[]).with(:silencer) { silencer } registry.stub(:[]).with(:adapter) { adapter } registry.stub(:[]).with(:record) { record } registry.stub(:[]).with(:change_pool) { change_pool } } describe "initialize" do it "sets paused to false" do expect(listener).not_to be_paused end it "sets block" do block = Proc.new { |modified, added, removed| } listener = Listen::Listener.new('lib', &block) expect(listener.block).not_to be_nil end it "sets directories with realpath" do listener = Listen::Listener.new('lib', 'spec') expect(listener.directories).to eq [Pathname.new("#{Dir.pwd}/lib"), Pathname.new("#{Dir.pwd}/spec")] end end describe "options" do it "sets default options" do expect(listener.options).to eq({ debug: false, latency: nil, wait_for_delay: 0.1, force_polling: false, polling_fallback_message: nil }) end it "sets new options on initialize" do listener = Listen::Listener.new('lib', latency: 1.234, wait_for_delay: 0.85) expect(listener.options).to eq({ debug: false, latency: 1.234, wait_for_delay: 0.85, force_polling: false, polling_fallback_message: nil }) end end describe "#start" do before { adapter.stub_chain(:async, :start) } it "traps INT signal" do expect(Signal).to receive(:trap).with('INT') listener.start end it "registers silencer" do expect(supervisor).to receive(:add).with(Listen::Silencer, as: :silencer, args: listener) listener.start end it "supervises change_pool" do expect(supervisor).to receive(:pool).with(Listen::Change, as: :change_pool, args: listener) listener.start end it "supervises adaper" do Listen::Adapter.stub(:select) { Listen::Adapter::Polling } expect(supervisor).to receive(:add).with(Listen::Adapter::Polling, as: :adapter, args: listener) listener.start end it "supervises record" do expect(supervisor).to receive(:add).with(Listen::Record, as: :record, args: listener) listener.start end it "builds record" do expect(record).to receive(:build) listener.start end it "sets paused to false" do listener.start expect(listener.paused).to be_false end it "starts adapter asynchronously" do async_stub = double expect(adapter).to receive(:async) { async_stub } expect(async_stub).to receive(:start) listener.start end it "starts adapter asynchronously" do async_stub = double expect(adapter).to receive(:async) { async_stub } expect(async_stub).to receive(:start) listener.start end it "calls block on changes" do listener.changes = [{ modified: 'foo' }] block_stub = double('block') listener.block = block_stub expect(block_stub).to receive(:call).with(['foo'], [], []) listener.start sleep 0.01 end end describe "#stop" do let(:thread) { double(join: true) } before { listener.stub(:thread) { thread } } it "joins thread" do expect(thread).to receive(:join) listener.stop end end describe "#pause" do it "sets paused to true" do listener.pause expect(listener.paused).to be_true end end describe "#unpause" do it "builds record" do expect(record).to receive(:build) listener.unpause end it "sets paused to false" do record.stub(:build) listener.unpause expect(listener.paused).to be_false end end describe "#paused?" do it "returns true when paused" do listener.paused = true expect(listener).to be_paused end it "returns false when not paused (nil)" do listener.paused = nil expect(listener).not_to be_paused end it "returns false when not paused (false)" do listener.paused = false expect(listener).not_to be_paused end end describe "#paused?" do it "returns true when not paused (false)" do listener.paused = false expect(listener.listen?).to be_true end it "returns false when not paused (nil)" do listener.paused = nil expect(listener.listen?).to be_false end it "returns false when paused" do listener.paused = true expect(listener.listen?).to be_false end end describe "#ignore" do let(:new_silencer) { double(Listen::Silencer) } before { Celluloid::Actor.stub(:[]=) } it "resets silencer actor" do expect(Listen::Silencer).to receive(:new).with(listener) { new_silencer } expect(registry).to receive(:[]=).with(:silencer, new_silencer) listener.ignore(/foo/) end context "with existing ignore options" do let(:options) { { ignore: /bar/ } } it "adds up to existing ignore options" do expect(Listen::Silencer).to receive(:new).with(listener) listener.ignore(/foo/) expect(listener.options).to include(ignore: [/bar/, /foo/]) end end context "with existing ignore options (array)" do let(:options) { { ignore: [/bar/] } } it "adds up to existing ignore options" do expect(Listen::Silencer).to receive(:new).with(listener) listener.ignore(/foo/) expect(listener.options).to include(ignore: [[/bar/], /foo/]) end end end describe "#ignore!" do let(:new_silencer) { double(Listen::Silencer) } before { Celluloid::Actor.stub(:[]=) } it "resets silencer actor" do expect(Listen::Silencer).to receive(:new).with(listener) { new_silencer } expect(registry).to receive(:[]=).with(:silencer, new_silencer) listener.ignore!(/foo/) expect(listener.options).to include(ignore!: /foo/) end context "with existing ignore! options" do let(:options) { { ignore!: /bar/ } } it "overwrites existing ignore options" do expect(Listen::Silencer).to receive(:new).with(listener) listener.ignore!([/foo/]) expect(listener.options).to include(ignore!: [/foo/]) end end context "with existing ignore options" do let(:options) { { ignore: /bar/ } } it "deletes ignore options" do expect(Listen::Silencer).to receive(:new).with(listener) listener.ignore!([/foo/]) expect(listener.options).to_not include(ignore: /bar/) end end end describe "#only" do let(:new_silencer) { double(Listen::Silencer) } before { Celluloid::Actor.stub(:[]=) } it "resets silencer actor" do expect(Listen::Silencer).to receive(:new).with(listener) { new_silencer } expect(registry).to receive(:[]=).with(:silencer, new_silencer) listener.only(/foo/) end context "with existing only options" do let(:options) { { only: /bar/ } } it "overwrites existing ignore options" do expect(Listen::Silencer).to receive(:new).with(listener) listener.only([/foo/]) expect(listener.options).to include(only: [/foo/]) end end end end listen-2.4.0/spec/lib/listen/adapter_spec.rb0000644000004100000410000000423512261232670021025 0ustar www-datawww-datarequire 'spec_helper' describe Listen::Adapter do let(:listener) { double(Listen::Listener, options: {}) } before { Listen::Adapter::BSD.stub(:usable?) { false } Listen::Adapter::Darwin.stub(:usable?) { false } Listen::Adapter::Linux.stub(:usable?) { false } Listen::Adapter::Windows.stub(:usable?) { false } } describe ".select" do it "returns Polling adapter if forced" do klass = Listen::Adapter.select(force_polling: true) expect(klass).to eq Listen::Adapter::Polling end it "returns BSD adapter when usable" do Listen::Adapter::BSD.stub(:usable?) { true } klass = Listen::Adapter.select expect(klass).to eq Listen::Adapter::BSD end it "returns Darwin adapter when usable" do Listen::Adapter::Darwin.stub(:usable?) { true } klass = Listen::Adapter.select expect(klass).to eq Listen::Adapter::Darwin end it "returns Linux adapter when usable" do Listen::Adapter::Linux.stub(:usable?) { true } klass = Listen::Adapter.select expect(klass).to eq Listen::Adapter::Linux end it "returns Windows adapter when usable" do Listen::Adapter::Windows.stub(:usable?) { true } klass = Listen::Adapter.select expect(klass).to eq Listen::Adapter::Windows end context "no usable adapters" do before { Kernel.stub(:warn) } it "returns Polling adapter" do klass = Listen::Adapter.select(force_polling: true) expect(klass).to eq Listen::Adapter::Polling end it "warns polling fallback with default message" do expect(Kernel).to receive(:warn).with("[Listen warning]:\n #{described_class::POLLING_FALLBACK_MESSAGE}") Listen::Adapter.select end it "doesn't warn if polling_fallback_message is false" do expect(Kernel).to_not receive(:warn) Listen::Adapter.select(polling_fallback_message: false) end it "warns polling fallback with custom message if set" do expect(Kernel).to receive(:warn).with("[Listen warning]:\n custom fallback message") Listen::Adapter.select(polling_fallback_message: 'custom fallback message') end end end end listen-2.4.0/spec/lib/listen/change_spec.rb0000644000004100000410000000671012261232670020632 0ustar www-datawww-datarequire 'spec_helper' describe Listen::Change do let(:change) { Listen::Change.new(listener) } let(:registry) { double(Celluloid::Registry) } let(:listener) { double(Listen::Listener, registry: registry, options: {}) } let(:listener_changes) { double("listener_changes") } before { listener.stub(:changes) { listener_changes } } describe "#change" do let(:silencer) { double('Listen::Silencer', silenced?: false) } before { registry.stub(:[]).with(:silencer) { silencer } } context "file path" do context "with known change" do it "notifies change directly to listener" do expect(listener_changes).to receive(:<<).with(modified: 'file_path') change.change('file_path', type: 'File', change: :modified) end it "doesn't notify to listener if path is silenced" do # expect(silencer).to receive(:silenced?).with('file_path', 'File').and_return(true) expect(silencer).to receive(:silenced?).and_return(true) expect(listener_changes).to_not receive(:<<) change.change('file_path', type: 'File', change: :modified) end end context "with unknown change" do let(:file) { double('Listen::File') } before { Listen::File.stub(:new) { file } } it "calls Listen::File#change" do expect(Listen::File).to receive(:new).with(listener, 'file_path') { file } expect(file).to receive(:change) change.change('file_path', type: 'File') end it "doesn't call Listen::File#change if path is silenced" do expect(silencer).to receive(:silenced?).with('file_path', 'File').and_return(true) expect(Listen::File).to_not receive(:new) change.change('file_path', type: 'File') end context "that returns a change" do before { file.stub(:change) { :modified } } context "listener listen" do before { listener.stub(:listen?) { true } } it "notifies change to listener" do expect(listener_changes).to receive(:<<).with(modified: 'file_path') change.change('file_path', type: 'File') end context "silence option" do it "notifies change to listener" do expect(listener_changes).to_not receive(:<<) change.change('file_path', type: 'File', silence: true) end end end context "listener doesn't listen" do before { listener.stub(:listen?) { false } } it "notifies change to listener" do expect(listener_changes).to_not receive(:<<) change.change('file_path', type: 'File') end end end context "that returns no change" do before { file.stub(:change) { nil } } it "doesn't notifies no change" do expect(listener_changes).to_not receive(:<<) change.change('file_path', type: 'File') end end end end context "directory path" do let(:dir) { double(Listen::Directory) } let(:dir_options) { { type: 'Dir', recursive: true } } before { Listen::Directory.stub(:new) { dir } } it "calls Listen::Directory#scan" do expect(Listen::Directory).to receive(:new).with(listener, 'dir_path', dir_options) { dir } expect(dir).to receive(:scan) change.change('dir_path', dir_options) end end end end listen-2.4.0/spec/lib/listen/adapter/0000755000004100000410000000000012261232670017462 5ustar www-datawww-datalisten-2.4.0/spec/lib/listen/adapter/windows_spec.rb0000644000004100000410000000135212261232670022514 0ustar www-datawww-datarequire 'spec_helper' describe Listen::Adapter::Windows do if windows? let(:listener) { double(Listen::Listener) } let(:adapter) { described_class.new(listener) } describe ".usable?" do it "returns always true" do expect(described_class).to be_usable end it 'requires wdm gem' do described_class.usable? expect(defined?(WDM)).to be_true end end end if darwin? it "isn't usable on Darwin" do expect(described_class).to_not be_usable end end if linux? it "isn't usable on Linux" do expect(described_class).to_not be_usable end end if bsd? it "isn't usable on BSD" do expect(described_class).to_not be_usable end end end listen-2.4.0/spec/lib/listen/adapter/bsd_spec.rb0000644000004100000410000000144712261232670021577 0ustar www-datawww-datarequire 'spec_helper' describe Listen::Adapter::BSD do if bsd? let(:listener) { double(Listen::Listener) } let(:adapter) { described_class.new(listener) } describe ".usable?" do it "returns always true" do expect(described_class).to be_usable end it 'requires rb-kqueue and find gem' do described_class.usable? expect(defined?(KQueue)).to be_true expect(defined?(Find)).to be_true end end end if darwin? it "isn't usable on Darwin" do expect(described_class).to_not be_usable end end if linux? it "isn't usable on Linux" do expect(described_class).to_not be_usable end end if windows? it "isn't usable on Windows" do expect(described_class).to_not be_usable end end end listen-2.4.0/spec/lib/listen/adapter/darwin_spec.rb0000644000004100000410000000144112261232670022305 0ustar www-datawww-datarequire 'spec_helper' describe Listen::Adapter::Darwin do if darwin? let(:listener) { double(Listen::Listener) } let(:adapter) { described_class.new(listener) } describe ".usable?" do it "returns always true" do expect(described_class).to be_usable end end describe '#initialize' do it 'requires rb-fsevent gem' do described_class.new(listener) expect(defined?(FSEvent)).to be_true end end end if windows? it "isn't usable on Windows" do expect(described_class).to_not be_usable end end if linux? it "isn't usable on Linux" do expect(described_class).to_not be_usable end end if bsd? it "isn't usable on BSD" do expect(described_class).to_not be_usable end end end listen-2.4.0/spec/lib/listen/adapter/base_spec.rb0000644000004100000410000000276112261232670021741 0ustar www-datawww-datarequire 'spec_helper' describe Listen::Adapter::Base do let(:adapter) { described_class.new(listener) } let(:registry) { double(Celluloid::Registry) } let(:listener) { double(Listen::Listener, registry: registry, options: {}) } describe "#_latency" do it "returns default_latency with listener actor latency not present" do expect(adapter.send(:_latency)).to eq Listen::Adapter::Base::DEFAULT_LATENCY end it "returns latency from listener actor if present" do listener.stub(:options) { { latency: 1234 } } expect(adapter.send(:_latency)).to eq 1234 end end describe "#_notify_change" do let(:change_pool) { double(Listen::Change) } let(:change_pool_async) { double('ChangePoolAsync') } before { change_pool.stub(:async) { change_pool_async } registry.stub(:[]).with(:change_pool) { change_pool } } context "listener listen" do before { listener.stub(:listen?) { true} } it "calls change on change_pool asynchronously" do expect(change_pool_async).to receive(:change).with('path', type: 'Dir', recurcise: true) adapter.send(:_notify_change, 'path', type: 'Dir', recurcise: true) end end context "listener doesn't listen" do before { listener.stub(:listen?) { false } } it "calls change on change_pool asynchronously" do expect(change_pool_async).to_not receive(:change) adapter.send(:_notify_change, 'path', type: 'Dir', recurcise: true) end end end end listen-2.4.0/spec/lib/listen/adapter/linux_spec.rb0000644000004100000410000000144112261232670022160 0ustar www-datawww-datarequire 'spec_helper' describe Listen::Adapter::Linux do if linux? let(:listener) { double(Listen::Listener) } let(:adapter) { described_class.new(listener) } describe ".usable?" do it "returns always true" do expect(described_class).to be_usable end end describe '#initialize' do it 'requires rb-inotify gem' do described_class.new(listener) expect(defined?(INotify)).to be_true end end end if darwin? it "isn't usable on Darwin" do expect(described_class).to_not be_usable end end if windows? it "isn't usable on Windows" do expect(described_class).to_not be_usable end end if bsd? it "isn't usable on BSD" do expect(described_class).to_not be_usable end end end listen-2.4.0/spec/lib/listen/adapter/polling_spec.rb0000644000004100000410000000261712261232670022473 0ustar www-datawww-datarequire 'spec_helper' describe Listen::Adapter::Polling do let(:registry) { double(Celluloid::Registry) } let(:listener) { double(Listen::Listener, registry: registry, options: {}, listen?: true) } let(:adapter) { described_class.new(listener) } let(:change_pool) { double(Listen::Change, terminate: true) } let(:change_pool_async) { double('ChangePoolAsync') } before { change_pool.stub(:async) { change_pool_async } registry.stub(:[]).with(:change_pool) { change_pool } } describe ".usable?" do it "returns always true" do expect(described_class).to be_usable end end describe "#start" do let(:directories) { ['directory_path'] } before { listener.stub(:options) { {} } listener.stub(:directories) { directories } } it "notifies change on every listener directories path" do expect(change_pool_async).to receive(:change).with('directory_path', type: 'Dir', recursive: true) t = Thread.new { adapter.start } sleep 0.01 t.kill end end describe "#_latency" do it "returns default_latency with listener actor latency not present" do expect(adapter.send(:_latency)).to eq Listen::Adapter::Polling::DEFAULT_POLLING_LATENCY end it "returns latency from listener actor if present" do listener.stub(:options) { { latency: 1234 } } expect(adapter.send(:_latency)).to eq 1234 end end end listen-2.4.0/spec/lib/listen/record_spec.rb0000644000004100000410000000500512261232670020657 0ustar www-datawww-datarequire 'spec_helper' describe Listen::Record do let(:registry) { double(Celluloid::Registry) } let(:listener) { double(Listen::Listener, registry: registry, options: {}) } let(:record) { Listen::Record.new(listener) } let(:path) { '/dir/path/file.rb' } let(:data) { { type: 'File' } } describe "#set_path" do it "sets path by spliting direname and basename" do record.set_path(path, data) expect(record.paths).to eq({ '/dir/path' => { 'file.rb' => data } }) end it "sets path and keeps old data not overwritten" do record.set_path(path, data.merge(foo: 1, bar: 2)) record.set_path(path, data.merge(foo: 3)) expect(record.paths).to eq({ '/dir/path' => { 'file.rb' => data.merge(foo: 3, bar: 2) } }) end end describe "#unset_path" do context "path is present" do before { record.set_path(path, data) } it "unsets path" do record.unset_path(path) expect(record.paths).to eq({ '/dir/path' => {} }) end end context "path not present" do it "unsets path" do record.unset_path(path) expect(record.paths).to eq({ '/dir/path' => {} }) end end end describe "#file_data" do context "path is present" do before { record.set_path(path, data) } it "returns file data" do expect(record.file_data(path)).to eq data end end context "path not present" do it "return empty hash" do expect(record.file_data(path)).to be_empty end end end describe "#dir_entries" do context "path is present" do before { record.set_path(path, data) } it "returns file path" do expect(record.dir_entries('/dir/path')).to eq({ 'file.rb' => data }) end end context "path not present" do it "unsets path" do expect(record.dir_entries('/dir/path')).to eq({}) end end end describe "#build" do let(:directories) { ['dir_path'] } let(:change_pool) { double(Listen::Change, terminate: true) } before { change_pool.stub(:change) registry.stub(:[]).with(:change_pool) { change_pool } listener.stub(:directories) { directories } } it "re-inits paths" do record.set_path(path, data) record.build expect(record.file_data(path)).to be_empty end it "calls change asynchronously on all directories to build record" do expect(change_pool).to receive(:change).with('dir_path', type: 'Dir', recursive: true, silence: true) record.build end end end listen-2.4.0/spec/lib/listen/directory_spec.rb0000644000004100000410000001342112261232670021406 0ustar www-datawww-datarequire 'spec_helper' describe Listen::Directory do let(:registry) { double(Celluloid::Registry) } let(:listener) { double(Listen::Listener, registry: registry, options: {}) } let(:record) { double(Listen::Record, async: double(set_path: true, unset_path: true)) } let(:change_pool) { double(Listen::Change) } let(:change_pool_async) { double('ChangePoolAsync') } let(:path) { Pathname.new(Dir.pwd) } around { |example| fixtures { |path| example.run } } before { change_pool.stub(:async) { change_pool_async } registry.stub(:[]).with(:record) { record } registry.stub(:[]).with(:change_pool) { change_pool } } describe "#scan" do let(:dir_path) { path.join('dir') } let(:file_path) { dir_path.join('file.rb') } let(:other_file_path) { dir_path.join('other_file.rb') } let(:inside_dir_path) { dir_path.join('inside_dir') } let(:other_inside_dir_path) { dir_path.join('other_inside_dir') } let(:dir) { Listen::Directory.new(listener, dir_path, options) } context "with recursive off" do let(:options) { { recursive: false } } context "file & inside_dir paths present in record" do let(:record_dir_entries) { { 'file.rb' => { type: 'File' }, 'inside_dir' => { type: 'Dir' } } } before { record.stub_chain(:future, :dir_entries) { double(value: record_dir_entries) } change_pool_async.stub(:change) } context "empty dir" do around { |example| mkdir dir_path; example.run } it "sets record dir path" do expect(record.async).to receive(:set_path).with(dir_path, type: 'Dir') dir.scan end it "calls change for file path and dir that doesn't exist" do expect(change_pool_async).to receive(:change).with(file_path, type: 'File', recursive: false) expect(change_pool_async).to receive(:change).with(inside_dir_path, type: 'Dir', recursive: false) dir.scan end end context "other file path present in dir" do around { |example| mkdir dir_path; touch other_file_path; example.run } it "calls change for file & other_file paths and dir that doesn't exist" do expect(change_pool_async).to receive(:change).with(file_path, type: 'File', recursive: false) expect(change_pool_async).to receive(:change).with(other_file_path, type: 'File', recursive: false) expect(change_pool_async).to receive(:change).with(inside_dir_path, type: 'Dir', recursive: false) dir.scan end end end context "dir paths not present in record" do before { record.stub_chain(:future, :dir_entries) { double(value: {}) } } context "non-existing dir path" do it "calls change only for file path" do expect(change_pool_async).to_not receive(:change) dir.scan end it "unsets record dir path" do expect(record.async).to receive(:unset_path).with(dir_path) dir.scan end end context "other file path present in dir" do around { |example| mkdir dir_path; touch file_path; example.run } it "calls change for file & other_file paths" do expect(change_pool_async).to receive(:change).with(file_path, type: 'File', recursive: false) expect(change_pool_async).to_not receive(:change).with(other_file_path, type: 'File', recursive: false) expect(change_pool_async).to_not receive(:change).with(inside_dir_path, type: 'Dir', recursive: false) dir.scan end end end end context "with recursive on" do let(:options) { { recursive: true } } context "file & inside_dir paths present in record" do let(:record_dir_entries) { { 'file.rb' => { type: 'File' }, 'inside_dir' => { type: 'Dir' } } } before { record.stub_chain(:future, :dir_entries) { double(value: record_dir_entries) } } context "empty dir" do it "calls change for file & inside_dir path" do expect(change_pool_async).to receive(:change).with(file_path, type: 'File', recursive: true) expect(change_pool_async).to receive(:change).with(inside_dir_path, type: 'Dir', recursive: true) dir.scan end end context "other inside_dir path present in dir" do around { |example| mkdir dir_path; mkdir other_inside_dir_path; example.run } it "calls change for file, other_file & inside_dir paths" do expect(change_pool_async).to receive(:change).with(file_path, type: 'File', recursive: true) expect(change_pool_async).to receive(:change).with(inside_dir_path, type: 'Dir', recursive: true) expect(change_pool_async).to receive(:change).with(other_inside_dir_path, type: 'Dir', recursive: true) dir.scan end end end context "dir paths not present in record" do before { record.stub_chain(:future, :dir_entries) { double(value: {}) } } context "non-existing dir path" do it "calls change only for file path" do expect(change_pool_async).to_not receive(:change) dir.scan end end context "other file path present in dir" do around { |example| mkdir dir_path; mkdir other_inside_dir_path; example.run } it "calls change for file & other_file paths" do expect(change_pool_async).to receive(:change).with(other_inside_dir_path, type: 'Dir', recursive: true) dir.scan end end end end end end listen-2.4.0/spec/lib/listen/silencer_spec.rb0000644000004100000410000001144412261232670021211 0ustar www-datawww-datarequire 'spec_helper' describe Listen::Silencer do let(:options) { {} } let(:listener) { double(Listen::Listener, directories: [Pathname.new(Dir.pwd), Pathname.new("/Users/Shared/")], options: options ) } let(:silencer) { Listen::Silencer.new(listener) } describe "#silenced?" do let(:pwd) { Pathname.new(Dir.pwd) } context "default ignore" do Listen::Silencer::DEFAULT_IGNORED_DIRECTORIES.each do |dir| describe do let(:path) { pwd.join(dir) } it "silences default ignored directory: #{dir}" do expect(silencer.silenced?(path)).to be_true end context "with a directory beginning with the same name" do let(:path) { pwd.join("#{dir}foo") } it "doesn't silences default ignored directory: #{dir}foo" do expect(silencer.silenced?(path)).to be_false end end context "with a directory ending with the same name" do let(:path) { pwd.join("foo#{dir}") } it "doesn't silences default ignored directory: foo#{dir}" do expect(silencer.silenced?(path)).to be_false end end end end Listen::Silencer::DEFAULT_IGNORED_EXTENSIONS.each do |extension| describe do let(:path) { pwd.join(extension) } it "silences default ignored extension: #{extension}" do expect(silencer.silenced?(path)).to be_true end end end end context 'with ignore options (regexp)' do let(:options) { { ignore: /\.pid$/ } } it "silences path matching custom ignore regex" do expect(silencer.silenced?(pwd.join('foo.pid'))).to be_true end end context 'with ignore options (array)' do let(:options) { { ignore: [%r{^foo/bar}, /\.pid$/] } } it "silences paths matching custom ignore regexes" do expect(silencer.silenced?(pwd.join('foo/bar/baz'))).to be_true expect(silencer.silenced?(pwd.join('foo.pid'))).to be_true end end context "with ignore! options" do let(:options) { { ignore!: /\.pid$/ } } it "silences custom ignored directory" do expect(silencer.silenced?(pwd.join('foo.pid'))).to be_true end it "doesn't silence default ignored directory" do path = pwd.join(Listen::Silencer::DEFAULT_IGNORED_DIRECTORIES.first) expect(silencer.silenced?(path)).to be_false end end context "with only options (regexp)" do let(:options) { { only: %r{foo} } } it "do not take only regex in account if type is Unknown" do path = pwd.join('baz') expect(silencer.silenced?(path)).to be_false end it "do not silence path matches only regex if type is File" do path = pwd.join('foo') expect(silencer.silenced?(path, 'File')).to be_false end it "silences other directory" do path = pwd.join('bar') expect(silencer.silenced?(path, 'File')).to be_true end end context "with only options (array)" do let(:options) { { only: [%r{^foo/}, %r{\.txt$}] } } it "do not take only regex in account if type is Unknown" do expect(silencer.silenced?(pwd.join('baz'))).to be_false end it "doesn't silence good directory" do expect(silencer.silenced?(pwd.join('foo/bar.rb'), 'File')).to be_false end it "doesn't silence good file" do expect(silencer.silenced?(pwd.join('bar.txt'), 'File')).to be_false end it "silences other directory" do expect(silencer.silenced?(pwd.join('bar/baz.rb'), 'File')).to be_true end it "silences other file" do expect(silencer.silenced?(pwd.join('bar.rb'), 'File')).to be_true end end context 'with ignore and only options' do let(:options) { { only: /\.pid$/, ignore: %r{^bar} } } it "do not take only regex in account if type is Unknown" do expect(silencer.silenced?(pwd.join('baz'))).to be_false end it "do not take only regex in account if type is Unknown but silences if ignore regex matches path" do expect(silencer.silenced?(pwd.join('bar'))).to be_true end it 'silences path not matching custom only regex' do expect(silencer.silenced?(pwd.join('foo.rb'), 'File')).to be_true end it 'silences path matching custom ignore regex' do expect(silencer.silenced?(pwd.join('bar.pid', 'File'))).to be_true end it 'do not silence path matching custom only regex and not matching custom ignore regex' do expect(silencer.silenced?(pwd.join('foo.pid', 'File'))).to be_false end end it "doesn't silence normal path" do path = pwd.join('some_dir', 'some_file.rb') expect(silencer.silenced?(path)).to be_false end end end listen-2.4.0/spec/lib/listen/file_spec.rb0000644000004100000410000000673512261232670020333 0ustar www-datawww-datarequire 'spec_helper' describe Listen::File do let(:registry) { double(Celluloid::Registry) } let(:listener) { double(Listen::Listener, registry: registry, options: {}) } let(:record) { double(Listen::Record, async: double(set_path: true, unset_path: true)) } let(:path) { Pathname.new(Dir.pwd) } around { |example| fixtures { |path| example.run } } before { registry.stub(:[]).with(:record) { record } } describe "#change" do let(:file_path) { path.join('file.rb') } let(:file) { Listen::File.new(listener, file_path) } let(:expected_data) { if darwin? { type: 'File', mtime: kind_of(Float), mode: kind_of(Integer), md5: kind_of(String) } else { type: 'File', mtime: kind_of(Float), mode: kind_of(Integer) } end } context "path present in record" do let(:record_mtime) { nil } let(:record_md5) { nil } let(:record_mode) { nil } let(:record_data) { { type: 'File', mtime: record_mtime, md5: record_md5, mode: record_mode } } before { record.stub_chain(:future, :file_data) { double(value: record_data) } } context "non-existing path" do it "returns added" do expect(file.change).to eq :removed end it "sets path in record" do expect(record.async).to receive(:unset_path).with(file_path) file.change end end context "existing path" do around { |example| touch file_path; example.run } context "old record path mtime" do let(:record_mtime) { (Time.now - 1).to_f } it "returns modified" do expect(file.change).to eq :modified end it "sets path in record with expected data" do expect(record.async).to receive(:set_path).with(file_path, expected_data) file.change end end context "same record path mtime" do let(:record_mtime) { ::File.lstat(file_path).mtime.to_f } let(:record_mode) { ::File.lstat(file_path).mode } let(:record_md5) { Digest::MD5.file(file_path).digest } context "same record path mode" do it "returns nil" do expect(file.change).to be_nil end end context "diferent record path mode" do let(:record_mode) { 'foo' } it "returns modified" do expect(file.change).to eq :modified end end context "same record path md5" do it "returns nil" do expect(file.change).to be_nil end end context "different record path md5" do let(:record_md5) { 'foo' } it "returns modified" do expect(file.change).to eq :modified end it "sets path in record with expected data" do expect(record.async).to receive(:set_path).with(file_path, expected_data) file.change end end end end end context "path not present in record" do before { record.stub_chain(:future, :file_data) { double(value: {}) } } context "existing path" do around { |example| touch file_path; example.run } it "returns added" do expect(file.change).to eq :added end it "sets path in record with expected data" do expect(record.async).to receive(:set_path).with(file_path, expected_data) file.change end end end end end listen-2.4.0/spec/lib/listen_spec.rb0000644000004100000410000000075612261232670017411 0ustar www-datawww-datarequire 'spec_helper' describe Listen do describe '.to' do it "initalizes listener" do expect(Listen::Listener).to receive(:new).with('/path') described_class.to('/path') end it "sets stopping at false" do allow(Listen::Listener).to receive(:new) Listen.to('/path') expect(Listen.stopping).to be_false end end describe '.stop' do it "stops all listeners" do Listen.stop expect(Listen.stopping).to be_true end end end listen-2.4.0/spec/support/0000755000004100000410000000000012261232670015512 5ustar www-datawww-datalisten-2.4.0/spec/support/acceptance_helper.rb0000644000004100000410000000177412261232670021475 0ustar www-datawww-datadef listen sleep 0.5 # wait for changes sleep_until_next_second reset_changes yield sleep 0.5 # wait for changes @changes end def setup_listener(options, callback) reset_changes Listen.to(paths, options, &callback) end def reset_changes @changes = { modified: [], added: [], removed: [] } end def add_changes(type, changes) @changes[type] += relative_path(changes) @changes[type].sort! end def relative_path(changes) changes.map do |change| [paths].flatten.each { |path| change.gsub!(%r{#{path.to_s}/}, '') } change end end # Generates a small time difference before performing a time sensitive # task (like comparing mtimes of files). # # @note Modification time for files only includes the milliseconds on Linux with MRI > 1.9.2 # and platform that support it (OS X 10.8 not included), # that's why we generate a difference that's greater than 1 second. # def sleep_until_next_second return unless darwin? t = Time.now diff = t.to_f - t.to_i sleep(1.05 - diff) end listen-2.4.0/spec/support/platform_helper.rb0000644000004100000410000000037112261232670021223 0ustar www-datawww-datadef darwin? RbConfig::CONFIG['target_os'] =~ /darwin/i end def linux? RbConfig::CONFIG['target_os'] =~ /linux/i end def bsd? RbConfig::CONFIG['target_os'] =~ /freebsd/i end def windows? RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i end listen-2.4.0/spec/support/fixtures_helper.rb0000644000004100000410000000133212261232670021246 0ustar www-datawww-datarequire 'tmpdir' include FileUtils # Prepares temporary fixture-directories and # cleans them afterwards. # # @param [Fixnum] number_of_directories the number of fixture-directories to make # # @yield [path1, path2, ...] the empty fixture-directories # @yieldparam [String] path the path to a fixture directory # def fixtures(number_of_directories = 1) current_pwd = pwd paths = 1.upto(number_of_directories).map do File.expand_path(File.join(pwd, "spec/.fixtures/#{Time.now.to_f.to_s.sub('.', '') + rand(9999).to_s}")) end # Create the dirs paths.each { |p| mkdir_p(p) } cd(paths.first) if number_of_directories == 1 yield(*paths) ensure cd current_pwd paths.map { |p| rm_rf(p) if File.exists?(p) } end listen-2.4.0/spec/acceptance/0000755000004100000410000000000012261232670016064 5ustar www-datawww-datalisten-2.4.0/spec/acceptance/listen_spec.rb0000644000004100000410000001745312261232670020733 0ustar www-datawww-data# encoding: UTF-8 require 'spec_helper' describe "Listen" do let(:options) { { } } let(:callback) { ->(modified, added, removed) { add_changes(:modified, modified) add_changes(:added, added) add_changes(:removed, removed) } } let(:listener) { @listener } before { @listener = setup_listener(options, callback) @listener.start } after { listener.stop } context "with one listen dir" do let(:paths) { Pathname.new(Dir.pwd) } around { |example| fixtures { |path| example.run } } context "with change block raising" do let(:callback) { ->(x,y,z) { raise 'foo' } } it "warns the backtrace" do expect(Kernel).to receive(:warn).with("[Listen warning]: Change block raised an exception: foo") expect(Kernel).to receive(:warn).with(/^Backtrace:.*/) listen { touch 'file.rb' } end end [false, true].each do |polling| context "force_polling option to #{polling}" do let(:options) { { force_polling: polling, latency: 0.1 } } context "nothing in listen dir" do it "listens to file addition" do expect(listen { touch 'file.rb' }).to eq({ modified: [], added: ['file.rb'], removed: [] }) end it "listens to multiple files addition" do expect(listen { touch 'file1.rb' touch 'file2.rb' }).to eq({ modified: [], added: ['file1.rb', 'file2.rb'], removed: [] }) end it "listens to file moved inside" do touch '../file.rb' expect(listen { mv '../file.rb', 'file.rb' }).to eq({ modified: [], added: ['file.rb'], removed: [] }) end end context "file in listen dir" do around { |example| touch 'file.rb'; example.run } it "listens to file touch" do expect(listen { touch 'file.rb' }).to eq({ modified: ['file.rb'], added: [], removed: [] }) end it "listens to file modification" do expect(listen { open('file.rb', 'w') { |f| f.write('foo') } }).to eq({ modified: ['file.rb'], added: [], removed: [] }) end it "listens to file modification and wait" do expect(listen { open('file.rb', 'w') { |f| f.write('foo') } sleep 0.5 }).to eq({ modified: ['file.rb'], added: [], removed: [] }) end it "listens to file echo" do expect(listen { `echo foo > #{Dir.pwd}/file.rb` }).to eq({ modified: ['file.rb'], added: [], removed: [] }) end it "listens to file removal" do expect(listen { rm 'file.rb' }).to eq({ modified: [], added: [], removed: ['file.rb'] }) end it "listens to file moved out" do expect(listen { mv 'file.rb', '../file.rb' }).to eq({ modified: [], added: [], removed: ['file.rb'] }) end it "listens to file mode change" do expect(listen { chmod 0777, 'file.rb' }).to eq({ modified: ['file.rb'], added: [], removed: [] }) end end context "hidden file in listen dir" do around { |example| touch '.hidden'; example.run } it "listens to file touch" do expect(listen { touch '.hidden' }).to eq({ modified: ['.hidden'], added: [], removed: [] }) end end context "dir in listen dir" do around { |example| mkdir_p 'dir'; example.run } it "listens to file touch" do expect(listen { touch 'dir/file.rb' }).to eq({ modified: [], added: ['dir/file.rb'], removed: [] }) end end context "dir with file in listen dir" do around { |example| mkdir_p 'dir'; touch 'dir/file.rb'; example.run } it "listens to file move" do expect(listen { mv 'dir/file.rb', 'file.rb' }).to eq({ modified: [], added: ['file.rb'], removed: ['dir/file.rb'] }) end end context "two dirs with files in listen dir" do around { |example| mkdir_p 'dir1'; touch 'dir1/file1.rb' mkdir_p 'dir2'; touch 'dir2/file2.rb' example.run } it "listens to multiple file moves" do expect(listen { mv 'dir1/file1.rb', 'dir2/file1.rb' mv 'dir2/file2.rb', 'dir1/file2.rb' }).to eq({ modified: [], added: ['dir1/file2.rb', 'dir2/file1.rb'], removed: ['dir1/file1.rb', 'dir2/file2.rb'] }) end it "listens to dir move" do expect(listen { mv 'dir1', 'dir2/' }).to eq({ modified: [], added: ['dir2/dir1/file1.rb'], removed: ['dir1/file1.rb'] }) end end context "default ignored dir with file in listen dir" do around { |example| mkdir_p '.bundle'; touch '.bundle/file.rb'; example.run } let(:options) { { force_polling: polling, latency: 0.1 } } it "doesn't listen to file touch" do expect(listen { touch '.bundle/file.rb' }).to eq({ modified: [], added: [], removed: [] }) end end context "ignored dir with file in listen dir" do around { |example| mkdir_p 'ignored_dir'; touch 'ignored_dir/file.rb'; example.run } let(:options) { { force_polling: polling, latency: 0.1, ignore: /ignored_dir/ } } it "doesn't listen to file touch" do expect(listen { touch 'ignored_dir/file.rb' }).to eq({ modified: [], added: [], removed: [] }) end end context "with ignored file in listen dir" do around { |example| touch 'file.rb'; example.run } let(:options) { { force_polling: polling, latency: 0.1, ignore: /\.rb$/ } } it "doesn't listen to file touch" do expect(listen { touch 'file.rb' }).to eq({ modified: [], added: [], removed: [] }) end end context "with only option" do let(:options) { { force_polling: polling, latency: 0.1, only: /\.rb$/ } } it "listens only to file touch matching with only patterns" do expect(listen { touch 'file.rb' touch 'file.txt' }).to eq({ modified: [], added: ['file.rb'], removed: [] }) end end context "with ignore and only option" do let(:options) { { force_polling: polling, latency: 0.1, ignore: /bar\.rb$/, only: /\.rb$/ } } it "listens only to file touch matching with only patterns" do expect(listen { touch 'file.rb' touch 'bar.rb' touch 'file.txt' }).to eq({ modified: [], added: ['file.rb'], removed: [] }) end end describe "#ignore" do around { |example| touch 'file.rb'; example.run } let(:options) { { force_polling: polling, latency: 0.1, ignore: /\.rb$/ } } it "overwrites existing patterns" do expect(listen { listener.ignore(/\.txt/) touch 'file.rb' touch 'file.txt' }).to eq({ modified: [], added: [], removed: [] }) end end describe "#ignore!" do let(:options) { { force_polling: polling, latency: 0.1, ignore: /\.rb$/ } } it "overwrites existing patterns" do expect(listen { listener.ignore!(/\.txt/) touch 'file.rb' touch 'file.txt' }).to eq({ modified: [], added: ['file.rb'], removed: [] }) end end end end end end listen-2.4.0/.travis.yml0000644000004100000410000000036712261232670015163 0ustar www-datawww-datalanguage: ruby bundler_args: --without tool rvm: - 1.9.3 - 2.0.0 - jruby-19mode - rbx-19mode matrix: allow_failures: - rvm: jruby-19mode - rvm: rbx-19mode notifications: recipients: - thibaud@thibaud.me - remy@rymai.me listen-2.4.0/listen.gemspec0000644000004100000410000000200212261232670015701 0ustar www-datawww-data# coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'listen/version' Gem::Specification.new do |s| s.name = 'listen' s.version = Listen::VERSION s.license = 'MIT' s.author = 'Thibaud Guillaume-Gentil' s.email = 'thibaud@thibaud.me' s.homepage = 'https://github.com/guard/listen' s.summary = 'Listen to file modifications' s.description = 'The Listen gem listens to file modifications and notifies you about the changes. Works everywhere!' s.files = `git ls-files`.split($/) s.test_files = s.files.grep(%r{^spec/}) s.require_path = 'lib' s.required_ruby_version = ">= 1.9.3" s.add_dependency 'celluloid', '>= 0.15.2' s.add_dependency 'rb-fsevent', '>= 0.9.3' s.add_dependency 'rb-inotify', '>= 0.9' s.add_development_dependency 'bundler', '>= 1.3.5' s.add_development_dependency 'rake' s.add_development_dependency 'rspec' s.add_development_dependency 'rspec-retry' end listen-2.4.0/lib/0000755000004100000410000000000012261232670013612 5ustar www-datawww-datalisten-2.4.0/lib/listen/0000755000004100000410000000000012261232670015110 5ustar www-datawww-datalisten-2.4.0/lib/listen/directory.rb0000644000004100000410000000321412261232670017441 0ustar www-datawww-datamodule Listen class Directory attr_accessor :listener, :path, :options def initialize(listener, path, options = {}) @listener = listener @path = path @options = options end def scan _update_record _all_entries.each do |entry_path, data| case data[:type] when 'File' _async_change(entry_path, options.merge(type: 'File')) when 'Dir' _async_change(entry_path, options.merge(type: 'Dir')) if _recursive_scan?(entry_path) end end end private def _update_record if ::Dir.exists?(path) _record.async.set_path(path, { type: 'Dir'}) else _record.async.unset_path(path) end end def _all_entries _record_entries.merge(_entries) end def _entries return {} unless ::Dir.exists?(path) entries = ::Dir.entries(path) - %w[. ..] entries = entries.map { |entry| [entry, type: _entry_type(entry)] } Hash[*entries.flatten] end def _entry_type(entry_path) entry_path = path.join(entry_path) if entry_path.file? 'File' elsif entry_path.directory? 'Dir' end end def _record_entries future = _record.future.dir_entries(path) future.value end def _record listener.registry[:record] end def _change_pool listener.registry[:change_pool] end def _recursive_scan?(path) !::Dir.exists?(path) || options[:recursive] end def _async_change(entry_path, options) entry_path = path.join(entry_path) _change_pool.async.change(entry_path, options) end end end listen-2.4.0/lib/listen/adapter/0000755000004100000410000000000012261232670016530 5ustar www-datawww-datalisten-2.4.0/lib/listen/adapter/polling.rb0000644000004100000410000000175512261232670020531 0ustar www-datawww-datamodule Listen module Adapter # Polling Adapter that works cross-platform and # has no dependencies. This is the adapter that # uses the most CPU processing power and has higher # file IO than the other implementations. # class Polling < Base DEFAULT_POLLING_LATENCY = 1.0 def self.usable? true end def start Thread.new { _poll_directories } end private def _latency listener.options[:latency] || DEFAULT_POLLING_LATENCY end def _poll_directories _napped_loop do listener.directories.each do |path| _notify_change(path, type: 'Dir', recursive: true) end end end def _napped_loop loop do _nap_time { yield } end end def _nap_time start = Time.now.to_f yield nap_time = _latency - (Time.now.to_f - start) sleep(nap_time) if nap_time > 0 end end end end listen-2.4.0/lib/listen/adapter/linux.rb0000644000004100000410000000507112261232670020217 0ustar www-datawww-datamodule Listen module Adapter # Listener implementation for Linux `inotify`. # class Linux < Base # Watched inotify events # # @see http://www.tin.org/bin/man.cgi?section=7&topic=inotify # @see https://github.com/nex3/rb-inotify/blob/master/lib/rb-inotify/notifier.rb#L99-L177 # EVENTS = [:recursive, :attrib, :create, :delete, :move, :close_write] # The message to show when the limit of inotify watchers is not enough # INOTIFY_LIMIT_MESSAGE = <<-EOS.gsub(/^\s*/, '') Listen error: unable to monitor directories for changes. Please head to https://github.com/guard/listen/wiki/Increasing-the-amount-of-inotify-watchers for information on how to solve this issue. EOS def self.usable? RbConfig::CONFIG['target_os'] =~ /linux/i end def initialize(listener) require 'rb-inotify' super end def start worker = _init_worker Thread.new { worker.run } rescue Errno::ENOSPC abort(INOTIFY_LIMIT_MESSAGE) end private # Initializes a INotify worker and adds a watcher for # each directory passed to the adapter. # # @return [INotify::Notifier] initialized worker # def _init_worker INotify::Notifier.new.tap do |worker| _directories_path.each { |path| worker.watch(path, *EVENTS, &_worker_callback) } end end def _worker_callback lambda do |event| next if _skip_event?(event) if _dir_event?(event) _notify_change(_event_path(event), type: 'Dir') else _notify_change(_event_path(event), type: 'File', change: _change(event.flags)) end end end def _skip_event?(event) # Event on root directory return true if event.name == "" # INotify reports changes to files inside directories as events # on the directories themselves too. # # @see http://linux.die.net/man/7/inotify return true if _dir_event?(event) && (event.flags & [:close, :modify]).any? end def _change(event_flags) { modified: [:attrib], added: [:moved_to, :create], removed: [:moved_from, :delete] }.each do |change, flags| return change unless (flags & event_flags).empty? end nil end def _dir_event?(event) event.flags.include?(:isdir) end def _event_path(event) Pathname.new(event.absolute_name) end end end end listen-2.4.0/lib/listen/adapter/bsd.rb0000644000004100000410000000531212261232670017626 0ustar www-datawww-datamodule Listen module Adapter # Listener implementation for BSD's `kqueue`. # class BSD < Base # Watched kqueue events # # @see http://www.freebsd.org/cgi/man.cgi?query=kqueue # @see https://github.com/mat813/rb-kqueue/blob/master/lib/rb-kqueue/queue.rb # EVENTS = [:delete, :write, :extend, :attrib, :rename] # :link, :revoke # The message to show when wdm gem isn't available # BUNDLER_DECLARE_GEM = <<-EOS.gsub(/^ {6}/, '') Please add the following to your Gemfile to avoid polling for changes: require 'rbconfig' gem 'rb-kqueue', '>= 0.2' if RbConfig::CONFIG['target_os'] =~ /freebsd/i EOS def self.usable? if RbConfig::CONFIG['target_os'] =~ /freebsd/i require 'rb-kqueue' require 'find' true end rescue LoadError Kernel.warn BUNDLER_DECLARE_GEM false end def start worker = _init_worker Thread.new { worker.poll } end private # Initializes a kqueue Queue and adds a watcher for each files in # the directories passed to the adapter. # # @return [INotify::Notifier] initialized kqueue # def _init_worker KQueue::Queue.new.tap do |queue| _directories_path.each do |path| Find.find(path) { |file_path| _watch_file(file_path, queue) } end end end def _worker_callback lambda do |event| _notify_change(_event_path(event), type: 'File', change: _change(event.flags)) # If it is a directory, and it has a write flag, it means a # file has been added so find out which and deal with it. # No need to check for removed files, kqueue will forget them # when the vfs does. _watch_for_new_file(event) if _new_file_added?(event) end end def _change(event_flags) { modified: [:attrib, :extend], added: [:write], removed: [:rename, :delete] }.each do |change, flags| return change unless (flags & event_flags).empty? end nil end def _event_path(event) Pathname.new(event.watcher.path) end def _new_file_added?(event) File.directory?(event.watcher.path) && event.flags.include?(:write) end def _watch_for_new_file(event) queue = event.watcher.queue Find.find(path) do |file_path| _watch_file(file_path, queue) unless queue.watchers.detect { |k,v| v.path == file.to_s } end end def _watch_file(path, queue) queue.watch_file(path, *EVENTS, &_worker_callback) end end end end listen-2.4.0/lib/listen/adapter/base.rb0000644000004100000410000000141512261232670017770 0ustar www-datawww-datamodule Listen module Adapter class Base include Celluloid # The default delay between checking for changes. DEFAULT_LATENCY = 0.1 attr_accessor :listener def initialize(listener) @listener = listener end def self.usable? raise NotImplementedError end def start raise NotImplementedError end private def _latency listener.options[:latency] || DEFAULT_LATENCY end def _directories_path listener.directories.map(&:to_s) end def _notify_change(path, options) sleep 0.01 until listener.registry[:change_pool] listener.registry[:change_pool].async.change(path, options) if listener.listen? end end end end listen-2.4.0/lib/listen/adapter/windows.rb0000644000004100000410000000314312261232670020550 0ustar www-datawww-datamodule Listen module Adapter # Adapter implementation for Windows `wdm`. # class Windows < Base # The message to show when wdm gem isn't available # BUNDLER_DECLARE_GEM = <<-EOS.gsub(/^ {6}/, '') Please add the following to your Gemfile to avoid polling for changes: require 'rbconfig' gem 'wdm', '>= 0.1.0' if RbConfig::CONFIG['target_os'] =~ /mswin|mingw|cygwin/i EOS def self.usable? if RbConfig::CONFIG['target_os'] =~ /mswin|mingw|cygwin/i require 'wdm' true end rescue LoadError Kernel.warn BUNDLER_DECLARE_GEM false end def start worker = _init_worker Thread.new { worker.run! } end private # Initializes a WDM monitor and adds a watcher for # each directory passed to the adapter. # # @return [WDM::Monitor] initialized worker # def _init_worker WDM::Monitor.new.tap do |worker| _directories_path.each { |path| worker.watch_recursively(path, &_worker_callback) } end end def _worker_callback lambda do |change| _notify_change(_path(change.path), type: 'File', change: _change(change.type)) end end def _path(path) Pathname.new(path) end def _change(type) { modified: [:modified], added: [:added, :renamed_new_file], removed: [:removed, :renamed_old_file] }.each do |change, types| return change if types.include?(type) end nil end end end end listen-2.4.0/lib/listen/adapter/darwin.rb0000644000004100000410000000163012261232670020341 0ustar www-datawww-datamodule Listen module Adapter # Adapter implementation for Mac OS X `FSEvents`. # class Darwin < Base def self.usable? RbConfig::CONFIG['target_os'] =~ /darwin(1.+)?$/i end def initialize(listener) require 'rb-fsevent' super end def start worker = _init_worker Thread.new { worker.run } end private # Initializes a FSEvent worker and adds a watcher for # each directory listened. # def _init_worker FSEvent.new.tap do |worker| worker.watch(_directories_path, latency: _latency) do |changes| _changes_path(changes).each { |path| _notify_change(path, type: 'Dir') } end end end def _changes_path(changes) changes.map do |path| path.sub!(/\/$/, '') Pathname.new(path) end end end end end listen-2.4.0/lib/listen/listener.rb0000644000004100000410000001120212261232670017256 0ustar www-datawww-datarequire 'pathname' require 'listen/adapter' require 'listen/change' require 'listen/record' require 'listen/silencer' module Listen class Listener attr_accessor :options, :directories, :paused, :changes, :block, :thread attr_accessor :registry, :supervisor RELATIVE_PATHS_WITH_MULTIPLE_DIRECTORIES_WARNING_MESSAGE = "The relative_paths option doesn't work when listening to multiple diretories." # Initializes the directories listener. # # @param [String] directory the directories to listen to # @param [Hash] options the listen options (see Listen::Listener::Options) # # @yield [modified, added, removed] the changed files # @yieldparam [Array] modified the list of modified files # @yieldparam [Array] added the list of added files # @yieldparam [Array] removed the list of removed files # def initialize(*args, &block) @options = _init_options(args.last.is_a?(Hash) ? args.pop : {}) @directories = args.flatten.map { |path| Pathname.new(path).realpath } @changes = [] @block = block @registry = Celluloid::Registry.new @supervisor = Celluloid::SupervisionGroup.run!(@registry) _init_debug end # Starts the listener by initializing the adapter and building # the directory record concurrently, then it starts the adapter to watch # for changes. The current thread is not blocked after starting. # def start _signals_trap _init_actors unpause registry[:adapter].async.start @thread = Thread.new { _wait_for_changes } end # Terminates all Listen actors and kill the adapter. # def stop @stopping = true thread.join end # Pauses listening callback (adapter still running) # def pause @paused = true end # Unpauses listening callback # def unpause registry[:record].build @paused = false end # Returns true if Listener is paused # # @return [Boolean] # def paused? @paused == true end # Returns true if Listener is not paused # # @return [Boolean] # def listen? @paused == false end # Adds ignore patterns to the existing one (See DEFAULT_IGNORED_DIRECTORIES and DEFAULT_IGNORED_EXTENSIONS in Listen::Silencer) # # @param [Regexp, Array] new ignoring patterns. # def ignore(regexps) @options[:ignore] = [options[:ignore], regexps] registry[:silencer] = Silencer.new(self) end # Overwrites ignore patterns (See DEFAULT_IGNORED_DIRECTORIES and DEFAULT_IGNORED_EXTENSIONS in Listen::Silencer) # # @param [Regexp, Array] new ignoring patterns. # def ignore!(regexps) @options.delete(:ignore) @options[:ignore!] = regexps registry[:silencer] = Silencer.new(self) end # Sets only patterns, to listen only to specific regexps # # @param [Regexp, Array] new ignoring patterns. # def only(regexps) @options[:only] = regexps registry[:silencer] = Silencer.new(self) end private def _init_options(options = {}) { debug: false, latency: nil, wait_for_delay: 0.1, force_polling: false, polling_fallback_message: nil }.merge(options) end def _init_debug if options[:debug] Celluloid.logger.level = Logger::INFO else Celluloid.logger = nil end end def _init_actors supervisor.add(Silencer, as: :silencer, args: self) supervisor.add(Record, as: :record, args: self) supervisor.pool(Change, as: :change_pool, args: self) adapter_class = Adapter.select(options) supervisor.add(adapter_class, as: :adapter, args: self) end def _signals_trap return if defined?(JRUBY_VERSION) if Signal.list.keys.include?('INT') Signal.trap('INT') { stop } end end def _wait_for_changes loop do break if @stopping || Listen.stopping changes = _pop_changes unless changes.all? { |_,v| v.empty? } block.call(changes[:modified], changes[:added], changes[:removed]) end sleep options[:wait_for_delay] end supervisor.finalize rescue => ex Kernel.warn "[Listen warning]: Change block raised an exception: #{$!}" Kernel.warn "Backtrace:\n\t#{ex.backtrace.join("\n\t")}" end def _pop_changes changes = { modified: [], added: [], removed: [] } until @changes.empty? change = @changes.pop change.each { |k, v| changes[k] << v.to_s } end changes.each { |_, v| v.uniq! } end end end listen-2.4.0/lib/listen/change.rb0000644000004100000410000000163612261232670016670 0ustar www-datawww-datarequire 'listen/file' require 'listen/directory' module Listen class Change include Celluloid attr_accessor :listener def initialize(listener) @listener = listener end def change(path, options) return if _silencer.silenced?(path, options[:type]) if change = options[:change] _notify_listener(change, path) else send("_#{options[:type].downcase}_change", path, options) end end private def _file_change(path, options) change = File.new(listener, path).change if change && listener.listen? && !options[:silence] _notify_listener(change, path) end end def _dir_change(path, options) Directory.new(listener, path, options).scan end def _notify_listener(change, path) listener.changes << { change => path } end def _silencer listener.registry[:silencer] end end end listen-2.4.0/lib/listen/file.rb0000644000004100000410000000367712261232670016371 0ustar www-datawww-datamodule Listen class File attr_accessor :listener, :path, :data def initialize(listener, path) @listener = listener @path = path @data = { type: 'File' } end def change if _existing_path? && _modified? _set_record_data :modified elsif _new_path? _set_record_data :added elsif _removed_path? _unset_record_data :removed end end private def _new_path? _exist? && !_record_data? end def _existing_path? _exist? && _record_data? end def _removed_path? !_exist? end def _record_data? !_record_data.empty? end def _exist? @exist ||= ::File.exist?(path) end def _modified? _mtime > _record_data[:mtime] || _mode_modified? || _content_modified? end def _mode_modified? _mode != _record_data[:mode] end # Only useful on Darwin because of the file mtime second precision # def _content_modified? _record_data[:md5] && _md5 != _record_data[:md5] end def _set_record_data @data.merge!(_new_data) _record.async.set_path(path, data) end def _unset_record_data _record.async.unset_path(path) end # Only Darwin need md5 comparaison because of the file mtime second precision # def _new_data data = { mtime: _mtime, mode: _mode } data[:md5] = _md5 if RbConfig::CONFIG['target_os'] =~ /darwin/i data end def _record_data @_record_data ||= _record.future.file_data(path).value end def _record listener.registry[:record] end def _mtime @mtime ||= _lstat.mtime.to_f rescue 0.0 end def _mode @mode ||= _lstat.mode rescue nil end def _lstat @lstat ||= ::File.lstat(path) rescue nil end def _md5 @md5 ||= Digest::MD5.file(path).digest rescue nil end end end listen-2.4.0/lib/listen/record.rb0000644000004100000410000000153212261232670016714 0ustar www-datawww-datamodule Listen class Record include Celluloid attr_accessor :paths, :listener def initialize(listener) @listener = listener @paths = _init_paths end def set_path(path, data) @paths[::File.dirname(path)][::File.basename(path)] = file_data(path).merge(data) end def unset_path(path) @paths[::File.dirname(path)].delete(::File.basename(path)) end def file_data(path) @paths[::File.dirname(path)][::File.basename(path)] || {} end def dir_entries(path) @paths[path.to_s] end def build @paths = _init_paths listener.directories.each do |path| listener.registry[:change_pool].change(path, type: 'Dir', recursive: true, silence: true) end end private def _init_paths Hash.new { |h, k| h[k] = Hash.new } end end end listen-2.4.0/lib/listen/version.rb0000644000004100000410000000004612261232670017122 0ustar www-datawww-datamodule Listen VERSION = '2.4.0' end listen-2.4.0/lib/listen/silencer.rb0000644000004100000410000000364212261232670017246 0ustar www-datawww-datamodule Listen class Silencer include Celluloid # The default list of directories that get ignored. DEFAULT_IGNORED_DIRECTORIES = %w[.bundle .git .hg .rbx .svn bundle log tmp vendor/ruby vendor/bundle] # The default list of files that get ignored. DEFAULT_IGNORED_EXTENSIONS = %w[.DS_Store .tmp] attr_accessor :listener, :only_patterns, :ignore_patterns def initialize(listener) @listener = listener _init_only_patterns _init_ignore_patterns end def silenced?(path, type = 'Unknown') silenced = false if only_patterns && type == 'File' silenced = !only_patterns.any? { |pattern| _relative_path(path) =~ pattern } end silenced ||= ignore_patterns.any? { |pattern| _relative_path(path) =~ pattern } end private def _init_only_patterns if listener.options[:only] @only_patterns = Array(listener.options[:only]) end end def _init_ignore_patterns @ignore_patterns = [] @ignore_patterns << _default_ignore_patterns unless listener.options[:ignore!] @ignore_patterns << listener.options[:ignore] << listener.options[:ignore!] @ignore_patterns.compact! @ignore_patterns.flatten! end def _default_ignore_patterns [_default_ignored_directories_patterns, _default_ignored_extensions_patterns] end def _default_ignored_directories_patterns ignored_directories = DEFAULT_IGNORED_DIRECTORIES.map { |d| Regexp.escape(d) } %r{^(?:#{ignored_directories.join('|')})(/|$)} end def _default_ignored_extensions_patterns ignored_extensions = DEFAULT_IGNORED_EXTENSIONS.map { |e| Regexp.escape(e) } %r{(?:#{ignored_extensions.join('|')})$} end def _relative_path(path) relative_paths = listener.directories.map { |dir| path.relative_path_from(dir).to_s } relative_paths.detect { |path| !path.start_with?('../') } end end end listen-2.4.0/lib/listen/adapter.rb0000644000004100000410000000205412261232670017056 0ustar www-datawww-datarequire 'listen/adapter/base' require 'listen/adapter/bsd' require 'listen/adapter/darwin' require 'listen/adapter/linux' require 'listen/adapter/polling' require 'listen/adapter/windows' module Listen module Adapter OPTIMIZED_ADAPTERS = %w[Darwin Linux BSD Windows] POLLING_FALLBACK_MESSAGE = "Listen will be polling for changes. Learn more at https://github.com/guard/listen#polling-fallback." def self.select(options = {}) return Polling if options[:force_polling] return _usable_adapter_class if _usable_adapter_class _warn_polling_fallback(options) Polling end private def self._usable_adapter_class adapters = OPTIMIZED_ADAPTERS.map { |adapter| Adapter.const_get(adapter) } adapters.detect { |adapter| adapter.send(:usable?) } end def self._warn_polling_fallback(options) return if options[:polling_fallback_message] == false warning = options.fetch(:polling_fallback_message, POLLING_FALLBACK_MESSAGE) Kernel.warn "[Listen warning]:\n #{warning}" end end end listen-2.4.0/lib/listen.rb0000644000004100000410000000135712261232670015443 0ustar www-datawww-datarequire 'celluloid' require 'listen/listener' module Listen class << self attr_accessor :stopping # Listens to file system modifications on a either single directory or multiple directories. # # @param (see Listen::Listener#new) # # @yield [modified, added, removed] the changed files # @yieldparam [Array] modified the list of modified files # @yieldparam [Array] added the list of added files # @yieldparam [Array] removed the list of removed files # # @return [Listen::Listener] the listener # def to(*args, &block) @stopping = false Listener.new(*args, &block) end # Stop all listeners # def stop @stopping = true end end end listen-2.4.0/metadata.yml0000644000004100000410000001243612261232670015355 0ustar www-datawww-data--- !ruby/object:Gem::Specification name: listen version: !ruby/object:Gem::Version version: 2.4.0 platform: ruby authors: - Thibaud Guillaume-Gentil autorequire: bindir: bin cert_chain: [] date: 2013-12-07 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: celluloid requirement: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: 0.15.2 type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: 0.15.2 - !ruby/object:Gem::Dependency name: rb-fsevent requirement: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: 0.9.3 type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: 0.9.3 - !ruby/object:Gem::Dependency name: rb-inotify requirement: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '0.9' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '0.9' - !ruby/object:Gem::Dependency name: bundler requirement: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: 1.3.5 type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: 1.3.5 - !ruby/object:Gem::Dependency name: rake requirement: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: rspec requirement: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '0' - !ruby/object:Gem::Dependency name: rspec-retry requirement: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '0' description: The Listen gem listens to file modifications and notifies you about the changes. Works everywhere! email: thibaud@thibaud.me executables: [] extensions: [] extra_rdoc_files: [] files: - .gitignore - .rspec - .travis.yml - .yardopts - CHANGELOG.md - CONTRIBUTING.md - Gemfile - Guardfile - LICENSE.txt - README.md - Rakefile - lib/listen.rb - lib/listen/adapter.rb - lib/listen/adapter/base.rb - lib/listen/adapter/bsd.rb - lib/listen/adapter/darwin.rb - lib/listen/adapter/linux.rb - lib/listen/adapter/polling.rb - lib/listen/adapter/windows.rb - lib/listen/change.rb - lib/listen/directory.rb - lib/listen/file.rb - lib/listen/listener.rb - lib/listen/record.rb - lib/listen/silencer.rb - lib/listen/version.rb - listen.gemspec - spec/acceptance/listen_spec.rb - spec/lib/listen/adapter/base_spec.rb - spec/lib/listen/adapter/bsd_spec.rb - spec/lib/listen/adapter/darwin_spec.rb - spec/lib/listen/adapter/linux_spec.rb - spec/lib/listen/adapter/polling_spec.rb - spec/lib/listen/adapter/windows_spec.rb - spec/lib/listen/adapter_spec.rb - spec/lib/listen/change_spec.rb - spec/lib/listen/directory_spec.rb - spec/lib/listen/file_spec.rb - spec/lib/listen/listener_spec.rb - spec/lib/listen/record_spec.rb - spec/lib/listen/silencer_spec.rb - spec/lib/listen_spec.rb - spec/spec_helper.rb - spec/support/acceptance_helper.rb - spec/support/fixtures_helper.rb - spec/support/platform_helper.rb homepage: https://github.com/guard/listen licenses: - MIT metadata: {} post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: 1.9.3 required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - '>=' - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: rubygems_version: 2.1.11 signing_key: specification_version: 4 summary: Listen to file modifications test_files: - spec/acceptance/listen_spec.rb - spec/lib/listen/adapter/base_spec.rb - spec/lib/listen/adapter/bsd_spec.rb - spec/lib/listen/adapter/darwin_spec.rb - spec/lib/listen/adapter/linux_spec.rb - spec/lib/listen/adapter/polling_spec.rb - spec/lib/listen/adapter/windows_spec.rb - spec/lib/listen/adapter_spec.rb - spec/lib/listen/change_spec.rb - spec/lib/listen/directory_spec.rb - spec/lib/listen/file_spec.rb - spec/lib/listen/listener_spec.rb - spec/lib/listen/record_spec.rb - spec/lib/listen/silencer_spec.rb - spec/lib/listen_spec.rb - spec/spec_helper.rb - spec/support/acceptance_helper.rb - spec/support/fixtures_helper.rb - spec/support/platform_helper.rb has_rdoc: listen-2.4.0/.gitignore0000644000004100000410000000036712261232670015042 0ustar www-datawww-datapkg/* doc/* *.gem *.rbc .*.swp *.bak bundle .bundle .yardoc .rbx .rvmrc .vagrant Gemfile.lock spec/.fixtures coverage .ruby-version example* test.txt ## MAC OS .DS_Store .Trashes .com.apple.timemachine.supported .fseventsd Desktop DB Desktop DF listen-2.4.0/.yardopts0000644000004100000410000000025412261232670014713 0ustar www-datawww-data--title 'Listen Documentation' --readme README.md --markup markdown --markup-provider redcarpet --private --protected --output-dir ./doc lib/**/*.rb - CHANGELOG.md LICENSE listen-2.4.0/CONTRIBUTING.md0000644000004100000410000000314412261232670015277 0ustar www-datawww-dataContribute to Listen =================== File an issue ------------- You can report bugs and feature requests to [GitHub Issues](https://github.com/guard/listen/issues). **Please don't ask question in the issue tracker**, instead ask them in our [Google group](http://groups.google.com/group/guard-dev) or on `#guard` (irc.freenode.net). Try to figure out where the issue belongs to: Is it an issue with Listen itself or with Guard? When you file a bug, please try to follow these simple rules if applicable: * Make sure you run Listen with `bundle exec` first. * Add your `Guardfile` (if used) and `Gemfile` to the issue. * Make sure that the issue is reproducible with your description. **It's most likely that your bug gets resolved faster if you provide as much information as possible!** Development ----------- * Documentation hosted at [RubyDoc](http://rubydoc.info/github/guard/listen/master/frames). * Source hosted at [GitHub](https://github.com/guard/listen). Pull requests are very welcome! Please try to follow these simple rules if applicable: * Please create a topic branch for every separate change you make. * Make sure your patches are well tested. All specs run with `rake spec` must pass. * Update the [Yard](http://yardoc.org/) documentation. * Update the [README](https://github.com/guard/listen/blob/master/README.md). * Update the [CHANGELOG](https://github.com/guard/listen/blob/master/CHANGELOG.md) for noteworthy changes. * Please **do not change** the version number. For questions please join us in our [Google group](http://groups.google.com/group/guard-dev) or on `#guard` (irc.freenode.net). listen-2.4.0/CHANGELOG.md0000644000004100000410000000011512261232670014652 0ustar www-datawww-data# Moved to [GitHub releases](https://github.com/guard/listen/releases) page. listen-2.4.0/checksums.yaml.gz0000444000004100000410000000041612261232670016333 0ustar www-datawww-datazRe;N@ D"Z9RѠT %Ȓeɚ7 ُx*&Uڔ$)1j>_#~u*Se@6(u* qC['?S_n$j _ـ)>!H<#*G1Mv$d}CtDͫ1s , nx c칶A8X0#Ebk5acz6(qU1hZ&f>-listen-2.4.0/README.md0000644000004100000410000002342712261232670014333 0ustar www-datawww-data# Listen [![Gem Version](https://badge.fury.io/rb/listen.png)](http://badge.fury.io/rb/listen) [![Build Status](https://travis-ci.org/guard/listen.png)](https://travis-ci.org/guard/listen) [![Dependency Status](https://gemnasium.com/guard/listen.png)](https://gemnasium.com/guard/listen) [![Code Climate](https://codeclimate.com/github/guard/listen.png)](https://codeclimate.com/github/guard/listen) [![Coverage Status](https://coveralls.io/repos/guard/listen/badge.png?branch=master)](https://coveralls.io/r/guard/listen) The Listen gem listens to file modifications and notifies you about the changes. ## Features * Supports watching multiple directories from a single listener. * OS-specific adapters on MRI for Mac OS X 10.6+, Linux, *BSD and Windows, [more info](#listen-adapters) below. * Detects file modification, addition and removal. * Allows supplying regexp-patterns to ignore paths for better results. * File content checksum comparison for modifications made under the same second (OS X only). * Tested on MRI Ruby environments (1.9+ only) via [Travis CI](https://travis-ci.org/guard/listen), Please note that: - Specs suite on JRuby and Rubinius aren't reliable on Travis CI, but should work. - Windows and *BSD adapter aren't continuously and automaticaly tested. ## Pending features * Non-recursive directory scanning. [#111](https://github.com/guard/listen/issues/111) * Symlinks support. [#25](https://github.com/guard/listen/issues/25) Pull request or help is very welcome for these. ## Install The simplest way to install Listen is to use [Bundler](http://bundler.io). ```ruby gem 'listen', '~> 2.0' ``` ## Usage Call `Listen.to` with either a single directory or multiple directories, then define the "changes" callback in a block. ``` ruby listener = Listen.to('dir/to/listen', 'dir/to/listen2') do |modified, added, removed| puts "modified absolute path: #{modified}" puts "added absolute path: #{added}" puts "removed absolute path: #{removed}" end listener.start # not blocking sleep ``` ### Pause / unpause / stop Listeners can also be easily paused/unpaused: ``` ruby listener = Listen.to('dir/path/to/listen') { |modified, added, removed| # ... } listener.start listener.listen? # => true listener.pause # stop listening to changes listener.paused? # => true listener.unpause # start listening to changes again listener.stop # stop completely the listener ``` ### Ignore / ignore! Listen ignores some directories and extensions by default (See DEFAULT_IGNORED_DIRECTORIES and DEFAULT_IGNORED_EXTENSIONS in Listen::Silencer), you can add ignoring patterns with the `ignore` option/method or overwrite default with `ignore!` option/method. ``` ruby listener = Listen.to('dir/path/to/listen', ignore: /\.txt/) { |modified, added, removed| # ... } listener.start listener.ignore! /\.pkg/ # overwrite all patterns and only ignore pkg extension. listener.ignore /\.rb/ # ignore rb extension in addition of pkg. sleep ``` Note: Ignoring regexp patterns are evaluated against relative paths. ### Only Listen catches all files (less the ignored once) by default, if you want to only listen to a specific type of file (ie: just rb extension) you should use the `only` option/method. ``` ruby listener = Listen.to('dir/path/to/listen', only: /\.rb$/) { |modified, added, removed| # ... } listener.start listener.only /_spec\.rb$/ # overwrite all existing only patterns. sleep ``` Note: Only regexp patterns are evaluated only against relative **file** paths. ## Changes callback Changes to the listened-to directories gets reported back to the user in a callback. The registered callback gets invoked, when there are changes, with **three** parameters: `modified`, `added` and `removed` paths, in that particular order. Paths are always returned in their absolute form. Example: ```ruby listener = Listen.to('path/to/app') do |modified, added, removed| # This block will be called when there are changes. end listener.start sleep ``` or ... ```ruby # Create a callback callback = Proc.new do |modified, added, removed| # This proc will be called when there are changes. end listener = Listen.to('dir', &callback) listener.start sleep ``` ## Options All the following options can be set through the `Listen.to` after the directory path(s) params. ```ruby ignore: [%r{/foo/bar}, /\.pid$/, /\.coffee$/] # Ignore a list of paths # default: See DEFAULT_IGNORED_DIRECTORIES and DEFAULT_IGNORED_EXTENSIONS in Listen::Silencer ignore!: %r{/foo/bar} # Same as ignore options, but overwrite default ignored paths. only: %r{.rb$} # Only listen to specific files # default: none latency: 0.5 # Set the delay (**in seconds**) between checking for changes # default: 0.25 sec (1.0 sec for polling) wait_for_delay: 4 # Set the delay (**in seconds**) between calls to the callback when changes exist # default: 0.10 sec force_polling: true # Force the use of the polling adapter # default: none polling_fallback_message: 'custom message' # Set a custom polling fallback message (or disable it with false) # default: "Listen will be polling for changes. Learn more at https://github.com/guard/listen#polling-fallback." debug: true # Enable Celluloid logger # default: false ``` ## Listen adapters The Listen gem has a set of adapters to notify it when there are changes. There are 4 OS-specific adapters to support Darwin, Linux, *BSD and Windows. These adapters are fast as they use some system-calls to implement the notifying function. There is also a polling adapter which is a cross-platform adapter and it will work on any system. This adapter is slower than the rest of the adapters. The Darwin and Linux adapters are dependencies of the Listen gem so they work out of the box. For other adapters a specific gem will have to be added to your Gemfile, please read below. The Listen gem will choose the best adapter automatically, if present. If you want to force the use of the polling adapter, use the `:force_polling` option while initializing the listener. ### On Windows If your are on Windows, you can try to use the [`wdm`](https://github.com/Maher4Ever/wdm) instead of polling. Please add the following to your Gemfile: ```ruby require 'rbconfig' gem 'wdm', '>= 0.1.0' if RbConfig::CONFIG['target_os'] =~ /mswin|mingw|cygwin/i ``` ### On *BSD If your are on *BSD you can try to use the [`rb-kqueue`](https://github.com/mat813/rb-kqueue) instead of polling. Please add the following to your Gemfile: ```ruby require 'rbconfig' gem 'rb-kqueue', '>= 0.2' if RbConfig::CONFIG['target_os'] =~ /freebsd/i ``` ### Issues Sometimes OS-specific adapters don't work. :'( Here are some things you could try to avoid forcing polling. * [Update your Dropbox client](http://www.dropbox.com/downloading), if you have Dropbox installed. * Move or rename the listened directory. * Update/reboot your OS. * Increase latency. If your application keeps using the polling-adapter and you can't figure out why, feel free to [open an issue](https://github.com/guard/listen/issues/new) (and be sure to [give all the details](https://github.com/guard/listen/blob/master/CONTRIBUTING.md)). ## Development * Documentation hosted at [RubyDoc](http://rubydoc.info/github/guard/listen/master/frames). * Source hosted at [GitHub](https://github.com/guard/listen). Pull requests are very welcome! Please try to follow these simple rules if applicable: * Please create a topic branch for every separate change you make. * Make sure your patches are well tested. All specs must pass on [Travis CI](https://travis-ci.org/guard/listen). * Update the [Yard](http://yardoc.org/) documentation. * Update the [README](https://github.com/guard/listen/blob/master/README.md). * Please **do not change** the version number. For questions please join us in our [Google group](http://groups.google.com/group/guard-dev) or on `#guard` (irc.freenode.net). ## Acknowledgments * [Michael Kessler (netzpirat)][] for having written the [initial specs](https://github.com/guard/listen/commit/1e457b13b1bb8a25d2240428ce5ed488bafbed1f). * [Travis Tilley (ttilley)][] for this awesome work on [fssm][] & [rb-fsevent][]. * [Nathan Weizenbaum (nex3)][] for [rb-inotify][], a thorough inotify wrapper. * [Mathieu Arnold (mat813)][] for [rb-kqueue][], a simple kqueue wrapper. * [Maher Sallam][] for [wdm][], windows support wouldn't exist without him. * [Yehuda Katz (wycats)][] for [vigilo][], that has been a great source of inspiration. ## Author * [Thibaud Guillaume-Gentil (thibaudgg)][] ([@thibaudgg](http://twitter.com/thibaudgg)) ## Contributors [https://github.com/guard/listen/graphs/contributors](https://github.com/guard/listen/graphs/contributors) [Thibaud Guillaume-Gentil (thibaudgg)]: https://github.com/thibaudgg [Maher Sallam]: https://github.com/Maher4Ever [Michael Kessler (netzpirat)]: https://github.com/netzpirat [Travis Tilley (ttilley)]: https://github.com/ttilley [fssm]: https://github.com/ttilley/fssm [rb-fsevent]: https://github.com/thibaudgg/rb-fsevent [Mathieu Arnold (mat813)]: https://github.com/mat813 [Nathan Weizenbaum (nex3)]: https://github.com/nex3 [rb-inotify]: https://github.com/nex3/rb-inotify [stereobooster]: https://github.com/stereobooster [rb-fchange]: https://github.com/stereobooster/rb-fchange [rb-kqueue]: https://github.com/mat813/rb-kqueue [Yehuda Katz (wycats)]: https://github.com/wycats [vigilo]: https://github.com/wycats/vigilo [wdm]: https://github.com/Maher4Ever/wdm listen-2.4.0/Guardfile0000644000004100000410000000031512261232670014670 0ustar www-datawww-dataguard :rspec do watch(%r{^spec/.+_spec\.rb$}) watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" } watch(%r{^spec/support/*}) { 'spec' } watch('spec/spec_helper.rb') { 'spec' } end