class Core::Watch::System

public

The system watcher.

Attributes

interval[RW]
public

The configured polling interval.

latency[R]
public

The current latency.

status[R]
public

The status object.

Public Class Methods

new(interval: 0.1, strategy: :digest) click to toggle source
# File lib/core/watch/system.rb, line 33
def initialize(interval: 0.1, strategy: :digest)
  @interval = interval
  @status = Status.new
  @mutex = Mutex.new
  @watched = []
  @ignored = []
  @callbacks = []
  @working = false
  @latency = nil

  @strategy = case strategy.to_sym
  when :digest
    require_relative "strategies/digest"
    Strategies::Digest
  when :timestamp
    require_relative "strategies/timestamp"
    Strategies::Timestamp
  else
    raise ArgumentError, "unknown watch strategy `#{strategy.inspect}'"
  end
end

Public Instance Methods

callback(matcher = nil, &block) click to toggle source
public

Register a callback to be called when a change is detected. The callback will only be called once per

tick and will be given a `Core::Watch::Diff` instance. If `matcher` is passed, the callback will only be called for matching changes. The matcher can be a string or any object that responds to `match?`.

# File lib/core/watch/system.rb, line 87
def callback(matcher = nil, &block)
  raise ArgumentError, "expected a block" if block.nil?

  @mutex.synchronize do
    @callbacks << Callback.build(matcher, &block)
  end
end
ignore(path) click to toggle source
public

Ignore changes at `path`.

# File lib/core/watch/system.rb, line 71
def ignore(path)
  path = path.to_s

  @mutex.synchronize do
    unless @ignored.include?(path)
      mutate do
        @ignored << path
      end
    end
  end
end
pause() click to toggle source
public

Pause the watcher, causing it to continue running but not invoke callbacks.

# File lib/core/watch/system.rb, line 117
def pause
  @status.paused!
end
resume() click to toggle source
public

Resume a paused watcher.

# File lib/core/watch/system.rb, line 123
def resume
  @status.running!
end
start() click to toggle source
public

Start the watcher.

# File lib/core/watch/system.rb, line 97
def start
  await do
    unless @status.running?
      @future = run
    end
  end
end
stop(force: false) click to toggle source
public

Stop the watcher.

# File lib/core/watch/system.rb, line 107
def stop(force: false)
  if !@working || force
    @future&.cancel
  end

  @status.stopped!
end
watch(path) click to toggle source
public

Watch `path` for changes.

# File lib/core/watch/system.rb, line 57
def watch(path)
  path = Pathname(path).expand_path

  @mutex.synchronize do
    unless @watched.include?(path)
      mutate do
        @watched << path
      end
    end
  end
end

Private Instance Methods

current_time() click to toggle source
# File lib/core/watch/system.rb, line 186
        def current_time
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
end
invoke_callbacks(diff) click to toggle source
# File lib/core/watch/system.rb, line 165
        def invoke_callbacks(diff)
  return unless diff.changed?

  await do
    @working = true
    @callbacks.each do |callback|
      maybe_invoke_callback(callback, diff)
    end
  end
ensure
  @working = false
end
maybe_invoke_callback(callback, diff) click to toggle source
# File lib/core/watch/system.rb, line 178
        def maybe_invoke_callback(callback, diff)
  diff.each_change do |path, event|
    if callback.match?(path)
      return async { callback.call(diff) }
    end
  end
end
mutate() { || ... } click to toggle source
# File lib/core/watch/system.rb, line 153
        def mutate
  yield

  if @status.running?
    rebuild_snapshot
  end
end
rebuild_snapshot() click to toggle source
# File lib/core/watch/system.rb, line 161
        def rebuild_snapshot
  @snapshot = Snapshot.new(*@watched, ignore: @ignored, strategy: @strategy)
end
run() click to toggle source
# File lib/core/watch/system.rb, line 127
        def run
  async {
    @status.running!

    rebuild_snapshot

    until @status.stopped?
      sleep(@interval)

      started = current_time

      # Don't process this tick if the watch was paused or stopped while sleeping.
      #
      next if @status.paused? || @status.stopped?

      # Do the work, updating the snapshot in the process.
      #
      invoke_callbacks(@snapshot.diff)

      # Measure the latency for this tick.
      #
      @latency = [0, current_time - started - @interval].max
    end
  }
end