Class: Middleman::Sources

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Contracts
Defined in:
middleman-core/lib/middleman-core/sources.rb

Overview

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

Defined Under Namespace

Classes: CallbackDescriptor

Constant Summary collapse

OUTPUT_TYPES =

Types which could cause output to change.

%i[source locales data].freeze
CODE_TYPES =

Types which require a reload to eval ruby

[:reload].freeze
Matcher =
Or[Regexp, RespondTo[:call]
HANDLER =

Duck-typed definition of a valid source watcher

RespondTo[:on_change]

Constants included from Contracts

Contracts::ImmutableSetOf, Contracts::ImmutableSortedSetOf, Contracts::OldResourceList, Contracts::PATH_MATCHER, Contracts::ResourceList, Contracts::VectorOf

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Contracts

#Contract

Constructor Details

#initialize(app, _options_hash = ::Middleman::EMPTY_HASH, watchers = []) ⇒ Sources

Returns a new instance of Sources



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'middleman-core/lib/middleman-core/sources.rb', line 60

def initialize(app, _options_hash = ::Middleman::EMPTY_HASH, watchers = [])
  @app = app
  @watchers = watchers
  @sorted_watchers = @watchers.dup.freeze

  ::Middleman::Sources.file_cache = {}

  # Set of procs wanting to be notified of changes
  @on_change_callbacks = ::Hamster::Vector.empty

  # Global ignores
  @ignores = ::Hamster::Hash.empty

  # 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

Instance Attribute Details

#appObject (readonly)

Returns the value of attribute app



37
38
39
# File 'middleman-core/lib/middleman-core/sources.rb', line 37

def app
  @app
end

#optionsObject (readonly)

Returns the value of attribute options



44
45
46
# File 'middleman-core/lib/middleman-core/sources.rb', line 44

def options
  @options
end

Instance Method Details

#by_type(type) ⇒ Object



173
174
175
# File 'middleman-core/lib/middleman-core/sources.rb', line 173

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

#changed(matcher = nil, &_block) ⇒ Object



293
294
295
296
297
298
299
# File 'middleman-core/lib/middleman-core/sources.rb', line 293

def changed(matcher = nil, &_block)
  on_change OUTPUT_TYPES do |updated, _removed|
    updated
      .select { |f| matcher.nil? ? true : matches?(matcher, f) }
      .each { |f| yield f[:relative_path] }
  end
end

#deleted(matcher = nil, &_block) ⇒ Object



305
306
307
308
309
310
311
# File 'middleman-core/lib/middleman-core/sources.rb', line 305

def deleted(matcher = nil, &_block)
  on_change OUTPUT_TYPES do |_updated, removed|
    removed
      .select { |f| matcher.nil? ? true : matches?(matcher, f) }
      .each { |f| yield f[:relative_path] }
  end
end

#exists?(types, path) ⇒ Boolean

Returns:

  • (Boolean)


221
222
223
# File 'middleman-core/lib/middleman-core/sources.rb', line 221

def exists?(types, path)
  watchers.any? { |d| includes_type?(types, d.type) && d.exists?(path) }
end

#filesObject



181
182
183
# File 'middleman-core/lib/middleman-core/sources.rb', line 181

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

#find(types, path, glob = false) ⇒ Object



192
193
194
195
196
197
198
199
# File 'middleman-core/lib/middleman-core/sources.rb', line 192

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

#find_new_files!Object



239
240
241
242
243
244
# File 'middleman-core/lib/middleman-core/sources.rb', line 239

def find_new_files!
  return [] unless @update_count != @last_update_count

  @last_update_count = @update_count
  watchers.reduce([]) { |sum, w| sum + w.find_new_files! }
end

#globally_ignored?(file) ⇒ Boolean

Returns:

  • (Boolean)


105
106
107
108
109
110
# File 'middleman-core/lib/middleman-core/sources.rb', line 105

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

#ignore(name, type, regex = nil, &block) ⇒ Object



92
93
94
95
96
97
98
# File 'middleman-core/lib/middleman-core/sources.rb', line 92

def ignore(name, type, regex = nil, &block)
  @ignores = @ignores.put(name, type: type,
                                validator: (block_given? ? block : regex))

  bump_count
  poll_once! if @running
end

#ignored?(path) ⇒ Boolean

Returns:

  • (Boolean)


317
318
319
320
# File 'middleman-core/lib/middleman-core/sources.rb', line 317

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

#includes_type?(types, type) ⇒ Boolean

Returns:

  • (Boolean)


207
208
209
210
211
212
213
# File 'middleman-core/lib/middleman-core/sources.rb', line 207

def includes_type?(types, type)
  if types.is_a? Symbol
    types == type
  else
    types.include? type
  end
end

#on_change(types, &block)

This method returns an undefined value.

Disconnect a specific watcher.

Parameters:



159
# File 'middleman-core/lib/middleman-core/sources.rb', line 159

Contract RespondTo[:on_change] => Any

#poll_once!Object



250
251
252
253
254
255
# File 'middleman-core/lib/middleman-core/sources.rb', line 250

def poll_once!
  return [] unless @update_count != @last_update_count

  @last_update_count = @update_count
  watchers.reduce([]) { |sum, w| sum + w.poll_once! }
end

#start!Object



261
262
263
264
# File 'middleman-core/lib/middleman-core/sources.rb', line 261

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

#stop!Object



270
271
272
273
# File 'middleman-core/lib/middleman-core/sources.rb', line 270

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

#Symbol

This method returns an undefined value.

Add a proc to ignore paths with either a regex or block.

Parameters:

  • name (Symbol)

    A name for the ignore.

  • type (Symbol)

    The type of content to apply the ignore to.

  • regex (Regexp)

    Ignore by path regex.

  • block (Proc)

    Ignore by block evaluation.



91
# File 'middleman-core/lib/middleman-core/sources.rb', line 91

Contract Symbol, Symbol, Or[Regexp, Proc] => Any

#unwatch(watcher) ⇒ Object



160
161
162
163
164
165
166
# File 'middleman-core/lib/middleman-core/sources.rb', line 160

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

  watcher.unwatch

  bump_count
end

#watch(type_or_handler, options_hash = ::Middleman::EMPTY_HASH) ⇒ Object



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'middleman-core/lib/middleman-core/sources.rb', line 120

def watch(type_or_handler, options_hash = ::Middleman::EMPTY_HASH)
  handler = if type_or_handler.is_a? Symbol
              path = File.expand_path(options_hash.delete(:path), app.root)
              SourceWatcher.new(self, type_or_handler, path, options_hash)
            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

#watcher_for_path(types, path) ⇒ Object



231
232
233
# File 'middleman-core/lib/middleman-core/sources.rb', line 231

def watcher_for_path(types, path)
  watchers.detect { |d| includes_type?(types, d.type) && d.exists?(path) }
end

#watchersObject



151
152
153
# File 'middleman-core/lib/middleman-core/sources.rb', line 151

def watchers
  @sorted_watchers
end