rails_select_on_includes 0.5.2 → 0.5.3
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 +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
|