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.
- checksums.yaml +7 -0
- data/LICENSE +1 -1
- data/README.md +268 -0
- data/Rakefile +140 -64
- data/images/giraffe.jpeg +0 -0
- data/images/map.png +0 -0
- data/lib/map/_lib.rb +85 -0
- data/lib/map/ordering.rb +126 -0
- data/lib/map.rb +151 -121
- data/map.gemspec +21 -12
- data/specs/001-ordered-map-module/checklists/requirements.md +62 -0
- data/specs/001-ordered-map-module/data-model.md +250 -0
- data/specs/001-ordered-map-module/plan.md +139 -0
- data/specs/001-ordered-map-module/quickstart.md +430 -0
- data/specs/001-ordered-map-module/research.md +189 -0
- data/specs/001-ordered-map-module/spec.md +108 -0
- data/specs/001-ordered-map-module/tasks.md +264 -0
- data/test/map_test.rb +21 -57
- metadata +39 -23
- data/a.rb +0 -10
- data/lib/map/integrations/active_record.rb +0 -140
- data/lib/map/params.rb +0 -79
- data/lib/map/struct.rb +0 -52
|
@@ -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
|