summaryrefslogtreecommitdiffstats
path: root/lib/facter/util/fact.rb
blob: 935b3c17f2f3c302882e573437fcb14fc1b9f2d6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
require 'facter'
require 'facter/util/resolution'

class Facter::Util::Fact
    TIMEOUT = 5

    attr_accessor :name, :ldapname

    # Create a new fact, with no resolution mechanisms.
    def initialize(name, options = {})
        @name = name.to_s.downcase.intern

        # LAK:NOTE: This is slow for many options, but generally we won't have any and at
        # worst we'll have one.  If we add more, this should be made more efficient.
        options.each do |name, value|
            case name
            when :ldapname; self.ldapname = value
            else
                raise ArgumentError, "Invalid fact option '%s'" % name
            end
        end

        @ldapname ||= @name.to_s

        @resolves = []
        @searching = false

        @value = nil
    end

    # Add a new resolution mechanism.  This requires a block, which will then
    # be evaluated in the context of the new mechanism.
    def add(&block)
        raise ArgumentError, "You must pass a block to Fact<instance>.add" unless block_given?

        resolve = Facter::Util::Resolution.new(@name)

        resolve.instance_eval(&block)

        @resolves << resolve

        # Immediately sort the resolutions, so that we always have
        # a sorted list for looking up values.
        @resolves.sort! { |a, b| b.weight <=> a.weight }

        return resolve
    end

    # Flush any cached values.
    def flush
        @value = nil
        @suitable = nil
    end

    # Return the value for a given fact.  Searches through all of the mechanisms
    # and returns either the first value or nil.
    def value
        return @value if @value

        if @resolves.length == 0
            Facter.debug "No resolves for %s" % @name
            return nil
        end

        searching do
            @value = nil

            foundsuits = false
            @value = @resolves.inject(nil) { |result, resolve|
                next unless resolve.suitable?
                foundsuits = true

                tmp = resolve.value

                break tmp unless tmp.nil? or tmp == ""
            }

            unless foundsuits
                Facter.debug "Found no suitable resolves of %s for %s" % [@resolves.length, @name]
            end
        end

        if @value.nil?
            # nothing
            Facter.debug("value for %s is still nil" % @name)
            return nil
        else
            return @value
        end
    end

    private

    # Are we in the midst of a search?
    def searching?
        @searching
    end

    # Lock our searching process, so we never ge stuck in recursion.
    def searching
        if searching?
            Facter.debug "Caught recursion on %s" % @name

            # return a cached value if we've got it
            if @value
                return @value
            else
                return nil
            end
        end

        # 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
end