class RSence::Dependencies

Description:

Generic dependency calculator. Used by PluginManager.

Usage:

This is an almost real world example:

## Initialize with pre-satisfied dependencies
deps = RSence::Dependencies.new( [:foo1,:foo2] )

## :client_pkg doesn't depend on anything
deps.set_deps( :client_pkg, nil )

## :system is the category of :client_pkg
deps.set_deps( :system, :client_pkg )

## :index_html depends on :client_pkg
deps.set_deps( :index_html, :client_pkg )

## :system is the category of :index_html
deps.set_deps( :system, :index_html )

## :main depends on :index_html
deps.set_deps( :main, :index_html )

## :system is the category of :main
deps.set_deps( :system, :main )

## :impossible has several dependencies, of which :foo3 can't be satisfied
deps.set_deps( :impossible, [:foo1, :foo2, :foo3] )

## :ticket has no dependencies
deps.set_deps( :ticket, nil )

## :system is the category of :ticket
deps.set_deps( :system, :ticket )

## :welcome depends on the :system category
deps.set_deps( :welcome, :system )

## :first doesn't depend on anything
deps.set_deps( :first, nil )

## Prepending is handled like this:
deps.set_deps( :client_pkg, :first )
deps.set_deps( :ticket, :first )

## Calculates the list of dependencies and returns them in an Array
p deps.list

## Output of the example above:
# impossible dependencies:
#   :impossible => [:foo3]
# [:foo1, :foo2, :first, :ticket, :client_pkg, :index_html, :main, :system, :welcome]

Attributes

categories[R]

List of categories

Public Class Methods

new( resolved = [], categories = {}, quiet=true ) click to toggle source

Don’t use Dependencies for external projects yet. It’s subject to change without deprecation warnings. resolved and categories are optional.

# File lib/rsence/dependencies.rb, line 60
def initialize( resolved = [], categories = {}, quiet=true )
  @quiet = quiet
  @pre_resolved = resolved.clone
  @depends_on = {
    # :name => [ :dep1, :dep2, :dep3, ... ]
  }
  @dependencies_of = {
    #:dep1 => [ :name1, :name2, ... ]
  }
  @categories = []
  categories.each_key do |cat_name, cat_items|
    add_category( cat_name )
    set_deps( cat_name, nil )
  end
  @unresolved = []
  recalculate!
end

Public Instance Methods

add_category( name ) click to toggle source

Adds the category name.

# File lib/rsence/dependencies.rb, line 123
def add_category( name )
  @categories.push( name ) unless @categories.include?( name )
end
category?( name ) click to toggle source

Returns true, if name is a category.

# File lib/rsence/dependencies.rb, line 134
def category?( name )
  @categories.include?( name )
end
clone_dependencies_of() click to toggle source

Returns clone of the @dependencies_of Array

# File lib/rsence/dependencies.rb, line 174
def clone_dependencies_of
  Marshal.load( Marshal.dump( @dependencies_of ) )
end
clone_depends_on() click to toggle source

Returns copy of the @depends_on Array

# File lib/rsence/dependencies.rb, line 169
def clone_depends_on
  Marshal.load( Marshal.dump( @depends_on ) )
end
del_category( name ) click to toggle source

Deletes the category name, but doesn’t dissolve its dependencies. Essentially turns name into a regular item.

# File lib/rsence/dependencies.rb, line 129
def del_category( name )
  @categories.delete( name ) if @categories.include?( name )
end
del_deps( name ) click to toggle source

Deletes name and all dependencies of name

# File lib/rsence/dependencies.rb, line 214
def del_deps( name )
  del_order( name ).each do |del_name|
    del_item( del_name )
  end
end
del_item( name ) click to toggle source

Remove name from dependency system, throws error if it’s depended on another item.

# File lib/rsence/dependencies.rb, line 179
def del_item( name )
  d_of = deps_of( name )
  if not d_of.empty?
    d_of.each do |dep|
      if category?( dep )
        @depends_on[ dep ].delete( name )
      else
        throw "Dependencies.del_item error: the following items depend on #{name.inspect} -> #{deps_of(name).inspect}"
      end
    end
  end
  unless category?( name )
    deps_on( name ).each do |dep|
      @dependencies_of[dep].delete(name) if @dependencies_of.has_key?(dep)
    end
    @depends_on.delete( name )
    @dependencies_of.delete( name )
  end
  recalculate!
end
del_order( name ) click to toggle source

Returns the order of deletion possible without breaking the dependency chain

# File lib/rsence/dependencies.rb, line 201
def del_order( name )
  d_order = []
  d_of = deps_of( name )
  list.reverse.each do |dep|
    if d_of.include?( dep )
      d_order.push( dep )
    end
  end
  d_order.push( name )
  return d_order
end
dependencies_of( name )
Alias for: deps_of
depends_on( name )
Alias for: deps_on
deps_of( name ) click to toggle source

Returns list of items that are dependencies of name in no particular order.

# File lib/rsence/dependencies.rb, line 90
def deps_of( name )
  outp = []
  if @dependencies_of.has_key?(name)
    @dependencies_of[name].each do |dep|
      outp.push( dep )
      outp += deps_of( dep )
    end
  end
  outp.uniq!
  outp.delete( name )
  return outp
end
Also aliased as: dependencies_of
deps_on( name ) click to toggle source

Returns list of items that name depends on in no particular order.

# File lib/rsence/dependencies.rb, line 105
def deps_on( name )
  outp = []
  if @depends_on.has_key?(name)
    @depends_on[name].each do |dep|
      outp.push( dep )
      outp += deps_on( dep )
    end
  end
  outp.uniq!
  outp.delete( name )
  return outp
end
Also aliased as: depends_on
list() click to toggle source

Returns dependencies sorted in load order

# File lib/rsence/dependencies.rb, line 271
def list
  @resolved.clone
end
Also aliased as: load_order
load_order()
Alias for: list
loadable?( name ) click to toggle source

Returns true, if name is not a pre-resolved dependency and not a category.

# File lib/rsence/dependencies.rb, line 84
def loadable?( name )
  return (not pre_resolved?(name) and not category?(name))
end
pre_resolved?( name ) click to toggle source

Returns true, if name is a pre-resolved dependency.

# File lib/rsence/dependencies.rb, line 79
def pre_resolved?( name )
  @pre_resolved.include?( name )
end
recalculate!() click to toggle source

Recalculates the list and unresolved items.

# File lib/rsence/dependencies.rb, line 232
def recalculate!
  unresolved = []
  resolved = @pre_resolved.clone
  same_len = false
  depends_on = clone_depends_on
  target_len = depends_on.keys.length + resolved.length
  until resolved.length == target_len
    len = resolved.length
    depends_on.each do | name, deps |
      if deps.empty? and not resolved.include?(name)
        resolved.push( name )
      elsif deps.empty?
        next
      else
        deps.each do |dep|
          deps.delete(dep) if resolved.include?(dep)
        end
      end
    end
    if len == resolved.length
      if same_len
        warn "impossible dependencies:" unless @quiet
        (depends_on.keys - resolved).each do |unsatisfied|
          warn "  #{unsatisfied.inspect} => #{depends_on[unsatisfied].inspect}" unless @quiet
          unresolved.push( unsatisfied )
        end
        break
      else
        same_len = true
      end
    else
      same_len = false
    end
  end
  @unresolved = unresolved
  @resolved = resolved
end
resolved?( name ) click to toggle source

Returns true, if name is resolved (a valid dependency). Inverse of self#unresolved?

# File lib/rsence/dependencies.rb, line 227
def resolved?( name )
  (not unresolved?( name ))
end
set_dependencies( name, deps )
Alias for: set_deps
set_dependency( name, deps )
Alias for: set_deps
set_deps( name, deps ) click to toggle source

Set list of dependency names as deps that name depends on

# File lib/rsence/dependencies.rb, line 139
def set_deps( name, deps )
  if deps.class == Symbol
    deps = [deps]
  elsif deps.class == String
    deps = [deps.to_sym]
  elsif deps.class == NilClass
    deps = []
  elsif deps.class != Array
    raise "Dependencies.set_deps error: the deps is an unsupported type: #{deps.class}"
  end
  if not @depends_on.has_key?(name)
    @depends_on[name] = deps
  else
    deps.each do |dep|
      @depends_on[name].push( dep ) unless @depends_on[name].include?( dep )
    end
  end
  deps.each do |dep|
    if not @dependencies_of.has_key?( dep )
      @dependencies_of[dep] = [name]
    elsif not @dependencies_of[dep].include?( name )
      @dependencies_of[dep].push( name )
    end
  end
  recalculate!
end
Also aliased as: set_dependencies, set_dependency
unresolved?( name ) click to toggle source

Returns true, if name is unresolved. The list of unresolved items is updated every time self#recalculate is called.

# File lib/rsence/dependencies.rb, line 222
def unresolved?( name )
  @unresolved.include?( name )
end