map 6.6.0 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,62 @@
1
+ # Specification Quality Checklist: Conditional Ordered Map Module
2
+
3
+ **Purpose**: Validate specification completeness and quality before proceeding to planning
4
+ **Created**: 2025-11-10
5
+ **Feature**: [spec.md](../spec.md)
6
+
7
+ ## Content Quality
8
+
9
+ - [x] No implementation details (languages, frameworks, APIs)
10
+ - [x] Focused on user value and business needs
11
+ - [x] Written for non-technical stakeholders
12
+ - [x] All mandatory sections completed
13
+
14
+ ## Requirement Completeness
15
+
16
+ - [x] No [NEEDS CLARIFICATION] markers remain
17
+ - [x] Requirements are testable and unambiguous
18
+ - [x] Success criteria are measurable
19
+ - [x] Success criteria are technology-agnostic (no implementation details)
20
+ - [x] All acceptance scenarios are defined
21
+ - [x] Edge cases are identified
22
+ - [x] Scope is clearly bounded
23
+ - [x] Dependencies and assumptions identified
24
+
25
+ ## Feature Readiness
26
+
27
+ - [x] All functional requirements have clear acceptance criteria
28
+ - [x] User scenarios cover primary flows
29
+ - [x] Feature meets measurable outcomes defined in Success Criteria
30
+ - [x] No implementation details leak into specification
31
+
32
+ ## Validation Notes
33
+
34
+ ### Passed Items
35
+
36
+ **Content Quality**: All items passed
37
+ - Spec focuses on WHAT and WHY, not HOW
38
+ - Written in business/user language (backward compatibility, ordering behavior, testing)
39
+ - No framework-specific or implementation details in requirements
40
+
41
+ **Requirement Completeness**: All items passed
42
+ - No [NEEDS CLARIFICATION] markers present
43
+ - All 9 functional requirements are testable (can verify module extraction, conditional inclusion, version detection, API compatibility, etc.)
44
+ - Success criteria include specific metrics (100% ordering operations correct, no @keys variable present, max 5% test time regression)
45
+ - Success criteria are technology-agnostic (focus on outcomes like "memory footprint reduced" not "implement using X")
46
+ - 3 user stories with clear acceptance scenarios covering legacy support, modern optimization, and testing
47
+ - Edge cases identified (alternative Ruby implementations, Marshal compatibility, inheritance)
48
+ - Scope clearly bounded by backward compatibility and API stability requirements
49
+ - Assumptions document defaults (Ruby 1.9 cutoff, Marshal compatibility tradeoff, KISS testing approach)
50
+ - Dependencies identified (Ruby version environments, test suite adequacy, no API breaking changes)
51
+
52
+ **Feature Readiness**: All items passed
53
+ - Each functional requirement maps to acceptance scenarios in user stories
54
+ - User scenarios cover all three priority flows: P1 (legacy support), P2 (modern optimization), P3 (testing)
55
+ - Success criteria define clear measurable outcomes (test pass rates, memory inspection, performance benchmarks)
56
+ - Spec maintains clean separation - no leakage of module names, version detection mechanisms, or test implementation details into WHAT the system must do
57
+
58
+ ## Conclusion
59
+
60
+ **Status**: ✅ READY FOR PLANNING
61
+
62
+ All checklist items have passed validation. The specification is complete, unambiguous, and ready to proceed to `/speckit.clarify` (if needed) or `/speckit.plan`.
@@ -0,0 +1,250 @@
1
+ # Data Model: Conditional Ordered Map Module
2
+
3
+ **Date**: 2025-11-10
4
+ **Feature**: Conditional Ordered Map Module
5
+ **Phase**: 1 - Design
6
+
7
+ ## Overview
8
+
9
+ This document describes the internal state model and behavior of the Map class under the conditional ordering module architecture.
10
+
11
+ ## Entity: Map Class
12
+
13
+ ### Description
14
+
15
+ Map is a string/symbol-indifferent ordered hash that extends Ruby's Hash class. Its internal state varies based on Ruby version to optimize for native Hash ordering capabilities.
16
+
17
+ ### State Model
18
+
19
+ Map has two distinct internal state configurations depending on the Ruby runtime:
20
+
21
+ #### Configuration 1: Ruby < 1.9 (Legacy Ordering)
22
+
23
+ **Includes**: `Map::Ordering` module
24
+
25
+ **Instance Variables**:
26
+ - `@keys` (Array): Maintains insertion order of keys as strings
27
+
28
+ **Behavior**:
29
+ - Keys are tracked in `@keys` array upon insertion
30
+ - Iteration methods (`each`, `each_key`, `each_value`) use `@keys` for deterministic ordering
31
+ - `first` and `last` methods rely on `@keys.first` and `@keys.last`
32
+ - `delete` removes key from both Hash storage and `@keys` array
33
+ - `values` returns array built by iterating `@keys`
34
+
35
+ **State Transitions**:
36
+ ```
37
+ [Empty Map]
38
+ |
39
+ | Map[:a, 1] -> @keys = ['a'], Hash = {'a' => 1}
40
+ |
41
+ [Map with one entry]
42
+ |
43
+ | map[:b] = 2 -> @keys = ['a', 'b'], Hash = {'a' => 1, 'b' => 2}
44
+ |
45
+ [Map with two entries]
46
+ |
47
+ | map.delete(:a) -> @keys = ['b'], Hash = {'b' => 2}
48
+ |
49
+ [Map with one entry]
50
+ ```
51
+
52
+ #### Configuration 2: Ruby >= 1.9 (Native Ordering)
53
+
54
+ **Includes**: No ordering module
55
+
56
+ **Instance Variables**:
57
+ - None (Map-specific) - relies on Hash superclass storage
58
+
59
+ **Behavior**:
60
+ - Hash superclass maintains insertion order natively
61
+ - Iteration methods delegate to Hash superclass implementations
62
+ - `first` and `last` use Hash#first and Hash#last (available in Ruby 1.9+)
63
+ - `delete` uses Hash#delete (automatically maintains ordering)
64
+ - `values` uses Hash#values (preserves ordering)
65
+
66
+ **State Transitions**:
67
+ ```
68
+ [Empty Map]
69
+ |
70
+ | Map[:a, 1] -> Hash = {'a' => 1} (ordered)
71
+ |
72
+ [Map with one entry]
73
+ |
74
+ | map[:b] = 2 -> Hash = {'a' => 1, 'b' => 2} (ordered)
75
+ |
76
+ [Map with two entries]
77
+ |
78
+ | map.delete(:a) -> Hash = {'b' => 2} (ordered)
79
+ |
80
+ [Map with one entry]
81
+ ```
82
+
83
+ ## Entity: Map::Ordering Module
84
+
85
+ ### Description
86
+
87
+ Module containing all ordering-related methods that manually track insertion order using an `@keys` array. This module is only included in Map when Ruby < 1.9 or when forced via `ENV['MAP_FORCE_ORDERING']`.
88
+
89
+ ### Methods
90
+
91
+ #### Class Methods
92
+
93
+ - `allocate`: Overrides Map.allocate to initialize `@keys = []` on new instances
94
+
95
+ #### Instance Methods
96
+
97
+ - `keys`: Returns `@keys` array (overrides Hash#keys)
98
+ - `[]=` (store): Tracks new keys in `@keys` array, delegates storage to Hash
99
+ - `values`: Builds array by iterating `@keys`, fetching each value
100
+ - `first`: Returns `[keys.first, self[keys.first]]`
101
+ - `last`: Returns `[keys.last, self[keys.last]]`
102
+ - `each` / `each_pair`: Iterates `@keys`, yielding `[key, self[key]]` pairs
103
+ - `each_key`: Iterates `@keys`, yielding each key
104
+ - `each_value`: Iterates `@keys`, yielding `self[key]` for each
105
+ - `each_with_index`: Iterates `@keys.each_with_index`, yielding `[[key, self[key]], index]`
106
+ - `delete`: Removes key from `@keys` array, then calls `super` to delete from Hash
107
+
108
+ ### Constraints
109
+
110
+ - `@keys` must contain only string keys (symbol keys are converted to strings by Map#convert_key)
111
+ - Order of `@keys` must match insertion order
112
+ - No duplicate keys in `@keys` array
113
+ - `@keys` and Hash storage must stay synchronized (every key in one must be in the other)
114
+
115
+ ## Version Detection Logic
116
+
117
+ ### Conditional Inclusion
118
+
119
+ ```ruby
120
+ # Pseudo-code for lib/map.rb
121
+ class Map < Hash
122
+ # ... existing Map code ...
123
+
124
+ # Conditional module inclusion at class definition time
125
+ if RUBY_VERSION < '1.9' || ENV['MAP_FORCE_ORDERING']
126
+ require_relative 'map/ordering'
127
+ include Map::Ordering
128
+ end
129
+ end
130
+ ```
131
+
132
+ ### Detection Rules
133
+
134
+ | Condition | Module Included? | Rationale |
135
+ |-----------|-----------------|-----------|
136
+ | RUBY_VERSION < '1.9' | Yes | Hash is unordered, need manual tracking |
137
+ | RUBY_VERSION >= '1.9' | No | Hash is ordered natively |
138
+ | ENV['MAP_FORCE_ORDERING'] = '1' | Yes | Testing mode - force ordering module |
139
+ | ENV['MAP_FORCE_ORDERING'] unset | Per version | Normal operation |
140
+
141
+ ## API Compatibility Matrix
142
+
143
+ All public API methods maintain identical behavior regardless of configuration:
144
+
145
+ | Method | Ruby < 1.9 (with module) | Ruby >= 1.9 (without module) | Compatible? |
146
+ |--------|-------------------------|------------------------------|-------------|
147
+ | `keys` | Returns `@keys` | Returns Hash#keys (ordered) | ✅ Yes |
148
+ | `values` | Iterates `@keys` to build array | Returns Hash#values (ordered) | ✅ Yes |
149
+ | `each` | Iterates `@keys` | Uses Hash#each (ordered) | ✅ Yes |
150
+ | `first` | `[keys.first, self[keys.first]]` | Hash#first | ✅ Yes |
151
+ | `last` | `[keys.last, self[keys.last]]` | Hash#last | ✅ Yes |
152
+ | `delete` | Removes from `@keys` + Hash | Hash#delete (maintains order) | ✅ Yes |
153
+ | `[]=` | Tracks in `@keys` + Hash | Hash#[]= (maintains order) | ✅ Yes |
154
+
155
+ ## Invariants
156
+
157
+ ### Cross-Configuration Invariants
158
+
159
+ These properties must hold true in BOTH configurations:
160
+
161
+ 1. **Insertion order preservation**: `map.keys` always returns keys in insertion order
162
+ 2. **String/symbol indifference**: `map[:a]` and `map['a']` access the same value
163
+ 3. **Deterministic iteration**: Multiple calls to `each` yield keys in same order
164
+ 4. **First/last consistency**: `map.first[0] == map.keys.first`
165
+ 5. **No nil default**: Map doesn't work with non-nil default values (existing constraint)
166
+
167
+ ### Configuration-Specific Invariants
168
+
169
+ #### Ruby < 1.9 with Ordering Module:
170
+
171
+ 1. **Synchronization**: `@keys.size == Hash.size` always
172
+ 2. **Key membership**: Every key in `@keys` exists in Hash, and vice versa
173
+ 3. **No duplicates**: `@keys.uniq.size == @keys.size`
174
+
175
+ #### Ruby >= 1.9 without Ordering Module:
176
+
177
+ 1. **No @keys variable**: `instance_variables` should not include `@keys`
178
+ 2. **Hash delegation**: Ordering methods delegate to Hash superclass
179
+
180
+ ## Memory Footprint Comparison
181
+
182
+ ### Ruby < 1.9 (with Ordering Module)
183
+
184
+ Per Map instance:
185
+ - Hash storage: ~40 bytes + (n * 24 bytes per entry)
186
+ - `@keys` array: ~40 bytes + (n * 8 bytes per string reference)
187
+ - **Total overhead**: ~80 bytes + (n * 32 bytes)
188
+
189
+ ### Ruby >= 1.9 (without Ordering Module)
190
+
191
+ Per Map instance:
192
+ - Hash storage: ~40 bytes + (n * 24 bytes per entry)
193
+ - **Total overhead**: ~40 bytes + (n * 24 bytes)
194
+
195
+ ### Memory Savings
196
+
197
+ For a Map with 100 entries:
198
+ - Ruby < 1.9: ~3,280 bytes
199
+ - Ruby >= 1.9: ~2,440 bytes
200
+ - **Savings**: ~840 bytes (25% reduction)
201
+
202
+ For a Map with 1,000 entries:
203
+ - Ruby < 1.9: ~32,080 bytes
204
+ - Ruby >= 1.9: ~24,040 bytes
205
+ - **Savings**: ~8,040 bytes (25% reduction)
206
+
207
+ ## Subclassing Considerations
208
+
209
+ ### Map::Options
210
+
211
+ The existing `Map::Options` subclass inherits ordering behavior from Map:
212
+
213
+ - If Map includes `Map::Ordering`, Map::Options inherits those methods
214
+ - If Map doesn't include `Map::Ordering`, Map::Options uses Hash ordering
215
+ - No changes required to Map::Options implementation
216
+
217
+ ### Future Subclasses
218
+
219
+ Any subclass of Map will:
220
+ - Automatically get appropriate ordering behavior based on Ruby version
221
+ - Inherit `@keys` initialization if Ordering module is included
222
+ - Be able to override ordering methods if needed (standard Ruby inheritance)
223
+
224
+ ## Testing Verification Points
225
+
226
+ To verify correct data model implementation:
227
+
228
+ 1. **Instance variable presence**: Check `instance_variables.include?(:@keys)` matches expected configuration
229
+ 2. **Ordering correctness**: Verify `keys`, `values`, `each` return deterministic order
230
+ 3. **Synchronization**: On Ruby < 1.9, verify `@keys.size == size` after all operations
231
+ 4. **Memory**: On Ruby >= 1.9, verify no `@keys` array allocated
232
+ 5. **Marshal**: Verify Maps serialize/deserialize within same Ruby version
233
+ 6. **Subclass inheritance**: Verify Map::Options exhibits same ordering behavior as Map
234
+
235
+ ## Edge Cases
236
+
237
+ 1. **Empty Map**: Should work identically in both configurations (no keys, no @keys)
238
+ 2. **Marshal across versions**: May fail or produce inconsistent state (documented limitation)
239
+ 3. **Forking**: `@keys` array duplicated in child process on Ruby < 1.9 (standard Ruby behavior)
240
+ 4. **Thread safety**: Same thread-safety guarantees as Hash (no additional synchronization)
241
+
242
+ ## Migration Path
243
+
244
+ For users upgrading from older Ruby versions:
245
+
246
+ 1. **No code changes required**: API remains identical
247
+ 2. **Memory reduction automatic**: Ruby 1.9+ users get automatic optimization
248
+ 3. **Marshal incompatibility**: If loading Marshal dumps from Ruby 1.8.x on Ruby 1.9+:
249
+ - Workaround: `map.to_hash.to_map` to normalize
250
+ - Alternative: Use JSON/YAML for cross-version serialization
@@ -0,0 +1,139 @@
1
+ # Implementation Plan: Conditional Ordered Map Module
2
+
3
+ **Branch**: `001-ordered-map-module` | **Date**: 2025-11-10 | **Spec**: [spec.md](spec.md)
4
+ **Input**: Feature specification from `/specs/001-ordered-map-module/spec.md`
5
+
6
+ **Note**: This template is filled in by the `/speckit.plan` command. See `.specify/templates/commands/plan.md` for the execution workflow.
7
+
8
+ ## Summary
9
+
10
+ Extract ordering-related code (using `@keys` array) into a separate module and conditionally include it only in Ruby versions where Hash is not ordered by default (Ruby < 1.9). This maintains backward compatibility for legacy Ruby versions while optimizing memory and performance for modern Ruby versions that have native Hash ordering. The implementation must preserve API compatibility and provide a simple testing mechanism to validate both code paths.
11
+
12
+ ## Technical Context
13
+
14
+ **Language/Version**: Ruby 3.0+ (current minimum per gemspec), must support Ruby 1.8.x through 3.x behavior
15
+ **Primary Dependencies**: None (map.rb is dependency-free per project philosophy)
16
+ **Storage**: N/A (in-memory data structure library)
17
+ **Testing**: Custom testing framework (test/lib/testing.rb) using `Testing` DSL
18
+ **Target Platform**: Cross-Ruby (MRI 1.8.x through 3.x, JRuby, Rubinius, TruffleRuby)
19
+ **Project Type**: Single Ruby gem library
20
+ **Performance Goals**: Zero performance regression on Ruby 1.9+, maintain current performance on Ruby 1.8.x
21
+ **Constraints**: No external dependencies, maintain existing test suite without modification, 100% backward API compatibility
22
+ **Scale/Scope**: Core library with ~800 lines of code, ~200 test cases, used in production for 14+ years
23
+
24
+ ## Constitution Check
25
+
26
+ *GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
27
+
28
+ **Status**: ✅ PASS - Constitution file is template-only, no specific project principles defined yet.
29
+
30
+ This refactoring:
31
+ - Maintains zero external dependencies (core map.rb principle)
32
+ - Preserves 100% API compatibility (no breaking changes)
33
+ - Uses existing test framework without modification
34
+ - Follows KISS principle for testing mechanism
35
+ - Improves code organization through modularization
36
+
37
+ ## Project Structure
38
+
39
+ ### Documentation (this feature)
40
+
41
+ ```text
42
+ specs/[###-feature]/
43
+ ├── plan.md # This file (/speckit.plan command output)
44
+ ├── research.md # Phase 0 output (/speckit.plan command)
45
+ ├── data-model.md # Phase 1 output (/speckit.plan command)
46
+ ├── quickstart.md # Phase 1 output (/speckit.plan command)
47
+ ├── contracts/ # Phase 1 output (/speckit.plan command)
48
+ └── tasks.md # Phase 2 output (/speckit.tasks command - NOT created by /speckit.plan)
49
+ ```
50
+
51
+ ### Source Code (repository root)
52
+
53
+ ```text
54
+ lib/
55
+ ├── map.rb # Main Map class (modified: conditional module inclusion)
56
+ ├── map/
57
+ │ ├── _lib.rb # Utility methods
58
+ │ ├── options.rb # Map::Options subclass
59
+ │ └── ordering.rb # NEW: OrderingModule for legacy Ruby support
60
+
61
+ test/
62
+ ├── map_test.rb # Existing test suite (no modifications)
63
+ ├── lib/
64
+ │ └── testing.rb # Custom testing framework
65
+ └── leak.rb # Memory leak tests
66
+ ```
67
+
68
+ **Structure Decision**: Single gem library structure. The feature adds one new file (`lib/map/ordering.rb`) containing the extracted ordering module, and modifies the main `lib/map.rb` to conditionally include the module based on Ruby version detection. All existing files remain backward compatible.
69
+
70
+ ## Complexity Tracking
71
+
72
+ > **Fill ONLY if Constitution Check has violations that must be justified**
73
+
74
+ N/A - No constitution violations. This is a simplification refactoring that reduces code complexity by conditionally including only necessary functionality.
75
+
76
+ ---
77
+
78
+ ## Phase 0: Research Complete ✅
79
+
80
+ **Output**: [research.md](research.md)
81
+
82
+ **Key Decisions**:
83
+ - Ruby 1.9.0 version cutoff for conditional module inclusion
84
+ - Use RUBY_VERSION constant for detection
85
+ - Extract 11 ordering-dependent methods to Map::Ordering module
86
+ - KISS testing via `MAP_FORCE_ORDERING=1` environment variable
87
+ - Accept Marshal cross-version incompatibility as documented limitation
88
+
89
+ **Status**: All technical unknowns resolved, ready for Phase 1 design.
90
+
91
+ ---
92
+
93
+ ## Phase 1: Design & Contracts Complete ✅
94
+
95
+ ### Artifacts Generated
96
+
97
+ 1. **[data-model.md](data-model.md)**: Internal state model documentation
98
+ - Two configurations: with/without `@keys` array
99
+ - State transitions and invariants
100
+ - Memory footprint analysis (25% savings on Ruby 1.9+)
101
+ - API compatibility matrix
102
+
103
+ 2. **contracts/**: N/A (internal refactoring, no external APIs)
104
+
105
+ 3. **[quickstart.md](quickstart.md)**: Developer guide
106
+ - Architecture diagrams
107
+ - Testing both code paths
108
+ - Code organization guidelines
109
+ - Troubleshooting guide
110
+
111
+ ### Constitution Check (Post-Phase 1) ✅
112
+
113
+ **Status**: ✅ PASS - All design decisions align with project principles
114
+
115
+ **Design Review**:
116
+ - ✅ Zero external dependencies maintained
117
+ - ✅ Backward compatibility preserved (100% API compatible)
118
+ - ✅ Testing approach is KISS (single environment variable)
119
+ - ✅ Code organization improved (separation of concerns via module)
120
+ - ✅ Memory optimization achieved without breaking changes
121
+ - ✅ Existing test suite requires no modifications
122
+
123
+ **No violations identified** - The design enhances the codebase while maintaining all core principles.
124
+
125
+ ---
126
+
127
+ ## Next Steps
128
+
129
+ The planning phase is complete. To proceed with implementation:
130
+
131
+ 1. Run `/speckit.tasks` to generate the implementation task list (tasks.md)
132
+ 2. Review and approve the generated tasks
133
+ 3. Execute tasks sequentially to implement the feature
134
+
135
+ **Implementation will include**:
136
+ - Creating `lib/map/ordering.rb` with extracted methods
137
+ - Modifying `lib/map.rb` to conditionally include the module
138
+ - Adding tests to verify both code paths
139
+ - Updating documentation and CHANGELOG