class Middleman::Sources

data. The ‘locales` type represents localization YAML, and so on.
the sitemap uses to build a project. The `data` type represents YAML
queried. For example, the `source` type represents all content that
a Middleman project. They are separated by `type` which can then be
Sources handle multiple on-disk collections of files which make up

def bump_count

def bump_count
  @update_count += 1
end

def by_type(type)

def by_type(type)
  self.class.new @app, @options, watchers.select { |d| d.type == type }
end

def changed(matcher=nil, &block)

Parameters:
  • matcher (nil, Regexp) -- A Regexp to match the change path against
def changed(matcher=nil, &block)
  on_change :source do |updated, _removed|
    updated.select { |f|
      matcher.nil? ? true : matches?(matcher, f)
    }.each do |f|
      block.call(f[:relative_path])
    end
  end
end

def deleted(matcher=nil, &block)

Parameters:
  • matcher (nil, Regexp) -- A Regexp to match the change path against
def deleted(matcher=nil, &block)
  on_change :source do |_updated, removed|
    removed.select { |f|
      matcher.nil? ? true : matches?(matcher, f)
    }.each do |f|
      block.call(f[:relative_path])
    end
  end
end

def did_change(updated_files, removed_files, watcher)

def did_change(updated_files, removed_files, watcher)
  valid_updated = updated_files.select do |file|
    watcher_for_path(file[:types], file[:relative_path].to_s) == watcher
  end
  valid_removed = removed_files.select do |file|
    watcher_for_path(file[:types], file[:relative_path].to_s).nil?
  end
  return if valid_updated.empty? && valid_removed.empty?
  bump_count
  run_callbacks(@on_change_callbacks, valid_updated, valid_removed)
end

def exists?(type, path)

def exists?(type, path)
  watchers
    .lazy
    .select { |d| d.type == type }
    .any? { |d| d.exists?(path) }
end

def files

def files
  watchers.flat_map(&:files).uniq { |f| f[:relative_path] }
end

def find(type, path, glob=false)

def find(type, path, glob=false)
  watchers
    .lazy
    .select { |d| d.type == type }
    .map { |d| d.find(path, glob) }
    .reject(&:nil?)
    .first
end

def find_new_files!

def find_new_files!
  return unless @update_count != @last_update_count
  @last_update_count = @update_count
  watchers.each(&:poll_once!)
end

def globally_ignored?(file)

def globally_ignored?(file)
  @ignores.values.any? do |descriptor|
    ((descriptor[:type] == :all) || file[:types].include?(descriptor[:type])) &&
      matches?(descriptor[:validator], file)
  end
end

def ignore(name, type, regex=nil, &block)

def ignore(name, type, regex=nil, &block)
  @ignores[name] = { type: type, validator: (block_given? ? block : regex) }
  bump_count
  find_new_files! if @running
end

def ignored?(path)

def ignored?(path)
  descriptor = find(:source, path)
  !descriptor || globally_ignored?(descriptor)
end

def initialize(app, options={}, watchers=[])

def initialize(app, options={}, watchers=[])
  @app = app
  @watchers = watchers
  @sorted_watchers = @watchers.dup.freeze
  @options = options
  # Set of procs wanting to be notified of changes
  @on_change_callbacks = []
  # Global ignores
  @ignores = {}
  # Whether we're "running", which means we're in a stable
  # watch state after all initialization and config.
  @running = false
  @update_count = 0
  @last_update_count = -1
  # When the app is about to shut down, stop our watchers.
  @app.before_shutdown(&method(:stop!))
end

def matches?(validator, file)

def matches?(validator, file)
  path = file[:relative_path]
  if validator.is_a? Regexp
    !!validator.match(path.to_s)
  else
    !!validator.call(path, @app)
  end
end

def on_change(type, &block)

def on_change(type, &block)
  @on_change_callbacks << CallbackDescriptor.new(type, block)
  @on_change_callbacks
end

def run_callbacks(callback_descriptors, updated_files, removed_files)

def run_callbacks(callback_descriptors, updated_files, removed_files)
  callback_descriptors.each do |callback|
    if callback[:type] == :all
      callback[:proc].call(updated_files, removed_files)
    else
      valid_updated = updated_files.select { |f| f[:types].include?(callback[:type]) }
      valid_removed = removed_files.select { |f| f[:types].include?(callback[:type]) }
      callback[:proc].call(valid_updated, valid_removed) unless valid_updated.empty? && valid_removed.empty?
    end
  end
end

def start!

def start!
  watchers.each(&:listen!)
  @running = true
end

def stop!

def stop!
  watchers.each(&:stop_listener!)
  @running = false
end

def unwatch(watcher)

def unwatch(watcher)
  @watchers.delete(watcher)
  watcher.unwatch
  bump_count
end

def watch(type_or_handler, options={})

def watch(type_or_handler, options={})
  handler = if type_or_handler.is_a? Symbol
    SourceWatcher.new(self, type_or_handler, options.delete(:path), options)
  else
    type_or_handler
  end
  @watchers << handler
  # The index trick is used so that the sort is stable - watchers with the same priority
  # will always be ordered in the same order as they were registered.
  n = 0
  @sorted_watchers = @watchers.sort_by do |w|
    priority = w.options.fetch(:priority, 50)
    n += 1
    [priority, n]
  end.reverse.freeze
  handler.on_change(&method(:did_change))
  if @running
    handler.poll_once!
    handler.listen!
  end
  handler
end

def watcher_for_path(types, path)

def watcher_for_path(types, path)
  watchers
    .select { |d| types.include?(d.type) }
    .find { |d| d.exists?(path) }
end

def watchers

def watchers
  @sorted_watchers
end