# File lib/boxgrinder-build/util/permissions/fs-monitor.rb, line 31 def initialize @flag = GetSet.new @lock_a = Mutex.new @lock_b = Mutex.new set_hooks end
Add a path string. Called by the hooked methods when an applicable action
is triggered and capturing is enabled. Fires the :add_path
command, and includes the full path as :data
.
If no observers have been assigned before a path is added, they will be silently lost.
@param [String] path Filesystem path. @return [Boolean] False if no observers were present.
# File lib/boxgrinder-build/util/permissions/fs-monitor.rb, line 84 def add_path(path) @lock_b.synchronize do changed(true) notify_observers(:command => :add_path, :data => realpath(path)) end end
Start capturing paths. Providing a block automatically stops the capture process upon termination of the scope.
@param Array<#update> Observers to be notified of capture
events. Each observer should expect a hash{} containing a +:command+, and potentially +:data+.
@yield Block that automatically calls stop at the end of scope
to cease capture.
# File lib/boxgrinder-build/util/permissions/fs-monitor.rb, line 47 def capture(*observers, &block) @lock_a.synchronize do add_observers(observers) _capture(&block) if block_given? yield _stop end end end
Stop any capturing and delete all observers. Useful for testing. @see stop
# File lib/boxgrinder-build/util/permissions/fs-monitor.rb, line 68 def reset @lock_a.synchronize do _stop delete_observers end end
Explicitly stop capturing paths. This should be utilised if capture was not
used with a block. Fires the :stop_capture
command to indicate
that capturing has ceased.
# File lib/boxgrinder-build/util/permissions/fs-monitor.rb, line 62 def stop @lock_a.synchronize { _stop } end
Trigger ownership change immediately, but without ceasing. Fires the
:chown
command on all observers.
@return [boolean] False if no observers were present.
# File lib/boxgrinder-build/util/permissions/fs-monitor.rb, line 95 def trigger changed(true) notify_observers(:command => :chown) end
The hooks will all use the same get-and-set to determine when to begin/cease capturing paths.
# File lib/boxgrinder-build/util/permissions/fs-monitor.rb, line 104 def _capture @flag.get_set(true) end
# File lib/boxgrinder-build/util/permissions/fs-monitor.rb, line 108 def _stop @flag.get_set(false) changed(true) notify_observers(:command => :stop_capture) end
# File lib/boxgrinder-build/util/permissions/fs-monitor.rb, line 165 def add_observers(observers) observers.each{ |o| add_observer(o) unless o.nil? } end
Cracks open the target class, and injects a wrapper to enable monitoring. By aliasing the original method the wrapper intercepts the call, which it forwards onto the 'real' method before executing the hook.
The hook's functionality is provided via a &block, which is passed
the caller's self
in addition to the wrapped method's
parameters.
Locking the flag
signals the hook to begin capturing.
# File lib/boxgrinder-build/util/permissions/fs-monitor.rb, line 151 def alias_and_capture(klazz, m_sym, flag, &blk) alias_m_sym = "__alias_#{m_sym}" klazz.class_eval do alias_method alias_m_sym, m_sym define_method(m_sym) do |*args, &blx| response = send(alias_m_sym, *args, &blx) blk.call(self, *args) if flag.get_set response end end end
Hooks into class methods by accessing the eigenclass (virtual class).
# File lib/boxgrinder-build/util/permissions/fs-monitor.rb, line 134 def eigen_capture(klazz, m_sym, flag, &blk) v_klazz = (class << klazz; self; end) instance_capture(v_klazz, m_sym, flag, &blk) end
# File lib/boxgrinder-build/util/permissions/fs-monitor.rb, line 139 def instance_capture(klazz, m_sym, flag, &blk) Array(m_sym).each{ |sym| alias_and_capture(klazz, sym, flag, &blk) } end
Transform relative to absolute path
# File lib/boxgrinder-build/util/permissions/fs-monitor.rb, line 170 def realpath(path) Pathname.new(path).realpath.to_s end
For a path relative such as 'x/y/z' returns 'x'. Useful for mkdir_p where the entire new path is returned at once.
# File lib/boxgrinder-build/util/permissions/fs-monitor.rb, line 176 def root_dir(relpath) r = relpath.match(%r(^[/]?.+?[/$])) return relpath if r.nil? r[0] || relpath end
Hooks to capture any standard file, link or directory creation. Other methods (e.g. FileUtils#mkdir_p, FileUtils#move), ultimately bottom out into these primitive functions.
# File lib/boxgrinder-build/util/permissions/fs-monitor.rb, line 117 def set_hooks # Final splat var captures any other variables we are not interested in, # and avoids them being squashed into the final var. eigen_capture(File, [:open, :new], @flag) do |klazz, path, mode, *other| add_path(path) if klazz == File && mode =~ /^(t|b)?((w|a)[+]?)(t|b)?$/ end eigen_capture(File, [:rename, :symlink, :link], @flag) do |klazz, old, new, *other| add_path(new) end eigen_capture(Dir, :mkdir, @flag) do |klazz, path, *other| add_path(root_dir(path)) end end