Skip to content

Commit 5010109

Browse files
silugCopilot
andauthored
fix: Hash-like Collection methods return Collections (#91)
* Bump version to 0.3.0 Fixes #37 --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent f2d3070 commit 5010109

5 files changed

Lines changed: 110 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
### 0.3.0 / 2026-03-19
2+
* Hash-like Collection methods return Collection objects (#37)
3+
14
### 0.2.2 / 2026-03-02
25
* Ensure that cloned/duped objects get independent collection instances
36

lib/compliance_engine/collection.rb

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ def keys
6767
to_h.keys
6868
end
6969

70+
# Returns the values of the collection
71+
#
72+
# @return [Array] the values of the collection
73+
def values
74+
to_h.values
75+
end
76+
7077
# Return a single value from the collection
7178
#
7279
# @param key [String] the key of the value to return
@@ -78,22 +85,34 @@ def [](key)
7885
# Iterates over the collection
7986
#
8087
# @param block [Proc] the block to execute
88+
# @return [self, Enumerator]
8189
def each(&block)
90+
return to_enum(:each) unless block
91+
8292
to_h.each(&block)
93+
self
8394
end
8495

8596
# Iterates over values in the collection
8697
#
8798
# @param block [Proc] the block to execute
99+
# @return [self, Enumerator]
88100
def each_value(&block)
101+
return to_enum(:each_value) unless block
102+
89103
to_h.each_value(&block)
104+
self
90105
end
91106

92107
# Iterates over keys in the collection
93108
#
94109
# @param block [Proc] the block to execute
110+
# @return [self, Enumerator]
95111
def each_key(&block)
112+
return to_enum(:each_key) unless block
113+
96114
to_h.each_key(&block)
115+
self
97116
end
98117

99118
# Return true if any of the values in the collection match the block
@@ -115,17 +134,35 @@ def all?(&block)
115134
# Select values in the collection
116135
#
117136
# @param block [Proc] the block to execute
118-
# @return [Hash] the filtered hash
137+
# @return [ComplianceEngine::Collection, Enumerator] the filtered collection or an Enumerator when no block is given
119138
def select(&block)
120-
to_h.select(&block)
139+
return to_enum(:select) unless block_given?
140+
141+
result = dup
142+
result.collection = result.to_h.select(&block)
143+
result
121144
end
122145

146+
alias filter select
147+
123148
# Filter out values in the collection
124149
#
125150
# @param block [Proc] the block to execute
126-
# @return [Hash] the filtered hash
151+
# @return [ComplianceEngine::Collection, Enumerator] the filtered collection or an Enumerator when no block is given
127152
def reject(&block)
128-
to_h.reject(&block)
153+
return to_enum(:reject) unless block_given?
154+
155+
result = dup
156+
result.collection = result.to_h.reject(&block)
157+
result
158+
end
159+
160+
# Transform values in the collection
161+
#
162+
# @param block [Proc] the block to execute
163+
# @return [Hash, Enumerator] a hash with transformed values, or an Enumerator when no block is given
164+
def transform_values(&block)
165+
to_h.transform_values(&block)
129166
end
130167

131168
private

lib/compliance_engine/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# frozen_string_literal: true
22

33
module ComplianceEngine
4-
VERSION = '0.2.2'
4+
VERSION = '0.3.0'
55

66
# Handle supported compliance data versions
77
class Version

metadata.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "simp-compliance_engine",
3-
"version": "0.2.2",
3+
"version": "0.3.0",
44
"author": "Sicura",
55
"summary": "Hiera backend for Sicura Compliance Engine data",
66
"license": "Apache-2.0",

spec/classes/compliance_engine/ces_spec.rb

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,70 @@
1212
expect(ces).to be_instance_of(described_class)
1313
end
1414

15+
# ---------------------------------------------------------------------------
16+
# Hash-like methods returning Collections (issue #37)
17+
# ---------------------------------------------------------------------------
18+
describe 'Hash-like methods that return Collections' do
19+
subject(:ces) { described_class.new(ComplianceEngine::Data.new(ComplianceEngine::DataLoader.new(compliance_data))) }
20+
21+
let(:compliance_data) do
22+
{
23+
'version' => '2.0.0',
24+
'ce' => {
25+
'ce_one' => { 'title' => 'CE One' },
26+
'ce_two' => { 'title' => 'CE Two' },
27+
'ce_three' => { 'title' => 'CE Three' },
28+
},
29+
}
30+
end
31+
32+
describe '#select' do
33+
it 'returns a Collection of the same type' do
34+
result = ces.select { |k, _| k == 'ce_one' }
35+
expect(result).to be_instance_of(described_class)
36+
end
37+
38+
it 'contains only the selected keys' do
39+
result = ces.select { |k, _| k == 'ce_one' }
40+
expect(result.keys).to eq(['ce_one'])
41+
end
42+
43+
it 'does not modify the original collection' do
44+
ces.select { |k, _| k == 'ce_one' }
45+
expect(ces.keys).to contain_exactly('ce_one', 'ce_two', 'ce_three')
46+
end
47+
end
48+
49+
describe '#reject' do
50+
it 'returns a Collection of the same type' do
51+
result = ces.reject { |k, _| k == 'ce_one' }
52+
expect(result).to be_instance_of(described_class)
53+
end
54+
55+
it 'excludes the rejected keys' do
56+
result = ces.reject { |k, _| k == 'ce_one' }
57+
expect(result.keys).to contain_exactly('ce_two', 'ce_three')
58+
end
59+
60+
it 'does not modify the original collection' do
61+
ces.reject { |k, _| k == 'ce_one' }
62+
expect(ces.keys).to contain_exactly('ce_one', 'ce_two', 'ce_three')
63+
end
64+
end
65+
66+
describe '#transform_values' do
67+
it 'returns a Hash' do
68+
result = ces.transform_values(&:title)
69+
expect(result).to be_instance_of(Hash)
70+
end
71+
72+
it 'maps each component to its transformed value' do
73+
result = ces.transform_values(&:title)
74+
expect(result).to eq('ce_one' => 'CE One', 'ce_two' => 'CE Two', 'ce_three' => 'CE Three')
75+
end
76+
end
77+
end
78+
1579
# ---------------------------------------------------------------------------
1680
# clone/dup isolation (Collection behavior)
1781
#

0 commit comments

Comments
 (0)