Hello community,

here is the log from the commit of package rubygem-cfa for openSUSE:Factory 
checked in at 2016-10-18 13:25:57
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/rubygem-cfa (Old)
 and      /work/SRC/openSUSE:Factory/.rubygem-cfa.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "rubygem-cfa"

Changes:
--------
--- /work/SRC/openSUSE:Factory/rubygem-cfa/rubygem-cfa.changes  2016-06-02 
12:48:45.000000000 +0200
+++ /work/SRC/openSUSE:Factory/.rubygem-cfa.new/rubygem-cfa.changes     
2016-10-18 13:25:57.000000000 +0200
@@ -1,0 +2,14 @@
+Tue Oct 11 14:58:24 UTC 2016 - jreidin...@suse.com
+
+- optimize loading configuration files with augeas by reducing
+  number of augeas match calls (bsc#877047)
+- 0.4.1
+
+-------------------------------------------------------------------
+Tue Sep 27 09:17:51 UTC 2016 - jreidin...@suse.com
+
+- support augeas nodes containing value and also attached tree
+  below it like e.g. ntp.conf has (bnc#983486)
+- 0.4.0
+
+-------------------------------------------------------------------

Old:
----
  cfa-0.3.1.gem

New:
----
  cfa-0.4.1.gem

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ rubygem-cfa.spec ++++++
--- /var/tmp/diff_new_pack.J7wIID/_old  2016-10-18 13:25:58.000000000 +0200
+++ /var/tmp/diff_new_pack.J7wIID/_new  2016-10-18 13:25:58.000000000 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           rubygem-cfa
-Version:        0.3.1
+Version:        0.4.1
 Release:        0
 %define mod_name cfa
 %define mod_full_name %{mod_name}-%{version}

++++++ cfa-0.3.1.gem -> cfa-0.4.1.gem ++++++
Files old/checksums.yaml.gz and new/checksums.yaml.gz differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lib/cfa/augeas_parser.rb new/lib/cfa/augeas_parser.rb
--- old/lib/cfa/augeas_parser.rb        2016-05-24 16:16:10.000000000 +0200
+++ new/lib/cfa/augeas_parser.rb        2016-10-12 16:39:53.000000000 +0200
@@ -47,6 +47,24 @@
     end
   end
 
+  # Represents node that contain value and also subtree below it
+  # For easier traversing it pass #[] to subtree
+  class AugeasTreeValue
+    # value in node
+    attr_accessor :value
+    # subtree below node
+    attr_accessor :tree
+
+    def initialize(tree, value)
+      @tree = tree
+      @value = value
+    end
+
+    def [](value)
+      tree[value]
+    end
+  end
+
   # Represent parsed augeas config tree with user friendly methods
   class AugeasTree
     # low level access to augeas structure
@@ -64,12 +82,22 @@
       @data.reject! { |entry| entry[:key] == key }
     end
 
+    # adds the given value for the key in tree.
+    # @param value can be value of node, {AugeasTree}
+    #   attached to key or its combination as {AugeasTreeValue}
+    # @param placer object determining where to insert value in tree.
+    #   Useful e.g. to specify order of keys or placing comment above of given
+    #   key.
     def add(key, value, placer = AppendPlacer.new)
       element = placer.new_element(self)
       element[:key] = key
       element[:value] = value
     end
 
+    # finds given value in tree.
+    # @return It can return value of node, {AugeasTree}
+    #   attached to key or its combination as {AugeasTreeValue}.
+    #   Also nil can be returned if key not found.
     def [](key)
       entry = @data.find { |d| d[:key] == key }
       return entry[:value] if entry
@@ -77,6 +105,8 @@
       nil
     end
 
+    # Sets the given value for the key in tree. It can be value of node,
+    # {AugeasTree} attached to key or its combination as {AugeasTreeValue}
     def []=(key, value)
       entry = @data.find { |d| d[:key] == key }
       if entry
@@ -95,13 +125,12 @@
 
     # @note for internal usage only
     # @private
-    def load_from_augeas(aug, prefix)
-      matches = aug.match("#{prefix}/*")
-
-      @data = matches.map do |aug_key|
+    def load_from_augeas(aug, prefix, keys_cache)
+      @data = keys_cache.keys_for_prefix(prefix).map do |key|
+        aug_key = prefix + "/" + key
         {
           key:   load_key(prefix, aug_key),
-          value: load_value(aug, aug_key)
+          value: load_value(aug, aug_key, keys_cache)
         }
       end
     end
@@ -112,35 +141,27 @@
       arrays = {}
 
       @data.each do |entry|
-        aug_key = obtain_aug_key(prefix, entry, arrays)
-        if entry[:value].is_a? AugeasTree
-          entry[:value].save_to_augeas(aug, aug_key)
-        else
-          report_error(aug) unless aug.set(aug_key, entry[:value])
-        end
+        save_entry(entry[:key], entry[:value], arrays, aug, prefix)
       end
     end
 
-    # @note for debugging purpose only
-    def dump_tree(prefix = "")
-      arrays = {}
+  private
 
-      @data.each_with_object("") do |entry, res|
-        aug_key = obtain_aug_key(prefix, entry, arrays)
-        if entry[:value].is_a? AugeasTree
-          res << entry[:value].dump_tree(aug_key)
-        else
-          res << aug_key << "\n"
-        end
+    def save_entry(key, value, arrays, aug, prefix)
+      aug_key = obtain_aug_key(prefix, key, arrays)
+      case value
+      when AugeasTree then value.save_to_augeas(aug, aug_key)
+      when AugeasTreeValue
+        report_error(aug) unless aug.set(aug_key, value.value)
+        value.tree.save_to_augeas(aug, aug_key)
+      else
+        report_error(aug) unless aug.set(aug_key, value)
       end
     end
 
-  private
-
-    def obtain_aug_key(prefix, entry, arrays)
-      key = entry[:key]
+    def obtain_aug_key(prefix, key, arrays)
       if key.end_with?("[]")
-        array_key = key.sub(/\[\]$/, "")
+        array_key = key[0..-3] # remove trailing []
         arrays[array_key] ||= 0
         arrays[array_key] += 1
         key = array_key + "[#{arrays[array_key]}]"
@@ -156,18 +177,23 @@
     end
 
     def load_key(prefix, aug_key)
-      key = aug_key.sub(/^#{Regexp.escape(prefix)}\//, "")
-      key.sub(/\[\d+\]$/, "[]")
+      # clean from key prefix and for collection remove number inside []
+      # +1 for size due to ending '/' not part of prefix
+      key = aug_key[(prefix.size + 1)..-1]
+      key.end_with?("]") ? key.sub(/\[\d+\]$/, "[]") : key
     end
 
-    def load_value(aug, aug_key)
-      nested = !aug.match("#{aug_key}/*").empty?
+    def load_value(aug, aug_key, keys_cache)
+      subkeys = keys_cache.keys_for_prefix(aug_key)
+
+      nested = !subkeys.empty?
+      value = aug.get(aug_key)
       if nested
         subtree = AugeasTree.new
-        subtree.load_from_augeas(aug, aug_key)
-        subtree
+        subtree.load_from_augeas(aug, aug_key, keys_cache)
+        value ? AugeasTreeValue.new(subtree, value) : subtree
       else
-        aug.get(aug_key)
+        value
       end
     end
   end
@@ -186,6 +212,7 @@
       @lens = lens
     end
 
+    # parses given string and returns AugeasTree instance
     def parse(raw_string)
       @old_content = raw_string
 
@@ -196,13 +223,16 @@
         aug.set("/input", raw_string)
         report_error(aug) unless aug.text_store(@lens, "/input", "/store")
 
+        keys_cache = AugeasKeysCache.new(aug)
+
         tree = AugeasTree.new
-        tree.load_from_augeas(aug, "/store")
+        tree.load_from_augeas(aug, "/store", keys_cache)
 
         return tree
       end
     end
 
+    # Serializes AugeasTree instance into returned string
     def serialize(data)
       # open augeas without any autoloading and it should not touch disk and
       # load lenses as needed only
@@ -228,13 +258,62 @@
     def report_error(aug)
       error = aug.error
       # zero is no error, so problem in lense
-      if aug.error[:code] != 0
+      if aug.error[:code].nonzero?
         raise "Augeas error #{error[:message]}. Details: #{error[:details]}."
-      else
-        msg = aug.get("/augeas/text/store/error/message")
-        location = aug.get("/augeas/text/store/error/lens")
-        raise "Augeas parsing/serializing error: #{msg} at #{location}"
       end
+
+      msg = aug.get("/augeas/text/store/error/message")
+      location = aug.get("/augeas/text/store/error/lens")
+      raise "Augeas parsing/serializing error: #{msg} at #{location}"
+    end
+  end
+end
+
+# Cache that holds all avaiable keys in augeas tree. It is used to
+# prevent too many aug.match calls which are expensive.
+class AugeasKeysCache
+  STORE_PREFIX = "/store".freeze
+  STORE_LEN = STORE_PREFIX.size
+  STORE_LEN_1 = STORE_LEN + 1
+
+  # initialize cache from passed augeas object
+  def initialize(aug)
+    fill_cache(aug)
+  end
+
+  # returns list of keys available on given prefix
+  def keys_for_prefix(prefix)
+    cut = prefix.length > STORE_LEN ? STORE_LEN_1 : STORE_LEN
+    path = prefix[cut..-1]
+    path = path.split("/")
+    matches = path.reduce(@cache) { |a, e| a[e] }
+
+    matches.keys
+  end
+
+private
+
+  def fill_cache(aug)
+    @cache = {}
+    search_path = "#{STORE_PREFIX}/*"
+    loop do
+      matches = aug.match(search_path)
+      break if matches.empty?
+      assign_matches(matches, @cache)
+
+      search_path += "/*"
+    end
+  end
+
+  def assign_matches(matches, cache)
+    matches.each do |match|
+      path = match[STORE_LEN_1..-1].split("/")
+      leap = path.pop
+      target = path.reduce(cache) do |acc, p|
+        acc[p]
+      end
+
+      target[leap] = {}
     end
   end
 end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/metadata new/metadata
--- old/metadata        2016-05-24 16:16:14.000000000 +0200
+++ new/metadata        2016-10-12 16:39:53.000000000 +0200
@@ -1,14 +1,14 @@
 --- !ruby/object:Gem::Specification
 name: cfa
 version: !ruby/object:Gem::Version
-  version: 0.3.1
+  version: 0.4.1
 platform: ruby
 authors:
 - Josef Reidinger
 autorequire: 
 bindir: bin
 cert_chain: []
-date: 2016-05-24 00:00:00.000000000 Z
+date: 2016-10-12 00:00:00.000000000 Z
 dependencies:
 - !ruby/object:Gem::Dependency
   name: ruby-augeas
@@ -58,7 +58,7 @@
       version: 1.3.6
 requirements: []
 rubyforge_project: 
-rubygems_version: 2.4.5.1
+rubygems_version: 2.2.2
 signing_key: 
 specification_version: 4
 summary: CFA (Config Files API) provides an easy way to create models on top 
of configuration


Reply via email to