Merge pull request #69 from salt-formulas/andrewp-fix-inv-query-merging

Fix merging inv query results
diff --git a/README-extentions.rst b/README-extensions.rst
similarity index 93%
rename from README-extentions.rst
rename to README-extensions.rst
index 2bc4816..6693256 100644
--- a/README-extentions.rst
+++ b/README-extensions.rst
@@ -336,6 +336,37 @@
     ...
 
 
+Load classes with relative names
+--------------------------------
+
+Load referenced class from a relative location to the current class.
+To load class from relative location start the class uri with "." char.
+The only supported reference is to nested tree structure below the current class.
+
+You are allowed to use syntax for relative uri to required class on any place on your model (first class loaded, init.yml, regular class .yml).
+
+The feature is expected to improve flexibility while sharing classes between your models.
+
+It's a new feature use it with care and mind that using "relative syntax" lower traceability of
+your pillar composition.
+
+Example usage of relative class name:
+
+.. code-block:: yaml
+
+  #/etc/reclass/classes/component/defaults.yml
+  classes:
+    component:
+      config:
+        a: b
+
+.. code-block:: yaml
+
+  #/etc/reclass/classes/component/init.yml
+  classes:
+    - .defaults
+
+
 Inventory Queries
 -----------------
 
diff --git a/README.rst b/README.rst
index b865e4f..5dcb5a1 100644
--- a/README.rst
+++ b/README.rst
@@ -16,15 +16,16 @@
 * Ignore class notfound/regexp option
 
 
-.. include:: ./README-extensions.rst
-
-
 Documentation
 =============
 
-Documentation covering the original version is in the doc directory.
-See the README-extensions.rst file for documentation on the extentions.
+.. _README-extensions: README-extensions.rst
 
+Documentation covering the original version is in the doc directory.
+See the `README-extensions`_ file for documentation on the extentions.
+
+
+.. include:: ./README-extensions.rst
 
 
 Reclass related projects/tools
diff --git a/reclass/core.py b/reclass/core.py
index bc89738..66c74f5 100644
--- a/reclass/core.py
+++ b/reclass/core.py
@@ -113,6 +113,7 @@
             context = Entity(self._settings, name='empty (@{0})'.format(nodename))
 
         for klass in entity.classes.as_list():
+            # class name contain reference
             if klass.count('$') > 0:
                 try:
                     klass = str(self._parser.parse(klass, self._settings).render(merge_base.parameters.as_dict(), {}))
@@ -122,6 +123,10 @@
                     except ResolveError as e:
                         raise ClassNameResolveError(klass, nodename, entity.uri)
 
+            # for convenience, first level classes_uri/class.yml can have un-interpolated "."
+            if klass.startswith('.'):
+                klass = klass[1:]
+
             if klass not in seen:
                 try:
                     class_entity = self._storage.get_class(klass, environment, self._settings)
diff --git a/reclass/storage/yaml_fs/__init__.py b/reclass/storage/yaml_fs/__init__.py
index 0ea0b05..7540aa4 100644
--- a/reclass/storage/yaml_fs/__init__.py
+++ b/reclass/storage/yaml_fs/__init__.py
@@ -108,7 +108,13 @@
             path = os.path.join(self.classes_uri, self._classes[name])
         except KeyError as e:
             raise reclass.errors.ClassNotFound(self.name, name, self.classes_uri)
-        entity = YamlData.from_file(path).get_entity(name, settings)
+
+        if path.endswith('init{}'.format(FILE_EXTENSION)):
+            parent_class=name
+        else:
+            # for regular class yml file, strip its name
+            parent_class='.'.join(name.split('.')[:-1])
+        entity = YamlData.from_file(path).get_entity(name, settings, parent_class)
         return entity
 
     def enumerate_nodes(self):
diff --git a/reclass/storage/yaml_git/__init__.py b/reclass/storage/yaml_git/__init__.py
index 45cb6c0..61c4551 100644
--- a/reclass/storage/yaml_git/__init__.py
+++ b/reclass/storage/yaml_git/__init__.py
@@ -258,7 +258,15 @@
             raise reclass.errors.NotFoundError("File " + name + " missing from " + uri.repo + " branch " + uri.branch)
         file = self._repos[uri.repo].files[uri.branch][name]
         blob = self._repos[uri.repo].get(file.id)
-        entity = YamlData.from_string(blob.data, 'git_fs://{0} {1} {2}'.format(uri.repo, uri.branch, file.path)).get_entity(name, settings)
+
+        if file.name.endswith('init{}'.format(FILE_EXTENSION)):
+            parent_class=name
+        else:
+            # for regular class yml file, strip its name
+            parent_class='.'.join(name.split('.')[:-1])
+
+        entity = YamlData.from_string(blob.data, 'git_fs://{0} {1} {2}'.format(uri.repo, uri.branch,
+            file.path)).get_entity(name, settings, parent_class)
         return entity
 
     def enumerate_nodes(self):
diff --git a/reclass/storage/yamldata.py b/reclass/storage/yamldata.py
index a861154..52547ac 100644
--- a/reclass/storage/yamldata.py
+++ b/reclass/storage/yamldata.py
@@ -53,13 +53,16 @@
     def get_data(self):
         return self._data
 
-    def get_entity(self, name, settings):
+    def get_entity(self, name, settings, parent_class=None):
         #if name is None:
         #    name = self._uri
 
         classes = self._data.get('classes')
         if classes is None:
             classes = []
+        if parent_class:
+            classes = \
+                [parent_class + c for c in classes if c.startswith('.')]
         classes = datatypes.Classes(classes)
 
         applications = self._data.get('applications')
diff --git a/test/model/extensions/classes/defaults.yml b/test/model/extensions/classes/defaults.yml
new file mode 100644
index 0000000..5d17c2b
--- /dev/null
+++ b/test/model/extensions/classes/defaults.yml
@@ -0,0 +1,4 @@
+
+parameters:
+  config:
+    defaults: True
diff --git a/test/model/extensions/classes/relative/init.yml b/test/model/extensions/classes/relative/init.yml
new file mode 100644
index 0000000..117e4fa
--- /dev/null
+++ b/test/model/extensions/classes/relative/init.yml
@@ -0,0 +1,3 @@
+
+classes:
+  - .nested
diff --git a/test/model/extensions/classes/relative/nested/common.yml b/test/model/extensions/classes/relative/nested/common.yml
new file mode 100644
index 0000000..28cc0b2
--- /dev/null
+++ b/test/model/extensions/classes/relative/nested/common.yml
@@ -0,0 +1,5 @@
+
+parameters:
+  nested:
+    deep:
+      common: to be overriden
diff --git a/test/model/extensions/classes/relative/nested/deep/common.yml b/test/model/extensions/classes/relative/nested/deep/common.yml
new file mode 100644
index 0000000..b77a24c
--- /dev/null
+++ b/test/model/extensions/classes/relative/nested/deep/common.yml
@@ -0,0 +1,5 @@
+
+parameters:
+  nested:
+    deep:
+      common: False
diff --git a/test/model/extensions/classes/relative/nested/deep/init.yml b/test/model/extensions/classes/relative/nested/deep/init.yml
new file mode 100644
index 0000000..cd12d10
--- /dev/null
+++ b/test/model/extensions/classes/relative/nested/deep/init.yml
@@ -0,0 +1,9 @@
+
+classes:
+  - .common
+
+parameters:
+  nested:
+    deep:
+      init: True
+      common: True
diff --git a/test/model/extensions/classes/relative/nested/dive/session.yml b/test/model/extensions/classes/relative/nested/dive/session.yml
new file mode 100644
index 0000000..9abd1ee
--- /dev/null
+++ b/test/model/extensions/classes/relative/nested/dive/session.yml
@@ -0,0 +1,5 @@
+
+parameters:
+  nested:
+    deep:
+      session: True
diff --git a/test/model/extensions/classes/relative/nested/init.yml b/test/model/extensions/classes/relative/nested/init.yml
new file mode 100644
index 0000000..9f02383
--- /dev/null
+++ b/test/model/extensions/classes/relative/nested/init.yml
@@ -0,0 +1,10 @@
+
+classes:
+  - .common
+  - .deep
+  - .dive.session
+
+parameters:
+  nested:
+    deep:
+      init: True
diff --git a/test/model/extensions/classes/second.yml b/test/model/extensions/classes/second.yml
index a9babd3..929d746 100644
--- a/test/model/extensions/classes/second.yml
+++ b/test/model/extensions/classes/second.yml
@@ -1,5 +1,6 @@
 classes:
 - first
+- relative
 
 parameters:
   will:
diff --git a/test/model/extensions/classes/third.yml b/test/model/extensions/classes/third.yml
index 20a937c..a5157cf 100644
--- a/test/model/extensions/classes/third.yml
+++ b/test/model/extensions/classes/third.yml
@@ -1,6 +1,7 @@
 classes:
 - missing.class
 - second
+- .defaults
 
 parameters:
   _param:
diff --git a/test/model/extensions/nodes/reclass.yml b/test/model/extensions/nodes/reclass.yml
index 94b7519..5d5b3ec 100644
--- a/test/model/extensions/nodes/reclass.yml
+++ b/test/model/extensions/nodes/reclass.yml
@@ -1,3 +1,3 @@
 
 classes:
-- third
+- .third