Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down
45 changes: 41 additions & 4 deletions lib/compliance_engine/collection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -115,17 +134,35 @@ def all?(&block)
# Select values in the collection
#
# @param block [Proc] the block to execute
# @return [Hash] the filtered hash
# @return [ComplianceEngine::Collection, Enumerator] the filtered collection or an Enumerator when no block is given
def select(&block)
to_h.select(&block)
return to_enum(:select) unless block_given?

result = dup
result.collection = result.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, Enumerator] the filtered collection or an Enumerator when no block is given
def reject(&block)
to_h.reject(&block)
return to_enum(:reject) unless block_given?

result = dup
result.collection = result.to_h.reject(&block)
result
end

# Transform values in the collection
#
# @param block [Proc] the block to execute
# @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

private
Expand Down
2 changes: 1 addition & 1 deletion lib/compliance_engine/version.rb
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion metadata.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
64 changes: 64 additions & 0 deletions spec/classes/compliance_engine/ces_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
#
Expand Down
Loading