summaryrefslogtreecommitdiffstats
path: root/lib/puppet/parser/functions.rb
blob: a99e8dc95ea39753c2cd8b4f98f5bf9ff515c007 (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
121
122
123
124
125
126
127
128
require 'puppet/util/autoload'
require 'puppet/parser/scope'
require 'monitor'

# A module for managing parser functions.  Each specified function
# is added to a central module that then gets included into the Scope
# class.
module Puppet::Parser::Functions

    (@functions = Hash.new { |h,k| h[k] = {} }).extend(MonitorMixin)
    (@modules   = {}                          ).extend(MonitorMixin)

    class << self
        include Puppet::Util
    end

    def self.autoloader
        unless defined?(@autoloader)

                        @autoloader = Puppet::Util::Autoload.new(
                self,
                "puppet/parser/functions",
        
                :wrap => false
            )
        end

        @autoloader
    end

    Environment = Puppet::Node::Environment

    def self.environment_module(env = nil)
        @modules.synchronize {
            @modules[ env || Environment.current || Environment.root ] ||= Module.new
        }
    end

    # Create a new function type.
    def self.newfunction(name, options = {}, &block)
        name = symbolize(name)

        if functions.include?(name)
            raise Puppet::DevError, "Function %s already defined" % name
        end

        ftype = options[:type] || :statement

        unless ftype == :statement or ftype == :rvalue
            raise Puppet::DevError, "Invalid statement type %s" % ftype.inspect
        end

        fname = "function_" + name.to_s
        environment_module.send(:define_method, fname, &block)

        # Someday we'll support specifying an arity, but for now, nope
        #functions[name] = {:arity => arity, :type => ftype}
        functions[name] = {:type => ftype, :name => fname}
        if options[:doc]
            functions[name][:doc] = options[:doc]
        end
    end

    # Remove a function added by newfunction
    def self.rmfunction(name)
        name = symbolize(name)

        unless functions.include? name
            raise Puppet::DevError, "Function %s is not defined" % name
        end

        functions.delete name

        fname = "function_" + name.to_s
        environment_module.send(:remove_method, fname)
    end

    # Determine if a given name is a function
    def self.function(name)
        name = symbolize(name)

        unless functions.include?(name) or functions(Puppet::Node::Environment.root).include?(name)
            autoloader.load(name,Environment.current || Environment.root)
        end

        ( functions(Environment.root)[name] || functions[name] || {:name => false} )[:name]
    end

    def self.functiondocs
        autoloader.loadall

        ret = ""

        functions.sort { |a,b| a[0].to_s <=> b[0].to_s }.each do |name, hash|
            #ret += "%s\n%s\n" % [name, hash[:type]]
            ret += "%s\n%s\n" % [name, "-" * name.to_s.length]
            if hash[:doc]
                ret += Puppet::Util::Docs.scrub(hash[:doc])
            else
                ret += "Undocumented.\n"
            end

            ret += "\n\n- **Type**: %s\n\n" % hash[:type]
        end

        return ret
    end

    def self.functions(env = nil)
        @functions.synchronize {
            @functions[ env || Environment.current || Environment.root ]
        }
    end

    # Determine if a given function returns a value or not.
    def self.rvalue?(name)
        (functions[symbolize(name)] || {})[:type] == :rvalue
    end

    # Runs a newfunction to create a function for each of the log levels

    Puppet::Util::Log.levels.each do |level|
        newfunction(level, :doc => "Log a message on the server at level #{level.to_s}.") do |vals|
            send(level, vals.join(" "))
        end
    end

end