rails_select_on_includes 0.5.2 → 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/rails_select_on_includes.rb +255 -124
- data/lib/rails_select_on_includes/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5f26d06137a196f73e828264c19083cbd2c40dd6
|
4
|
+
data.tar.gz: 5bc21a1aa9c61a595c87afcdd6d6d6e2e00a192f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f2ca94a77cdac843e6b63b2ed5e1ab6158c6165dd4782d4fae5ec72bcce839c94dac1c8b552aec9624df2501e780d9a4b4a9a1f63e682155313ff50cb26e1707
|
7
|
+
data.tar.gz: f299a77e138a5dc2acaafa8f72d8b656e94894cfd5b7d13f17ea6d38e651c3f8bf0365cb19e302d73435f714aaaec35af4430ff4c808645241b75f9949e93dec
|
@@ -1,141 +1,272 @@
|
|
1
1
|
require 'active_support/deprecation'
|
2
2
|
require 'active_support/core_ext/string/filters'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
selected_column = sub_sv[/\..+/][1..-1]
|
44
|
-
@base_class_node_aliases << [selected_column, selected_column]
|
45
|
-
@virtual_attributes_names << selected_column
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
4
|
+
::ActiveRecord::Associations::JoinDependency::Aliases.class_eval do
|
5
|
+
def initialize(tables)
|
6
|
+
@tables = tables
|
7
|
+
@alias_cache = tables.each_with_object({}) { |table,h|
|
8
|
+
h[table.node] = table.columns.each_with_object({}) { |column,i|
|
9
|
+
i[column.name] = column.alias
|
10
|
+
}
|
11
|
+
}
|
12
|
+
@name_and_alias_cache = tables.each_with_object({}) { |table,h|
|
13
|
+
h[table.node] = table.columns.map { |column|
|
14
|
+
[column.name, column.alias]
|
15
|
+
}
|
16
|
+
@base_class_node_aliases ||= h[table.node] if table.node.is_a?(ActiveRecord::Associations::JoinDependency::JoinBase)
|
17
|
+
}
|
18
|
+
|
19
|
+
@virtual_attributes_names = []
|
20
|
+
end
|
21
|
+
# valid formats are:
|
22
|
+
# 1 'table_name.column' or 'table_name.column as column_1' will be parsed! distinct on can be used also
|
23
|
+
# 2 {table_name: column} or { table_name: [column1, column2] }
|
24
|
+
# 3 table_name: 2
|
25
|
+
def update_aliases_to_select_values( select_values )
|
26
|
+
return if select_values.blank?
|
27
|
+
select_values.each do |sv|
|
28
|
+
# if sv is symbol that we assume that its a base table column and it will be aliased and added as usual
|
29
|
+
# all we need is some specials joins+select from related tables
|
30
|
+
if sv.is_a?(Hash)
|
31
|
+
flatten_hash_values(sv).each{|sub_sv| @base_class_node_aliases << [sub_sv, sub_sv]; @virtual_attributes_names << sub_sv }
|
32
|
+
elsif sv.is_a?(String)
|
33
|
+
# this is the case of long raw select
|
34
|
+
sv.split( ", " ).each do |sub_sv|
|
35
|
+
if sub_sv[/.+ as .+/i]
|
36
|
+
selected_column = sub_sv[/ as .+/i][4..-1]
|
37
|
+
@base_class_node_aliases << [selected_column, selected_column]
|
38
|
+
@virtual_attributes_names << selected_column
|
39
|
+
elsif sub_sv[/.+\.[^\*]+/]
|
40
|
+
selected_column = sub_sv[/\..+/][1..-1]
|
41
|
+
@base_class_node_aliases << [selected_column, selected_column]
|
42
|
+
@virtual_attributes_names << selected_column
|
49
43
|
end
|
50
44
|
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
51
48
|
|
52
|
-
|
53
|
-
|
54
|
-
|
49
|
+
def slice_selected_attr_types( column_types )
|
50
|
+
column_types.slice( *@virtual_attributes_names )
|
51
|
+
end
|
55
52
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
53
|
+
private
|
54
|
+
def flatten_hash_values( some_hash )
|
55
|
+
some_hash.values.map{ |value| value.is_a?(Hash) ? flatten_hash_values( value ) : value }.flatten
|
56
|
+
end
|
57
|
+
end
|
61
58
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
59
|
+
::ActiveRecord::Associations::JoinDependency::JoinPart.class_eval do
|
60
|
+
def instantiate(row, aliases, column_types = {}, &block)
|
61
|
+
base_klass.instantiate(extract_record(row, aliases), column_types, &block)
|
62
|
+
end
|
63
|
+
end
|
67
64
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
seen = Hash.new { |i, object_id|
|
72
|
-
i[object_id] = Hash.new { |j, child_class|
|
73
|
-
j[child_class] = {}
|
74
|
-
}
|
75
|
-
}
|
76
|
-
|
77
|
-
model_cache = Hash.new { |h,klass| h[klass] = {} }
|
78
|
-
parents = model_cache[join_root]
|
79
|
-
column_aliases = aliases.column_aliases join_root
|
80
|
-
|
81
|
-
message_bus = ActiveSupport::Notifications.instrumenter
|
82
|
-
|
83
|
-
payload = {
|
84
|
-
record_count: result_set.length,
|
85
|
-
class_name: join_root.base_klass.name
|
86
|
-
}
|
87
|
-
|
88
|
-
message_bus.instrument('instantiation.active_record', payload) do
|
89
|
-
result_set.each { |row_hash|
|
90
|
-
parent_key = primary_key ? row_hash[primary_key] : row_hash
|
91
|
-
# DISTINCTION PART > join_root.instantiate(row_hash, column_aliases, aliases.slice_selected_attr_types( result_set.column_types ) )
|
92
|
-
# PREVIOUS > join_root.instantiate(row_hash, column_aliases )
|
93
|
-
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, aliases.slice_selected_attr_types( result_set.column_types ) )
|
94
|
-
construct(parent, join_root, row_hash, result_set, seen, model_cache, aliases)
|
95
|
-
}
|
96
|
-
end
|
97
|
-
parents.values
|
98
|
-
end
|
65
|
+
::ActiveRecord::Associations::JoinDependency.class_eval do
|
66
|
+
def instantiate(result_set, aliases)
|
67
|
+
primary_key = aliases.column_alias(join_root, join_root.primary_key)
|
99
68
|
|
100
|
-
|
69
|
+
seen = Hash.new { |i, object_id|
|
70
|
+
i[object_id] = Hash.new { |j, child_class|
|
71
|
+
j[child_class] = {}
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
model_cache = Hash.new { |h,klass| h[klass] = {} }
|
76
|
+
parents = model_cache[join_root]
|
77
|
+
column_aliases = aliases.column_aliases join_root
|
78
|
+
|
79
|
+
message_bus = ActiveSupport::Notifications.instrumenter
|
80
|
+
|
81
|
+
payload = {
|
82
|
+
record_count: result_set.length,
|
83
|
+
class_name: join_root.base_klass.name
|
84
|
+
}
|
85
|
+
|
86
|
+
message_bus.instrument('instantiation.active_record', payload) do
|
87
|
+
result_set.each { |row_hash|
|
88
|
+
parent_key = primary_key ? row_hash[primary_key] : row_hash
|
89
|
+
# DISTINCTION PART > join_root.instantiate(row_hash, column_aliases, aliases.slice_selected_attr_types( result_set.column_types ) )
|
90
|
+
# PREVIOUS > join_root.instantiate(row_hash, column_aliases )
|
91
|
+
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, aliases.slice_selected_attr_types( result_set.column_types ) )
|
92
|
+
construct(parent, join_root, row_hash, result_set, seen, model_cache, aliases)
|
93
|
+
}
|
101
94
|
end
|
95
|
+
parents.values
|
96
|
+
end
|
102
97
|
end
|
103
98
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
end
|
137
|
-
end
|
99
|
+
|
100
|
+
::ActiveRecord::FinderMethods.class_eval do
|
101
|
+
def find_with_associations
|
102
|
+
# NOTE: the JoinDependency constructed here needs to know about
|
103
|
+
# any joins already present in `self`, so pass them in
|
104
|
+
#
|
105
|
+
# failing to do so means that in cases like activerecord/test/cases/associations/inner_join_association_test.rb:136
|
106
|
+
# incorrect SQL is generated. In that case, the join dependency for
|
107
|
+
# SpecialCategorizations is constructed without knowledge of the
|
108
|
+
# preexisting join in joins_values to categorizations (by way of
|
109
|
+
# the `has_many :through` for categories).
|
110
|
+
#
|
111
|
+
join_dependency = construct_join_dependency(joins_values)
|
112
|
+
|
113
|
+
aliases = join_dependency.aliases
|
114
|
+
relation = select aliases.columns
|
115
|
+
relation = apply_join_dependency(relation, join_dependency)
|
116
|
+
|
117
|
+
if block_given?
|
118
|
+
yield relation
|
119
|
+
else
|
120
|
+
if ActiveRecord::NullRelation === relation
|
121
|
+
[]
|
122
|
+
else
|
123
|
+
arel = relation.arel
|
124
|
+
rows = connection.select_all(arel, 'SQL', relation.bound_attributes)
|
125
|
+
#1 DISTINCTION IS HERE:
|
126
|
+
# now we gently mokey-patching existing column aliases with select values
|
127
|
+
aliases.update_aliases_to_select_values(values[:select]) unless values[:select].blank?
|
128
|
+
|
129
|
+
join_dependency.instantiate(rows, aliases)
|
130
|
+
end
|
138
131
|
end
|
139
132
|
end
|
140
133
|
end
|
141
134
|
|
135
|
+
# module ActiveRecord
|
136
|
+
# module Associations
|
137
|
+
# class JoinDependency # :nodoc:
|
138
|
+
# # class Aliases # :nodoc:
|
139
|
+
# # def initialize(tables)
|
140
|
+
# # @tables = tables
|
141
|
+
# # @alias_cache = tables.each_with_object({}) { |table,h|
|
142
|
+
# # h[table.node] = table.columns.each_with_object({}) { |column,i|
|
143
|
+
# # i[column.name] = column.alias
|
144
|
+
# # }
|
145
|
+
# # }
|
146
|
+
# # @name_and_alias_cache = tables.each_with_object({}) { |table,h|
|
147
|
+
# # h[table.node] = table.columns.map { |column|
|
148
|
+
# # [column.name, column.alias]
|
149
|
+
# # }
|
150
|
+
# # @base_class_node_aliases ||= h[table.node] if table.node.is_a?(ActiveRecord::Associations::JoinDependency::JoinBase)
|
151
|
+
# # }
|
152
|
+
# #
|
153
|
+
# # @virtual_attributes_names = []
|
154
|
+
# # end
|
155
|
+
# # # valid formats are:
|
156
|
+
# # # 1 'table_name.column' or 'table_name.column as column_1' will be parsed! distinct on can be used also
|
157
|
+
# # # 2 {table_name: column} or { table_name: [column1, column2] }
|
158
|
+
# # # 3 table_name: 2
|
159
|
+
# # def update_aliases_to_select_values( select_values )
|
160
|
+
# # return if select_values.blank?
|
161
|
+
# # select_values.each do |sv|
|
162
|
+
# # # if sv is symbol that we assume that its a base table column and it will be aliased and added as usual
|
163
|
+
# # # all we need is some specials joins+select from related tables
|
164
|
+
# # if sv.is_a?(Hash)
|
165
|
+
# # flatten_hash_values(sv).each{|sub_sv| @base_class_node_aliases << [sub_sv, sub_sv]; @virtual_attributes_names << sub_sv }
|
166
|
+
# # elsif sv.is_a?(String)
|
167
|
+
# # # this is the case of long raw select
|
168
|
+
# # sv.split( ", " ).each do |sub_sv|
|
169
|
+
# # if sub_sv[/.+ as .+/i]
|
170
|
+
# # selected_column = sub_sv[/ as .+/i][4..-1]
|
171
|
+
# # @base_class_node_aliases << [selected_column, selected_column]
|
172
|
+
# # @virtual_attributes_names << selected_column
|
173
|
+
# # elsif sub_sv[/.+\.[^\*]+/]
|
174
|
+
# # selected_column = sub_sv[/\..+/][1..-1]
|
175
|
+
# # @base_class_node_aliases << [selected_column, selected_column]
|
176
|
+
# # @virtual_attributes_names << selected_column
|
177
|
+
# # end
|
178
|
+
# # end
|
179
|
+
# # end
|
180
|
+
# # end
|
181
|
+
# # end
|
182
|
+
# #
|
183
|
+
# # def slice_selected_attr_types( column_types )
|
184
|
+
# # column_types.slice( *@virtual_attributes_names )
|
185
|
+
# # end
|
186
|
+
# #
|
187
|
+
# # private
|
188
|
+
# # def flatten_hash_values( some_hash )
|
189
|
+
# # some_hash.values.map{ |value| value.is_a?(Hash) ? flatten_hash_values( value ) : value }.flatten
|
190
|
+
# # end
|
191
|
+
# # end
|
192
|
+
#
|
193
|
+
# # class JoinPart
|
194
|
+
# # def instantiate(row, aliases, column_types = {}, &block)
|
195
|
+
# # base_klass.instantiate(extract_record(row, aliases), column_types, &block)
|
196
|
+
# # end
|
197
|
+
# # end
|
198
|
+
#
|
199
|
+
# # def instantiate(result_set, aliases)
|
200
|
+
# # primary_key = aliases.column_alias(join_root, join_root.primary_key)
|
201
|
+
# #
|
202
|
+
# # seen = Hash.new { |i, object_id|
|
203
|
+
# # i[object_id] = Hash.new { |j, child_class|
|
204
|
+
# # j[child_class] = {}
|
205
|
+
# # }
|
206
|
+
# # }
|
207
|
+
# #
|
208
|
+
# # model_cache = Hash.new { |h,klass| h[klass] = {} }
|
209
|
+
# # parents = model_cache[join_root]
|
210
|
+
# # column_aliases = aliases.column_aliases join_root
|
211
|
+
# #
|
212
|
+
# # message_bus = ActiveSupport::Notifications.instrumenter
|
213
|
+
# #
|
214
|
+
# # payload = {
|
215
|
+
# # record_count: result_set.length,
|
216
|
+
# # class_name: join_root.base_klass.name
|
217
|
+
# # }
|
218
|
+
# #
|
219
|
+
# # message_bus.instrument('instantiation.active_record', payload) do
|
220
|
+
# # result_set.each { |row_hash|
|
221
|
+
# # parent_key = primary_key ? row_hash[primary_key] : row_hash
|
222
|
+
# # # DISTINCTION PART > join_root.instantiate(row_hash, column_aliases, aliases.slice_selected_attr_types( result_set.column_types ) )
|
223
|
+
# # # PREVIOUS > join_root.instantiate(row_hash, column_aliases )
|
224
|
+
# # parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, aliases.slice_selected_attr_types( result_set.column_types ) )
|
225
|
+
# # construct(parent, join_root, row_hash, result_set, seen, model_cache, aliases)
|
226
|
+
# # }
|
227
|
+
# # end
|
228
|
+
# # parents.values
|
229
|
+
# # end
|
230
|
+
#
|
231
|
+
# end
|
232
|
+
# end
|
233
|
+
# end
|
234
|
+
|
235
|
+
# module ActiveRecord
|
236
|
+
# module FinderMethods
|
237
|
+
#
|
238
|
+
# # def find_with_associations
|
239
|
+
# # # NOTE: the JoinDependency constructed here needs to know about
|
240
|
+
# # # any joins already present in `self`, so pass them in
|
241
|
+
# # #
|
242
|
+
# # # failing to do so means that in cases like activerecord/test/cases/associations/inner_join_association_test.rb:136
|
243
|
+
# # # incorrect SQL is generated. In that case, the join dependency for
|
244
|
+
# # # SpecialCategorizations is constructed without knowledge of the
|
245
|
+
# # # preexisting join in joins_values to categorizations (by way of
|
246
|
+
# # # the `has_many :through` for categories).
|
247
|
+
# # #
|
248
|
+
# # join_dependency = construct_join_dependency(joins_values)
|
249
|
+
# #
|
250
|
+
# # aliases = join_dependency.aliases
|
251
|
+
# # relation = select aliases.columns
|
252
|
+
# # relation = apply_join_dependency(relation, join_dependency)
|
253
|
+
# #
|
254
|
+
# # if block_given?
|
255
|
+
# # yield relation
|
256
|
+
# # else
|
257
|
+
# # if ActiveRecord::NullRelation === relation
|
258
|
+
# # []
|
259
|
+
# # else
|
260
|
+
# # arel = relation.arel
|
261
|
+
# # rows = connection.select_all(arel, 'SQL', relation.bound_attributes)
|
262
|
+
# # #1 DISTINCTION IS HERE:
|
263
|
+
# # # now we gently mokey-patching existing column aliases with select values
|
264
|
+
# # aliases.update_aliases_to_select_values(values[:select]) unless values[:select].blank?
|
265
|
+
# #
|
266
|
+
# # join_dependency.instantiate(rows, aliases)
|
267
|
+
# # end
|
268
|
+
# # end
|
269
|
+
# # end
|
270
|
+
# end
|
271
|
+
# end
|
272
|
+
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_select_on_includes
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- alekseyl
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-02-
|
11
|
+
date: 2017-02-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|