From 7ac23d0737a6eee80b1926860030247931282ba1 Mon Sep 17 00:00:00 2001 From: Ewoud Kohl van Wijngaarden Date: Fri, 21 Jul 2023 12:10:11 +0200 Subject: [PATCH] Provide filter_results to refine a search rspec-puppet-facts has a pattern where it does a lot of small searches for every OS. By doing a single complex FacterDB.get_facts call to get the only supported operating systems and then further refining that result it can use the cache provided by FacterDB.get_facts. This is quite hacky now because it duplicates internals from JGrep to avoid serializing to JSON and parsing that again. The conversions to symbols and back to strings is also inefficient. --- lib/facterdb.rb | 16 ++++++++++++++++ spec/facterdb_spec.rb | 18 ++++++++++++++++++ spec/spec_helper.rb | 1 + 3 files changed, 35 insertions(+) diff --git a/lib/facterdb.rb b/lib/facterdb.rb index e30e9cef..87641055 100644 --- a/lib/facterdb.rb +++ b/lib/facterdb.rb @@ -149,4 +149,20 @@ def self.get_facts(filter = nil, cache = true) end result end + + # @param results [Array[Hash]] The results from a get_facts call + # @param filter [Object] The filter to convert to jgrep string + # @return [Array[Hash[Symbol, Any]]] Array of hashes of facts + def self.filter_results(results, filter) + # TODO: it's really inefficient to do to_s and to_sym all the time + database = results.map { |hash| hash.to_h { |k, v| [k.to_s, v] } } + + # TODO: This duplicates JGrep.jgrep to avoid JSON.parse(database.to_json) + # JGrep should provide an API for that + expression = generate_filter_str(filter) + call_stack = JGrep::Parser.new(expression).execution_stack + filtered = database.filter { |document| JGrep.eval_statement(document, call_stack) } + + filtered.map { |hash| hash.to_h { |k, v| [k.to_sym, v] } } + end end diff --git a/spec/facterdb_spec.rb b/spec/facterdb_spec.rb index 5fac2a26..076c51f8 100644 --- a/spec/facterdb_spec.rb +++ b/spec/facterdb_spec.rb @@ -353,4 +353,22 @@ end end end + + describe '.filter_results' do + subject(:filtered) { FacterDB.filter_results(results, filter) } + + let(:filters) { 'osfamily=Debian or osfamily=RedHat' } + let(:results) { FacterDB.get_facts(filters) } + + # Just to make sure our results set contains what we want + it 'has both Debian and Red Hat in the unfiltered result' do + expect(results).to include(hash_including(osfamily: 'Debian')).and include(hash_including(osfamily: 'RedHat')) + end + + context 'with filter for Debian' do + let(:filter) { 'osfamily=Debian' } + + it { is_expected.to include(hash_including(osfamily: 'Debian')).and not_include(hash_including(osfamily: 'RedHat')) } + end + end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9d2ee2bd..71429fbb 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -14,6 +14,7 @@ RSpec::Matchers.define_negated_matcher :not_be_nil, :be_nil RSpec::Matchers.define_negated_matcher :not_be_empty, :be_empty +RSpec::Matchers.define_negated_matcher :not_include, :include def project_dir File.dirname File.dirname(File.expand_path(__FILE__))