summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrice Figureau <brice-puppet@daysofwonder.com>2009-07-26 14:03:14 +0200
committerJames Turnbull <james@lovedthanlost.net>2009-08-01 11:15:28 +1000
commitf357a9192647fc5d436d4c934b1912f59996389f (patch)
treec6d94f9a4b284f95eb5c1a7927cd25995924d970
parentd40ef291191e627a91d6ec73b438853e2d3a73e8 (diff)
downloadpuppet-f357a9192647fc5d436d4c934b1912f59996389f.tar.gz
puppet-f357a9192647fc5d436d4c934b1912f59996389f.tar.xz
puppet-f357a9192647fc5d436d4c934b1912f59996389f.zip
Implement ephemeral scope variables
Those variables have been created to be short lived and used mainly to define temporary special variables. They do not persist after a call to unset_ephemeral_var. Also Scope#set_ephemeral_from can be used to promote a regexp MatchData to ephemeral values. Signed-off-by: Brice Figureau <brice-puppet@daysofwonder.com>
-rw-r--r--lib/puppet/parser/ast/vardef.rb2
-rw-r--r--lib/puppet/parser/scope.rb61
-rwxr-xr-xspec/unit/parser/ast/vardef.rb4
-rwxr-xr-xspec/unit/parser/scope.rb68
4 files changed, 107 insertions, 28 deletions
diff --git a/lib/puppet/parser/ast/vardef.rb b/lib/puppet/parser/ast/vardef.rb
index 2d5f623f7..ae24b3233 100644
--- a/lib/puppet/parser/ast/vardef.rb
+++ b/lib/puppet/parser/ast/vardef.rb
@@ -17,7 +17,7 @@ class Puppet::Parser::AST
value = @value.safeevaluate(scope)
parsewrap do
- scope.setvar(name,value, @file, @line, @append)
+ scope.setvar(name,value, :file => @file, :line => @line, :append => @append)
end
end
diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb
index be4dc12ed..5b3c6d04a 100644
--- a/lib/puppet/parser/scope.rb
+++ b/lib/puppet/parser/scope.rb
@@ -128,6 +128,11 @@ class Puppet::Parser::Scope
# The symbol table for this scope. This is where we store variables.
@symtable = {}
+ # the ephemeral symbol tables
+ # those should not persist long, and are used for the moment only
+ # for $0..$xy capture variables of regexes
+ @ephemeral = {}
+
# All of the defaults set for types. It's a hash of hashes,
# with the first key being the type, then the second key being
# the parameter.
@@ -189,16 +194,17 @@ class Puppet::Parser::Scope
# Look up a variable. The simplest value search we do. Default to returning
# an empty string for missing values, but support returning a constant.
def lookupvar(name, usestring = true)
+ table = ephemeral?(name) ? @ephemeral : @symtable
# If the variable is qualified, then find the specified scope and look the variable up there instead.
if name =~ /::/
return lookup_qualified_var(name, usestring)
end
- # We can't use "if @symtable[name]" here because the value might be false
- if @symtable.include?(name)
- if usestring and @symtable[name] == :undef
+ # We can't use "if table[name]" here because the value might be false
+ if table.include?(name)
+ if usestring and table[name] == :undef
return ""
else
- return @symtable[name]
+ return table[name]
end
elsif self.parent
return parent.lookupvar(name, usestring)
@@ -286,34 +292,35 @@ class Puppet::Parser::Scope
# Set a variable in the current scope. This will override settings
# in scopes above, but will not allow variables in the current scope
# to be reassigned.
- def setvar(name,value, file = nil, line = nil, append = false)
+ def setvar(name,value, options = {})
+ table = options[:ephemeral] ? @ephemeral : @symtable
#Puppet.debug "Setting %s to '%s' at level %s mode append %s" %
# [name.inspect,value,self.level, append]
- if @symtable.include?(name)
- unless append
+ if table.include?(name)
+ unless options[:append]
error = Puppet::ParseError.new("Cannot reassign variable %s" % name)
else
error = Puppet::ParseError.new("Cannot append, variable %s is defined in this scope" % name)
end
- if file
- error.file = file
+ if options[:file]
+ error.file = options[:file]
end
- if line
- error.line = line
+ if options[:line]
+ error.line = options[:line]
end
raise error
end
- unless append
- @symtable[name] = value
+ unless options[:append]
+ table[name] = value
else # append case
# lookup the value in the scope if it exists and insert the var
- @symtable[name] = lookupvar(name)
+ table[name] = lookupvar(name)
# concatenate if string, append if array, nothing for other types
if value.is_a?(Array)
- @symtable[name] += value
+ table[name] += value
else
- @symtable[name] << value
+ table[name] << value
end
end
end
@@ -391,8 +398,26 @@ class Puppet::Parser::Scope
# Undefine a variable; only used for testing.
def unsetvar(var)
- if @symtable.include?(var)
- @symtable.delete(var)
+ table = ephemeral?(var) ? @ephemeral : @table
+ if table.include?(var)
+ table.delete(var)
+ end
+ end
+
+ def unset_ephemeral_var
+ @ephemeral = {}
+ end
+
+ def ephemeral?(name)
+ @ephemeral.include?(name)
+ end
+
+ def ephemeral_from(match, file = nil, line = nil)
+ raise(ArgumentError,"Invalid regex match data") unless match.is_a?(MatchData)
+
+ setvar("0", match[0], :file => file, :line => line, :ephemeral => true)
+ match.captures.each_with_index do |m,i|
+ setvar("#{i+1}", m, :file => file, :line => line, :ephemeral => true)
end
end
end
diff --git a/spec/unit/parser/ast/vardef.rb b/spec/unit/parser/ast/vardef.rb
index 14de68923..9730ceedb 100755
--- a/spec/unit/parser/ast/vardef.rb
+++ b/spec/unit/parser/ast/vardef.rb
@@ -25,7 +25,7 @@ describe Puppet::Parser::AST::VarDef do
name = stub 'name', :safeevaluate => "var"
value = stub 'value', :safeevaluate => "1"
- @scope.expects(:setvar).with { |name,value,file,line,append| append == nil }
+ @scope.expects(:setvar).with { |name,value,options| options[:append] == nil }
vardef = Puppet::Parser::AST::VarDef.new :name => name, :value => value, :file => nil,
:line => nil
@@ -36,7 +36,7 @@ describe Puppet::Parser::AST::VarDef do
name = stub 'name', :safeevaluate => "var"
value = stub 'value', :safeevaluate => "1"
- @scope.expects(:setvar).with { |name,value,file,line,append| append == true }
+ @scope.expects(:setvar).with { |name,value,options| options[:append] == true }
vardef = Puppet::Parser::AST::VarDef.new :name => name, :value => value, :file => nil,
:line => nil, :append => true
diff --git a/spec/unit/parser/scope.rb b/spec/unit/parser/scope.rb
index a9784e03b..641a3f946 100755
--- a/spec/unit/parser/scope.rb
+++ b/spec/unit/parser/scope.rb
@@ -111,24 +111,24 @@ describe Puppet::Parser::Scope do
describe "when setvar is called with append=true" do
it "should raise error if the variable is already defined in this scope" do
- @scope.setvar("var","1",nil,nil,false)
- lambda { @scope.setvar("var","1",nil,nil,true) }.should raise_error(Puppet::ParseError)
+ @scope.setvar("var","1", :append => false)
+ lambda { @scope.setvar("var","1", :append => true) }.should raise_error(Puppet::ParseError)
end
it "it should lookup current variable value" do
@scope.expects(:lookupvar).with("var").returns("2")
- @scope.setvar("var","1",nil,nil,true)
+ @scope.setvar("var","1", :append => true)
end
it "it should store the concatenated string '42'" do
- @topscope.setvar("var","4",nil,nil,false)
- @scope.setvar("var","2",nil,nil,true)
+ @topscope.setvar("var","4", :append => false)
+ @scope.setvar("var","2", :append => true)
@scope.lookupvar("var").should == "42"
end
it "it should store the concatenated array [4,2]" do
- @topscope.setvar("var",[4],nil,nil,false)
- @scope.setvar("var",[2],nil,nil,true)
+ @topscope.setvar("var",[4], :append => false)
+ @scope.setvar("var",[2], :append => true)
@scope.lookupvar("var").should == [4,2]
end
@@ -195,4 +195,58 @@ describe Puppet::Parser::Scope do
Puppet::Parser::Scope.number?("0x89g").should be_nil
end
end
+
+ describe "when using ephemeral variables" do
+ it "should store the variable value" do
+ @scope.setvar("1", :value, :ephemeral => true)
+
+ @scope.lookupvar("1").should == :value
+ end
+
+ it "should remove the variable value when unset_ephemeral_var is called" do
+ @scope.setvar("1", :value, :ephemeral => true)
+ @scope.stubs(:parent).returns(nil)
+
+ @scope.unset_ephemeral_var
+
+ @scope.lookupvar("1", false).should == :undefined
+ end
+
+ it "should not remove classic variables when unset_ephemeral_var is called" do
+ @scope.setvar("myvar", :value1)
+ @scope.setvar("1", :value2, :ephemeral => true)
+ @scope.stubs(:parent).returns(nil)
+
+ @scope.unset_ephemeral_var
+
+ @scope.lookupvar("myvar", false).should == :value1
+ end
+ end
+
+ describe "when setting ephemeral vars from matches" do
+ before :each do
+ @match = stub 'match', :is_a? => true
+ @match.stubs(:[]).with(0).returns("this is a string")
+ @match.stubs(:captures).returns([])
+ @scope.stubs(:setvar)
+ end
+
+ it "should accept only MatchData" do
+ lambda { @scope.ephemeral_from("match") }.should raise_error
+ end
+
+ it "should set $0 with the full match" do
+ @scope.expects(:setvar).with { |*arg| arg[0] == "0" and arg[1] == "this is a string" and arg[2][:ephemeral] }
+
+ @scope.ephemeral_from(@match)
+ end
+
+ it "should set every capture as ephemeral var" do
+ @match.stubs(:captures).returns([:capture1,:capture2])
+ @scope.expects(:setvar).with { |*arg| arg[0] == "1" and arg[1] == :capture1 and arg[2][:ephemeral] }
+ @scope.expects(:setvar).with { |*arg| arg[0] == "2" and arg[1] == :capture2 and arg[2][:ephemeral] }
+
+ @scope.ephemeral_from(@match)
+ end
+ end
end