class BoxGrinder::FSMonitor

Public Class Methods

new() click to toggle source
# 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

Public Instance Methods

add_path(path) click to toggle source

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
capture(*observers) { || ... } click to toggle source

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
reset() click to toggle source

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
stop() click to toggle source

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() click to toggle source

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

Private Instance Methods

_capture() click to toggle source

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
_stop() click to toggle source
# 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
add_observers(observers) click to toggle source
# 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
alias_and_capture(klazz, m_sym, flag, &blk) click to toggle source

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
eigen_capture(klazz, m_sym, flag, &blk) click to toggle source

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
instance_capture(klazz, m_sym, flag, &blk) click to toggle source
# 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
realpath(path) click to toggle source

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
root_dir(relpath) click to toggle source

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
set_hooks() click to toggle source

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