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::PATH_MATCHER

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

Returns a new instance of SourceWatcher.


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
90
91
# File 'middleman-core/lib/middleman-core/sources/source_watcher.rb', line 64

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.


46
47
48
# File 'middleman-core/lib/middleman-core/sources/source_watcher.rb', line 46

def directory
  @directory
end

#listenerObject (readonly)

Reference to lower level listener


53
54
55
# File 'middleman-core/lib/middleman-core/sources/source_watcher.rb', line 53

def listener
  @listener
end

#optionsObject (readonly)

Returns the value of attribute options.


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

def options
  @options
end

#typeObject (readonly)

Returns the value of attribute type.


42
43
44
# File 'middleman-core/lib/middleman-core/sources/source_watcher.rb', line 42

def type
  @type
end

Instance Method Details

#Any

This method returns an undefined value.

Stop the listener.


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

Contract Any

#exists?(path) ⇒ Boolean

Returns:

  • (Boolean)

166
167
168
# File 'middleman-core/lib/middleman-core/sources/source_watcher.rb', line 166

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

#filesObject


129
130
131
# File 'middleman-core/lib/middleman-core/sources/source_watcher.rb', line 129

def files
  @files.values
end

#find(path, glob = false) ⇒ Object


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

def find(path, glob = false)
  path = path.to_s.encode('UTF-8', 'UTF-8-MAC') if RUBY_PLATFORM.match?(/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


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

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


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

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(%r{^vendor/bundle})

  @listener.start
end

#poll_once!Object


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

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


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

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.


234
235
236
# File 'middleman-core/lib/middleman-core/sources/source_watcher.rb', line 234

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

#unwatchObject


121
122
123
# File 'middleman-core/lib/middleman-core/sources/source_watcher.rb', line 121

def unwatch
  stop_listener!
end

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


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

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


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

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