summaryrefslogtreecommitdiffstats
path: root/lib/puppet/indirector.rb
blob: d30f8a63d0fca3d6af585557766b61f414db7772 (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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# Manage indirections to termini.  They are organized in terms of indirections -
# - e.g., configuration, node, file, certificate -- and each indirection has one
# or more terminus types defined.  The indirection is configured via the
# +indirects+ method, which will be called by the class extending itself
# with this module.
module Puppet::Indirector
    # LAK:FIXME We need to figure out how to handle documentation for the
    # different indirection types.

# JRB:TODO factor this out into its own class, with specs, and require it here
# require 'puppet/indirector/terminus'

    # A simple class that can function as the base class for indirected types.
    class Terminus
        require 'puppet/util/docs'
        extend Puppet::Util::Docs
        
        class << self
            attr_accessor :name, :indirection
        end
        
        def name
            self.class.name
        end
        def indirection
            self.class.indirection
        end
    end

    require 'puppet/indirector/indirection'

    # This handles creating the terminus classes.
    require 'puppet/util/classgen'
    extend Puppet::Util::ClassGen

    # This manages reading in all of our files for us and then retrieving
    # loaded instances.  We still have to define the 'newX' method, but this
    # does all of the rest -- loading, storing, and retrieving by name.
    require 'puppet/util/instance_loader'
    extend Puppet::Util::InstanceLoader

# JRB:TODO - where did this come from, re: the specs?  also, shouldn't this be protected/private?
    
    # Register a given indirection type.  The classes including this module
    # handle creating terminus instances, but the module itself handles
    # loading them and managing the classes.
    def self.enable_autoloading_indirection(indirection)
        # Set up autoloading of the appropriate termini.
        instance_load indirection, "puppet/indirector/%s" % indirection
    end
    
# JRB:TODO -- where did this come from, re: the specs? also, any way to make this protected/private?
    
    # Define a new indirection terminus.  This method is used by the individual
    # termini in their separate files.  Again, the autoloader takes care of
    # actually loading these files.
    #   Note that the termini are being registered on the Indirector module, not
    # on the classes including the module.  This allows a given indirection to
    # be used in multiple classes.
    def self.register_terminus(indirection, terminus, options = {}, &block)
        klass = genclass(terminus,
            :prefix => indirection.to_s.capitalize,
            :hash => instance_hash(indirection),
            :attributes => options,
            :block => block,
            :parent => options[:parent] || Terminus,
# JRB:FIXME -- why do I have to use overwrite here?            
            :overwrite => 'please do motherfucker'
        )
        klass.indirection = indirection
        klass.name = terminus
    end

# JRB:TODO where did this come from, re: the specs?  also, shouldn't this be protected/private?    
    # Retrieve a terminus class by indirection and name.
# JRB:FIXME -- should be protected/private
    def self.terminus(indirection, terminus)
        loaded_instance(indirection, terminus)
    end
    
    # clear out the list of known indirections
    def self.reset
      @indirections = {}
      @class_indirections = {}
    end
    
    # return a hash of registered indirections, keys are indirection names, values are classes which handle the indirections
    def self.indirections
      @indirections ||= {}
      @indirections
    end
    
    # associate an indirection name with the class which handles the indirection
    def self.register_indirection(name, klass)
      @indirections ||= {}
      @class_indirections ||= {}
      
      raise ArgumentError, "Already performing an indirection of %s; cannot redirect %s" % [name, klass.name] if @indirections[name]
      raise ArgumentError, "Class %s is already redirecting to %s; cannot redirect to %s" % 
        [klass.name, @class_indirections[klass.name], name] if @class_indirections[klass.name]
      @class_indirections[klass.name] = name
      @indirections[name] = klass
    end
    
    def self.terminus_for_indirection(name)
# JRB:TODO make this do something useful, aka look something up in a .yml file
      :ldap
    end

    # Declare that the including class indirects its methods to
    # this terminus.  The terminus name must be the name of a Puppet
    # default, not the value -- if it's the value, then it gets
    # evaluated at parse time, which is before the user has had a chance
    # to override it.
    def indirects(indirection, options = {})
#JRB:TODO remove options hash  ^^^

        # associate the name :node, with this class, Node
        # also, do error checking (already registered, etc.)
        Puppet::Indirector.register_indirection(indirection, self)

        # populate this registered class with the various new methods
        extend ClassMethods
        include InstanceMethods

        # look up the type of Terminus for this name (:node => :ldap)
        terminus = Puppet::Indirector.terminus_for_indirection(indirection)

        # instantiate the actual Terminus for that type and this name (:ldap, w/ args :node)
        # & hook the instantiated Terminus into this registered class (Node: @indirection = terminus)
        Puppet::Indirector.enable_autoloading_indirection indirection
        @indirection = Puppet::Indirector.terminus(indirection, terminus)
    end

    module ClassMethods   
      attr_reader :indirection
         
      def find(*args)
        self.indirection.find(args)
        # JRB:TODO look up the indirection, and call its .find method
      end

      def destroy(*args)
        self.indirection.destroy(args)
      end

      def search(*args)
        self.indirection.search(args)
      end
    end

    module InstanceMethods
      # these become instance methods 
      def save(*args)
        self.class.indirection.save(args)
      end
    end
    
    # JRB:FIXME: these methods to be deprecated:

    # Define methods for each of the HTTP methods.  These just point to the
    # termini, with consistent error-handling.  Each method is called with
    # the first argument being the indirection type and the rest of the
    # arguments passed directly on to the indirection terminus.  There is
    # currently no attempt to standardize around what the rest of the arguments
    # should allow or include or whatever.
    #   There is also no attempt to pre-validate that a given indirection supports
    # the method in question.  We should probably require that indirections
    # declare supported methods, and then verify that termini implement all of
    # those methods.
    # [:get, :post, :put, :delete].each do |method_name|
    #     define_method(method_name) do |*args|
    #         redirect(method_name, *args)
    #     end
    # end
    # 
    # private
    # 
    # 
    # # JRB:TODO this needs to be renamed, as it actually ends up on the model class, where it might conflict with something
    # # Redirect one of our methods to the corresponding method on the Terminus
    # def redirect(method_name, *args)
    #     begin
    #         @indirection.terminus.send(method_name, *args)
    #     rescue NoMethodError => detail
    #         puts detail.backtrace if Puppet[:trace]
    #         raise ArgumentError, "The %s terminus of the %s indirection failed to respond to %s: %s" %
    #             [@indirection.terminus.name, @indirection.name, method_name, detail]
    #     end
    # end
end