Class: Listen::Listener
- Inherits:
-
Object
- Object
- Listen::Listener
- Defined in:
- lib/listen/listener.rb
Direct Known Subclasses
Constant Summary collapse
- RELATIVE_PATHS_WITH_MULTIPLE_DIRECTORIES_WARNING_MESSAGE =
"The relative_paths option doesn't work when listening to multiple diretories."
Instance Attribute Summary collapse
-
#block ⇒ Object
Returns the value of attribute block.
-
#changes ⇒ Object
Returns the value of attribute changes.
-
#directories ⇒ Object
Returns the value of attribute directories.
-
#options ⇒ Object
Returns the value of attribute options.
-
#paused ⇒ Object
Returns the value of attribute paused.
-
#registry ⇒ Object
Returns the value of attribute registry.
-
#stopping ⇒ Object
Returns the value of attribute stopping.
-
#supervisor ⇒ Object
Returns the value of attribute supervisor.
Instance Method Summary collapse
- #_calculate_add_remove_difference(actions, path, default_if_exists) ⇒ Object private
- #_detect_possible_editor_save(changes) ⇒ Object private
- #_init_actors ⇒ Object private
- #_init_debug ⇒ Object private
- #_init_options(options = {}) ⇒ Object private
- #_local_fs? ⇒ Boolean private
- #_logical_action_for(path, actions) ⇒ Object private
- #_pop_changes ⇒ Object private
-
#_reinterpret_related_changes(cookies) ⇒ Object
private
remove extraneous rb-inotify events, keeping them only if it's a possible editor rename() call (e.g. Kate and Sublime).
- #_silenced?(path) ⇒ Boolean private
- #_smoosh_changes(changes) ⇒ Object private
- #_squash_changes(changes) ⇒ Object private
- #_wait_for_changes ⇒ Object private
-
#ignore(regexps) ⇒ Object
Adds ignore patterns to the existing one (See DEFAULT_IGNORED_DIRECTORIES and DEFAULT_IGNORED_EXTENSIONS in Listen::Silencer).
-
#ignore!(regexps) ⇒ Object
Overwrites ignore patterns (See DEFAULT_IGNORED_DIRECTORIES and DEFAULT_IGNORED_EXTENSIONS in Listen::Silencer).
-
#initialize(*args) {|modified, added, removed| ... } ⇒ Listener
constructor
Initializes the directories listener.
-
#listen? ⇒ Boolean
Returns true if Listener is neither paused nor stopped.
-
#only(regexps) ⇒ Object
Sets only patterns, to listen only to specific regexps.
-
#pause ⇒ Object
Pauses listening callback (adapter still running).
-
#paused? ⇒ Boolean
Returns true if Listener is paused.
-
#start ⇒ Object
Starts the listener by initializing the adapter and building the directory record concurrently, then it starts the adapter to watch for changes.
-
#stop ⇒ Object
Terminates all Listen actors and kill the adapter.
-
#unpause ⇒ Object
Unpauses listening callback.
Constructor Details
#initialize(*args) {|modified, added, removed| ... } ⇒ Listener
Initializes the directories listener.
24 25 26 27 28 29 30 31 |
# File 'lib/listen/listener.rb', line 24 def initialize(*args, &block) @options = (args.last.is_a?(Hash) ? args.pop : {}) @directories = args.flatten.map { |path| Pathname.new(path).realpath } @changes = [] @block = block @registry = Celluloid::Registry.new _init_debug end |
Instance Attribute Details
#block ⇒ Object
Returns the value of attribute block.
9 10 11 |
# File 'lib/listen/listener.rb', line 9 def block @block end |
#changes ⇒ Object
Returns the value of attribute changes.
9 10 11 |
# File 'lib/listen/listener.rb', line 9 def changes @changes end |
#directories ⇒ Object
Returns the value of attribute directories.
9 10 11 |
# File 'lib/listen/listener.rb', line 9 def directories @directories end |
#options ⇒ Object
Returns the value of attribute options.
9 10 11 |
# File 'lib/listen/listener.rb', line 9 def @options end |
#paused ⇒ Object
Returns the value of attribute paused.
9 10 11 |
# File 'lib/listen/listener.rb', line 9 def paused @paused end |
#registry ⇒ Object
Returns the value of attribute registry.
10 11 12 |
# File 'lib/listen/listener.rb', line 10 def registry @registry end |
#stopping ⇒ Object
Returns the value of attribute stopping.
9 10 11 |
# File 'lib/listen/listener.rb', line 9 def stopping @stopping end |
#supervisor ⇒ Object
Returns the value of attribute supervisor.
10 11 12 |
# File 'lib/listen/listener.rb', line 10 def supervisor @supervisor end |
Instance Method Details
#_calculate_add_remove_difference(actions, path, default_if_exists) ⇒ Object (private)
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/listen/listener.rb', line 201 def _calculate_add_remove_difference(actions, path, default_if_exists) added = actions.count { |x| x == :added } removed = actions.count { |x| x == :removed } diff = added - removed if path.exist? if diff > 0 :added elsif diff.zero? && added > 0 :modified else default_if_exists end else diff < 0 ? :removed : nil end end |
#_detect_possible_editor_save(changes) ⇒ Object (private)
235 236 237 238 239 240 241 242 243 244 245 |
# File 'lib/listen/listener.rb', line 235 def _detect_possible_editor_save(changes) return unless changes.size == 2 from, to = changes.sort { |x,y| x.keys.first <=> y.keys.first } from, to = from[:moved_from], to[:moved_to] return unless from and to # Expect an ignored moved_from and non-ignored moved_to # to qualify as an "editor modify" _silenced?(from) && !_silenced?(to) ? to : nil end |
#_init_actors ⇒ Object (private)
127 128 129 130 131 132 133 134 135 |
# File 'lib/listen/listener.rb', line 127 def _init_actors @supervisor = Celluloid::SupervisionGroup.run!(registry) 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() supervisor.add(adapter_class, as: :adapter, args: self) end |
#_init_debug ⇒ Object (private)
119 120 121 122 123 124 125 |
# File 'lib/listen/listener.rb', line 119 def _init_debug if [:debug] || ENV['LISTEN_GEM_DEBUGGING'] =~ /true|1/i Celluloid.logger.level = Logger::INFO else Celluloid.logger.level = Logger::FATAL end end |
#_init_options(options = {}) ⇒ Object (private)
111 112 113 114 115 116 117 |
# File 'lib/listen/listener.rb', line 111 def ( = {}) { debug: false, latency: nil, wait_for_delay: 0.1, force_polling: false, polling_fallback_message: nil }.merge() end |
#_local_fs? ⇒ Boolean (private)
175 176 177 |
# File 'lib/listen/listener.rb', line 175 def _local_fs? !registry[:adapter].is_a?(Adapter::TCP) end |
#_logical_action_for(path, actions) ⇒ Object (private)
193 194 195 196 197 198 199 |
# File 'lib/listen/listener.rb', line 193 def _logical_action_for(path, actions) actions << :added if actions.delete(:moved_to) actions << :removed if actions.delete(:moved_from) modified = actions.find { |x| x == :modified } _calculate_add_remove_difference(actions, path, modified) end |
#_pop_changes ⇒ Object (private)
157 158 159 160 161 |
# File 'lib/listen/listener.rb', line 157 def _pop_changes popped = [] popped << @changes.shift until @changes.empty? popped end |
#_reinterpret_related_changes(cookies) ⇒ Object (private)
remove extraneous rb-inotify events, keeping them only if it's a possible editor rename() call (e.g. Kate and Sublime)
221 222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/listen/listener.rb', line 221 def () table = { moved_to: :added, moved_from: :removed } .map do |, changes| file = _detect_possible_editor_save(changes) if file [[:modified, file]] else changes.map(&:first).reject do |type, path| _silenced?(path) end.map { |type, path| [table.fetch(type, type), path] } end end.flatten(1) end |
#_silenced?(path) ⇒ Boolean (private)
247 248 249 250 |
# File 'lib/listen/listener.rb', line 247 def _silenced?(path) type = path.directory? ? 'Dir' : 'File' registry[:silencer].silenced?(path, type) end |
#_smoosh_changes(changes) ⇒ Object (private)
163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/listen/listener.rb', line 163 def _smoosh_changes(changes) if _local_fs? = changes.group_by { |x| x[:cookie] } _squash_changes(()) else smooshed = { modified: [], added: [], removed: [] } changes.each { |h| type = h.keys.first; smooshed[type] << h[type].to_s } smooshed.each { |_, v| v.uniq! } smooshed end end |
#_squash_changes(changes) ⇒ Object (private)
179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/listen/listener.rb', line 179 def _squash_changes(changes) actions = changes.group_by(&:last).map do |path, action_list| [_logical_action_for(path, action_list.map(&:first)), path.to_s] end Celluloid.logger.info "listen: raw changes: #{actions.inspect}" { modified: [], added: [], removed: [] }.tap do |squashed| actions.each do |type, path| squashed[type] << path unless type.nil? end Celluloid.logger.info "listen: final changes: #{squashed.inspect}" end end |
#_wait_for_changes ⇒ Object (private)
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/listen/listener.rb', line 137 def _wait_for_changes loop do break if @stopping changes = [] begin sleep [:wait_for_delay] # wait for changes to accumulate new_changes = _pop_changes changes += new_changes end until new_changes.empty? unless changes.empty? hash = _smoosh_changes(changes) block.call(hash[:modified], hash[:added], hash[:removed]) end end rescue => ex Kernel.warn "[Listen warning]: Change block raised an exception: #{$!}" Kernel.warn "Backtrace:\n\t#{ex.backtrace.join("\n\t")}" end |
#ignore(regexps) ⇒ Object
Adds ignore patterns to the existing one (See DEFAULT_IGNORED_DIRECTORIES and DEFAULT_IGNORED_EXTENSIONS in Listen::Silencer)
85 86 87 88 |
# File 'lib/listen/listener.rb', line 85 def ignore(regexps) @options[:ignore] = [[:ignore], regexps] registry[:silencer] = Silencer.new(self) end |
#ignore!(regexps) ⇒ Object
Overwrites ignore patterns (See DEFAULT_IGNORED_DIRECTORIES and DEFAULT_IGNORED_EXTENSIONS in Listen::Silencer)
94 95 96 97 98 |
# File 'lib/listen/listener.rb', line 94 def ignore!(regexps) @options.delete(:ignore) @options[:ignore!] = regexps registry[:silencer] = Silencer.new(self) end |
#listen? ⇒ Boolean
Returns true if Listener is neither paused nor stopped
77 78 79 |
# File 'lib/listen/listener.rb', line 77 def listen? @paused == false && @stopping == false end |
#only(regexps) ⇒ Object
Sets only patterns, to listen only to specific regexps
104 105 106 107 |
# File 'lib/listen/listener.rb', line 104 def only(regexps) @options[:only] = regexps registry[:silencer] = Silencer.new(self) end |
#pause ⇒ Object
Pauses listening callback (adapter still running)
54 55 56 |
# File 'lib/listen/listener.rb', line 54 def pause @paused = true end |
#paused? ⇒ Boolean
Returns true if Listener is paused
69 70 71 |
# File 'lib/listen/listener.rb', line 69 def paused? @paused == true end |
#start ⇒ Object
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.
37 38 39 40 41 42 43 |
# File 'lib/listen/listener.rb', line 37 def start _init_actors unpause @stopping = false registry[:adapter].async.start Thread.new { _wait_for_changes } end |
#stop ⇒ Object
Terminates all Listen actors and kill the adapter.
47 48 49 50 |
# File 'lib/listen/listener.rb', line 47 def stop @stopping = true supervisor.terminate end |
#unpause ⇒ Object
Unpauses listening callback
60 61 62 63 |
# File 'lib/listen/listener.rb', line 60 def unpause registry[:record].build @paused = false end |