diff options
-rw-r--r-- | lib/puppet/parser/ast/vardef.rb | 2 | ||||
-rw-r--r-- | lib/puppet/parser/scope.rb | 61 | ||||
-rwxr-xr-x | spec/unit/parser/ast/vardef.rb | 4 | ||||
-rwxr-xr-x | spec/unit/parser/scope.rb | 68 |
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 |