A inter-node dependency that is outside of the implicit ones expressed by the tree.
The behavior that determines if the dependency is met by any or all of the nodes.
The Hash of identifier states
The Array of sub-dependencies (instances of Dependency).
Construct a new instance using the specified hash
, which
should be in the same form as that generated by to_h:
{ behavior: <string>, identifiers: [<identifier_1>, <identifier_n>], subdeps: [<dephash_1>, <dephash_n>], }
# File lib/arborist/dependency.rb, line 42
def self::from_hash( hash )
self.log.debug "Creating a new %p from a hash: %p" % [ self, hash ]
hash[:subdeps] ||= []
subdeps = hash[:subdeps].map {|subhash| self.from_hash(subhash) }
return self.new( hash[:behavior], hash[:identifiers] + subdeps )
end
Create a new Dependency on the specified
nodes_or_subdeps
with the given behavior
(one of
:any or :all)
# File lib/arborist/dependency.rb, line 54
def initialize( behavior, *nodes_or_subdeps )
@behavior = behavior
@subdeps, identifiers = nodes_or_subdeps.flatten.
partition {|obj| obj.is_a?(self.class) }
@identifier_states = identifiers.product([ nil ]).to_h
end
Construct a new Dependency for the specified
behavior
on the given identifiers
with
prefixes
.
# File lib/arborist/dependency.rb, line 23
def self::on( behavior, *identifiers, prefixes: nil )
deps, identifiers = identifiers.flatten.uniq.partition {|obj| obj.is_a?(self.class) }
prefixes = Array( prefixes ).uniq
identifiers = prefixes.product( identifiers ).map {|pair| pair.join('-') } unless
prefixes.empty?
return self.new( behavior, identifiers + deps )
end
Equality comparison operator – return true if the other
dependency has the same behavior, identifiers, and sub-dependencies. Does
not consider identifier states.
# File lib/arborist/dependency.rb, line 295
def ==( other )
return true if other.equal?( self )
return false unless other.is_a?( self.class )
return self.behavior == other.behavior &&
self.identifiers == other.identifiers &&
self.subdeps == other.subdeps
end
Return the Set of this Dependency's identifiers as well as those of all of its sub-dependencies.
# File lib/arborist/dependency.rb, line 120
def all_identifiers
return self.identifiers + self.subdep_identifiers
end
Returns true
if this dependency cannot be met.
# File lib/arborist/dependency.rb, line 192
def down?
case self.behavior
when :all
self.identifier_states.values.any? || self.subdeps.any?( &:down? )
when :any
self.identifier_states.values.all? && self.subdeps.all?( &:down? )
end
end
Return a Set of identifiers which have been marked down in this dependency.
# File lib/arborist/dependency.rb, line 101
def down_identifiers
return Set.new( self.identifier_states.select {|_, mark| mark }.map(&:first) )
end
Return an English description of why any first tier dependencies are not
met, or nil
if there are none.
# File lib/arborist/dependency.rb, line 236
def down_primary_reasons
ids = self.down_identifiers
return nil if ids.empty?
msg = nil
case self.behavior
when :all
msg = ids.first.dup
if ids.size == 1
msg << " is unavailable"
else
msg << " (and %d other%s) are unavailable" % [ ids.size - 1, ids.size == 2 ? '' : 's' ]
end
msg << " as of %s" % [ self.earliest_down_time ]
when :any
msg = "%s are all unavailable" % [ ids.to_a.join(', ') ]
msg << " as of %s" % [ self.latest_down_time ]
else
raise "Don't know how to build a description of down behavior for %p" % [ self.behavior ]
end
end
Return an English description of why this dependency is not met. If it is
met, returns nil
.
# File lib/arborist/dependency.rb, line 222
def down_reason
parts = [
self.down_primary_reasons,
self.down_secondary_reasons
].compact
return nil if parts.empty?
return parts.join( '; ' )
end
Return an English description of why any subdependencies are not met, or
nil
if there are none.
# File lib/arborist/dependency.rb, line 264
def down_secondary_reasons
subdeps = self.down_subdeps
return nil if subdeps.empty?
return subdeps.map( &:down_reason ).join( ' and ' )
end
Return any of this dependency's sub-dependencies that are down.
# File lib/arborist/dependency.rb, line 126
def down_subdeps
return self.subdeps.select( &:down? )
end
Yield each unique identifier and Time of downed nodes from both direct and sub-dependencies.
# File lib/arborist/dependency.rb, line 139
def each_downed
return enum_for( __method__ ) unless block_given?
yielded = Set.new
self.identifier_states.each do |ident, time|
if time
yield( ident, time ) unless yielded.include?( ident )
yielded.add( ident )
end
end
self.subdeps.each do |subdep|
subdep.each_downed do |ident, time|
if time
yield( ident, time ) unless yielded.include?( ident )
yielded.add( ident )
end
end
end
end
Returns the earliest Time a node was marked down.
# File lib/arborist/dependency.rb, line 209
def earliest_down_time
return self.identifier_states.values.compact.min
end
Returns true
if this dependency doesn't contain any
identifiers or sub-dependencies.
# File lib/arborist/dependency.rb, line 168
def empty?
return self.all_identifiers.empty?
end
Returns true if other
is the same object or if they both have
the same identifiers, sub-dependencies, and identifier states.
# File lib/arborist/dependency.rb, line 284
def eql?( other )
self.log.debug "Comparing %p to %p (with states)" % [ self, other ]
return true if other.equal?( self )
return self == other &&
self.identifier_states.eql?( other.identifier_states ) &&
self.subdeps.eql?( other.subdeps )
end
Return a Set of identifiers belonging to this dependency.
# File lib/arborist/dependency.rb, line 95
def identifiers
return Set.new( self.identifier_states.keys )
end
Returns true
if the receiver includes all of the given
identifiers
.
# File lib/arborist/dependency.rb, line 161
def include?( *identifiers )
return self.all_identifiers.include?( *identifiers )
end
Returns the latest Time a node was marked down.
# File lib/arborist/dependency.rb, line 215
def latest_down_time
return self.identifier_states.values.compact.max
end
Mark the specified identifier
as being down and propagate it
to any subdependencies.
# File lib/arborist/dependency.rb, line 174
def mark_down( identifier, time=Time.now )
self.identifier_states[ identifier ] = time if self.identifier_states.key?( identifier )
self.subdeps.each do |dep|
dep.mark_down( identifier, time )
end
end
Mark the specified identifier
as being up and propagate it to
any subdependencies.
# File lib/arborist/dependency.rb, line 183
def mark_up( identifier )
self.subdeps.each do |dep|
dep.mark_up( identifier )
end
self.identifier_states[ identifier ] = nil if self.identifier_states.key?( identifier )
end
Return a Set of identifiers for all of this Dependency's sub-dependencies.
# File lib/arborist/dependency.rb, line 113
def subdep_identifiers
return self.subdeps.map( &:all_identifiers ).reduce( :+ ) || Set.new
end
Return the entire dependency tree as a nested Hash.
# File lib/arborist/dependency.rb, line 273
def to_h
return {
behavior: self.behavior,
identifiers: self.identifier_states.keys,
subdeps: self.subdeps.map( &:to_h )
}
end
Returns true
if this dependency is met.
# File lib/arborist/dependency.rb, line 203
def up?
return !self.down?
end
Return a Set of identifiers which have not been marked down in this dependency.
# File lib/arborist/dependency.rb, line 107
def up_identifiers
return Set.new( self.identifier_states.reject {|_, mark| mark }.map(&:first) )
end
Return any of this dependency's sub-dependencies that are up.
# File lib/arborist/dependency.rb, line 132
def up_subdeps
return self.subdeps.select( &:up? )
end