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
# 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? || @parent.options.fetch(:disable_watcher, false)
  @force_polling = @parent.options.fetch(:force_polling, false)
  @latency = @parent.options.fetch(:latency, 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.



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

Contract Any

#exists?(path) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#filesObject



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

def files
  @files.values
end

#find(path, glob = false) ⇒ Object



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/middleman-core/sources/source_watcher.rb', line 129

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



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

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



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/middleman-core/sources/source_watcher.rb', line 158

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

  config = {
    force_polling: @force_polling,
    wait_for_delay: 0.5
  }

  config[:latency] = @latency 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



200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/middleman-core/sources/source_watcher.rb', line 200

def poll_once!
  updated = ::Middleman::Util.all_files_under(@directory.to_s, &method(:should_not_recurse?))
  removed = @files.keys.reject { |p| updated.include?(p) }

  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



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

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.



218
219
220
# File 'lib/middleman-core/sources/source_watcher.rb', line 218

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

#unwatchObject



111
112
113
# File 'lib/middleman-core/sources/source_watcher.rb', line 111

def unwatch
  stop_listener!
end

#update_path(directory) ⇒ Object



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

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

  stop_listener! if @listener

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

  poll_once!

  listen! unless @disable_watcher
end