diff --git a/.travis.yml b/.travis.yml index 7b854736..2486c382 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,24 +3,6 @@ cache: bundler matrix: include: - - rvm: 2.3 - - rvm: 2.4 - - rvm: 2.5 - - rvm: 2.6 - - rvm: 2.5 - os: osx - env: - # TODO: 0.8 is enough on Linux, but 2 seems needed for Travis/OSX - - LISTEN_TESTS_DEFAULT_LAG=2 - - rvm: jruby - - rvm: truffleruby - - rvm: jruby-head - - rvm: ruby-head - - rvm: rbx-3 - allow_failures: - - rvm: truffleruby - rvm: jruby - - rvm: ruby-head - - rvm: jruby-head - - rvm: rbx-3 - fast_finish: true + env: + - TEST_LISTEN_ADAPTER_MODES=native diff --git a/lib/listen/adapter.rb b/lib/listen/adapter.rb index a1954d90..f04e8f6a 100644 --- a/lib/listen/adapter.rb +++ b/lib/listen/adapter.rb @@ -4,10 +4,11 @@ require 'listen/adapter/linux' require 'listen/adapter/polling' require 'listen/adapter/windows' +require 'listen/adapter/jruby' module Listen module Adapter - OPTIMIZED_ADAPTERS = [Darwin, Linux, BSD, Windows].freeze + OPTIMIZED_ADAPTERS = [Jruby, Darwin, Linux, BSD, Windows].freeze POLLING_FALLBACK_MESSAGE = 'Listen will be polling for changes.'\ 'Learn more at https://github.com/guard/listen#listen-adapters.'.freeze @@ -16,7 +17,11 @@ def select(options = {}) _log :debug, 'Adapter: considering polling ...' return Polling if options[:force_polling] _log :debug, 'Adapter: considering optimized backend...' - return _usable_adapter_class if _usable_adapter_class + _log :debug, "Adapter: RUBY_ENGINE=#{RUBY_ENGINE}" + if _usable_adapter_class + _log :debug, "Adapter: using #{_usable_adapter_class}" + return _usable_adapter_class + end _log :debug, 'Adapter: falling back to polling...' _warn_polling_fallback(options) Polling diff --git a/lib/listen/adapter/jruby.rb b/lib/listen/adapter/jruby.rb new file mode 100644 index 00000000..64c6b381 --- /dev/null +++ b/lib/listen/adapter/jruby.rb @@ -0,0 +1,65 @@ +module Listen + module Adapter + # @see https://docs.oracle.com/javase/tutorial/essential/io/notification.html + class Jruby < Base + def self.usable? + RUBY_ENGINE == 'jruby' + end + + private + + def _configure(directory, &_callback) + require 'java' + java_import 'java.nio.file.FileSystems' + java_import 'java.nio.file.Paths' + java_import 'java.nio.file.StandardWatchEventKinds' + + @event_kind_map ||= { + StandardWatchEventKinds::ENTRY_CREATE => :added, + StandardWatchEventKinds::ENTRY_MODIFY => :modified, + StandardWatchEventKinds::ENTRY_DELETE => :removed + } + + @watcher ||= FileSystems.getDefault.newWatchService + p @watcher.class.name + @keys ||= {} + path = Paths.get(directory.to_s) + key = path.register(@watcher, *@event_kind_map.keys) + @keys[key] = path + end + + def _run + loop do + key = @watcher.take + dir = @keys[key] + unless dir.nil? + key.pollEvents.each do |event| + kind = event.kind + next if kind == StandardWatchEventKinds::OVERFLOW + name = event.context + child = dir.resolve(name) + dirname = Pathname.new(child.to_s).dirname + full_path = Pathname.new(child.to_s) + if full_path.directory? + p [:dir, dirname] + _queue_change(:dir, dirname, '.', recursive: true) + elsif full_path.exist? + path = full_path.relative_path_from(dirname) + changed = @event_kind_map[kind] + p [:file, dirname, path.to_s, changed: changed] + _queue_change(:file, dirname, path.to_s, changed: changed) + end + end + end + valid = key.reset + unless valid + @keys.delete(key) + break if @keys.empty? + end + end + end + + def _process_event(dir, event); end + end + end +end diff --git a/spec/lib/listen/adapter/jruby_spec.rb b/spec/lib/listen/adapter/jruby_spec.rb new file mode 100644 index 00000000..95740d08 --- /dev/null +++ b/spec/lib/listen/adapter/jruby_spec.rb @@ -0,0 +1,11 @@ +RSpec.describe Listen::Adapter::Jruby do + describe 'class' do + subject { described_class } + + if jruby? + it { should be_usable } + else + it { should_not be_usable } + end + end +end diff --git a/spec/lib/listen/adapter_spec.rb b/spec/lib/listen/adapter_spec.rb index e894ab3b..d4ab7f48 100644 --- a/spec/lib/listen/adapter_spec.rb +++ b/spec/lib/listen/adapter_spec.rb @@ -5,6 +5,7 @@ allow(Listen::Adapter::Darwin).to receive(:usable?) { false } allow(Listen::Adapter::Linux).to receive(:usable?) { false } allow(Listen::Adapter::Windows).to receive(:usable?) { false } + allow(Listen::Adapter::Jruby).to receive(:usable?) { false } end describe '.select' do @@ -37,6 +38,12 @@ expect(klass).to eq Listen::Adapter::Windows end + it 'returns JRuby adapter when usable' do + allow(Listen::Adapter::Jruby).to receive(:usable?) { true } + klass = Listen::Adapter.select + expect(klass).to eq Listen::Adapter::Jruby + end + context 'no usable adapters' do before { allow(Kernel).to receive(:warn) } diff --git a/spec/support/platform_helper.rb b/spec/support/platform_helper.rb index 4505fe64..509c888e 100644 --- a/spec/support/platform_helper.rb +++ b/spec/support/platform_helper.rb @@ -13,3 +13,7 @@ def bsd? def windows? RbConfig::CONFIG['target_os'] =~ /mswin|mingw|cygwin/i end + +def jruby? + RUBY_ENGINE == 'jruby' +end