1+ #!/usr/bin/env ruby
2+
3+ require 'bundler/setup'
4+ require 'benchmark'
5+ require 'json'
6+
7+ # Convert array to hash with numeric keys
8+ def array_to_hash ( array )
9+ hash = { }
10+ array . each_with_index do |item , index |
11+ hash [ index . to_s ] = item
12+ end
13+ hash
14+ end
15+
16+ # Convert hash back to array (preserve order)
17+ def hash_to_array ( hash )
18+ # Sort by numeric key to preserve order
19+ hash . keys . sort_by ( &:to_i ) . map { |key | hash [ key ] }
20+ end
21+
22+ # Test the conversion
23+ users_array = [
24+ { id : 1 , name : "Alice" } ,
25+ { id : 2 , name : "Bob" } ,
26+ { id : 3 , name : "Charlie" }
27+ ]
28+
29+ puts "Original array:"
30+ puts users_array . inspect
31+
32+ # Convert to hash
33+ users_hash = array_to_hash ( users_array )
34+ puts "\n As hash:"
35+ puts users_hash . inspect
36+
37+ # Convert back to array
38+ restored_array = hash_to_array ( users_hash )
39+ puts "\n Restored array:"
40+ puts restored_array . inspect
41+
42+ puts "\n Are they equal? #{ users_array == restored_array } "
43+
44+ # Test JSON serialization
45+ original_json = JSON . generate ( users_array )
46+ hash_json = JSON . generate ( users_hash )
47+ restored_json = JSON . generate ( restored_array )
48+
49+ puts "\n JSON comparison:"
50+ puts "Original: #{ original_json } "
51+ puts "Hash: #{ hash_json } "
52+ puts "Restored: #{ restored_json } "
53+
54+ # But we can reconstruct the array format in JSON
55+ def hash_to_array_json ( hash )
56+ sorted_values = hash . keys . sort_by ( &:to_i ) . map { |key | hash [ key ] }
57+ JSON . generate ( sorted_values )
58+ end
59+
60+ reconstructed_json = hash_to_array_json ( users_hash )
61+ puts "Reconstructed: #{ reconstructed_json } "
62+ puts "JSON equal? #{ original_json == reconstructed_json } "
63+
64+ # Now let's test caching with this approach
65+ class HashArrayDSL
66+ def initialize
67+ @root = { }
68+ @stack = [ ]
69+ @current = @root
70+ @array_markers = { } # Track which keys should be arrays
71+ end
72+
73+ def set! ( key , value = nil , options = { } )
74+ key = key . to_s
75+
76+ if options [ :cache ] && ( cached_data = Rails . cache . read ( options [ :cache ] ) )
77+ # Cached data is already a hash, just merge it
78+ @current [ key ] = cached_data
79+ return
80+ end
81+
82+ if block_given?
83+ @stack . push ( @current )
84+ old_current = @current
85+ @current = { }
86+
87+ result = yield
88+
89+ if result == :array_marker
90+ @array_markers [ generate_path ( old_current , key ) ] = true
91+ end
92+
93+ old_current [ key ] = @current
94+
95+ # Cache the result if requested
96+ if options [ :cache ]
97+ Rails . cache . write ( options [ :cache ] , @current )
98+ end
99+
100+ @current = @stack . pop
101+ else
102+ @current [ key ] = value
103+ end
104+
105+ nil
106+ end
107+
108+ def array! ( collection = nil , &block )
109+ if collection
110+ collection . each_with_index do |item , index |
111+ @stack . push ( @current )
112+ item_obj = { }
113+ @current = item_obj
114+
115+ yield item
116+
117+ @current = @stack . pop
118+ @current [ index . to_s ] = item_obj # Use string index as key
119+ end
120+ end
121+
122+ :array_marker
123+ end
124+
125+ def result!
126+ JSON . generate ( convert_hash_arrays_to_json ( @root , @array_markers , "" ) )
127+ end
128+
129+ private
130+
131+ def generate_path ( parent , key )
132+ # This is simplified - would need proper path tracking
133+ key
134+ end
135+
136+ def convert_hash_arrays_to_json ( obj , markers , path )
137+ if obj . is_a? ( Hash )
138+ if markers [ path ] || has_numeric_keys ( obj )
139+ # Convert hash with numeric keys back to array
140+ sorted_items = obj . keys . sort_by ( &:to_i ) . map do |key |
141+ item_path = "#{ path } .#{ key } "
142+ convert_hash_arrays_to_json ( obj [ key ] , markers , item_path )
143+ end
144+ return sorted_items
145+ else
146+ # Regular object
147+ result = { }
148+ obj . each do |key , value |
149+ item_path = "#{ path } .#{ key } "
150+ result [ key ] = convert_hash_arrays_to_json ( value , markers , item_path )
151+ end
152+ return result
153+ end
154+ else
155+ obj
156+ end
157+ end
158+
159+ def has_numeric_keys ( hash )
160+ return false unless hash . is_a? ( Hash )
161+ return false if hash . empty?
162+
163+ # Check if all keys are numeric strings in sequence
164+ keys = hash . keys . sort_by ( &:to_i )
165+ keys == ( 0 ...keys . length ) . map ( &:to_s )
166+ end
167+ end
168+
169+ # Test this approach
170+ puts "\n === Testing Hash-Array DSL ==="
171+
172+ # Mock Rails.cache for testing
173+ module Rails
174+ class << self
175+ def cache
176+ @cache ||= MockCache . new
177+ end
178+ end
179+
180+ class MockCache
181+ def initialize
182+ @data = { }
183+ end
184+
185+ def read ( key )
186+ @data [ key ]
187+ end
188+
189+ def write ( key , value )
190+ @data [ key ] = value
191+ end
192+
193+ def clear
194+ @data . clear
195+ end
196+ end
197+ end
198+
199+ Rails . cache . clear
200+
201+ # Test the DSL
202+ dsl = HashArrayDSL . new
203+ dsl . set! ( :users ) do
204+ dsl . array! ( users_array ) do |user |
205+ dsl . set! ( :id , user [ :id ] )
206+ dsl . set! ( :name , user [ :name ] )
207+ end
208+ end
209+ dsl . set! ( :status , "success" )
210+
211+ result = dsl . result!
212+ puts "DSL Result: #{ result } "
213+
214+ # Test with caching
215+ Rails . cache . clear
216+ dsl2 = HashArrayDSL . new
217+
218+ # First render - should cache
219+ dsl2 . set! ( :users , cache : "users_key" ) do
220+ dsl2 . array! ( users_array ) do |user |
221+ dsl2 . set! ( :id , user [ :id ] )
222+ dsl2 . set! ( :name , user [ :name ] )
223+ end
224+ end
225+
226+ puts "Cached data: #{ Rails . cache . read ( 'users_key' ) } "
227+
228+ # Second render - should use cache
229+ dsl3 = HashArrayDSL . new
230+ dsl3 . set! ( :users , cache : "users_key" ) do
231+ # This block shouldn't execute
232+ raise "Cache miss!"
233+ end
234+ dsl3 . set! ( :status , "from_cache" )
235+
236+ cached_result = dsl3 . result!
237+ puts "Cached Result: #{ cached_result } "
0 commit comments