class Facter::Util::Fact
Attributes
@return [String] @deprecated
The location from where fact is resolved @return [String]
The name of the fact @return [String]
Fact
options e.g. fact_type
Weight of the resolution that was used to obtain the fact value
Public Class Methods
Creates a new fact, with no resolution mechanisms. See {Facter.add} for the public API for creating facts. @param name [String] the fact name @param options [Hash] optional parameters @option options [String] :ldapname set the ldapname property on the fact
@api private
# File lib/facter/custom_facts/util/fact.rb, line 37 def initialize(name, options = {}) @name = LegacyFacter::Util::Normalization.normalize(name.to_s.downcase).intern @options = options.dup extract_ldapname_option!(options) @ldapname ||= @name.to_s @resolves = [] @searching = false @used_resolution_weight = 0 @value = nil end
Public Instance Methods
Adds a new {Facter::Util::Resolution resolution}. This requires a block, which will then be evaluated in the context of the new resolution.
@param options [Hash] A hash of options to set on the resolution
@return [Facter::Util::Resolution]
@api private
# File lib/facter/custom_facts/util/fact.rb, line 61 def add(options = {}, &block) @options = @options.merge(options) @location = options[:file] @location ||= block.source_location if block_given? define_resolution(nil, options, &block) end
Define a new named resolution or return an existing resolution with the given name.
@param resolution_name [String] The name of the resolve to define or look up @param options [Hash] A hash of options to set on the resolution @return [Facter::Util::Resolution]
@api public
# File lib/facter/custom_facts/util/fact.rb, line 78 def define_resolution(resolution_name, options = {}, &block) resolution_type = options.delete(:type) || :simple resolve = create_or_return_resolution(resolution_name, resolution_type) resolve.options(options) unless options.empty? resolve.evaluate(&block) if block resolve rescue StandardError => e msg = "Unable to add resolve #{resolution_name.inspect} for fact '#{@name}': #{e.message}" msg += "\n#{e.backtrace.join("\n")}" if Options[:trace] log.error(msg, true) nil end
@api private @deprecated
# File lib/facter/custom_facts/util/fact.rb, line 147 def extract_ldapname_option!(options) return unless options[:ldapname] log.warnonce('ldapname is deprecated and will be removed in a future version') self.ldapname = options.delete(:ldapname) end
Flushes any cached values.
@return [void]
@api private
# File lib/facter/custom_facts/util/fact.rb, line 111 def flush @resolves.each(&:flush) @value = nil end
Retrieve an existing resolution by name
@param name [String]
@return [Facter::Util::Resolution, nil] The resolution if exists, nil if
it doesn't exist or name is nil
# File lib/facter/custom_facts/util/fact.rb, line 100 def resolution(name) return nil if name.nil? @resolves.find { |resolve| resolve.name == name } end
Returns the value for this fact. This searches all resolutions by suitability and weight (see {Facter::Util::Resolution}). If no suitable resolution is found, it returns nil.
@api public
# File lib/facter/custom_facts/util/fact.rb, line 121 def value return @value unless @value.nil? if @resolves.empty? log.debug format('No resolves for %<name>s', name: @name) return nil end searching do suitable_resolutions = sort_by_weight(find_suitable_resolutions(@resolves)) Facter::Framework::Benchmarking::Timer.measure(@name) do @value = find_first_real_value(suitable_resolutions) end announce_when_no_suitable_resolution(suitable_resolutions) announce_when_no_value_found(@value) @value = resolve_value end @value end
Private Instance Methods
# File lib/facter/custom_facts/util/fact.rb, line 231 def announce_when_no_suitable_resolution(resolutions) return unless resolutions.empty? log.debug format('Found no suitable resolves of %<resolver_length> for %<name>s', resolver_length: @resolves.length, name: @name) end
# File lib/facter/custom_facts/util/fact.rb, line 238 def announce_when_no_value_found(value) log.debug(format('value for %<name>s is still nil', name: @name)) if value.nil? end
# File lib/facter/custom_facts/util/fact.rb, line 242 def create_or_return_resolution(resolution_name, resolution_type) resolve = resolution(resolution_name) if resolve if resolution_type != resolve.resolution_type raise ArgumentError, "Cannot return resolution #{resolution_name} with type" \ " #{resolution_type}; already defined as #{resolve.resolution_type}" end else case resolution_type when :simple resolve = Facter::Util::Resolution.new(resolution_name, self) when :aggregate resolve = Facter::Core::Aggregate.new(resolution_name, self) else raise ArgumentError, "Expected resolution type to be one of (:simple, :aggregate) but was #{resolution_type}" end @resolves << resolve end resolve end
# File lib/facter/custom_facts/util/fact.rb, line 210 def find_first_real_value(resolutions) resolutions.each do |resolve| begin value = resolve.value rescue Facter::ResolveCustomFactError break end @used_resolution_weight = resolve.weight next if value.nil? log_fact_path(resolve) return value end nil end
# File lib/facter/custom_facts/util/fact.rb, line 185 def find_suitable_resolutions(resolutions) resolutions.find_all(&:suitable?) end
# File lib/facter/custom_facts/util/fact.rb, line 156 def log @log ||= Facter::Log.new(self) end
# File lib/facter/custom_facts/util/fact.rb, line 227 def log_fact_path(resolve) log.debug("#{resolve.fact_type} fact #{resolve.fact.name} was resolved from: #{resolve.last_evaluated}") end
# File lib/facter/custom_facts/util/fact.rb, line 160 def resolve_value return Facter.core_value(name) if @value.nil? core_value = Facter.core_value(name) if @used_resolution_weight <= 0 core_value.nil? ? @value : core_value end
Lock our searching process, so we never get stuck in recursion.
# File lib/facter/custom_facts/util/fact.rb, line 173 def searching raise "Caught recursion on #{@name}" if searching? # If we've gotten this far, we're not already searching, so go ahead and do so. @searching = true begin yield ensure @searching = false end end
Are we in the midst of a search?
# File lib/facter/custom_facts/util/fact.rb, line 168 def searching? @searching end
# File lib/facter/custom_facts/util/fact.rb, line 189 def sort_by_weight(resolutions) # sort resolutions: # - descending by weight # - multiple facts have the same weight but different types, the :external fact take precedence # - multiple facts with the same weight and type, the order is preserved. # note: sort_by with index is slower than .sort # we cannot use .sort because it is not stable: https://bugs.ruby-lang.org/issues/1089 # solution from: https://bugs.ruby-lang.org/issues/1089#note-10 # rubocop:disable Style/NestedTernaryOperator idx = 0 resolutions.sort_by do |x| [ -x.weight, x.respond_to?(:fact_type) ? x.fact_type == :external ? 0 : 1 : 1, idx += 1 ] end # rubocop:enable Style/NestedTernaryOperator end