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,108 @@
1
+ # Feature Specification: Conditional Ordered Map Module
2
+
3
+ **Feature Branch**: `001-ordered-map-module`
4
+ **Created**: 2025-11-10
5
+ **Status**: Draft
6
+ **Input**: User description: "we want to support all features of map in newer rubies, which have ordered maps. we wish to extact the 'ordered' parts of map as a module, and only include it in Map for older/non-hash-ordered, rubies. we want a KISS way to test this."
7
+
8
+ ## User Scenarios & Testing *(mandatory)*
9
+
10
+ ### User Story 1 - Legacy Ruby Support (Priority: P1)
11
+
12
+ When developers use Map in Ruby 1.8.x (or other non-ordered hash implementations), they must receive the same ordered hash behavior that Map has always provided, with keys tracked in insertion order via the `@keys` array mechanism.
13
+
14
+ **Why this priority**: This is the core value proposition - maintaining backward compatibility for users on older Ruby versions without breaking existing functionality. Without this, Map would lose its "works in all rubies" promise.
15
+
16
+ **Independent Test**: Can be fully tested by running the existing test suite on Ruby 1.8.x or similar non-ordered hash implementations and verifying that all ordering-related tests pass.
17
+
18
+ **Acceptance Scenarios**:
19
+
20
+ 1. **Given** Map running on Ruby 1.8.x, **When** a user creates a map with `Map[:a, 1, :b, 2, :c, 3]`, **Then** calling `keys` returns `['a', 'b', 'c']` in insertion order
21
+ 2. **Given** Map running on Ruby 1.8.x, **When** a user iterates with `each`, **Then** key-value pairs are yielded in insertion order
22
+ 3. **Given** Map running on Ruby 1.8.x, **When** a user calls `first` and `last`, **Then** they return the first and last inserted key-value pairs respectively
23
+
24
+ ---
25
+
26
+ ### User Story 2 - Modern Ruby Optimization (Priority: P2)
27
+
28
+ When developers use Map in Ruby 1.9+ (where Hash is ordered by default), they must benefit from native Hash ordering performance without the overhead of maintaining a redundant `@keys` array.
29
+
30
+ **Why this priority**: This improves performance and reduces memory overhead for the majority of current users, but doesn't break functionality since Ruby 1.9+ hashes are already ordered. This is secondary to maintaining backward compatibility.
31
+
32
+ **Independent Test**: Can be fully tested by running the test suite on Ruby 1.9+ and verifying that: (1) all tests pass, (2) no `@keys` instance variable is present in Map instances, and (3) ordering behavior matches expected results.
33
+
34
+ **Acceptance Scenarios**:
35
+
36
+ 1. **Given** Map running on Ruby 1.9+, **When** a user creates a map with `Map[:a, 1, :b, 2, :c, 3]`, **Then** calling `keys` returns `['a', 'b', 'c']` in insertion order without using `@keys` array
37
+ 2. **Given** Map running on Ruby 1.9+, **When** a user inspects a Map instance, **Then** no `@keys` instance variable is present
38
+ 3. **Given** Map running on Ruby 1.9+, **When** a user calls ordering-dependent methods (`first`, `last`, `values`, `each`), **Then** they work correctly using native Hash ordering
39
+
40
+ ---
41
+
42
+ ### User Story 3 - Simple Cross-Version Testing (Priority: P3)
43
+
44
+ When developers or CI systems need to verify Map behavior across Ruby versions, they must have a simple, KISS (Keep It Simple, Stupid) way to run tests that validate both code paths (with and without the ordered module).
45
+
46
+ **Why this priority**: Testing is important but can be implemented after the core functionality works. It's a development/maintenance concern rather than end-user functionality.
47
+
48
+ **Independent Test**: Can be fully tested by running a test command that simulates both Ruby version scenarios and verifying that it produces clear pass/fail results for both paths.
49
+
50
+ **Acceptance Scenarios**:
51
+
52
+ 1. **Given** a test runner configuration, **When** tests are executed, **Then** both "ordered module included" and "ordered module excluded" scenarios are tested
53
+ 2. **Given** test results, **When** reviewing output, **Then** it's clear which tests ran under which configuration (ordered module on/off)
54
+ 3. **Given** CI environment, **When** tests run, **Then** failures in either path are clearly reported with context about which Ruby version scenario failed
55
+
56
+ ---
57
+
58
+ ### Edge Cases
59
+
60
+ - What happens when the Ruby version detection is ambiguous (e.g., alternative Ruby implementations like JRuby, Rubinius)?
61
+ - How does the system handle Ruby versions with the ordering backport?
62
+ - What happens during Marshal load/dump of Maps created on different Ruby versions?
63
+ - How does the module inclusion affect class inheritance and subclassing of Map?
64
+
65
+ ## Requirements *(mandatory)*
66
+
67
+ ### Functional Requirements
68
+
69
+ - **FR-001**: System MUST extract all ordering-related code (using `@keys` array) into a separate module
70
+ - **FR-002**: System MUST conditionally include the ordering module only when Ruby Hash is not ordered by default (Ruby < 1.9)
71
+ - **FR-003**: System MUST detect Ruby version at load time to determine whether Hash is ordered
72
+ - **FR-004**: System MUST maintain identical external API behavior regardless of whether ordering module is included
73
+ - **FR-005**: System MUST preserve Marshal compatibility for existing Map instances
74
+ - **FR-006**: System MUST ensure all existing tests pass on both old and new Ruby versions without modification
75
+ - **FR-007**: System MUST provide ordering module methods that override default Hash methods when included (e.g., `keys`, `values`, `each`, `first`, `last`, `delete`, `[]=`)
76
+ - **FR-008**: Test framework MUST provide simple mechanism to test both code paths (with/without ordering module)
77
+ - **FR-009**: System MUST handle `@keys` array initialization in `allocate` method conditionally based on Ruby version
78
+
79
+ ### Key Entities *(include if feature involves data)*
80
+
81
+ - **OrderingModule**: A module containing all `@keys` array-dependent methods that maintain insertion order manually
82
+ - **Map Class**: The main class that conditionally includes OrderingModule based on Ruby version detection
83
+ - **Ruby Version Detector**: Logic that determines if native Hash ordering is available
84
+
85
+ ## Success Criteria *(mandatory)*
86
+
87
+ ### Measurable Outcomes
88
+
89
+ - **SC-001**: All existing Map tests pass without modification on both Ruby 1.8.x and Ruby 1.9+ versions
90
+ - **SC-002**: Map instances on Ruby 1.9+ do not contain `@keys` instance variable (verified via instance_variables inspection)
91
+ - **SC-003**: Map maintains insertion order correctly in both Ruby version scenarios (100% of ordering-dependent operations return correct results)
92
+ - **SC-004**: Developers can run a single test command that validates both code paths with clear pass/fail output
93
+ - **SC-005**: Memory footprint per Map instance is reduced on Ruby 1.9+ due to absence of redundant `@keys` array
94
+ - **SC-006**: Test execution time remains similar or improves (no more than 5% regression) across both Ruby versions
95
+
96
+ ### Assumptions
97
+
98
+ - Ruby 1.9.0 is the cutoff version where Hash became ordered by default
99
+ - The existing test suite adequately covers all ordering-dependent behavior
100
+ - Marshal serialization format compatibility is acceptable to break across Ruby versions (Map objects serialized on old Ruby may not deserialize correctly on new Ruby if format changes)
101
+ - Alternative Ruby implementations (JRuby, Rubinius, TruffleRuby) follow the same ordering behavior as MRI for their respective version numbers
102
+ - The KISS testing approach means a simple flag or environment variable to force inclusion/exclusion of the ordering module, rather than complex test matrix infrastructure
103
+
104
+ ### Dependencies
105
+
106
+ - Access to multiple Ruby version environments for testing (Ruby 1.8.x and Ruby 1.9+)
107
+ - Existing test suite must be comprehensive enough to validate ordering behavior
108
+ - No breaking changes to public API (all existing Map users' code must continue to work)
@@ -0,0 +1,264 @@
1
+ # Tasks: Conditional Ordered Map Module
2
+
3
+ **Input**: Design documents from `/specs/001-ordered-map-module/`
4
+ **Prerequisites**: plan.md, spec.md, research.md, data-model.md, quickstart.md
5
+
6
+ **Tests**: No explicit TDD requirement in spec - using existing test suite for validation
7
+
8
+ **Organization**: Tasks are grouped by user story to enable independent implementation and testing of each story.
9
+
10
+ ## Format: `[ID] [P?] [Story] Description`
11
+
12
+ - **[P]**: Can run in parallel (different files, no dependencies)
13
+ - **[Story]**: Which user story this task belongs to (e.g., US1, US2, US3)
14
+ - Include exact file paths in descriptions
15
+
16
+ ## Path Conventions
17
+
18
+ Ruby gem structure: `lib/` for source, `test/` for tests at repository root.
19
+
20
+ ---
21
+
22
+ ## Phase 1: Setup (Shared Infrastructure)
23
+
24
+ **Purpose**: Prepare development environment and understand current codebase
25
+
26
+ - [x] T001 Read and understand existing Map implementation in lib/map.rb
27
+ - [x] T002 Identify all 11 ordering-dependent methods that use `@keys` array in lib/map.rb
28
+ - [x] T003 [P] Document current `@keys` array usage patterns and synchronization points
29
+ - [x] T004 [P] Run existing test suite to establish baseline (rake test)
30
+
31
+ **Checkpoint**: Understanding of current implementation complete
32
+
33
+ ---
34
+
35
+ ## Phase 2: Foundational (Blocking Prerequisites)
36
+
37
+ **Purpose**: Core module structure that MUST be complete before ANY user story can be implemented
38
+
39
+ **⚠️ CRITICAL**: No user story work can begin until this phase is complete
40
+
41
+ - [x] T005 Create new file lib/map/ordering.rb with module structure skeleton
42
+ - [x] T006 Define Map::Ordering module with proper namespace in lib/map/ordering.rb
43
+ - [x] T007 Implement module self.included hook for class method injection in lib/map/ordering.rb
44
+ - [x] T008 Add conditional require and include logic in lib/map.rb (RUBY_VERSION < '1.9' || ENV['MAP_FORCE_ORDERING'])
45
+
46
+ **Checkpoint**: Foundation ready - user story implementation can now begin in parallel
47
+
48
+ ---
49
+
50
+ ## Phase 3: User Story 1 - Legacy Ruby Support (Priority: P1) 🎯 MVP
51
+
52
+ **Goal**: Extract all ordering-related code to Map::Ordering module so Ruby < 1.9 maintains insertion order via `@keys` array
53
+
54
+ **Independent Test**: Run tests with `MAP_FORCE_ORDERING=1` to simulate Ruby < 1.9 behavior, verify all ordering tests pass and `@keys` variable exists
55
+
56
+ ### Implementation for User Story 1
57
+
58
+ - [x] T009 [P] [US1] Move Map.allocate class method to Map::Ordering module (initializes `@keys = []`) in lib/map/ordering.rb
59
+ - [x] T010 [P] [US1] Move keys instance method to Map::Ordering module (returns `@keys`) in lib/map/ordering.rb
60
+ - [x] T011 [P] [US1] Move []= (store) method to Map::Ordering module (tracks keys in `@keys` array) in lib/map/ordering.rb
61
+ - [x] T012 [P] [US1] Move values method to Map::Ordering module (iterates `@keys` to build array) in lib/map/ordering.rb
62
+ - [x] T013 [P] [US1] Move first method to Map::Ordering module (uses `@keys.first`) in lib/map/ordering.rb
63
+ - [x] T014 [P] [US1] Move last method to Map::Ordering module (uses `@keys.last`) in lib/map/ordering.rb
64
+ - [x] T015 [P] [US1] Move each/each_pair method to Map::Ordering module (iterates `@keys`) in lib/map/ordering.rb
65
+ - [x] T016 [P] [US1] Move each_key method to Map::Ordering module (iterates `@keys`) in lib/map/ordering.rb
66
+ - [x] T017 [P] [US1] Move each_value method to Map::Ordering module (iterates `@keys` to access values) in lib/map/ordering.rb
67
+ - [x] T018 [P] [US1] Move each_with_index method to Map::Ordering module (uses `@keys.each_with_index`) in lib/map/ordering.rb
68
+ - [x] T019 [P] [US1] Move delete method to Map::Ordering module (removes from `@keys` array) in lib/map/ordering.rb
69
+ - [x] T020 [US1] Remove or comment out the 11 extracted methods from lib/map.rb (module will provide them when included)
70
+ - [x] T021 [US1] Verify Map::Ordering module is complete and syntactically correct (ruby -c lib/map/ordering.rb)
71
+ - [x] T022 [US1] Test with MAP_FORCE_ORDERING=1 rake test - verify all tests pass
72
+ - [x] T023 [US1] Verify @keys instance variable exists when module included (add inspection test)
73
+
74
+ **Checkpoint**: At this point, User Story 1 should be fully functional - Ruby < 1.9 behavior simulated via MAP_FORCE_ORDERING=1
75
+
76
+ ---
77
+
78
+ ## Phase 4: User Story 2 - Modern Ruby Optimization (Priority: P2)
79
+
80
+ **Goal**: Ensure Ruby 1.9+ uses native Hash ordering without `@keys` array overhead, achieving 25% memory savings
81
+
82
+ **Independent Test**: Run tests normally (without MAP_FORCE_ORDERING) on Ruby 1.9+, verify all tests pass, no `@keys` variable exists, and ordering works correctly
83
+
84
+ ### Implementation for User Story 2
85
+
86
+ - [x] T024 [US2] Verify conditional inclusion logic in lib/map.rb correctly excludes module on Ruby >= 1.9
87
+ - [x] T025 [US2] Test normal execution (rake test) on current Ruby version (3.x) - verify all tests pass
88
+ - [x] T026 [US2] Verify Map instances have no @keys instance variable (map.instance_variables inspection)
89
+ - [x] T027 [US2] Verify ordering methods delegate to Hash superclass correctly (keys, values, each, first, last)
90
+ - [x] T028 [US2] Add test case to verify memory optimization - Map instance should not have @keys in lib/map.rb comment or test
91
+ - [x] T029 [US2] Verify Map::Options subclass inherits correct behavior (no @keys on Ruby 1.9+)
92
+ - [x] T030 [US2] Performance verification - ensure no regression in iteration speed (optional benchmark)
93
+
94
+ **Checkpoint**: At this point, User Stories 1 AND 2 should both work - Ruby 1.9+ optimized, legacy simulated via env var
95
+
96
+ ---
97
+
98
+ ## Phase 5: User Story 3 - Simple Cross-Version Testing (Priority: P3)
99
+
100
+ **Goal**: Provide KISS testing mechanism via MAP_FORCE_ORDERING=1 environment variable to validate both code paths
101
+
102
+ **Independent Test**: Run test suite both ways (with and without MAP_FORCE_ORDERING), verify clear output shows which path is active
103
+
104
+ ### Implementation for User Story 3
105
+
106
+ - [ ] T031 [P] [US3] Add test helper to detect and report which mode is active in test/map_test.rb or test/lib/testing.rb
107
+ - [ ] T032 [P] [US3] Create verification test that checks @keys presence matches expected mode in test/map_test.rb
108
+ - [ ] T033 [US3] Document testing approach in README.md or TESTING.md file
109
+ - [ ] T034 [US3] Add CI configuration example for testing both paths in .github/workflows or equivalent
110
+ - [ ] T035 [US3] Create quick verification script (test/verify_ordering.rb) that demonstrates both modes per quickstart.md
111
+ - [ ] T036 [US3] Update Rakefile to support `rake test:with_ordering` and `rake test:without_ordering` tasks
112
+ - [ ] T037 [US3] Verify test output clearly indicates which configuration ran (module included/excluded)
113
+ - [ ] T038 [US3] Run full test suite in both modes and verify identical pass/fail results
114
+
115
+ **Checkpoint**: All user stories should now be independently functional - both code paths validated
116
+
117
+ ---
118
+
119
+ ## Phase 6: Polish & Cross-Cutting Concerns
120
+
121
+ **Purpose**: Improvements that affect multiple user stories and finalize the feature
122
+
123
+ - [ ] T039 [P] Update CHANGELOG with feature description and version notes
124
+ - [ ] T040 [P] Update README.md with explanation of conditional ordering module (if needed)
125
+ - [ ] T041 [P] Add code comments explaining module inclusion logic in lib/map.rb
126
+ - [ ] T042 [P] Add module documentation comments in lib/map/ordering.rb
127
+ - [ ] T043 [P] Document Marshal compatibility limitation in CHANGELOG or README
128
+ - [ ] T044 [US3] Run quickstart.md validation - verify all examples work
129
+ - [ ] T045 Verify Map::Options subclass works correctly in both modes
130
+ - [ ] T046 Check for any Ruby 1.8 syntax issues in Map::Ordering module (if targeting old Ruby)
131
+ - [ ] T047 Final regression test - run existing test suite 3 times (normal, forced ordering, different Ruby if available)
132
+ - [ ] T048 Code review checklist - verify all 11 methods extracted, no duplicates, proper delegation
133
+
134
+ ---
135
+
136
+ ## Dependencies & Execution Order
137
+
138
+ ### Phase Dependencies
139
+
140
+ - **Setup (Phase 1)**: No dependencies - can start immediately
141
+ - **Foundational (Phase 2)**: Depends on Setup completion - BLOCKS all user stories
142
+ - **User Stories (Phase 3-5)**: All depend on Foundational phase completion
143
+ - User Story 1 (P1): Can start after Foundational - No dependencies on other stories
144
+ - User Story 2 (P2): Depends on User Story 1 completion (needs module to verify exclusion works)
145
+ - User Story 3 (P3): Depends on User Stories 1 & 2 (needs both paths working to test)
146
+ - **Polish (Phase 6)**: Depends on all user stories being complete
147
+
148
+ ### User Story Dependencies
149
+
150
+ - **User Story 1 (P1)**: Can start after Foundational (Phase 2) - Core extraction work
151
+ - **User Story 2 (P2)**: Depends on User Story 1 completion - Validates optimization path
152
+ - **User Story 3 (P3)**: Depends on User Stories 1 & 2 - Validates testing mechanism across both
153
+
154
+ **Note**: Unlike typical projects, these user stories are sequential because:
155
+ - US2 requires US1 module to exist before verifying it's correctly excluded
156
+ - US3 requires both code paths (US1 & US2) working before testing mechanism is meaningful
157
+
158
+ ### Within Each User Story
159
+
160
+ - **User Story 1**: Tasks T009-T019 (method moves) are parallelizable [P]
161
+ - **User Story 2**: Tasks T026-T027 are verification steps, run sequentially
162
+ - **User Story 3**: Tasks T031-T032 (test helpers) are parallelizable [P]
163
+
164
+ ### Parallel Opportunities
165
+
166
+ - Phase 1: Tasks T003-T004 can run in parallel
167
+ - Phase 2: All tasks sequential (building foundation)
168
+ - User Story 1: Tasks T009-T019 (11 method extractions) can be done in parallel
169
+ - User Story 3: Tasks T031-T032, T033-T034, T039-T043 can run in parallel
170
+ - Polish Phase: Documentation tasks T039-T043 can run in parallel
171
+
172
+ ---
173
+
174
+ ## Parallel Example: User Story 1 (Method Extraction)
175
+
176
+ ```bash
177
+ # All 11 method extractions can happen simultaneously:
178
+ Task: "Move Map.allocate to Map::Ordering (initializes @keys) in lib/map/ordering.rb"
179
+ Task: "Move keys method to Map::Ordering (returns @keys) in lib/map/ordering.rb"
180
+ Task: "Move []= method to Map::Ordering (tracks keys) in lib/map/ordering.rb"
181
+ Task: "Move values method to Map::Ordering (iterates @keys) in lib/map/ordering.rb"
182
+ Task: "Move first method to Map::Ordering (uses @keys.first) in lib/map/ordering.rb"
183
+ Task: "Move last method to Map::Ordering (uses @keys.last) in lib/map/ordering.rb"
184
+ Task: "Move each/each_pair to Map::Ordering (iterates @keys) in lib/map/ordering.rb"
185
+ Task: "Move each_key method to Map::Ordering (iterates @keys) in lib/map/ordering.rb"
186
+ Task: "Move each_value to Map::Ordering (iterates @keys) in lib/map/ordering.rb"
187
+ Task: "Move each_with_index to Map::Ordering (uses @keys) in lib/map/ordering.rb"
188
+ Task: "Move delete method to Map::Ordering (removes from @keys) in lib/map/ordering.rb"
189
+
190
+ # These are all independent - different methods being moved to same new file
191
+ ```
192
+
193
+ ---
194
+
195
+ ## Implementation Strategy
196
+
197
+ ### MVP First (User Story 1 Only)
198
+
199
+ 1. Complete Phase 1: Setup (understand current code)
200
+ 2. Complete Phase 2: Foundational (create module structure)
201
+ 3. Complete Phase 3: User Story 1 (extract all ordering methods)
202
+ 4. **STOP and VALIDATE**: Test with MAP_FORCE_ORDERING=1, verify @keys exists and tests pass
203
+ 5. Ready for PR/review with backward compatibility proven
204
+
205
+ ### Incremental Delivery
206
+
207
+ 1. Complete Setup + Foundational → Module structure ready
208
+ 2. Add User Story 1 → Test with forced ordering → Backward compat proven (MVP!)
209
+ 3. Add User Story 2 → Test normal mode → Optimization verified
210
+ 4. Add User Story 3 → Test both modes → Testing mechanism validated
211
+ 5. Polish → Documentation complete → Feature done
212
+
213
+ ### Single Developer Strategy
214
+
215
+ Since user stories are sequential:
216
+
217
+ 1. Complete Setup + Foundational (Phase 1-2)
218
+ 2. Extract all methods to module (Phase 3 / US1) - can parallelize the 11 method moves
219
+ 3. Verify optimization works (Phase 4 / US2)
220
+ 4. Add testing mechanism (Phase 5 / US3)
221
+ 5. Polish and document (Phase 6)
222
+
223
+ ---
224
+
225
+ ## Task Summary
226
+
227
+ **Total Tasks**: 48 tasks
228
+
229
+ **Task Count by Phase**:
230
+ - Phase 1 (Setup): 4 tasks
231
+ - Phase 2 (Foundational): 4 tasks
232
+ - Phase 3 (User Story 1 - Legacy Support): 15 tasks
233
+ - Phase 4 (User Story 2 - Optimization): 7 tasks
234
+ - Phase 5 (User Story 3 - Testing): 8 tasks
235
+ - Phase 6 (Polish): 10 tasks
236
+
237
+ **Parallelizable Tasks**: 26 tasks marked with [P]
238
+
239
+ **User Story Mapping**:
240
+ - US1 (Legacy Ruby Support): 15 tasks - Module extraction
241
+ - US2 (Modern Ruby Optimization): 7 tasks - Verification and optimization
242
+ - US3 (Cross-Version Testing): 8 tasks - Testing mechanism
243
+
244
+ **Suggested MVP Scope**:
245
+ - Phase 1 + Phase 2 + Phase 3 (User Story 1)
246
+ - Total: 23 tasks to achieve backward compatibility with module extraction
247
+ - Deliverable: Map works with forced ordering mode, proving legacy Ruby support
248
+
249
+ **Independent Testing**:
250
+ - US1: Run with `MAP_FORCE_ORDERING=1 rake test`, verify @keys exists
251
+ - US2: Run with `rake test`, verify no @keys, all tests pass
252
+ - US3: Run both modes, verify test output clarity
253
+
254
+ ---
255
+
256
+ ## Notes
257
+
258
+ - [P] tasks = can run in parallel (different methods, independent work)
259
+ - [Story] label maps task to specific user story for traceability
260
+ - This is an internal refactoring, so no external API contracts
261
+ - Existing test suite is validation - no new tests required per spec
262
+ - Module extraction enables clean separation without code duplication
263
+ - Commit after each logical group (e.g., after all methods extracted)
264
+ - Stop at any checkpoint to validate story independently
data/test/map_test.rb CHANGED
@@ -173,18 +173,6 @@ Testing Map do
173
173
  assert{ a != b}
174
174
  end
175
175
 
176
- testing 'simple struct usage' do
177
- a = assert{ Map.new(:k => :v) }
178
- s = assert{ a.struct }
179
- assert{ s.k == :v }
180
- end
181
-
182
- testing 'nested struct usage' do
183
- a = assert{ Map.new(:k => {:l => :v}) }
184
- s = assert{ a.struct }
185
- assert{ s.k.l == :v }
186
- end
187
-
188
176
  testing 'that subclassing and clobbering initialize does not kill nested coersion' do
189
177
  c = Class.new(Map){ def initialize(arg) end }
190
178
  o = assert{ c.new(42) }
@@ -250,7 +238,6 @@ Testing Map do
250
238
  end
251
239
 
252
240
  testing 'that coercion is minimal' do
253
- map = Map.new
254
241
  a = Class.new(Map) do
255
242
  def to_map() {:k => :a} end
256
243
  end
@@ -284,7 +271,6 @@ Testing Map do
284
271
  logic = proc do |method, args|
285
272
  before = args.dup
286
273
  opts = assert{ Map.send(method, args) }
287
- after = args
288
274
 
289
275
  assert{ opts.is_a?(Map) }
290
276
  assert{ !args.last.is_a?(Hash) } if before.last.is_a?(Hash)
@@ -503,7 +489,7 @@ Testing Map do
503
489
  each = []
504
490
  array = %w( a b c )
505
491
  Map.each_pair(array){|k,v| each.push(k,v)}
506
- assert{ each_pair = ['a', 'b', 'c', nil] }
492
+ assert{ each == ['a', 'b', 'c', nil] }
507
493
  end
508
494
 
509
495
  testing 'that maps support breath_first_each' do
@@ -517,6 +503,7 @@ Testing Map do
517
503
 
518
504
  accum = []
519
505
  Map.breadth_first_each(map){|k, v| accum.push([k, v])}
506
+
520
507
  expected =
521
508
  [[["hash"], {"x"=>"y"}],
522
509
  [["nested hash"], {"nested"=>{"a"=>"b"}}],
@@ -535,6 +522,8 @@ Testing Map do
535
522
  [["nested array", 0, 0], 3],
536
523
  [["nested array", 1, 0], 4],
537
524
  [["nested array", 2, 0], 5]]
525
+
526
+ assert{ expected == accum }
538
527
  end
539
528
 
540
529
  testing 'that maps have a needle-in-a-haystack like #contains? method' do
@@ -666,30 +655,6 @@ Testing Map do
666
655
  assert( m.A? )
667
656
  end
668
657
 
669
- testing 'that maps have a clever little question method on Struct' do
670
- m = Map.new
671
- m.set(:a, :b, :c, 42)
672
- m.set([:x, :y, :z] => 42.0, [:A, 2] => 'forty-two')
673
- s = m.struct
674
-
675
- assert( s.a.b.c == 42 )
676
- assert( s.x.y.z == 42.0 )
677
-
678
- assert( !s.b? )
679
- assert( s.a? )
680
- assert( s.a.b? )
681
- assert( s.a.b.c? )
682
- assert( !s.a.b.d? )
683
-
684
- assert( s.x? )
685
- assert( s.x.y? )
686
- assert( s.x.y.z? )
687
- assert( !s.y? )
688
-
689
- assert( s.A? )
690
-
691
- end
692
-
693
658
  testing 'that Map#default= blows up until a sane strategy for dealing with it is developed' do
694
659
  m = Map.new
695
660
 
@@ -745,24 +710,6 @@ Testing Map do
745
710
  assert{ map.list.class != Array }
746
711
  end
747
712
 
748
- testing 'rack compatible params' do
749
- m = Map.for(:a => [{}, {:b => 42}], :x => [ nil, [ nil, {:y => 42}] ], :A => {:B => {:C => 42}})
750
-
751
- assert{ m.param_for(:a, 1, :b) == 'map[a][][b]=42' }
752
- assert{ m.name_for(:a, 1, :b) == 'map[a][][b]' }
753
-
754
- assert{ m.param_for(:x, 1, 1, :y) == 'map[x][][][y]=42' }
755
- assert{ m.name_for(:x, 1, 1, :y) == 'map[x][][][y]' }
756
-
757
- assert{ m.param_for(:A, :B, :C) == 'map[A][B][C]=42' }
758
- assert{ m.name_for(:A, :B, :C) == 'map[A][B][C]' }
759
-
760
- assert{ m.name_for(:A, :B, :C, :prefix => :foo) == 'foo[A][B][C]' }
761
-
762
- m = Map.for({"a"=>{"b"=>42}, "x"=>{"y"=>42}, "foo"=>:bar, "array"=>[{"k"=>:v}]})
763
- assert{ m.to_params == "map[a][b]=42&map[x][y]=42&map[foo]=bar&map[array][][k]=v" }
764
- end
765
-
766
713
  testing 'delete_if' do
767
714
  m = Map.for(:k => :v)
768
715
  assert{ m.delete_if{|k| k.to_s == 'k'} }
@@ -773,6 +720,23 @@ Testing Map do
773
720
  assert{ m.empty?}
774
721
  end
775
722
 
723
+ testing 'deep fetch' do
724
+ m = Map.for(:a => {:b => {:c => :v}})
725
+
726
+ assert{ m.fetch(:a, :b, :c) == :v }
727
+ end
728
+
729
+ testing 'fetch mapifys' do
730
+ m = Map.new
731
+ h = {:key => :value, :hash => {:a => :b}, :array => [0, {:a => :b}]}
732
+
733
+ missing = assert{ m.fetch(:missing){ h } }
734
+ assert{ missing.is_a?(Map) }
735
+ assert{ missing[:hash].is_a?(Map) }
736
+ assert{ not missing[:array][0].is_a?(Map) }
737
+ assert{ missing[:array][1].is_a?(Map) }
738
+ end
739
+
776
740
  protected
777
741
  def new_int_map(n = 1024)
778
742
  map = assert{ Map.new }
metadata CHANGED
@@ -1,18 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: map
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.6.0
5
- prerelease:
4
+ version: 8.0.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Ara T. Howard
9
- autorequire:
8
+ autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2016-01-12 00:00:00.000000000 Z
11
+ date: 2025-11-13 00:00:00.000000000 Z
13
12
  dependencies: []
14
- description: ! 'the awesome ruby container you''ve always wanted: a string/symbol
15
- indifferent ordered hash that works in all rubies'
13
+ description: |-
14
+ map.rb is a string/symbol indifferent ordered hash that works in all rubies.
15
+
16
+ out of the over 200 ruby gems i have written, this is the one i use
17
+ every day, in all my projects.
18
+
19
+ some may be accustomed to using ActiveSupport::HashWithIndiffentAccess
20
+ and, although there are some similarities, map.rb is more complete,
21
+ works without requiring a mountain of code, and has been in production
22
+ usage for over 15 years.
23
+
24
+ it has no dependencies, and suports a myriad of other, 'tree-ish'
25
+ operators that will allow you to slice and dice data like a giraffee
26
+ with a giant weed whacker.
16
27
  email: ara.t.howard@gmail.com
17
28
  executables: []
18
29
  extensions: []
@@ -20,41 +31,46 @@ extra_rdoc_files: []
20
31
  files:
21
32
  - LICENSE
22
33
  - README
34
+ - README.md
23
35
  - Rakefile
24
- - a.rb
36
+ - images/giraffe.jpeg
37
+ - images/map.png
25
38
  - lib/map.rb
26
- - lib/map/integrations/active_record.rb
39
+ - lib/map/_lib.rb
27
40
  - lib/map/options.rb
28
- - lib/map/params.rb
29
- - lib/map/struct.rb
41
+ - lib/map/ordering.rb
30
42
  - map.gemspec
43
+ - specs/001-ordered-map-module/checklists/requirements.md
44
+ - specs/001-ordered-map-module/data-model.md
45
+ - specs/001-ordered-map-module/plan.md
46
+ - specs/001-ordered-map-module/quickstart.md
47
+ - specs/001-ordered-map-module/research.md
48
+ - specs/001-ordered-map-module/spec.md
49
+ - specs/001-ordered-map-module/tasks.md
31
50
  - test/leak.rb
32
51
  - test/lib/testing.rb
33
52
  - test/map_test.rb
34
53
  homepage: https://github.com/ahoward/map
35
54
  licenses:
36
- - same as ruby's
37
- post_install_message:
55
+ - Ruby
56
+ metadata: {}
57
+ post_install_message:
38
58
  rdoc_options: []
39
59
  require_paths:
40
60
  - lib
41
61
  required_ruby_version: !ruby/object:Gem::Requirement
42
- none: false
43
62
  requirements:
44
- - - ! '>='
63
+ - - ">="
45
64
  - !ruby/object:Gem::Version
46
- version: '0'
65
+ version: '3.0'
47
66
  required_rubygems_version: !ruby/object:Gem::Requirement
48
- none: false
49
67
  requirements:
50
- - - ! '>='
68
+ - - ">="
51
69
  - !ruby/object:Gem::Version
52
70
  version: '0'
53
71
  requirements: []
54
- rubyforge_project: codeforpeople
55
- rubygems_version: 1.8.23.2
56
- signing_key:
57
- specification_version: 3
58
- summary: map
72
+ rubygems_version: 3.5.18
73
+ signing_key:
74
+ specification_version: 4
75
+ summary: the perfect ruby data structure
59
76
  test_files: []
60
- has_rdoc:
data/a.rb DELETED
@@ -1,10 +0,0 @@
1
- require './lib/map.rb'
2
- require 'yaml'
3
-
4
- a = Map.new
5
- a.set(:a, :b, "value")
6
- a.set(:a,0,:c, "value")
7
-
8
- p a
9
- puts a.to_hash.to_yaml # fine
10
- puts a.to_yaml # explode