Skip to content

Instantly share code, notes, and snippets.

@rschiang
Created January 8, 2015 08:28
Show Gist options
  • Save rschiang/7e1fc9ee98266cf6ed30 to your computer and use it in GitHub Desktop.
Save rschiang/7e1fc9ee98266cf6ed30 to your computer and use it in GitHub Desktop.
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
internal encoding: nil
external encoding: #<Encoding:UTF-8>
rel_path encoding: #<Encoding:UTF-8>
string encoding: #<Encoding:UTF-8>
FS encoding: UTF-8
/Users/RSChiang/原始碼/
require 'pathname'
require 'listen/version'
require 'listen/adapter'
require 'listen/change'
require 'listen/record'
require 'listen/silencer'
require 'listen/queue_optimizer'
require 'English'
require 'listen/internals/logging'
module Listen
class Listener
include Celluloid::FSM
include QueueOptimizer
attr_accessor :block
attr_reader :silencer
# TODO: deprecate
attr_reader :options, :directories
attr_reader :registry, :supervisor
# TODO: deprecate
# NOTE: these are VERY confusing (broadcast + recipient modes)
attr_reader :host, :port
# 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<String>] modified the list of modified files
# @yieldparam [Array<String>] added the list of added files
# @yieldparam [Array<String>] removed the list of removed files
#
def initialize(*args, &block)
@options = _init_options(args.last.is_a?(Hash) ? args.pop : {})
# Setup logging first
if Celluloid.logger
Celluloid.logger.level = _debug_level
_info "Celluloid loglevel set to: #{Celluloid.logger.level}"
_info "Listen version: #{Listen::VERSION}"
end
@silencer = Silencer.new
_reconfigure_silencer({})
@tcp_mode = nil
if [:recipient, :broadcaster].include? args[1]
target = args.shift
@tcp_mode = args.shift
_init_tcp_options(target)
end
@directories = args.flatten.map { |path| Pathname.new(path).realpath }
@queue = Queue.new
@block = block
@registry = Celluloid::Registry.new
transition :stopped
end
default_state :initializing
state :initializing, to: :stopped
state :paused, to: [:processing, :stopped]
state :stopped, to: [:processing] do
_stop_wait_thread
if @supervisor
@supervisor.terminate
@supervisor = nil
end
end
state :processing, to: [:paused, :stopped] do
if wait_thread # means - was paused
_wakeup_wait_thread
else
@last_queue_event_time = nil
_start_wait_thread
_init_actors
# Note: make sure building is finished before starting adapter (for
# consistent results both in specs and normal usage)
sync(:record).build
_start_adapter
end
end
# Starts processing events and starts adapters
# or resumes invoking callbacks if paused
def start
transition :processing
end
# TODO: depreciate
alias_method :unpause, :start
# Stops processing and terminates all actors
def stop
transition :stopped
end
# Stops invoking callbacks (messages pile up)
def pause
transition :paused
end
# processing means callbacks are called
def processing?
state == :processing
end
def paused?
state == :paused
end
# TODO: deprecate
alias_method :listen?, :processing?
# TODO: deprecate
def paused=(value)
transition value ? :paused : :processing
end
# TODO: deprecate
alias_method :paused, :paused?
# Add files and dirs to ignore on top of defaults
#
# (@see Listen::Silencer for default ignored files and dirs)
#
def ignore(regexps)
_reconfigure_silencer(ignore: [options[:ignore], regexps])
end
# Replace default ignore patterns with provided regexp
def ignore!(regexps)
_reconfigure_silencer(ignore: [], ignore!: regexps)
end
# Listen only to files and dirs matching regexp
def only(regexps)
_reconfigure_silencer(only: regexps)
end
def async(type)
proxy = sync(type)
proxy ? proxy.async : nil
end
def sync(type)
@registry[type]
end
def queue(type, change, dir, path, options = {})
fail "Invalid type: #{type.inspect}" unless [:dir, :file].include? type
fail "Invalid change: #{change.inspect}" unless change.is_a?(Symbol)
fail "Invalid path: #{path.inspect}" unless path.is_a?(String)
@queue << [type, change, dir, path, options]
@last_queue_event_time = Time.now.to_f
_wakeup_wait_thread unless state == :paused
return unless @tcp_mode == :broadcaster
message = TCP::Message.new(type, change, dir, path, options)
registry[:broadcaster].async.broadcast(message.payload)
end
private
include Internals::Logging
def _init_options(options = {})
{ debug: false,
latency: nil,
wait_for_delay: 0.1,
force_polling: false,
polling_fallback_message: nil }.merge(options)
end
def _debug_level
debugging = ENV['LISTEN_GEM_DEBUGGING'] || options[:debug]
case debugging.to_s
when /2/
Logger::DEBUG
when /true|yes|1/i
Logger::INFO
else
Logger::ERROR
end
end
def _init_actors
options = [mq: self, directories: directories]
@supervisor = Celluloid::SupervisionGroup.run!(registry)
supervisor.add(Record, as: :record, args: self)
supervisor.pool(Change, as: :change_pool, args: self)
# TODO: broadcaster should be a separate plugin
if @tcp_mode == :broadcaster
require 'listen/tcp/broadcaster'
supervisor.add(TCP::Broadcaster, as: :broadcaster, args: [@host, @port])
# TODO: should be auto started, because if it crashes
# a new instance is spawned by supervisor, but it's 'start' isn't
# called
registry[:broadcaster].start
elsif @tcp_mode == :recipient
# TODO: adapter options should be configured in Listen.{on/to}
options.first.merge!(host: @host, port: @port)
end
supervisor.add(_adapter_class, as: :adapter, args: options)
end
def _wait_for_changes
latency = options[:wait_for_delay]
loop do
break if state == :stopped
if state == :paused || @queue.empty?
sleep
break if state == :stopped
end
# Assure there's at least latency between callbacks to allow
# for accumulating changes
now = Time.now.to_f
diff = latency + (@last_queue_event_time || now) - now
if diff > 0
sleep diff
next
end
_process_changes unless state == :paused
end
rescue RuntimeError
Kernel.warn _format_error('exception while processing events: %s %s')
end
def _silenced?(path, type)
@silencer.silenced?(path, type)
end
def _start_adapter
# Don't run async, because configuration has to finish first
adapter = sync(:adapter)
adapter.start
end
def _adapter_class
@adapter_class ||= Adapter.select(options)
end
# for easier testing without sleep loop
def _process_changes
return if @queue.empty?
@last_queue_event_time = nil
changes = []
changes << @queue.pop until @queue.empty?
return if block.nil?
hash = _smoosh_changes(changes)
result = [hash[:modified], hash[:added], hash[:removed]]
block_start = Time.now.to_f
# TODO: condition not tested, but too complex to test ATM
block.call(*result) unless result.all?(&:empty?)
_debug "Callback took #{Time.now.to_f - block_start} seconds"
end
attr_reader :wait_thread
def _init_tcp_options(target)
# Handle TCP options here
require 'listen/tcp'
fail ArgumentError, 'missing host/port for TCP' unless target
if @tcp_mode == :recipient
@host = 'localhost'
@options[:force_tcp] = true
end
if target.is_a? Fixnum
@port = target
else
@host, port = target.split(':')
@port = port.to_i
end
end
def _reconfigure_silencer(extra_options)
@options.merge!(extra_options)
# TODO: this should be directory specific
rules = [:only, :ignore, :ignore!].map do |option|
[option, @options[option]] if @options.key? option
end
@silencer.configure(Hash[rules.compact])
end
def _start_wait_thread
@wait_thread = Thread.new { _wait_for_changes }
end
def _wakeup_wait_thread
wait_thread.wakeup if wait_thread && wait_thread.alive?
end
def _stop_wait_thread
return unless wait_thread
if wait_thread.alive?
wait_thread.wakeup
wait_thread.join
end
@wait_thread = nil
end
def _queue_raw_change(type, dir, rel_path, options)
open('debug.out', 'a') { |f|
f << "internal encoding: #{Encoding.default_internal.inspect}\n"
f << "external encoding: #{Encoding.default_external.inspect}\n"
#f << "dir encoding: #{dir.encoding.inspect}"
f << "rel_path encoding: #{rel_path.encoding.inspect}\n"
f << "string encoding: #{"foo".encoding.inspect}\n"
f << "FS encoding: #{Pathname(dir).children.first.to_s.encoding}\n"
IO.binwrite("dir_value.bin", dir)
#IO.binwrite("watchdirs_value.bin", @directories.inspect)
}
#_debug "raw queue: #{[type, dir, rel_path, options].inspect}"
unless (worker = async(:change_pool))
_warn 'Failed to allocate worker from change pool'
return
end
worker.change(type, dir, rel_path, options)
rescue RuntimeError
_error_exception "_queue_raw_change exception %s:\n%s:\n"
raise
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment