Class: Arborist::Dependency

Inherits:
Object
  • Object
show all
Extended by:
Loggability
Defined in:
lib/arborist/dependency.rb

Overview

A inter-node dependency that is outside of the implicit ones expressed by the tree.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(behavior, *nodes_or_subdeps) ⇒ Dependency

Create a new Dependency on the specified nodes_or_subdeps with the given behavior (one of :any or :all)



55
56
57
58
59
60
# File 'lib/arborist/dependency.rb', line 55

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

Instance Attribute Details

#behaviorObject (readonly)

The behavior that determines if the dependency is met by any or all of the nodes.



84
85
86
# File 'lib/arborist/dependency.rb', line 84

def behavior
  @behavior
end

#identifier_statesObject (readonly)

The Hash of identifier states



88
89
90
# File 'lib/arborist/dependency.rb', line 88

def identifier_states
  @identifier_states
end

#subdepsObject (readonly)

The Array of sub-dependencies (instances of Dependency).



92
93
94
# File 'lib/arborist/dependency.rb', line 92

def subdeps
  @subdeps
end

Class Method Details

.from_hash(hash) ⇒ Object

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>],
}


43
44
45
46
47
48
49
50
# File 'lib/arborist/dependency.rb', line 43

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

.on(behavior, *identifiers, prefixes: nil) ⇒ Object

Construct a new Dependency for the specified behavior on the given identifiers with prefixes.



24
25
26
27
28
29
30
31
# File 'lib/arborist/dependency.rb', line 24

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

Instance Method Details

#==(other) ⇒ Object

Equality comparison operator – return true if the other dependency has the same behavior, identifiers, and sub-dependencies. Does not consider identifier states.



296
297
298
299
300
301
302
303
# File 'lib/arborist/dependency.rb', line 296

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

#all_identifiersObject

Return the Set of this Dependency’s identifiers as well as those of all of its sub-dependencies.



121
122
123
# File 'lib/arborist/dependency.rb', line 121

def all_identifiers
	return self.identifiers + self.subdep_identifiers
end

#down?Boolean

Returns true if this dependency cannot be met.

Returns:

  • (Boolean)


193
194
195
196
197
198
199
200
# File 'lib/arborist/dependency.rb', line 193

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

#down_identifiersObject

Return a Set of identifiers which have been marked down in this dependency.



102
103
104
# File 'lib/arborist/dependency.rb', line 102

def down_identifiers
	return Set.new( self.identifier_states.select {|_, mark| mark }.map(&:first) )
end

#down_primary_reasonsObject

Return an English description of why any first tier dependencies are not met, or nil if there are none.



237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/arborist/dependency.rb', line 237

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

#down_reasonObject

Return an English description of why this dependency is not met. If it is met, returns nil.



223
224
225
226
227
228
229
230
231
232
# File 'lib/arborist/dependency.rb', line 223

def down_reason
	parts = [
		self.down_primary_reasons,
		self.down_secondary_reasons
	].compact

	return nil if parts.empty?

	return parts.join( '; ' )
end

#down_secondary_reasonsObject

Return an English description of why any subdependencies are not met, or nil if there are none.



265
266
267
268
269
270
# File 'lib/arborist/dependency.rb', line 265

def down_secondary_reasons
	subdeps = self.down_subdeps
	return nil if subdeps.empty?

	return subdeps.map( &:down_reason ).join( ' and ' )
end

#down_subdepsObject

Return any of this dependency’s sub-dependencies that are down.



127
128
129
# File 'lib/arborist/dependency.rb', line 127

def down_subdeps
	return self.subdeps.select( &:down? )
end

#each_downedObject

Yield each unique identifier and Time of downed nodes from both direct and sub-dependencies.



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/arborist/dependency.rb', line 140

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

#earliest_down_timeObject

Returns the earliest Time a node was marked down.



210
211
212
# File 'lib/arborist/dependency.rb', line 210

def earliest_down_time
	return self.identifier_states.values.compact.min
end

#empty?Boolean

Returns true if this dependency doesn’t contain any identifiers or sub-dependencies.

Returns:

  • (Boolean)


169
170
171
# File 'lib/arborist/dependency.rb', line 169

def empty?
	return self.all_identifiers.empty?
end

#eql?(other) ⇒ Boolean

Returns true if other is the same object or if they both have the same identifiers, sub-dependencies, and identifier states.

Returns:

  • (Boolean)


285
286
287
288
289
290
291
# File 'lib/arborist/dependency.rb', line 285

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

#identifiersObject

Return a Set of identifiers belonging to this dependency.



96
97
98
# File 'lib/arborist/dependency.rb', line 96

def identifiers
	return Set.new( self.identifier_states.keys )
end

#include?(*identifiers) ⇒ Boolean

Returns true if the receiver includes all of the given identifiers.

Returns:

  • (Boolean)


162
163
164
# File 'lib/arborist/dependency.rb', line 162

def include?( *identifiers )
	return self.all_identifiers.include?( *identifiers )
end

#initialize_clone(original) ⇒ Object

Clone constructor – clone internal datastructures without ephemeral state on #clone.



71
72
73
74
# File 'lib/arborist/dependency.rb', line 71

def initialize_clone( original ) # :nodoc:
	@subdeps = @subdeps.map( &:clone )
	@identifier_states = @identifier_states.keys.product([ nil ]).to_h
end

#initialize_dup(original) ⇒ Object

Dup constructor – dup internal datastructures without ephemeral state on #dup.



64
65
66
67
# File 'lib/arborist/dependency.rb', line 64

def initialize_dup( original ) # :nodoc:
	@subdeps = @subdeps.map( &:dup )
	@identifier_states = @identifier_states.keys.product([ nil ]).to_h
end

#latest_down_timeObject

Returns the latest Time a node was marked down.



216
217
218
# File 'lib/arborist/dependency.rb', line 216

def latest_down_time
	return self.identifier_states.values.compact.max
end

#mark_down(identifier, time = Time.now) ⇒ Object

Mark the specified identifier as being down and propagate it to any subdependencies.



175
176
177
178
179
180
# File 'lib/arborist/dependency.rb', line 175

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_up(identifier) ⇒ Object

Mark the specified identifier as being up and propagate it to any subdependencies.



184
185
186
187
188
189
# File 'lib/arborist/dependency.rb', line 184

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

#subdep_identifiersObject

Return a Set of identifiers for all of this Dependency’s sub-dependencies.



114
115
116
# File 'lib/arborist/dependency.rb', line 114

def subdep_identifiers
	return self.subdeps.map( &:all_identifiers ).reduce( :+ ) || Set.new
end

#to_hObject

Return the entire dependency tree as a nested Hash.



274
275
276
277
278
279
280
# File 'lib/arborist/dependency.rb', line 274

def to_h
	return {
		behavior: self.behavior,
		identifiers: self.identifier_states.keys,
		subdeps: self.subdeps.map( &:to_h )
	}
end

#up?Boolean

Returns true if this dependency is met.

Returns:

  • (Boolean)


204
205
206
# File 'lib/arborist/dependency.rb', line 204

def up?
	return !self.down?
end

#up_identifiersObject

Return a Set of identifiers which have not been marked down in this dependency.



108
109
110
# File 'lib/arborist/dependency.rb', line 108

def up_identifiers
	return Set.new( self.identifier_states.reject {|_, mark| mark }.map(&:first) )
end

#up_subdepsObject

Return any of this dependency’s sub-dependencies that are up.



133
134
135
# File 'lib/arborist/dependency.rb', line 133

def up_subdeps
	return self.subdeps.select( &:up? )
end