class RFacter::Core::Aggregate
Aggregates provide a mechanism for facts to be resolved in multiple steps.
Aggregates are evaluated in two parts: generating individual chunks and then aggregating all chunks together. Each chunk is a block of code that generates a value, and may depend on other chunks when it runs. After all chunks have been evaluated they are passed to the aggregate block as `Hash<name, result>`. The aggregate block converts the individual chunks into a single value that is returned as the final value of the aggregate.
@api private @since 0.1.0
Attributes
@!attribute [r] confines
@return [Array<RFacter::Core::Confine>] An array of confines restricting this to a specific platform @see RFacter::Core::Suitable
@!attribute [r] deps
@return [RFacter::Core::DirectedGraph]
@!attribute [r] fact @return [RFacter::Util::Fact]
@!attribute [r] name
@return [Symbol] The name of the aggregate resolution
Public Class Methods
# File lib/rfacter/core/aggregate.rb, line 48 def initialize(name, fact, config: RFacter::Config.config, **options) @name = name @fact = fact @config = config @confines = [] @chunks = {} @aggregate = nil @deps = RFacter::Core::DirectedGraph.new end
Public Instance Methods
Define how all chunks should be combined
@example Merge all chunks
aggregate.aggregate do |chunks| final_result = {} chunks.each_value do |chunk| final_result.deep_merge(chunk) end final_result end
@example Sum all chunks
aggregate.aggregate do |chunks| total = 0 chunks.each_value do |chunk| total += chunk end total end
@yield [Hash<Symbol, Object>] A hash containing chunk names and
chunk values
@return [void]
# File lib/rfacter/core/aggregate.rb, line 141 def aggregate(&block) if block_given? @aggregate = block else raise ArgumentError, "#{self.class.name}#aggregate requires a block" end end
Define a new chunk for the given aggregate
@example Defining a chunk with no dependencies
aggregate.chunk(:mountpoints) do # generate mountpoint information end
@example Defining an chunk to add mount options
aggregate.chunk(:mount_options, :require => [:mountpoints]) do |mountpoints| # `mountpoints` is the result of the previous chunk # generate mount option information based on the mountpoints end
@param name [Symbol] A name unique to this aggregate describing the chunk @param opts [Hash] @option opts [Array<Symbol>, Symbol] require One or more chunks to evaluate
and pass to this block.
@yield [*Object] Zero or more chunk results
@return [void]
# File lib/rfacter/core/aggregate.rb, line 102 def chunk(name, opts = {}, &block) if not block_given? raise ArgumentError, "#{self.class.name}#chunk requires a block" end deps = Array(opts.delete(:require)) if not opts.empty? raise ArgumentError, "Unexpected options passed to #{self.class.name}#chunk: #{opts.keys.inspect}" end @deps[name] = deps @chunks[name] = block end
# File lib/rfacter/core/aggregate.rb, line 78 def evaluate(&block) instance_eval(&block) end
# File lib/rfacter/core/aggregate.rb, line 149 def resolution_type :aggregate end
# File lib/rfacter/core/aggregate.rb, line 60 def set_options(options) if options[:name] @name = options.delete(:name) end if options.has_key?(:timeout) @timeout = options.delete(:timeout) end if options.has_key?(:weight) @weight = options.delete(:weight) end if not options.keys.empty? raise ArgumentError, "Invalid aggregate options #{options.keys.inspect}" end end
Private Instance Methods
Process the results of all chunks with the aggregate block and return the results. If no aggregate block has been specified, fall back to deep merging the given data structure
@param results [Hash<Symbol, Object>] A hash of chunk names and the output
of that chunk.
@return [Object]
# File lib/rfacter/core/aggregate.rb, line 188 def aggregate_results(results) if @aggregate @aggregate.call(results) else default_aggregate(results) end end
# File lib/rfacter/core/aggregate.rb, line 196 def default_aggregate(results) results.values.inject do |result, current| RFacter::Util::Values.deep_merge(result, current) end rescue RFacter::Util::Values::DeepMergeError => e raise ArgumentError, "Could not deep merge all chunks (Original error: " + "#{e.message}), ensure that chunks return either an Array or Hash or " + "override the aggregate block", e.backtrace end
Order chunks based on their dependencies
@return [Array<Symbol, Proc>] A list of chunk names and blocks in evaluation order.
# File lib/rfacter/core/aggregate.rb, line 209 def order_chunks if not @deps.acyclic? raise DependencyError, "Could not order chunks; found the following dependency cycles: #{@deps.cycles.inspect}" end sorted_names = @deps.tsort sorted_names.map do |name| [name, @chunks[name]] end end
Evaluate the results of this aggregate.
@see RFacter::Core::Resolvable#value
@return [Object]
# File lib/rfacter/core/aggregate.rb, line 159 def resolve_value chunk_results = run_chunks() aggregate_results(chunk_results) end
Order all chunks based on their dependencies and evaluate each one, passing dependent chunks as needed.
@return [Hash<Symbol, Object>] A hash containing the chunk that
generated value and the related value.
# File lib/rfacter/core/aggregate.rb, line 169 def run_chunks results = {} order_chunks.each do |(name, block)| input = @deps[name].map { |dep_name| results[dep_name] } output = block.call(*input) results[name] = RFacter::Util::Values.deep_freeze(output) end results end