+.5 because I'm not 100% confident in my comprehension of the code.
But I really like it!

On Mon, May 10, 2010 at 11:55 PM, Luke Kanies <[email protected]> wrote:
> (Changes to parser.rb have been removed from this email.)
>
> This is just a first, simplest version, but you can
> now specify relationships directly in the language:
>
>  File[/foo] -> Service[bar]
>
> Specifies a normal dependency while:
>
>  File[/foo] => Service[bar]
>
> Specifies a subscription.
>
> Next in the plan is to support complete resources or collections
> on either side and to make the statements extendable:
>
>  File[/foo] -> Package[baz] -> Service[bar]
>
> Signed-off-by: Luke Kanies <[email protected]>
> ---
>  lib/puppet/parser/ast.rb            |    1 +
>  lib/puppet/parser/ast/dependency.rb |   31 +
>  lib/puppet/parser/compiler.rb       |   15 +-
>  lib/puppet/parser/dependency.rb     |   24 +
>  lib/puppet/parser/grammar.ra        |    9 +-
>  lib/puppet/parser/lexer.rb          |    4 +
>  lib/puppet/parser/parser.rb         | 2276 
> ++++++++++++++++++-----------------
>  spec/unit/parser/ast/dependency.rb  |   64 +
>  spec/unit/parser/compiler.rb        |   11 +-
>  spec/unit/parser/dependency.rb      |   60 +
>  10 files changed, 1381 insertions(+), 1114 deletions(-)
>  create mode 100644 lib/puppet/parser/ast/dependency.rb
>  create mode 100644 lib/puppet/parser/dependency.rb
>  create mode 100644 spec/unit/parser/ast/dependency.rb
>  create mode 100644 spec/unit/parser/dependency.rb
>
> diff --git a/lib/puppet/parser/ast.rb b/lib/puppet/parser/ast.rb
> index 6af7745..41bd721 100644
> --- a/lib/puppet/parser/ast.rb
> +++ b/lib/puppet/parser/ast.rb
> @@ -116,3 +116,4 @@ require 'puppet/parser/ast/resourceparam'
>  require 'puppet/parser/ast/selector'
>  require 'puppet/parser/ast/tag'
>  require 'puppet/parser/ast/vardef'
> +require 'puppet/parser/ast/dependency'
> diff --git a/lib/puppet/parser/ast/dependency.rb 
> b/lib/puppet/parser/ast/dependency.rb
> new file mode 100644
> index 0000000..5678ffa
> --- /dev/null
> +++ b/lib/puppet/parser/ast/dependency.rb
> @@ -0,0 +1,31 @@
> +require 'puppet/parser/ast'
> +require 'puppet/parser/ast/branch'
> +require 'puppet/parser/dependency'
> +
> +class Puppet::Parser::AST::Dependency < Puppet::Parser::AST::Branch
> +    attr_accessor :source, :target, :type
> +
> +    # Evaluate our object, but just return a simple array of the type
> +    # and name.
> +    def evaluate(scope)
> +        source = self.source.safeevaluate(scope)
> +        target = self.target.safeevaluate(scope)
> +        scope.compiler.add_dependency(Puppet::Parser::Dependency.new(source, 
> target, type))
> +    end
> +
> +    def initialize(left, right, type, args = {})
> +        super(args)
> +        if type.include?(">")
> +           �...@source = left
> +           �...@target = right
> +        else
> +           �...@source = right
> +           �...@target = left
> +        end
> +        if type.include?("-")
> +           �...@type = :dependency
> +        else
> +           �...@type = :subscription
> +        end
> +    end
> +end
> diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb
> index 8e84f5a..8208f9c 100644
> --- a/lib/puppet/parser/compiler.rb
> +++ b/lib/puppet/parser/compiler.rb
> @@ -21,13 +21,17 @@ class Puppet::Parser::Compiler
>         raise Puppet::Error, "#{detail} on node #{node.name}"
>     end
>
> -    attr_reader :node, :facts, :collections, :catalog, :node_scope, 
> :resources
> +    attr_reader :node, :facts, :collections, :catalog, :node_scope, 
> :resources, :dependencies
>
>     # Add a collection to the global list.
>     def add_collection(coll)
>         @collections << coll
>     end
>
> +    def add_dependency(dep)
> +       �...@dependencies << dep
> +    end
> +
>     # Store a resource override.
>     def add_override(override)
>         # If possible, merge the override in immediately.
> @@ -144,6 +148,10 @@ class Puppet::Parser::Compiler
>         found
>     end
>
> +    def evaluate_dependencies
> +       �[email protected] { |dep| dep.evaluate(catalog) }
> +    end
> +
>     # Return a resource by either its ref or its type and title.
>     def findresource(*args)
>         @catalog.resource(*args)
> @@ -337,6 +345,8 @@ class Puppet::Parser::Compiler
>     # Make sure all of our resources and such have done any last work
>     # necessary.
>     def finish
> +        evaluate_dependencies()
> +
>         resources.each do |resource|
>             # Add in any resource overrides.
>             if overrides = resource_overrides(resource)
> @@ -374,6 +384,9 @@ class Puppet::Parser::Compiler
>         # but they each refer back to the scope that created them.
>         @collections = []
>
> +        # The list of dependencies to evaluate.
> +       �...@dependencies = []
> +
>         # For maintaining the relationship between scopes and their resources.
>         @catalog = Puppet::Resource::Catalog.new(@node.name)
>         @catalog.version = known_resource_types.version
> diff --git a/lib/puppet/parser/dependency.rb b/lib/puppet/parser/dependency.rb
> new file mode 100644
> index 0000000..b085e6f
> --- /dev/null
> +++ b/lib/puppet/parser/dependency.rb
> @@ -0,0 +1,24 @@
> +class Puppet::Parser::Dependency
> +    attr_accessor :source, :target, :type
> +
> +    PARAM_MAP = {:dependency => :require, :subscription => :subscribe}
> +
> +    def evaluate(catalog)
> +        unless source_resource = catalog.resource(source)
> +            raise ArgumentError, "Could not find resource #{source} for 
> dependency on #{target}"
> +        end
> +        unless target_resource = catalog.resource(target)
> +            raise ArgumentError, "Could not find resource #{target} for 
> dependency from #{source}"
> +        end
> +        source_resource[param_name] ||= []
> +        source_resource[param_name] << target
> +    end
> +
> +    def initialize(source, target, type)
> +       �...@source, @target, @type = source.to_s, target.to_s, type
> +    end
> +
> +    def param_name
> +        PARAM_MAP[type] || raise(ArgumentError, "Invalid dependency type 
> #{type}")
> +    end
> +end
> diff --git a/lib/puppet/parser/grammar.ra b/lib/puppet/parser/grammar.ra
> index 5b9a609..6d19033 100644
> --- a/lib/puppet/parser/grammar.ra
> +++ b/lib/puppet/parser/grammar.ra
> @@ -11,7 +11,7 @@ token QMARK LPAREN RPAREN ISEQUAL GREATEREQUAL GREATERTHAN 
> LESSTHAN
>  token IF ELSE IMPORT DEFINE ELSIF VARIABLE CLASS INHERITS NODE BOOLEAN
>  token NAME SEMIC CASE DEFAULT AT LCOLLECT RCOLLECT CLASSNAME CLASSREF
>  token NOT OR AND UNDEF PARROW PLUS MINUS TIMES DIV LSHIFT RSHIFT UMINUS
> -token MATCH NOMATCH REGEX
> +token MATCH NOMATCH REGEX IN_EDGE OUT_EDGE IN_EDGE_SUB OUT_EDGE_SUB
>
>  prechigh
>     right NOT
> @@ -74,6 +74,13 @@ statement:    resource
>             | nodedef
>             | resourceoverride
>             | append
> +            | relationship
> +
> +relationship: resourceref edge resourceref {
> +    result = AST::Dependency.new(val[0], val[2], val[1], ast_context)
> +}
> +
> +edge: IN_EDGE | OUT_EDGE | IN_EDGE_SUB | OUT_EDGE_SUB
>
>  fstatement:   NAME LPAREN funcvalues RPAREN {
>     args = aryfy(val[2])
> diff --git a/lib/puppet/parser/lexer.rb b/lib/puppet/parser/lexer.rb
> index 2a1f88e..628a37a 100644
> --- a/lib/puppet/parser/lexer.rb
> +++ b/lib/puppet/parser/lexer.rb
> @@ -129,6 +129,10 @@ class Puppet::Parser::Lexer
>         ':' => :COLON,
>         '@' => :AT,
>         '<<|' => :LLCOLLECT,
> +        '->' => :IN_EDGE,
> +        '<-' => :OUT_EDGE,
> +        '=>' => :IN_EDGE_SUB,
> +        '<=' => :OUT_EDGE_SUB,
>         '|>>' => :RRCOLLECT,
>         '<|' => :LCOLLECT,
>         '|>' => :RCOLLECT,
> diff --git a/spec/unit/parser/ast/dependency.rb 
> b/spec/unit/parser/ast/dependency.rb
> new file mode 100644
> index 0000000..52283d6
> --- /dev/null
> +++ b/spec/unit/parser/ast/dependency.rb
> @@ -0,0 +1,64 @@
> +#!/usr/bin/env ruby
> +
> +require File.dirname(__FILE__) + '/../../../spec_helper'
> +
> +describe Puppet::Parser::AST::Dependency do
> +    before do
> +       �...@class = Puppet::Parser::AST::Dependency
> +    end
> +
> +    it "should set its type to :dependency if the relationship type is '->'" 
> do
> +       �[email protected](:left, :right, "->").type.should == :dependency
> +    end
> +
> +    it "should set its type to :dependency if the relationship type is '<-'" 
> do
> +       �[email protected](:left, :right, "<-").type.should == :dependency
> +    end
> +
> +    it "should set its type to :subscription if the relationship type is 
> '=>'" do
> +       �[email protected](:left, :right, "=>").type.should == :subscription
> +    end
> +
> +    it "should set its type to :subscription if the relationship type is 
> '<='" do
> +       �[email protected](:left, :right, "<=").type.should == :subscription
> +    end
> +
> +    it "should set its source to the first argument and target to the second 
> if the arrow points to the right" do
> +        dep = @class.new(:left, :right, "->")
> +        dep.source.should == :left
> +        dep.target.should == :right
> +        dep = @class.new(:left, :right, "=>")
> +        dep.source.should == :left
> +        dep.target.should == :right
> +    end
> +
> +    it "should set its source to the second argument and target to the first 
> if the arrow points to the left" do
> +        dep = @class.new(:left, :right, "<-")
> +        dep.source.should == :right
> +        dep.target.should == :left
> +        dep = @class.new(:left, :right, "<=")
> +        dep.source.should == :right
> +        dep.target.should == :left
> +    end
> +
> +    it "should set its line and file if provided" do
> +        dep = @class.new(:left, :right, "<=", :line => 50, :file => "/foo")
> +        dep.line.should == 50
> +        dep.file.should == "/foo"
> +    end
> +
> +    describe "when evaluating" do
> +        before do
> +           �...@compiler = 
> Puppet::Parser::Compiler.new(Puppet::Node.new("foo"))
> +           �...@scope = Puppet::Parser::Scope.new(:compiler => @compiler)
> +        end
> +
> +        it "should create a dependency with the evaluated source and target 
> and add it to the scope" do
> +            source = stub 'source', :safeevaluate => :left
> +            target = stub 'target', :safeevaluate => :right
> +           �[email protected](source, target, "->").evaluate(@scope)
> +           �[email protected][0].source.should == "left"
> +           �[email protected][0].target.should == "right"
> +        end
> +    end
> +end
> diff --git a/spec/unit/parser/compiler.rb b/spec/unit/parser/compiler.rb
> index 6fd4d1f..35fff54 100755
> --- a/spec/unit/parser/compiler.rb
> +++ b/spec/unit/parser/compiler.rb
> @@ -134,7 +134,7 @@ describe Puppet::Parser::Compiler do
>
>         def compile_methods
>             [:set_node_parameters, :evaluate_main, :evaluate_ast_node, 
> :evaluate_node_classes, :evaluate_generators, :fail_on_unevaluated,
> -                :finish, :store, :extract]
> +                :finish, :store, :extract, :evaluate_dependencies]
>         end
>
>         # Stub all of the main compile methods except the ones we're 
> specifically interested in.
> @@ -394,6 +394,15 @@ describe Puppet::Parser::Compiler do
>         end
>     end
>
> +    describe "when evaluating dependencies" do
> +        it "should evaluate each dependency with its catalog" do
> +            dep = stub 'dep'
> +            dep.expects(:evaluate).with(@compiler.catalog)
> +           �[email protected]_dependency dep
> +           �[email protected]_dependencies
> +        end
> +    end
> +
>     describe "when told to evaluate missing classes" do
>
>         it "should fail if there's no source listed for the scope" do
> diff --git a/spec/unit/parser/dependency.rb b/spec/unit/parser/dependency.rb
> new file mode 100644
> index 0000000..d8206c1
> --- /dev/null
> +++ b/spec/unit/parser/dependency.rb
> @@ -0,0 +1,60 @@
> +#!/usr/bin/env ruby
> +
> +require File.dirname(__FILE__) + '/../../spec_helper'
> +
> +require 'puppet/parser/dependency'
> +
> +describe Puppet::Parser::Dependency do
> +    before do
> +       �...@source = Puppet::Resource.new(:mytype, "source")
> +       �...@target = Puppet::Resource.new(:mytype, "target")
> +       �...@dep = Puppet::Parser::Dependency.new(@source, @target, 
> :dependency)
> +    end
> +
> +    it "should set the source to the stringified version" do
> +       �[email protected] == "Mytype[source]"
> +    end
> +
> +    it "should set the target to the stringified version" do
> +       �[email protected] == "Mytype[target]"
> +    end
> +
> +    describe "when evaluating" do
> +        before do
> +           �...@catalog = Puppet::Resource::Catalog.new
> +           �[email protected]_resource(@source)
> +           �[email protected]_resource(@target)
> +        end
> +
> +        it "should fail if the source resource cannot be found" do
> +           �...@catalog = Puppet::Resource::Catalog.new
> +           �[email protected]_resource @target
> +            lambda { @dep.evaluate(@catalog) }.should 
> raise_error(ArgumentError)
> +        end
> +
> +        it "should fail if the target resource cannot be found" do
> +           �...@catalog = Puppet::Resource::Catalog.new
> +           �[email protected]_resource @source
> +            lambda { @dep.evaluate(@catalog) }.should 
> raise_error(ArgumentError)
> +        end
> +
> +        it "should add the target as a 'require' value if the type is 
> 'dependency'" do
> +           �[email protected] = :dependency
> +           �[email protected](@catalog)
> +           �...@source[:require].should be_include("Mytype[target]")
> +        end
> +
> +        it "should add the target as a 'subscribe' value if the type is 
> 'subscription'" do
> +           �[email protected] = :subscription
> +           �[email protected](@catalog)
> +           �...@source[:subscribe].should be_include("Mytype[target]")
> +        end
> +
> +        it "should supplement rather than clobber existing relationship 
> values" do
> +           �...@source[:require] = "File[/bar]"
> +           �[email protected](@catalog)
> +           �...@source[:require].should be_include("Mytype[target]")
> +           �...@source[:require].should be_include("File[/bar]")
> +        end
> +    end
> +end
> --
> 1.6.1
>
> --
> 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.
>
>

-- 
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