Class: Middleman::SourceWatcher

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Includes:
Contracts
Defined in:
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 = {}) ⇒ SourceWatcher

Returns a new instance of 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 'lib/middleman-core/sources/source_watcher.rb', line 62

def initialize(parent, type, directory, options={})
  @parent = parent
  @options = options

  @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 'lib/middleman-core/sources/source_watcher.rb', line 44

def directory
  @directory
end

#listenerObject (readonly)

Reference to lower level listener



51
52
53
# File '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 '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 '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 'lib/middleman-core/sources/source_watcher.rb', line 118

Contract Any

#exists?(path) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#filesObject



127
128
129
# File '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
# File '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)

  p = @directory + p if p.relative?

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

#find_new_files!Object



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

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



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/middleman-core/sources/source_watcher.rb', line 166

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



208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/middleman-core/sources/source_watcher.rb', line 208

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



189
190
191
192
193
194
# File 'lib/middleman-core/sources/source_watcher.rb', line 189

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.



226
227
228
# File 'lib/middleman-core/sources/source_watcher.rb', line 226

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

#unwatchObject



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

def unwatch
  stop_listener!
end

#update_config(options = {}) ⇒ Object



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

def update_config(options={})
  without_listener_running do
    @disable_watcher = options.fetch(:disable_watcher, false)
    @force_polling = options.fetch(:force_polling, false)
    @latency = options.fetch(:latency, nil)
    @wait_for_delay = options.fetch(:wait_for_delay, nil)
  end
end

#update_path(directory) ⇒ Object



96
97
98
99
100
101
102
103
104
# File '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