<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -6,10 +6,13 @@
 # subscriptions are permanent associations determining how different
 # objects react to an event
 
+require 'puppet/util/json'
+
 # This is Puppet's class for modeling edges in its configuration graph.
 # It used to be a subclass of GRATR::Edge, but that class has weird hash
 # overrides that dramatically slow down the graphing.
 class Puppet::Relationship
+    extend Puppet::Util::Json
     attr_accessor :source, :target, :callback
 
     attr_reader :event</diff>
      <filename>lib/puppet/relationship.rb</filename>
    </modified>
    <modified>
      <diff>@@ -4,6 +4,7 @@ require 'puppet/simple_graph'
 require 'puppet/transaction'
 
 require 'puppet/util/cacher'
+require 'puppet/util/json'
 
 require 'puppet/util/tagging'
 
@@ -18,6 +19,7 @@ class Puppet::Resource::Catalog &lt; Puppet::SimpleGraph
     indirects :catalog, :terminus_class =&gt; :compiler
 
     include Puppet::Util::Tagging
+    extend Puppet::Util::Json
     include Puppet::Util::Cacher::Expirer
 
     # The host name this is a catalog for.
@@ -388,6 +390,69 @@ class Puppet::Resource::Catalog &lt; Puppet::SimpleGraph
         @resource_table.keys
     end
 
+    def self.from_json(data)
+        result = new(data['name'])
+
+        if tags = data['tags'] 
+            result.tag(*tags)
+        end
+
+        if version = data['version'] 
+            result.version = version
+        end
+
+        if resources = data['resources']
+            resources.each do |res|
+                resource_from_json(result, res)
+            end
+        end
+
+        if edges = data['edges']
+            edges.each do |edge|
+                edge_from_json(result, edge)
+            end
+        end
+
+        result
+    end
+
+    def self.edge_from_json(result, edge)
+        # If no json_class information was presented, we manually find
+        # the class.
+        edge = Puppet::Relationship.from_json(edge) if edge.is_a?(Hash)
+        unless source = result.resource(edge.source)
+            raise ArgumentError, &quot;Could not convert from json: Could not find relationship source '%s'&quot; % source
+        end
+        edge.source = source
+
+        unless target = result.resource(edge.target)
+            raise ArgumentError, &quot;Could not convert from json: Could not find relationship target '%s'&quot; % target
+        end
+        edge.target = target
+
+        result.add_edge(edge)
+    end
+
+    def self.resource_from_json(result, res)
+        # If no json_class information was presented, we manually find
+        # the class.
+        res = Puppet::Resource.from_json(res) if res.is_a?(Hash)
+        result.add_resource(res)
+    end
+
+    def to_json(*args)
+        {
+            'json_class' =&gt; 'Puppet::Resource::Catalog',
+            'data' =&gt; {
+                'tags' =&gt; tags,
+                'name' =&gt; name,
+                'version' =&gt; version,
+                'resources' =&gt; vertices.to_json(*args),
+                'edges' =&gt; edges.to_json(*args)
+            }
+        }.to_json(*args)
+    end
+
     # Convert our catalog into a RAL catalog.
     def to_ral
         to_catalog :to_ral</diff>
      <filename>lib/puppet/resource/catalog.rb</filename>
    </modified>
    <modified>
      <diff>@@ -72,6 +72,16 @@ describe Puppet::Relationship, &quot; when initializing&quot; do
         @edge.callback.should == :foo
         @edge.event.should == :bar
     end
+
+    it &quot;should accept events specified as strings&quot; do
+        @edge = Puppet::Relationship.new(:a, :b, &quot;event&quot; =&gt; :NONE)
+        @edge.event.should == :NONE
+    end
+
+    it &quot;should accept callbacks specified as strings&quot; do
+        @edge = Puppet::Relationship.new(:a, :b, &quot;callback&quot; =&gt; :foo)
+        @edge.callback.should == :foo
+    end
 end
 
 describe Puppet::Relationship, &quot; when matching edges with no specified event&quot; do
@@ -227,6 +237,10 @@ describe Puppet::Relationship, &quot;when converting from json&quot; do
         Puppet::Relationship.expects(:new).with { |*args| yield args }
     end
 
+    it &quot;should be extended with the JSON utility module&quot; do
+        Puppet::Relationship.metaclass.ancestors.should be_include(Puppet::Util::Json)
+    end
+
     # LAK:NOTE For all of these tests, we convert back to the edge so we can
     # trap the actual data structure then.
     it &quot;should pass the source in as the first argument&quot; do</diff>
      <filename>spec/unit/relationship.rb</filename>
    </modified>
    <modified>
      <diff>@@ -826,3 +826,188 @@ describe Puppet::Resource::Catalog, &quot;when compiling&quot; do
         end
     end
 end
+
+describe Puppet::Resource::Catalog, &quot;when converting to json&quot; do
+    confine &quot;Missing 'json' library&quot; =&gt; Puppet.features.json?
+
+    before do
+        @catalog = Puppet::Resource::Catalog.new(&quot;myhost&quot;)
+    end
+
+    def json_output_should
+        @catalog.class.expects(:json_create).with { |hash| yield hash }
+    end
+
+    # LAK:NOTE For all of these tests, we convert back to the resource so we can
+    # trap the actual data structure then.
+    it &quot;should set its json_class to 'Puppet::Resource::Catalog'&quot; do
+        json_output_should { |hash| hash['json_class'] == &quot;Puppet::Resource::Catalog&quot; }
+
+        JSON.parse @catalog.to_json
+    end
+
+    it &quot;should set its data as a hash&quot; do
+        json_output_should { |hash| hash['data'].is_a?(Hash) }
+        JSON.parse @catalog.to_json
+    end
+
+    [:name, :version, :tags].each do |param|
+        it &quot;should set its #{param} to the #{param} of the resource&quot; do
+            @catalog.send(param.to_s + &quot;=&quot;, &quot;testing&quot;) unless @catalog.send(param)
+
+            json_output_should { |hash| hash['data'][param.to_s] == @catalog.send(param) }
+            JSON.parse @catalog.to_json
+        end
+    end
+
+    it &quot;should convert its resources to a JSON-encoded array and store it as the 'resources' data&quot; do
+        one = stub 'one', :to_json =&gt; '&quot;one_resource&quot;', :ref =&gt; &quot;Foo[one]&quot;
+        two = stub 'two', :to_json =&gt; '&quot;two_resource&quot;', :ref =&gt; &quot;Foo[two]&quot;
+
+        @catalog.add_resource(one)
+        @catalog.add_resource(two)
+
+        # TODO this should really guarantee sort order
+        json_output_should { |hash| JSON.parse(hash['data']['resources']).sort == [&quot;one_resource&quot;, &quot;two_resource&quot;].sort }
+        JSON.parse @catalog.to_json
+    end
+
+    it &quot;should convert its edges to a JSON-encoded array and store it as the 'edges' data&quot; do
+        one = stub 'one', :to_json =&gt; '&quot;one_resource&quot;', :ref =&gt; 'Foo[one]'
+        two = stub 'two', :to_json =&gt; '&quot;two_resource&quot;', :ref =&gt; 'Foo[two]'
+        three = stub 'three', :to_json =&gt; '&quot;three_resource&quot;', :ref =&gt; 'Foo[three]'
+
+        @catalog.add_edge(one, two)
+        @catalog.add_edge(two, three)
+
+        @catalog.edge(one, two).expects(:to_json).returns '&quot;one_two_json&quot;'
+        @catalog.edge(two, three).expects(:to_json).returns '&quot;two_three_json&quot;'
+
+        json_output_should { |hash| JSON.parse(hash['data']['edges']).sort == %w{one_two_json two_three_json}.sort }
+        JSON.parse @catalog.to_json
+    end
+end
+
+describe Puppet::Resource::Catalog, &quot;when converting from json&quot; do
+    confine &quot;Missing 'json' library&quot; =&gt; Puppet.features.json?
+
+    def json_result_should
+        Puppet::Resource::Catalog.expects(:new).with { |hash| yield hash }
+    end
+
+    before do
+        @data = {
+            'name' =&gt; &quot;myhost&quot;
+        }
+        @json = {
+            'json_class' =&gt; 'Puppet::Resource::Catalog',
+            'data' =&gt; @data
+        }
+
+        @catalog = Puppet::Resource::Catalog.new(&quot;myhost&quot;)
+        Puppet::Resource::Catalog.stubs(:new).returns @catalog
+    end
+
+    it &quot;should be extended with the JSON utility module&quot; do
+        Puppet::Resource::Catalog.metaclass.ancestors.should be_include(Puppet::Util::Json)
+    end
+
+    it &quot;should create it with the provided name&quot; do
+        Puppet::Resource::Catalog.expects(:new).with('myhost').returns @catalog
+        JSON.parse @json.to_json
+    end
+
+    it &quot;should set the provided version on the catalog if one is set&quot; do
+        @data['version'] = 50
+        @catalog.expects(:version=).with(@data['version'])
+
+        JSON.parse @json.to_json
+    end
+
+    it &quot;should set any provided tags on the catalog&quot; do
+        @data['tags'] = %w{one two}
+        @catalog.expects(:tag).with(&quot;one&quot;, &quot;two&quot;)
+
+        JSON.parse @json.to_json
+    end
+
+    it 'should convert the resources list into resources and add each of them' do
+        @data['resources'] = [Puppet::Resource.new(:file, &quot;/foo&quot;), Puppet::Resource.new(:file, &quot;/bar&quot;)]
+
+        @catalog.expects(:add_resource).times(2).with { |res| res.type == &quot;File&quot; }
+
+        JSON.parse @json.to_json
+    end
+
+    it 'should convert resources even if they do not include &quot;json_class&quot; information' do
+        @data['resources'] = [Puppet::Resource.new(:file, &quot;/foo&quot;)]
+
+        @data['resources'][0].expects(:to_json).returns &quot;{\&quot;title\&quot;:\&quot;\\/foo\&quot;,\&quot;tags\&quot;:[\&quot;file\&quot;],\&quot;type\&quot;:\&quot;File\&quot;}&quot;
+
+        @catalog.expects(:add_resource).with { |res| res.type == &quot;File&quot; }
+
+        JSON.parse @json.to_json
+    end
+
+    it 'should convert the edges list into edges and add each of them' do
+        one = Puppet::Relationship.new(&quot;osource&quot;, &quot;otarget&quot;, :event =&gt; &quot;one&quot;, :callback =&gt; &quot;refresh&quot;)
+        two = Puppet::Relationship.new(&quot;tsource&quot;, &quot;ttarget&quot;, :event =&gt; &quot;two&quot;, :callback =&gt; &quot;refresh&quot;)
+
+        @data['edges'] = [one, two]
+
+        @catalog.stubs(:resource).returns(&quot;eh&quot;)
+
+        @catalog.expects(:add_edge).with { |edge| edge.event == &quot;one&quot; }
+        @catalog.expects(:add_edge).with { |edge| edge.event == &quot;two&quot; }
+
+        JSON.parse @json.to_json
+    end
+
+    it &quot;should be able to convert relationships that do not include 'json_class' information&quot; do
+        one = Puppet::Relationship.new(&quot;osource&quot;, &quot;otarget&quot;, :event =&gt; &quot;one&quot;, :callback =&gt; &quot;refresh&quot;)
+        one.expects(:to_json).returns &quot;{\&quot;event\&quot;:\&quot;one\&quot;,\&quot;callback\&quot;:\&quot;refresh\&quot;,\&quot;source\&quot;:\&quot;osource\&quot;,\&quot;target\&quot;:\&quot;otarget\&quot;}&quot;
+
+        @data['edges'] = [one]
+
+        @catalog.stubs(:resource).returns(&quot;eh&quot;)
+
+        @catalog.expects(:add_edge).with { |edge| edge.event == &quot;one&quot; }
+
+        JSON.parse @json.to_json
+    end
+
+    it &quot;should set the source and target for each edge to the actual resource&quot; do
+        edge = Puppet::Relationship.new(&quot;source&quot;, &quot;target&quot;)
+
+        @data['edges'] = [edge]
+
+        @catalog.expects(:resource).with(&quot;source&quot;).returns(&quot;source_resource&quot;)
+        @catalog.expects(:resource).with(&quot;target&quot;).returns(&quot;target_resource&quot;)
+
+        @catalog.expects(:add_edge).with { |edge| edge.source == &quot;source_resource&quot; and edge.target == &quot;target_resource&quot; }
+
+        JSON.parse @json.to_json
+    end
+
+    it &quot;should fail if the source resource cannot be found&quot; do
+        edge = Puppet::Relationship.new(&quot;source&quot;, &quot;target&quot;)
+
+        @data['edges'] = [edge]
+
+        @catalog.expects(:resource).with(&quot;source&quot;).returns(nil)
+        @catalog.stubs(:resource).with(&quot;target&quot;).returns(&quot;target_resource&quot;)
+
+        lambda { JSON.parse @json.to_json }.should raise_error(ArgumentError)
+    end
+
+    it &quot;should fail if the target resource cannot be found&quot; do
+        edge = Puppet::Relationship.new(&quot;source&quot;, &quot;target&quot;)
+
+        @data['edges'] = [edge]
+
+        @catalog.stubs(:resource).with(&quot;source&quot;).returns(&quot;source_resource&quot;)
+        @catalog.expects(:resource).with(&quot;target&quot;).returns(nil)
+
+        lambda { JSON.parse @json.to_json }.should raise_error(ArgumentError)
+    end
+end</diff>
      <filename>spec/unit/resource/catalog.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>c0bd0aa1a5aaed94dfab25f390199a722d0d5c0d</id>
    </parent>
  </parents>
  <author>
    <name>Luke Kanies</name>
    <email>luke@madstop.com</email>
  </author>
  <url>http://github.com/lak/puppet/commit/7b33b6da4bdcd2263e2c63b443e9bea6fbe8d161</url>
  <id>7b33b6da4bdcd2263e2c63b443e9bea6fbe8d161</id>
  <committed-date>2009-06-06T02:57:58-07:00</committed-date>
  <authored-date>2009-06-02T15:34:43-07:00</authored-date>
  <message>Adding JSON support to Catalogs

Signed-off-by: Luke Kanies &lt;luke@madstop.com&gt;</message>
  <tree>09b3612762b8b0d6a73a0ca6c0d303b2f9add84f</tree>
  <committer>
    <name>James Turnbull</name>
    <email>james@lovedthanlost.net</email>
  </committer>
</commit>
