diff --git a/examples/code/modules/sample-module.pp b/examples/code/modules/sample-module.pp new file mode 100644 index 000000000..57079a0aa --- /dev/null +++ b/examples/code/modules/sample-module.pp @@ -0,0 +1,10 @@ +# Jeff McCune +# 2007-08-14 +# +# Use: +# puppet --verbose --debug --modulepath=`pwd` ./sample-module.pp +# +# sample-module demonstrates the use of a custom language function +# included within the module bundle. + +include sample-module diff --git a/examples/code/modules/sample-module/README.txt b/examples/code/modules/sample-module/README.txt new file mode 100644 index 000000000..ee4b8201a --- /dev/null +++ b/examples/code/modules/sample-module/README.txt @@ -0,0 +1,17 @@ +Jeff McCune +2007-08-14 + +This small, sample module demonstrates how to extend the puppet language +with a new parser function. + +See: +manifests/init.pp +lib/puppet/parser/functions/hostname_to_dn.rb +templates/sample.erb + +Note the consistent naming of files for Puppet::Util::Autoload + +Reference Documents: +http://reductivelabs.com/trac/puppet/wiki/ModuleOrganisation +http://reductivelabs.com/trac/puppet/wiki/WritingYourOwnFunctions +http://reductivelabs.com/trac/puppet/wiki/FunctionReference diff --git a/examples/code/modules/sample-module/lib/puppet/parser/functions/hostname_to_dn.rb b/examples/code/modules/sample-module/lib/puppet/parser/functions/hostname_to_dn.rb new file mode 100644 index 000000000..9f732b5bc --- /dev/null +++ b/examples/code/modules/sample-module/lib/puppet/parser/functions/hostname_to_dn.rb @@ -0,0 +1,36 @@ +# Copyright (C) David Schmitt +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the Author nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +# Jeff McCune +# 2007-08-14 +# See: http://reductivelabs.com/trac/puppet/wiki/WritingYourOwnFunctions + +module Puppet::Parser::Functions + newfunction(:hostname_to_dn, :type => :rvalue, :doc => "Given 'foo.bar.com', return 'dc=foo,dc=bar,dc=com'.") do |args| + args[0].split(/\./).map do |s| "dc=%s"%[s] end.join(",") + end +end diff --git a/examples/code/modules/sample-module/manifests/init.pp b/examples/code/modules/sample-module/manifests/init.pp new file mode 100644 index 000000000..1af8dff1f --- /dev/null +++ b/examples/code/modules/sample-module/manifests/init.pp @@ -0,0 +1,12 @@ +# Jeff McCune +# +# Demonstration of a custom parser function and erb template within +# a module, working in concert. + +class sample-module { + $fqdn_to_dn = hostname_to_dn($domain) + $sample_template = template("sample-module/sample.erb") + + notice("hostname_to_dn module function returned: [$fqdn_to_dn]") + info("sample.erb looks like:\n$sample_template") +} diff --git a/examples/code/modules/sample-module/templates/sample.erb b/examples/code/modules/sample-module/templates/sample.erb new file mode 100644 index 000000000..b13561b45 --- /dev/null +++ b/examples/code/modules/sample-module/templates/sample.erb @@ -0,0 +1,5 @@ + +## Jeff McCune +fqdn: <%= fqdn %> +basedn: <%= fqdn_to_dn %> +## end sample.erb ## diff --git a/lib/puppet/util/autoload.rb b/lib/puppet/util/autoload.rb index 42aa56c32..be9e14671 100644 --- a/lib/puppet/util/autoload.rb +++ b/lib/puppet/util/autoload.rb @@ -1,148 +1,154 @@ # Autoload paths, either based on names or all at once. class Puppet::Util::Autoload include Puppet::Util @autoloaders = {} @loaded = {} class << self attr_reader :autoloaders private :autoloaders end # Send [], []=, and :clear to the @autloaders hash Puppet::Util.classproxy self, :autoloaders, "[]", "[]=" # Clear the list of autoloaders and loaded files. def self.clear @autoloaders.clear @loaded.clear end # List all loaded files. def self.list_loaded @loaded.sort { |a,b| a[0] <=> b[0] }.collect do |path, hash| "%s: %s" % [path, hash[:file]] end end # Has a given path been loaded? This is used for testing whether a # changed file should be loaded or just ignored. def self.loaded?(path) path = path.to_s.sub(/\.rb$/, '') @loaded[path] end # Save the fact that a given path has been loaded def self.loaded(path, file, loader) @loaded[path] = {:file => file, :autoloader => loader} end attr_accessor :object, :path, :objwarn, :wrap def initialize(obj, path, options = {}) @path = path.to_s if @path !~ /^\w/ raise ArgumentError, "Autoload paths cannot be fully qualified" end @object = obj self.class[obj] = self options.each do |opt, value| opt = opt.intern if opt.is_a? String begin self.send(opt.to_s + "=", value) rescue NoMethodError raise ArgumentError, "%s is not a valid option" % opt end end unless defined? @wrap @wrap = true end end # Load a single plugin by name. We use 'load' here so we can reload a # given plugin. def load(name) path = name.to_s + ".rb" eachdir do |dir| file = File.join(dir, path) next unless FileTest.exists?(file) begin Kernel.load file, @wrap name = symbolize(name) loaded name, file return true rescue LoadError => detail # I have no idea what's going on here, but different versions # of ruby are raising different errors on missing files. unless detail.to_s =~ /^no such file/i warn "Could not autoload %s: %s" % [name, detail] if Puppet[:trace] puts detail.backtrace end end return false end end return false end # Mark the named object as loaded. Note that this supports unqualified # queries, while we store the result as a qualified query in the class. def loaded(name, file) self.class.loaded(File.join(@path, name.to_s), file, object) end # Indicate whether the specfied plugin has been loaded. def loaded?(name) self.class.loaded?(File.join(@path, name.to_s)) end # Load all instances that we can. This uses require, rather than load, # so that already-loaded files don't get reloaded unnecessarily. def loadall # Load every instance of everything we can find. eachdir do |dir| Dir.glob("#{dir}/*.rb").each do |file| # Load here, rather than require, so that facts # can be reloaded. This has some short-comings, I # believe, but it works as long as real classes # aren't used. name = File.basename(file).sub(".rb", '').intern next if loaded?(name) next if $".include?(File.join(@path, name.to_s + ".rb")) filepath = File.join(@path, name.to_s + ".rb") begin Kernel.require file loaded(name, file) rescue => detail if Puppet[:trace] puts detail.backtrace end warn "Could not autoload %s: %s" % [file.inspect, detail] end end end end private # Yield each subdir in turn. def eachdir searchpath.each do |dir| subdir = File.join(dir, @path) yield subdir if FileTest.directory?(subdir) end end # The list of directories to search through for loadable plugins. def searchpath - [Puppet[:libdir], $:].flatten + # JJM: Search for optional lib directories in each module bundle. + module_lib_dirs = Puppet[:modulepath].split(":").collect do |d| + Dir.glob("%s/*/lib" % d).select do |f| + FileTest.directory?(f) + end + end.flatten + [module_lib_dirs, Puppet[:libdir], $:].flatten end end # $Id$