rails_select_on_includes 0.4.13 → 0.5.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.
- checksums.yaml +4 -4
- data/README.md +16 -27
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/rails_select_on_includes.rb +80 -129
- data/lib/rails_select_on_includes/version.rb +1 -1
- data/rails_select_on_includes.gemspec +1 -1
- metadata +13 -6
- data/CHANGELOG.md +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fb99c7d08c8db00bef1394aac2ac055df1707be5
|
4
|
+
data.tar.gz: 02591eb16f17bb61ba429b4a6d4eb6fad665bfa3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7639db4cbef9c67f0f5f04a5034709d2317dda111661f0519c5e5baefc60089edae19d32f844ec7d4293d0f826d878d9d2be3168625709aded1ddd7b8cc90405
|
7
|
+
data.tar.gz: c07bd198ed1cd3eb81d65bcdaa5ddcc25ebc541a505511941ab5ea0cfbdde981e3bcdcbcdde904eae7ac0e6a4696e86dab91b0550068db6793b441ea190446c2
|
data/README.md
CHANGED
@@ -1,15 +1,9 @@
|
|
1
|
-
# New Features
|
2
|
-
Selected virtual attributes will be now typecasted as usual attributes
|
3
|
-
|
4
|
-
#Rails version
|
5
|
-
Supports rails 4.x and rails 5 now!
|
6
|
-
|
7
1
|
# RailsSelectOnIncludes
|
8
2
|
|
9
|
-
This gem solves issue in rails: https://github.com/rails/rails/issues/15185 for base_class.
|
3
|
+
This gem solves issue in rails: https://github.com/rails/rails/issues/15185 for base_class.
|
10
4
|
|
11
5
|
It was impossible to select virtual attributes to object from its relations or any other way
|
12
|
-
when using includes and where
|
6
|
+
when using includes and where.
|
13
7
|
|
14
8
|
Example from upper rails issue:
|
15
9
|
|
@@ -36,7 +30,7 @@ post.comments.first.testval # Undefined method!
|
|
36
30
|
|
37
31
|
Данный gem решает проблему в рельсах с виртуальными аттрибутами при использовании includes,
|
38
32
|
когда рельсы собирают в запрос в joins с алиасами на все аттрибуты. В настоящий момент в модель не собираются
|
39
|
-
никаким боком виртуальные
|
33
|
+
никаким боком виртуальные аттрибуты.
|
40
34
|
|
41
35
|
В частности проблема описана здесь: https://github.com/rails/rails/issues/15185
|
42
36
|
|
@@ -62,16 +56,12 @@ post.comments.first.testval # Undefined method!
|
|
62
56
|
```
|
63
57
|
|
64
58
|
|
65
|
-
## Installation
|
59
|
+
## Installation
|
66
60
|
|
67
61
|
Add this line to your application's Gemfile:
|
68
62
|
|
69
63
|
```ruby
|
70
|
-
|
71
|
-
gem 'rails_select_on_includes', '~> 0.4.8'
|
72
|
-
|
73
|
-
#rails 5
|
74
|
-
gem 'rails_select_on_includes', '~> 0.5.2'
|
64
|
+
gem 'rails_select_on_includes'
|
75
65
|
```
|
76
66
|
|
77
67
|
And then execute:
|
@@ -84,28 +74,27 @@ Or install it yourself as:
|
|
84
74
|
|
85
75
|
## Usage
|
86
76
|
|
87
|
-
Works out of the box, monkey-
|
88
|
-
|
89
|
-
It not affecting query creation, since query already contains all columns, i.e. to_sql returns same string.
|
77
|
+
Works out of the box, gently monkey-patching base-class alias columns. It not affecting query creation,
|
78
|
+
since query already contains all columns, i.e. to_sql returns same string.
|
90
79
|
Works with selection in all formats:
|
91
80
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
4. Select with aliased arel attirubte: .select(Comment.arel_table[:column].as('column_alias'))
|
81
|
+
1 'table_name.column' or 'table_name.column as column_1' or "distinct on(..) table_name.column as column_1"
|
82
|
+
|
83
|
+
2 { table_name: column } or { table_name: [column1, column2] }
|
96
84
|
|
85
|
+
3 { table_name: 2 } where 2 relates to upper syntax
|
97
86
|
|
98
87
|
## Usage (рус)
|
99
88
|
|
100
|
-
Работает из коробки, нежно манки-патча алиасы прямо перед инстанцированием коллекции,
|
89
|
+
Работает из коробки, нежно манки-патча алиасы прямо перед инстанцированием коллекции, не влияет на создаваемый запрос в БД т.е to_sql не меняется.
|
101
90
|
|
102
91
|
Поддерживает select в следующих форматах :
|
103
92
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
4. Select with aliased arel attirubte: .select(Comment.arel_table[:column].as('column_alias'))
|
93
|
+
1 'table_name.column' or 'table_name.column as column_1' or "distinct on(..) table_name.column as column_1"
|
94
|
+
|
95
|
+
2 { table_name: column } or { table_name: [column1, column2] }
|
108
96
|
|
97
|
+
3 { table_name: 2 } where 2 relates to upper syntax
|
109
98
|
|
110
99
|
## Testing
|
111
100
|
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "rails_select_on_includes"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
@@ -1,143 +1,94 @@
|
|
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
|
-
|
4
|
+
module ActiveRecord
|
5
|
+
module Associations
|
6
|
+
class JoinDependency # :nodoc:
|
7
|
+
|
8
|
+
class Aliases # :nodoc:
|
9
|
+
def initialize(tables)
|
10
|
+
@tables = tables
|
11
|
+
@alias_cache = tables.each_with_object({}) { |table,h|
|
12
|
+
h[table.node] = table.columns.each_with_object({}) { |column,i|
|
13
|
+
i[column.name] = column.alias
|
14
|
+
}
|
15
|
+
}
|
16
|
+
@name_and_alias_cache = tables.each_with_object({}) { |table,h|
|
17
|
+
h[table.node] = table.columns.map { |column|
|
18
|
+
[column.name, column.alias]
|
19
|
+
}
|
20
|
+
@base_class_node_aliases ||= h[table.node] if table.node.is_a?(ActiveRecord::Associations::JoinDependency::JoinBase)
|
21
|
+
}
|
22
|
+
end
|
23
|
+
# valid formats are:
|
24
|
+
# 1 'table_name.column' or 'table_name.column as column_1' will be parsed! distinct on can be used also
|
25
|
+
# 2 {table_name: column} or { table_name: [column1, column2] }
|
26
|
+
# 3 table_name: 2
|
27
|
+
def update_aliases_to_select_values( select_values )
|
28
|
+
return if select_values.blank?
|
29
|
+
select_values.each do |sv|
|
30
|
+
# if sv is symbol that we assume that its a base table column and it will be aliased and added as usual
|
31
|
+
# all we need is some specials joins+select from related tables
|
32
|
+
if sv.is_a?(Hash)
|
33
|
+
flatten_hash_values(sv).each{|sub_sv| @base_class_node_aliases << [sub_sv, sub_sv] }
|
34
|
+
elsif sv.is_a?(String)
|
35
|
+
# this is the case of long raw select
|
36
|
+
sv.split( ", " ).each do |sub_sv|
|
37
|
+
if sub_sv[/.+ as .+/i]
|
38
|
+
selected_column = sub_sv[/ as .+/i][4..-1]
|
39
|
+
@base_class_node_aliases << [selected_column, selected_column]
|
40
|
+
elsif sub_sv[/.+\.[^\*]+/]
|
41
|
+
selected_column = sub_sv[/\..+/][1..-1]
|
42
|
+
@base_class_node_aliases << [selected_column, selected_column]
|
43
|
+
end
|
44
|
+
end
|
39
45
|
end
|
40
46
|
end
|
41
|
-
|
42
|
-
add_virtual_attribute(sv.right)
|
43
|
-
when Arel::Nodes::TableAlias
|
44
|
-
add_virtual_attribute(sv.right)
|
45
|
-
when Arel::Nodes::Function
|
46
|
-
add_virtual_attribute(sv.alias) if sv.alias.present?
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def slice_selected_attr_types( column_types )
|
52
|
-
column_types.slice( *@virtual_attributes_names )
|
53
|
-
end
|
54
|
-
|
55
|
-
private
|
56
|
-
def flatten_hash_values( some_hash )
|
57
|
-
some_hash.values.map{ |value| value.is_a?(Hash) ? flatten_hash_values( value ) : value }.flatten
|
58
|
-
end
|
59
|
-
|
60
|
-
def add_virtual_attribute(selected_column)
|
61
|
-
@base_class_node_aliases << [selected_column, selected_column]
|
62
|
-
@virtual_attributes_names << selected_column
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
::ActiveRecord::Associations::JoinDependency::JoinBase.class_eval do
|
67
|
-
def instantiate(row, aliases, column_types = {}, &block)
|
68
|
-
base_klass.instantiate(extract_record(row, aliases), column_types, &block)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
::ActiveRecord::Associations::JoinDependency.class_eval do
|
73
|
-
def instantiate(result_set, aliases)
|
74
|
-
primary_key = aliases.column_alias(join_root, join_root.primary_key)
|
75
|
-
|
76
|
-
seen = Hash.new { |h,parent_klass|
|
77
|
-
h[parent_klass] = Hash.new { |i,parent_id|
|
78
|
-
i[parent_id] = Hash.new { |j,child_klass| j[child_klass] = {} }
|
79
|
-
}
|
80
|
-
}
|
81
|
-
|
82
|
-
model_cache = Hash.new { |h,klass| h[klass] = {} }
|
83
|
-
parents = model_cache[join_root]
|
84
|
-
column_aliases = aliases.column_aliases join_root
|
85
|
-
|
86
|
-
message_bus = ActiveSupport::Notifications.instrumenter
|
87
|
-
|
88
|
-
payload = {
|
89
|
-
record_count: result_set.respond_to?(:length) ? result_set.length : result_set.rows.length,
|
90
|
-
class_name: join_root.base_klass.name
|
91
|
-
}
|
47
|
+
end
|
92
48
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
parent = parents[parent_key] ||= join_root.instantiate(row_hash, column_aliases, aliases.slice_selected_attr_types( result_set.column_types ) )
|
99
|
-
construct(parent, join_root, row_hash, result_set, seen, model_cache, aliases)
|
100
|
-
}
|
49
|
+
private
|
50
|
+
def flatten_hash_values( some_hash )
|
51
|
+
some_hash.values.map{ |value| value.is_a?(Hash) ? flatten_hash_values( value ) : value }.flatten
|
52
|
+
end
|
53
|
+
end
|
101
54
|
end
|
102
|
-
|
103
|
-
parents.values
|
104
55
|
end
|
105
56
|
end
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
else
|
128
|
-
if ActiveRecord::NullRelation === relation
|
129
|
-
[]
|
57
|
+
module ActiveRecord
|
58
|
+
module FinderMethods
|
59
|
+
|
60
|
+
def find_with_associations
|
61
|
+
# NOTE: the JoinDependency constructed here needs to know about
|
62
|
+
# any joins already present in `self`, so pass them in
|
63
|
+
#
|
64
|
+
# failing to do so means that in cases like activerecord/test/cases/associations/inner_join_association_test.rb:136
|
65
|
+
# incorrect SQL is generated. In that case, the join dependency for
|
66
|
+
# SpecialCategorizations is constructed without knowledge of the
|
67
|
+
# preexisting join in joins_values to categorizations (by way of
|
68
|
+
# the `has_many :through` for categories).
|
69
|
+
#
|
70
|
+
join_dependency = construct_join_dependency(joins_values)
|
71
|
+
|
72
|
+
aliases = join_dependency.aliases
|
73
|
+
relation = select aliases.columns
|
74
|
+
relation = apply_join_dependency(relation, join_dependency)
|
75
|
+
|
76
|
+
if block_given?
|
77
|
+
yield relation
|
130
78
|
else
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
79
|
+
if ActiveRecord::NullRelation === relation
|
80
|
+
[]
|
81
|
+
else
|
82
|
+
arel = relation.arel
|
83
|
+
rows = connection.select_all(arel, 'SQL', relation.bound_attributes)
|
84
|
+
#DISTINCTION IS HERE:
|
85
|
+
# now we gently mokey-patching existing column aliases with select values
|
86
|
+
aliases.update_aliases_to_select_values(values[:select]) unless values[:select].blank?
|
87
|
+
|
88
|
+
join_dependency.instantiate(rows, aliases)
|
89
|
+
end
|
138
90
|
end
|
139
|
-
end
|
140
91
|
end
|
92
|
+
end
|
141
93
|
end
|
142
94
|
|
143
|
-
|
@@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
|
|
30
30
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
31
31
|
spec.require_paths = ["lib"]
|
32
32
|
|
33
|
-
spec.add_dependency "activerecord", ">=
|
33
|
+
spec.add_dependency "activerecord", ">=5"
|
34
34
|
|
35
35
|
spec.add_development_dependency "bundler", "~> 1.13"
|
36
36
|
spec.add_development_dependency "rake", "~> 10.0"
|
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.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- alekseyl
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '5'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '5'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -61,11 +61,18 @@ extensions: []
|
|
61
61
|
extra_rdoc_files: []
|
62
62
|
files:
|
63
63
|
- ".gitignore"
|
64
|
-
-
|
64
|
+
- ".idea/.name"
|
65
|
+
- ".idea/misc.xml"
|
66
|
+
- ".idea/modules.xml"
|
67
|
+
- ".idea/rails_select_on_includes.iml"
|
68
|
+
- ".idea/vcs.xml"
|
69
|
+
- ".idea/workspace.xml"
|
65
70
|
- Gemfile
|
66
71
|
- LICENSE.txt
|
67
72
|
- README.md
|
68
73
|
- Rakefile
|
74
|
+
- bin/console
|
75
|
+
- bin/setup
|
69
76
|
- lib/rails_select_on_includes.rb
|
70
77
|
- lib/rails_select_on_includes/version.rb
|
71
78
|
- rails_select_on_includes.gemspec
|
@@ -90,7 +97,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
90
97
|
version: '0'
|
91
98
|
requirements: []
|
92
99
|
rubyforge_project:
|
93
|
-
rubygems_version: 2.
|
100
|
+
rubygems_version: 2.5.1
|
94
101
|
signing_key:
|
95
102
|
specification_version: 4
|
96
103
|
summary: Patching rails include/select/virtual attributes issue
|