+1, I like the way it finally came together

On Jun 11, 2010 4:10 PM, "Markus Roberts" <[email protected]> wrote:

Jesse and I are shooting for the minimal viable fix here, with the idea that
a great deal of refactoring is needed but isn't appropriate at this time.
 The
changes in this commit are:

* Index the function-holding modules by environment

* We need to know the "current environment" when we're defining a function
so
we can attach it to the proper module, and this information isn't
dynamically
available when user-defined functions are being created (we're being called
by
user written code that doesn't "know" about environments) so we cheat and
stash the value in Puppet::Node::Environment

* since we must do this anyway, it turns out to be cleaner & safer to do the
same when we are evaluating a functon.  This is the main change from the
prior
version of this patch.

* Add a special *root* environment for the built in functions, and extend
all
scopes with it.

* Index the function characteristics (name, type, docstring, etc.) by
environment

* Make the autoloader environment aware, so that it uses the modulepath for
the
specified environment rather than the default

* Turn off caching of the modulepath since it potentially changes for each
node

* Tweak tests that weren't environment aware

Signed-off-by: Markus Roberts <[email protected]>
---
 lib/puppet/node/environment.rb        |   12 +++++++++
 lib/puppet/parser/ast/function.rb     |    3 +-
 lib/puppet/parser/compiler.rb         |    1 +
 lib/puppet/parser/functions.rb        |   45
++++++++++++--------------------
 lib/puppet/parser/scope.rb            |    3 +-
 lib/puppet/parser/type_loader.rb      |    2 +-
 lib/puppet/util/autoload.rb           |   19 ++++++--------
 spec/unit/parser/functions/defined.rb |    4 +-
 spec/unit/parser/scope.rb             |    8 +++--
 spec/unit/util/autoload.rb            |    4 ---
 10 files changed, 49 insertions(+), 52 deletions(-)

diff --git a/lib/puppet/node/environment.rb b/lib/puppet/node/environment.rb
index d1a126a..4363eea 100644
--- a/lib/puppet/node/environment.rb
+++ b/lib/puppet/node/environment.rb
@@ -41,6 +41,18 @@ class Puppet::Node::Environment
        @seen[symbol] = obj
    end

+    def self.current
+        @current || root
+    end
+
+    def self.current=(env)
+        @current = new(env)
+    end
+
+    def self.root
+        @root ||= new(:'*root*')
+    end
+
    # This is only used for testing.
    def self.clear
        @seen.clear
diff --git a/lib/puppet/parser/ast/function.rb
b/lib/puppet/parser/ast/function.rb
index 4c3a5dc..ba49779 100644
--- a/lib/puppet/parser/ast/function.rb
+++ b/lib/puppet/parser/ast/function.rb
@@ -13,7 +13,7 @@ class Puppet::Parser::AST
        def evaluate(scope)

            # Make sure it's a defined function
-            unless @fname
+            unless @fname = Puppet::Parser::Functions.function(@name)
                raise Puppet::ParseError, "Unknown function %s" % @name
            end

@@ -48,7 +48,6 @@ class Puppet::Parser::AST

            super(hash)

-             @fname = Puppet::Parser::Functions.function(@name)
            # Lastly, check the parity
        end

diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb
index beba438..22e2f07 100644
--- a/lib/puppet/parser/compiler.rb
+++ b/lib/puppet/parser/compiler.rb
@@ -127,6 +127,7 @@ class Puppet::Parser::Compiler
                @environment = nil
            end
        end
+        Puppet::Node::Environment.current = @environment
        @environment
    end

diff --git a/lib/puppet/parser/functions.rb b/lib/puppet/parser/functions.rb
index 6ba3669..e0973c1 100644
--- a/lib/puppet/parser/functions.rb
+++ b/lib/puppet/parser/functions.rb
@@ -6,7 +6,7 @@ require 'puppet/parser/scope'
 # class.
 module Puppet::Parser::Functions

-    @functions = {}
+    @functions = Hash.new { |h,k| h[k] = {} }
    @modules = {}

    class << self
@@ -24,15 +24,17 @@ module Puppet::Parser::Functions
        @autoloader
    end

+    Environment = Puppet::Node::Environment
+
    def self.environment_module(env = nil)
-        @module ||= Module.new
+        @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
+        if functions.include?(name)
            raise Puppet::DevError, "Function %s already defined" % name
        end

@@ -46,10 +48,10 @@ module Puppet::Parser::Functions
        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}
+        #functions[name] = {:arity => arity, :type => ftype}
+        functions[name] = {:type => ftype, :name => fname}
        if options[:doc]
-            @functions[name][:doc] = options[:doc]
+            functions[name][:doc] = options[:doc]
        end
    end

@@ -57,11 +59,11 @@ module Puppet::Parser::Functions
    def self.rmfunction(name)
        name = symbolize(name)

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

-        @functions.delete(name)
+        functions.delete name

        fname = "function_" + name.to_s
        environment_module.send(:remove_method, fname)
@@ -71,15 +73,11 @@ module Puppet::Parser::Functions
    def self.function(name)
        name = symbolize(name)

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

-        if @functions.include? name
-            return @functions[name][:name]
-        else
-            return false
-        end
+        ( functions(Environment.root)[name] || functions[name] || {:name =>
false} )[:name]
    end

    def self.functiondocs
@@ -87,7 +85,7 @@ module Puppet::Parser::Functions

        ret = ""

-        @functions.sort { |a,b| a[0].to_s <=> b[0].to_s }.each do |name,
hash|
+        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]
@@ -102,22 +100,13 @@ module Puppet::Parser::Functions
        return ret
    end

-    def self.functions
-        @functions.keys
+    def self.functions(env = nil)
+        @functions[ env || Environment.current || Environment.root ]
    end

    # Determine if a given function returns a value or not.
    def self.rvalue?(name)
-        name = symbolize(name)
-
-        if @functions.include? name
-            case @functions[name][:type]
-            when :statement; return false
-            when :rvalue; return true
-            end
-        else
-            return false
-        end
+        (functions[symbolize(name)] || {})[:type] == :rvalue
    end

    # Runs a newfunction to create a function for each of the log levels
diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb
index 991e123..140c8c1 100644
--- a/lib/puppet/parser/scope.rb
+++ b/lib/puppet/parser/scope.rb
@@ -505,6 +505,7 @@ class Puppet::Parser::Scope
    private

    def extend_with_functions_module
-        extend Puppet::Parser::Functions.environment_module(compiler ?
environment : nil)
+        extend
Puppet::Parser::Functions.environment_module(Puppet::Node::Environment.root)
+        extend Puppet::Parser::Functions.environment_module(compiler ?
environment : nil )
    end
 end
diff --git a/lib/puppet/parser/type_loader.rb
b/lib/puppet/parser/type_loader.rb
index 3268eae..bcb7fa3 100644
--- a/lib/puppet/parser/type_loader.rb
+++ b/lib/puppet/parser/type_loader.rb
@@ -114,7 +114,7 @@ class Puppet::Parser::TypeLoader
    end

    def parse_file(file)
-        Puppet.debug("importing '#{file}'")
+        Puppet.debug("importing '#{file}' in environment #{environment}")
        parser = Puppet::Parser::Parser.new(environment)
        parser.file = file
        parser.parse
diff --git a/lib/puppet/util/autoload.rb b/lib/puppet/util/autoload.rb
index 7358618..4a687bf 100644
--- a/lib/puppet/util/autoload.rb
+++ b/lib/puppet/util/autoload.rb
@@ -73,12 +73,12 @@ class Puppet::Util::Autoload

    # Load a single plugin by name.  We use 'load' here so we can reload a
    # given plugin.
-    def load(name)
+    def load(name,env=nil)
        return false if named_file_missing?(name)

        path = name.to_s + ".rb"

-        searchpath.each do |dir|
+        searchpath(env).each do |dir|
            file = File.join(dir, path)
            next unless file_exist?(file)
            begin
@@ -130,25 +130,22 @@ class Puppet::Util::Autoload
    end

    # The list of directories to search through for loadable plugins.
-    # We have to hard-code the ttl because this library is used by
-    # so many other classes it's hard to get the load-order such that
-    # the defaults load before this.
-    cached_attr(:searchpath, :ttl => 15) do
-        search_directories.collect { |d| File.join(d, @path) }.find_all {
|d| FileTest.directory?(d) }
+    def searchpath(env=nil)
+        search_directories(env).collect { |d| File.join(d, @path)
}.find_all { |d| FileTest.directory?(d) }
    end

-    def module_directories
+    def module_directories(env=nil)
        # We have to require this late in the process because otherwise we
might have
        # load order issues.
        require 'puppet/node/environment'
-        Puppet::Node::Environment.new.modulepath.collect do |dir|
+        Puppet::Node::Environment.new(env).modulepath.collect do |dir|
            Dir.entries(dir).reject { |f| f =~ /^\./ }.collect { |f|
File.join(dir, f) }
        end.flatten.collect { |d| [File.join(d, "plugins"), File.join(d,
"lib")] }.flatten.find_all do |d|
            FileTest.directory?(d)
        end
    end

-    def search_directories(dummy_argument=:work_arround_for_ruby_GC_bug)
-        [module_directories, Puppet[:libdir].split(File::PATH_SEPARATOR),
$:].flatten
+    def search_directories(env=nil)
+        [module_directories(env),
Puppet[:libdir].split(File::PATH_SEPARATOR), $:].flatten
    end
 end
diff --git a/spec/unit/parser/functions/defined.rb
b/spec/unit/parser/functions/defined.rb
index 0da8c4a..03b0ef9 100755
--- a/spec/unit/parser/functions/defined.rb
+++ b/spec/unit/parser/functions/defined.rb
@@ -5,9 +5,9 @@ require File.dirname(__FILE__) + '/../../../spec_helper'
 describe "the 'defined' function" do

    before :each do
-        @scope = Puppet::Parser::Scope.new()
+        Puppet::Node::Environment.stubs(:current).returns(nil)
        @compiler = Puppet::Parser::Compiler.new(Puppet::Node.new("foo"))
-        @scope.compiler = @compiler
+        @scope = Puppet::Parser::Scope.new(:compiler => @compiler)
    end

    it "should exist" do
diff --git a/spec/unit/parser/scope.rb b/spec/unit/parser/scope.rb
index 7093279..b14b2d3 100755
--- a/spec/unit/parser/scope.rb
+++ b/spec/unit/parser/scope.rb
@@ -59,10 +59,12 @@ describe Puppet::Parser::Scope do
    end

    describe "when initializing" do
-        it "should extend itself with its environment's Functions module"
do
+        it "should extend itself with its environment's Functions module as
well as the default" do
            env = Puppet::Node::Environment.new("myenv")
            compiler = stub 'compiler', :environment => env
-            mod = Module.new
+            mod      = Module.new
+            root_mod = Module.new
+
 
Puppet::Parser::Functions.expects(:environment_module).with(Puppet::Node::Environment.root).returns
root_mod

Puppet::Parser::Functions.expects(:environment_module).with(env).returns mod

            Puppet::Parser::Scope.new(:compiler =>
compiler).metaclass.ancestors.should be_include(mod)
@@ -70,7 +72,7 @@ describe Puppet::Parser::Scope do

        it "should extend itself with the default Functions module if it has
no environment" do
            mod = Module.new
-
 Puppet::Parser::Functions.expects(:environment_module).with(nil).returns
mod
+
 
Puppet::Parser::Functions.expects(:environment_module).with(Puppet::Node::Environment.root).returns(mod)

            Puppet::Parser::Scope.new().metaclass.ancestors.should
be_include(mod)
        end
diff --git a/spec/unit/util/autoload.rb b/spec/unit/util/autoload.rb
index 220cb5f..5862073 100755
--- a/spec/unit/util/autoload.rb
+++ b/spec/unit/util/autoload.rb
@@ -15,10 +15,6 @@ describe Puppet::Util::Autoload do
        Puppet::Util::Autoload.ancestors.should
be_include(Puppet::Util::Cacher)
    end

-    it "should use a ttl of 15 for the search path" do
-        Puppet::Util::Autoload.attr_ttl(:searchpath).should == 15
-    end
-
    describe "when building the search path" do
        it "should collect all of the plugins and lib directories that exist
in the current environment's module path" do
            Puppet.settings.expects(:value).with(:environment).returns "foo"
--
1.6.4

--
You received this message because you are subscribed to the Google Groups
"Puppet Developers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected]<puppet-dev%[email protected]>
.
For more options, visit this group at
http://groups.google.com/group/puppet-dev?hl=en.

-- 
You received this message because you are subscribed to the Google Groups 
"Puppet Developers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/puppet-dev?hl=en.

Reply via email to