deltacloud-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From lut...@redhat.com
Subject [PATCH 6/7] CIMI: call a 'prepare' method on models before serializing
Date Wed, 10 Oct 2012 21:41:56 GMT
From: David Lutterkort <lutter@redhat.com>

This gives us an opportunity to
  * set the count of collections to the number of entries
  * generate id's for collections embedded in other objects
---
 server/lib/cimi/models/base.rb                     |    6 +
 server/lib/cimi/models/collection.rb               |   18 +++
 server/lib/cimi/models/schema.rb                   |    4 +
 server/tests/cimi/spec/cimi/data/container.json    |    8 ++
 server/tests/cimi/spec/cimi/data/container.xml     |    9 ++
 .../tests/cimi/spec/cimi/model/collection_spec.rb  |  109 ++++++++++++++++++++
 6 files changed, 154 insertions(+), 0 deletions(-)
 create mode 100644 server/tests/cimi/spec/cimi/data/container.json
 create mode 100644 server/tests/cimi/spec/cimi/data/container.xml
 create mode 100644 server/tests/cimi/spec/cimi/model/collection_spec.rb

diff --git a/server/lib/cimi/models/base.rb b/server/lib/cimi/models/base.rb
index 2e66694..fe6016d 100644
--- a/server/lib/cimi/models/base.rb
+++ b/server/lib/cimi/models/base.rb
@@ -166,6 +166,12 @@ class CIMI::Model::Base
   def []=(a, v)
     @attribute_values[a] = self.class.schema.convert(a, v)
   end
+
+  # Prepare to serialize
+  def prepare
+    self.class.schema.collections.map { |coll| coll.name }.each do |n|
+      self[n].href = "#{self.id}/#{n}" unless self[n].href
+    end
   end
 
   #
diff --git a/server/lib/cimi/models/collection.rb b/server/lib/cimi/models/collection.rb
index 86fa09b..db08047 100644
--- a/server/lib/cimi/models/collection.rb
+++ b/server/lib/cimi/models/collection.rb
@@ -27,12 +27,30 @@ module CIMI::Model
       if values[:entries]
         values[self.class.entry_name] = values.delete(:entries)
       end
+      values[self.class.entry_name] ||= []
       super(values)
     end
 
     def entries
       self[self.class.entry_name]
     end
+
+    # Prepare to serialize
+    def prepare
+      self.count = self.entries.size
+      self.count = nil if self.count == 0
+      if self.class.embedded
+        ["id", "href"].each { |a| self[a] = nil if self[a] == "" }
+        # Handle href and id, which are really just aliases of one another
+        unless self.href || self.id
+          raise "Collection #{self.class.name} must have one of id and href set"
+        end
+        if self.href && self.id && self.href != self.id
+          raise "id and href must be identical for collection #{self.class.name}, id = #{id.inspect},
href = #{href.inspect}"
+        end
+        self.href ||= self.id
+        self.id ||= self.href
+      end
     end
 
     def [](a)
diff --git a/server/lib/cimi/models/schema.rb b/server/lib/cimi/models/schema.rb
index cf8381a..e9504e9 100644
--- a/server/lib/cimi/models/schema.rb
+++ b/server/lib/cimi/models/schema.rb
@@ -234,6 +234,7 @@ class CIMI::Model::Schema
     end
 
     def to_xml(model, xml)
+      model[name].prepare
       if model[name].count.nil?
         xml[xml_name] = { "href" => model[name].href }
       else
@@ -242,6 +243,7 @@ class CIMI::Model::Schema
     end
 
     def to_json(model, json)
+      model[name].prepare
       if model[name].count.nil?
         json[json_name] = { "href" => model[name].href }
       else
@@ -294,6 +296,7 @@ class CIMI::Model::Schema
   def to_xml(model, xml = nil)
     xml ||= OrderedHash.new
     @attributes.freeze
+    model.prepare if model.respond_to?(:prepare)
     @attributes.each { |attr| attr.to_xml(model, xml) }
     xml
   end
@@ -309,6 +312,7 @@ class CIMI::Model::Schema
 
   def to_json(model, json = {})
     @attributes.freeze
+    model.prepare if model.respond_to?(:prepare)
     @attributes.each { |attr| attr.to_json(model, json) }
     json
   end
diff --git a/server/tests/cimi/spec/cimi/data/container.json b/server/tests/cimi/spec/cimi/data/container.json
new file mode 100644
index 0000000..522c6f2
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/data/container.json
@@ -0,0 +1,8 @@
+{ "id": "http://example.com/cimi",
+  "models": {
+    "href": "http://example.com/cimi/models",
+    "count": 2,
+    "id": "http://example.com/cimi/models",
+    "models": [ {"text":"m1"}, {"text":"m2"} ]
+  }
+}
diff --git a/server/tests/cimi/spec/cimi/data/container.xml b/server/tests/cimi/spec/cimi/data/container.xml
new file mode 100644
index 0000000..eec6ab7
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/data/container.xml
@@ -0,0 +1,9 @@
+<Container xmlns="http://schemas.dmtf.org/cimi/1">
+  <id>http://example.com/cimi</id>
+  <models href="http://example.com/cimi/models">
+    <id>http://example.com/cimi/models</id>
+    <count>2</count>
+    <Model text="m1" />
+    <Model text="m2" />
+  </models>
+</Container>
diff --git a/server/tests/cimi/spec/cimi/model/collection_spec.rb b/server/tests/cimi/spec/cimi/model/collection_spec.rb
new file mode 100644
index 0000000..a3a43c0
--- /dev/null
+++ b/server/tests/cimi/spec/cimi/model/collection_spec.rb
@@ -0,0 +1,109 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+require 'rubygems'
+require 'require_relative' if RUBY_VERSION < '1.9'
+
+require_relative '../../spec_helper.rb' if require 'minitest/autorun'
+
+require 'nokogiri'
+require 'json'
+
+describe "Collection class" do
+
+  BASE_URL = "http://example.com/cimi"
+  COLL_URL = BASE_URL + "/models"
+
+  class Model < CIMI::Model::Base
+    scalar :text
+  end
+
+  class Container < CIMI::Model::Base
+    collection :models, :class => Model
+  end
+
+  before do
+    @models = ["m1", "m2"].map { |s| Model.new(:text => s) }
+    @xml = IO::read(File::join(DATA_DIR, "container.xml"))
+    @json = IO::read(File::join(DATA_DIR, "container.json"))
+  end
+
+  describe "XML serialization" do
+    it "empty collection only has href" do
+      cont = Container.new(:id => BASE_URL)
+      doc = to_dom(cont)
+
+      (doc/"/c:Container/c:models/@href").text.must_equal COLL_URL
+      (doc/"/c:Container/c:models/c:id").size.must_equal 0
+    end
+
+    it "contains count of models" do
+      cont = Container.new(:id => BASE_URL, :models => @models)
+      doc = to_dom(cont)
+
+      (doc/"/c:Container/c:models/c:count").text.must_equal "2"
+      (doc/"/c:Container/c:models/c:Model").size.must_equal 2
+    end
+  end
+
+  describe "JSON serialization" do
+    it "empty collection only has href" do
+      cont = Container.new(:id => BASE_URL)
+      json = to_json(cont)
+
+      json["models"]["href"].must_equal COLL_URL
+      json["models"].keys.must_equal ["href"]
+    end
+
+    it "contains count of models" do
+      cont = Container.new(:id => BASE_URL, :models => @models)
+      json = to_json(cont)
+
+      json["models"]["count"].must_equal 2
+      json["models"]["models"].size.must_equal 2
+    end
+  end
+
+  it "deserializes from XML" do
+    cont = Container.from_xml(@xml)
+    cont.id.must_equal BASE_URL
+    cont.models.count.must_equal "2"
+    cont.models.entries.size.must_equal 2
+    cont.models.entries[0].text.must_equal "m1"
+    cont.models.entries[1].text.must_equal "m2"
+  end
+
+  it "deserializes from JSON" do
+    cont = Container.from_json(@json)
+    cont.id.must_equal BASE_URL
+    # FIXME: This is a very annoying difference between XML and JSON; in XML
+    # all scalars are strings, in JSON, strings that look like integers are
+    # converted to integer objects
+    cont.models.count.must_equal 2
+    cont.models.entries.size.must_equal 2
+    cont.models.entries[0].text.must_equal "m1"
+    cont.models.entries[1].text.must_equal "m2"
+  end
+
+  def to_dom(model)
+    doc = Nokogiri::XML(model.to_xml)
+    doc.root.add_namespace("c", doc.namespaces["xmlns"])
+    doc
+  end
+
+  def to_json(model)
+    JSON.parse(model.to_json)
+  end
+end
-- 
1.7.7.6


Mime
View raw message