From 974ddef5a0bb4811a9b14d373699f983d3c71d68 Mon Sep 17 00:00:00 2001 From: Steven Pritchard Date: Thu, 19 Mar 2026 16:01:20 +0000 Subject: [PATCH 1/4] fix: Hash-like Collection methods return Collections Fixes #37 --- lib/compliance_engine/collection.rb | 41 ++++++++++++-- spec/classes/compliance_engine/ces_spec.rb | 64 ++++++++++++++++++++++ 2 files changed, 101 insertions(+), 4 deletions(-) diff --git a/lib/compliance_engine/collection.rb b/lib/compliance_engine/collection.rb index 4d46411..11bfccf 100644 --- a/lib/compliance_engine/collection.rb +++ b/lib/compliance_engine/collection.rb @@ -67,6 +67,13 @@ def keys to_h.keys end + # Returns the values of the collection + # + # @return [Array] the values of the collection + def values + to_h.values + end + # Return a single value from the collection # # @param key [String] the key of the value to return @@ -78,22 +85,34 @@ def [](key) # Iterates over the collection # # @param block [Proc] the block to execute + # @return [self, Enumerator] def each(&block) + return to_enum(:each) unless block + to_h.each(&block) + self end # Iterates over values in the collection # # @param block [Proc] the block to execute + # @return [self, Enumerator] def each_value(&block) + return to_enum(:each_value) unless block + to_h.each_value(&block) + self end # Iterates over keys in the collection # # @param block [Proc] the block to execute + # @return [self, Enumerator] def each_key(&block) + return to_enum(:each_key) unless block + to_h.each_key(&block) + self end # Return true if any of the values in the collection match the block @@ -115,17 +134,31 @@ def all?(&block) # Select values in the collection # # @param block [Proc] the block to execute - # @return [Hash] the filtered hash + # @return [ComplianceEngine::Collection] the filtered collection def select(&block) - to_h.select(&block) + result = dup + result.collection = to_h.select(&block) + result end + alias filter select + # Filter out values in the collection # # @param block [Proc] the block to execute - # @return [Hash] the filtered hash + # @return [ComplianceEngine::Collection] the filtered collection def reject(&block) - to_h.reject(&block) + result = dup + result.collection = to_h.reject(&block) + result + end + + # Transform values in the collection + # + # @param block [Proc] the block to execute + # @return [Hash] a hash with transformed values + def transform_values(&block) + to_h.transform_values(&block) end private diff --git a/spec/classes/compliance_engine/ces_spec.rb b/spec/classes/compliance_engine/ces_spec.rb index 0988062..fd4b879 100644 --- a/spec/classes/compliance_engine/ces_spec.rb +++ b/spec/classes/compliance_engine/ces_spec.rb @@ -12,6 +12,70 @@ expect(ces).to be_instance_of(described_class) end + # --------------------------------------------------------------------------- + # Hash-like methods returning Collections (issue #37) + # --------------------------------------------------------------------------- + describe 'Hash-like methods that return Collections' do + subject(:ces) { described_class.new(ComplianceEngine::Data.new(ComplianceEngine::DataLoader.new(compliance_data))) } + + let(:compliance_data) do + { + 'version' => '2.0.0', + 'ce' => { + 'ce_one' => { 'title' => 'CE One' }, + 'ce_two' => { 'title' => 'CE Two' }, + 'ce_three' => { 'title' => 'CE Three' }, + }, + } + end + + describe '#select' do + it 'returns a Collection of the same type' do + result = ces.select { |k, _| k == 'ce_one' } + expect(result).to be_instance_of(described_class) + end + + it 'contains only the selected keys' do + result = ces.select { |k, _| k == 'ce_one' } + expect(result.keys).to eq(['ce_one']) + end + + it 'does not modify the original collection' do + ces.select { |k, _| k == 'ce_one' } + expect(ces.keys).to contain_exactly('ce_one', 'ce_two', 'ce_three') + end + end + + describe '#reject' do + it 'returns a Collection of the same type' do + result = ces.reject { |k, _| k == 'ce_one' } + expect(result).to be_instance_of(described_class) + end + + it 'excludes the rejected keys' do + result = ces.reject { |k, _| k == 'ce_one' } + expect(result.keys).to contain_exactly('ce_two', 'ce_three') + end + + it 'does not modify the original collection' do + ces.reject { |k, _| k == 'ce_one' } + expect(ces.keys).to contain_exactly('ce_one', 'ce_two', 'ce_three') + end + end + + describe '#transform_values' do + it 'returns a Hash' do + result = ces.transform_values(&:title) + expect(result).to be_instance_of(Hash) + end + + it 'maps each component to its transformed value' do + result = ces.transform_values(&:title) + expect(result).to eq('ce_one' => 'CE One', 'ce_two' => 'CE Two', 'ce_three' => 'CE Three') + end + end + end + # --------------------------------------------------------------------------- # clone/dup isolation (Collection behavior) # From 4ea007095e5ced44dae848586cadbec7fd106f55 Mon Sep 17 00:00:00 2001 From: Steven Pritchard Date: Thu, 19 Mar 2026 16:09:17 +0000 Subject: [PATCH 2/4] Bump version to 0.3.0 --- CHANGELOG.md | 3 +++ lib/compliance_engine/version.rb | 2 +- metadata.json | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73eef84..fff1736 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +### 0.3.0 / 2026-03-19 +* Hash-like Collection methods return Collection objects (#37) + ### 0.2.2 / 2026-03-02 * Ensure that cloned/duped objects get independent collection instances diff --git a/lib/compliance_engine/version.rb b/lib/compliance_engine/version.rb index d90256d..119606a 100644 --- a/lib/compliance_engine/version.rb +++ b/lib/compliance_engine/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module ComplianceEngine - VERSION = '0.2.2' + VERSION = '0.3.0' # Handle supported compliance data versions class Version diff --git a/metadata.json b/metadata.json index 406f72e..6c7f6b5 100644 --- a/metadata.json +++ b/metadata.json @@ -1,6 +1,6 @@ { "name": "simp-compliance_engine", - "version": "0.2.2", + "version": "0.3.0", "author": "Sicura", "summary": "Hiera backend for Sicura Compliance Engine data", "license": "Apache-2.0", From a17649b39899a592cbc61f44c4a8adda23465626 Mon Sep 17 00:00:00 2001 From: Steven Pritchard Date: Thu, 19 Mar 2026 11:41:47 -0500 Subject: [PATCH 3/4] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- lib/compliance_engine/collection.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/compliance_engine/collection.rb b/lib/compliance_engine/collection.rb index 11bfccf..ba2b40a 100644 --- a/lib/compliance_engine/collection.rb +++ b/lib/compliance_engine/collection.rb @@ -134,10 +134,12 @@ def all?(&block) # Select values in the collection # # @param block [Proc] the block to execute - # @return [ComplianceEngine::Collection] the filtered collection + # @return [ComplianceEngine::Collection, Enumerator] the filtered collection or an Enumerator when no block is given def select(&block) + return to_enum(:select) unless block_given? + result = dup - result.collection = to_h.select(&block) + result.collection = result.to_h.select(&block) result end @@ -146,10 +148,12 @@ def select(&block) # Filter out values in the collection # # @param block [Proc] the block to execute - # @return [ComplianceEngine::Collection] the filtered collection + # @return [ComplianceEngine::Collection, Enumerator] the filtered collection or an Enumerator when no block is given def reject(&block) + return to_enum(:reject) unless block_given? + result = dup - result.collection = to_h.reject(&block) + result.collection = result.to_h.reject(&block) result end From 3e16c092e6c0cabc6da10d33d34dd6ca96ccf8ad Mon Sep 17 00:00:00 2001 From: Steven Pritchard Date: Thu, 19 Mar 2026 16:54:32 +0000 Subject: [PATCH 4/4] Documentation update based on review feedback --- lib/compliance_engine/collection.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compliance_engine/collection.rb b/lib/compliance_engine/collection.rb index ba2b40a..f4d6250 100644 --- a/lib/compliance_engine/collection.rb +++ b/lib/compliance_engine/collection.rb @@ -160,7 +160,7 @@ def reject(&block) # Transform values in the collection # # @param block [Proc] the block to execute - # @return [Hash] a hash with transformed values + # @return [Hash, Enumerator] a hash with transformed values, or an Enumerator when no block is given def transform_values(&block) to_h.transform_values(&block) end