@@ -36,6 +36,59 @@ module ActiveRecord
36
36
class Result
37
37
include Enumerable
38
38
39
+ class IndexedRow
40
+ def initialize ( column_indexes , row )
41
+ @column_indexes = column_indexes
42
+ @row = row
43
+ end
44
+
45
+ def size
46
+ @column_indexes . size
47
+ end
48
+ alias_method :length , :size
49
+
50
+ def each_key ( &block )
51
+ @column_indexes . each_key ( &block )
52
+ end
53
+
54
+ def keys
55
+ @column_indexes . keys
56
+ end
57
+
58
+ def ==( other )
59
+ if other . is_a? ( Hash )
60
+ to_hash == other
61
+ else
62
+ super
63
+ end
64
+ end
65
+
66
+ def key? ( column )
67
+ @column_indexes . key? ( column )
68
+ end
69
+
70
+ def fetch ( column )
71
+ if index = @column_indexes [ column ]
72
+ @row [ index ]
73
+ elsif block_given?
74
+ yield
75
+ else
76
+ raise KeyError , "key not found: #{ column . inspect } "
77
+ end
78
+ end
79
+
80
+ def []( column )
81
+ if index = @column_indexes [ column ]
82
+ @row [ index ]
83
+ end
84
+ end
85
+
86
+ def to_h
87
+ @column_indexes . transform_values { |index | @row [ index ] }
88
+ end
89
+ alias_method :to_hash , :to_h
90
+ end
91
+
39
92
attr_reader :columns , :rows , :column_types
40
93
41
94
def self . empty ( async : false ) # :nodoc:
@@ -67,14 +120,16 @@ def length
67
120
end
68
121
69
122
# Calls the given block once for each element in row collection, passing
70
- # row as parameter.
123
+ # row as parameter. Each row is a Hash-like, read only object.
124
+ #
125
+ # To get real hashes, use +.to_a.each+.
71
126
#
72
127
# Returns an +Enumerator+ if no block is given.
73
128
def each ( &block )
74
129
if block_given?
75
- hash_rows . each ( &block )
130
+ indexed_rows . each ( &block )
76
131
else
77
- hash_rows . to_enum { @rows . size }
132
+ indexed_rows . to_enum { @rows . size }
78
133
end
79
134
end
80
135
@@ -134,14 +189,13 @@ def cast_values(type_overrides = {}) # :nodoc:
134
189
end
135
190
136
191
def initialize_copy ( other )
137
- @columns = columns
138
- @rows = rows . dup
192
+ @rows = rows . dup
139
193
@column_types = column_types . dup
140
- @hash_rows = nil
141
194
end
142
195
143
196
def freeze # :nodoc:
144
197
hash_rows . freeze
198
+ indexed_rows . freeze
145
199
super
146
200
end
147
201
@@ -154,7 +208,7 @@ def column_indexes # :nodoc:
154
208
hash [ columns [ index ] ] = index
155
209
index += 1
156
210
end
157
- hash
211
+ hash . freeze
158
212
end
159
213
end
160
214
@@ -167,6 +221,13 @@ def column_type(name, index, type_overrides)
167
221
end
168
222
end
169
223
224
+ def indexed_rows
225
+ @indexed_rows ||= begin
226
+ columns = column_indexes
227
+ @rows . map { |row | IndexedRow . new ( columns , row ) } . freeze
228
+ end
229
+ end
230
+
170
231
def hash_rows
171
232
# We use transform_values to rows.
172
233
# This is faster because we avoid any reallocs and avoid hashing entirely.
0 commit comments