Class: Middleman::SourceWatcher

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

Overview

The default source watcher implementation. Watches a directory on disk and responds to events on changes.

Constant Summary collapse

IGNORED_DIRECTORIES =
Set.new(%w[.git node_modules .sass-cache vendor/bundle .bundle])

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(parent, type, directory, options_hash = ::Middleman::EMPTY_HASH) ⇒ SourceWatcher


62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'middleman-core/lib/middleman-core/sources/source_watcher.rb', line 62

def initialize(parent, type, directory, options_hash = ::Middleman::EMPTY_HASH)
  @parent = parent
  @options = options_hash

  @type = type
  @directory = Pathname(directory)

  @files = {}
  @extensionless_files = {}

  @frontmatter = @options.fetch(:frontmatter, true)
  @binary = @options.fetch(:binary, false)
  @validator = @options.fetch(:validator, proc { true })
  @ignored = @options.fetch(:ignored, proc { false })
  @only = Array(@options.fetch(:only, []))

  @disable_watcher = app.build?
  @force_polling = false
  @latency = nil
  @wait_for_delay = nil

  @listener = nil

  @callbacks = ::Middleman::CallbackManager.new
  @callbacks.install_methods!(self, [:on_change])

  @waiting_for_existence = !@directory.exist?
end

Instance Attribute Details

#directoryObject (readonly)

Returns the value of attribute directory


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

def directory
  @directory
end

#listenerObject (readonly)

Reference to lower level listener


51
52
53
# File 'middleman-core/lib/middleman-core/sources/source_watcher.rb', line 51

def listener
  @listener
end

#optionsObject (readonly)

Returns the value of attribute options


48
49
50
# File 'middleman-core/lib/middleman-core/sources/source_watcher.rb', line 48

def options
  @options
end

#typeObject (readonly)

Returns the value of attribute type


40
41
42
# File 'middleman-core/lib/middleman-core/sources/source_watcher.rb', line 40

def type
  @type
end

Instance Method Details

#Any

This method returns an undefined value.

Stop the listener.


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

Contract Any

#exists?(path) ⇒ Boolean


164
165
166
# File 'middleman-core/lib/middleman-core/sources/source_watcher.rb', line 164

def exists?(path)
  !find(path).nil?
end

#filesObject


127
128
129
# File 'middleman-core/lib/middleman-core/sources/source_watcher.rb', line 127

def files
  @files.values
end

#find(path, glob = false) ⇒ Object


137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'middleman-core/lib/middleman-core/sources/source_watcher.rb', line 137

def find(path, glob = false)
  path = path.to_s.encode!('UTF-8', 'UTF-8-MAC') if RUBY_PLATFORM =~ /darwin/

  p = Pathname(path)

  return nil if p.absolute? && !p.to_s.start_with?(@directory.to_s)

  destination_dir = @options[:destination_dir]
  if !destination_dir.nil? && !destination_dir.empty? && p.to_s.start_with?(destination_dir)
    path_without_destination_dir = p.to_s[destination_dir.to_s.length + 1..-1]
    p = Pathname(path_without_destination_dir)
  end

  p = @directory + p if p.relative?

  if glob
    @extensionless_files[p]
  else
    @files[p]
  end
end

#find_new_files!Object


203
204
205
206
207
208
# File 'middleman-core/lib/middleman-core/sources/source_watcher.rb', line 203

def find_new_files!
  new_files = ::Middleman::Util.all_files_under(@directory.to_s, &method(:should_not_recurse?))
                               .reject { |p| @files.key?(p) }

  update(new_files, []).flatten.map { |s| s[:full_path] }
end

#listen!Object


172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'middleman-core/lib/middleman-core/sources/source_watcher.rb', line 172

def listen!
  return if @disable_watcher || @listener || @waiting_for_existence

  config = {
    force_polling: @force_polling
  }

  config[:wait_for_delay] = @wait_for_delay.try(:to_f) || 0.5
  config[:latency] = @latency.to_f if @latency

  @listener = ::Listen.to(@directory.to_s, config, &method(:on_listener_change))

  @listener.ignore(/^\.sass-cache/)
  @listener.ignore(/^node_modules/)
  @listener.ignore(/^vendor\/bundle/)

  @listener.start
end

#poll_once!Object


214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'middleman-core/lib/middleman-core/sources/source_watcher.rb', line 214

def poll_once!
  updated = ::Middleman::Util.all_files_under(@directory.to_s, &method(:should_not_recurse?))
  removed = @files.keys - updated

  result = update(updated, removed)

  if @waiting_for_existence && @directory.exist?
    @waiting_for_existence = false
    listen!
  end

  result.flatten.map { |s| s[:full_path] }
end

#stop_listener!Object


195
196
197
198
199
200
# File 'middleman-core/lib/middleman-core/sources/source_watcher.rb', line 195

def stop_listener!
  return unless @listener

  @listener.stop
  @listener = nil
end

#to_sObject Also known as: inspect

Work around this bug: http://bugs.ruby-lang.org/issues/4521 where Ruby will call to_s/inspect while printing exception messages, which can take a long time (minutes at full CPU) if the object is huge or has cyclic references, like this.


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

def to_s
  "#<Middleman::SourceWatcher:0x#{object_id} type=#{@type.inspect} directory=#{@directory.inspect}>"
end

#unwatchObject


119
120
121
# File 'middleman-core/lib/middleman-core/sources/source_watcher.rb', line 119

def unwatch
  stop_listener!
end

#update_config(options_hash = ::Middleman::EMPTY_HASH) ⇒ Object


106
107
108
109
110
111
112
113
# File 'middleman-core/lib/middleman-core/sources/source_watcher.rb', line 106

def update_config(options_hash = ::Middleman::EMPTY_HASH)
  without_listener_running do
    @disable_watcher = options_hash.fetch(:disable_watcher, false)
    @force_polling = options_hash.fetch(:force_polling, false)
    @latency = options_hash.fetch(:latency, nil)
    @wait_for_delay = options_hash.fetch(:wait_for_delay, nil)
  end
end

#update_path(directory) ⇒ Object


96
97
98
99
100
101
102
103
104
# File 'middleman-core/lib/middleman-core/sources/source_watcher.rb', line 96

def update_path(directory)
  @directory = Pathname(File.expand_path(directory, app.root))

  without_listener_running do
    update([], @files.values.map { |source_file| source_file[:full_path] })
  end

  poll_once!
end