active_record_extended 1.4.0 → 2.0.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 +8 -7
- data/lib/active_record_extended/active_record.rb +1 -10
- data/lib/active_record_extended/active_record/relation_patch.rb +16 -1
- data/lib/active_record_extended/arel.rb +1 -0
- data/lib/active_record_extended/arel/nodes.rb +22 -21
- data/lib/active_record_extended/arel/sql_literal.rb +16 -0
- data/lib/active_record_extended/query_methods/any_of.rb +5 -4
- data/lib/active_record_extended/query_methods/either.rb +1 -1
- data/lib/active_record_extended/query_methods/inet.rb +6 -2
- data/lib/active_record_extended/query_methods/json.rb +13 -16
- data/lib/active_record_extended/query_methods/select.rb +11 -10
- data/lib/active_record_extended/query_methods/unionize.rb +10 -4
- data/lib/active_record_extended/query_methods/where_chain.rb +14 -6
- data/lib/active_record_extended/query_methods/window.rb +4 -3
- data/lib/active_record_extended/query_methods/with_cte.rb +102 -35
- data/lib/active_record_extended/utilities/order_by.rb +9 -28
- data/lib/active_record_extended/utilities/support.rb +8 -15
- data/lib/active_record_extended/version.rb +1 -1
- data/spec/query_methods/any_of_spec.rb +2 -2
- data/spec/query_methods/json_spec.rb +5 -5
- data/spec/query_methods/select_spec.rb +13 -13
- data/spec/query_methods/unionize_spec.rb +5 -5
- data/spec/query_methods/with_cte_spec.rb +12 -2
- data/spec/spec_helper.rb +1 -1
- data/spec/sql_inspections/any_of_sql_spec.rb +2 -2
- data/spec/sql_inspections/contains_sql_queries_spec.rb +8 -8
- data/spec/sql_inspections/either_sql_spec.rb +3 -3
- data/spec/sql_inspections/json_sql_spec.rb +0 -1
- data/spec/sql_inspections/unionize_sql_spec.rb +2 -2
- data/spec/sql_inspections/window_sql_spec.rb +12 -0
- data/spec/sql_inspections/with_cte_sql_spec.rb +30 -1
- metadata +18 -20
- data/lib/active_record_extended/patch/5_0/predicate_builder_decorator.rb +0 -87
- data/lib/active_record_extended/patch/5_0/regex_match.rb +0 -10
@@ -17,13 +17,13 @@ RSpec.describe "Active Record Union Methods" do
|
|
17
17
|
|
18
18
|
it "should raise an error if the select statements do not align" do
|
19
19
|
expect { misaligned_cmd.to_a }.to(
|
20
|
-
raise_error(ActiveRecord::StatementInvalid, /each [[:alpha:]]+ query must have the same number of columns/)
|
20
|
+
raise_error(ActiveRecord::StatementInvalid, /each [[:alpha:]]+ query must have the same number of columns/)
|
21
21
|
)
|
22
22
|
end
|
23
23
|
|
24
24
|
it "should raise an argument error if there are less then two union statements" do
|
25
25
|
expect { lacking_union_cmd.to_a }.to(
|
26
|
-
raise_error(ArgumentError, "You are required to provide 2 or more unions to join!")
|
26
|
+
raise_error(ArgumentError, "You are required to provide 2 or more unions to join!")
|
27
27
|
)
|
28
28
|
end
|
29
29
|
end
|
@@ -88,7 +88,7 @@ RSpec.describe "Active Record Union Methods" do
|
|
88
88
|
query =
|
89
89
|
User.union.intersect(
|
90
90
|
User.select(:id, "profile_ls.likes").joins(:profile_l).where(profile_ls: { likes: 100 }),
|
91
|
-
User.select(:id, "profile_ls.likes").joins(:profile_l).where("profile_ls.likes < 150")
|
91
|
+
User.select(:id, "profile_ls.likes").joins(:profile_l).where("profile_ls.likes < 150")
|
92
92
|
)
|
93
93
|
|
94
94
|
expect(query.pluck(:id)).to have_attributes(size: 1).and(eq([user_one_pl.id]))
|
@@ -129,7 +129,7 @@ RSpec.describe "Active Record Union Methods" do
|
|
129
129
|
query =
|
130
130
|
User.union.all(
|
131
131
|
User.where(id: user_one.id),
|
132
|
-
User.where(id: user_three.id)
|
132
|
+
User.where(id: user_three.id)
|
133
133
|
).order_union(id: :desc)
|
134
134
|
|
135
135
|
expect(query).to eq([user_three, user_one])
|
@@ -144,7 +144,7 @@ RSpec.describe "Active Record Union Methods" do
|
|
144
144
|
query =
|
145
145
|
User.union.intersect(
|
146
146
|
User.where("id < ?", user_three.id),
|
147
|
-
User.where("id >= ?", user_one.id)
|
147
|
+
User.where("id >= ?", user_one.id)
|
148
148
|
).order_union(id: :desc)
|
149
149
|
|
150
150
|
expect(query).to eq([user_two, user_one])
|
@@ -3,8 +3,8 @@
|
|
3
3
|
require "spec_helper"
|
4
4
|
|
5
5
|
RSpec.describe "Active Record With CTE Query Methods" do
|
6
|
-
let!(:user_one)
|
7
|
-
let!(:user_two)
|
6
|
+
let!(:user_one) { User.create! }
|
7
|
+
let!(:user_two) { User.create! }
|
8
8
|
let!(:profile_one) { ProfileL.create!(user_id: user_one.id, likes: 200) }
|
9
9
|
let!(:profile_two) { ProfileL.create!(user_id: user_two.id, likes: 500) }
|
10
10
|
|
@@ -35,6 +35,16 @@ RSpec.describe "Active Record With CTE Query Methods" do
|
|
35
35
|
|
36
36
|
expect(query).to match_array([user_one])
|
37
37
|
end
|
38
|
+
|
39
|
+
it "should contain a unique list of ordered CTE keys when merging in multiple children" do
|
40
|
+
x = User.with(profile: ProfileL.where("likes < 300"))
|
41
|
+
y = User.with(profile: ProfileL.where("likes > 400"))
|
42
|
+
z = y.merge(x).joins("JOIN profile ON profile.id = users.id") # Y should reject X's CTE (FIFO)
|
43
|
+
query = User.with(my_profile: z).joins("JOIN my_profile ON my_profile.id = users.id")
|
44
|
+
|
45
|
+
expect(query.cte.with_keys).to eq([:profile, :my_profile])
|
46
|
+
expect(query).to match_array([user_two])
|
47
|
+
end
|
38
48
|
end
|
39
49
|
end
|
40
50
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -13,7 +13,7 @@ end
|
|
13
13
|
ActiveRecord::Base.establish_connection(ENV["DATABASE_URL"])
|
14
14
|
|
15
15
|
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require File.expand_path(f) }
|
16
|
-
Dir["#{File.dirname(__FILE__)}/**/*examples.rb"].each { |f| require f }
|
16
|
+
Dir["#{File.dirname(__FILE__)}/**/*examples.rb"].sort.each { |f| require f }
|
17
17
|
|
18
18
|
RSpec.configure do |config|
|
19
19
|
# Enable flags like --only-failures and --next-failure
|
@@ -5,8 +5,8 @@ require "spec_helper"
|
|
5
5
|
RSpec.describe "Any / None of SQL Queries" do
|
6
6
|
let(:equal_query) { '"users"."personal_id" = 1' }
|
7
7
|
let(:or_query) { 'OR "users"."personal_id" = 2' }
|
8
|
-
let(:equal_or) { equal_query
|
9
|
-
let(:join_query) { /INNER JOIN
|
8
|
+
let(:equal_or) { "#{equal_query} #{or_query}" }
|
9
|
+
let(:join_query) { /INNER JOIN "tags" ON "tags"."user_id" = "users"."id/ }
|
10
10
|
|
11
11
|
describe "where.any_of/1" do
|
12
12
|
it "should group different column arguments into nested or conditions" do
|
@@ -3,14 +3,14 @@
|
|
3
3
|
require "spec_helper"
|
4
4
|
|
5
5
|
RSpec.describe "Contains SQL Queries" do
|
6
|
-
let(:contains_array_regex) {
|
7
|
-
let(:contains_hstore_regex) {
|
8
|
-
let(:contains_jsonb_regex) {
|
9
|
-
let(:contained_in_array_regex) {
|
10
|
-
let(:contained_in_hstore_regex) {
|
11
|
-
let(:contained_in_jsonb_regex) {
|
12
|
-
let(:contains_equals_regex) {
|
13
|
-
let(:equality_regex) {
|
6
|
+
let(:contains_array_regex) { /"users"\."tag_ids" @> '\{1,2\}'/ }
|
7
|
+
let(:contains_hstore_regex) { /"users"\."data" @> '"nickname"=>"Dan"'/ }
|
8
|
+
let(:contains_jsonb_regex) { /"users"\."jsonb_data" @> '\{"nickname":"Dan"}'/ }
|
9
|
+
let(:contained_in_array_regex) { /"users"\."tag_ids" <@ '\{1,2\}'/ }
|
10
|
+
let(:contained_in_hstore_regex) { /"users"\."data" <@ '"nickname"=>"Dan"'/ }
|
11
|
+
let(:contained_in_jsonb_regex) { /"users"\."jsonb_data" <@ '\{"nickname":"Dan"}'/ }
|
12
|
+
let(:contains_equals_regex) { /"users"\."ip" >>= '127.0.0.1'/ }
|
13
|
+
let(:equality_regex) { /"users"\."tags" = '\{"?working"?\}'/ }
|
14
14
|
|
15
15
|
describe ".where.contains(:column => value)" do
|
16
16
|
it "generates the appropriate where clause for array columns" do
|
@@ -3,9 +3,9 @@
|
|
3
3
|
require "spec_helper"
|
4
4
|
|
5
5
|
RSpec.describe "Either Methods SQL Queries" do
|
6
|
-
let(:contains_array_regex) {
|
7
|
-
let(:profile_l_outer_join) { /LEFT OUTER JOIN
|
8
|
-
let(:profile_r_outer_join) { /LEFT OUTER JOIN
|
6
|
+
let(:contains_array_regex) { /"users"\."tag_ids" @> '\{1,2\}'/ }
|
7
|
+
let(:profile_l_outer_join) { /LEFT OUTER JOIN "profile_ls" ON "profile_ls"."user_id" = "users"."id"/ }
|
8
|
+
let(:profile_r_outer_join) { /LEFT OUTER JOIN "profile_rs" ON "profile_rs"."user_id" = "users"."id"/ }
|
9
9
|
let(:where_join_case) do
|
10
10
|
"WHERE ((CASE WHEN profile_ls.user_id IS NULL"\
|
11
11
|
" THEN profile_rs.user_id"\
|
@@ -27,7 +27,6 @@ RSpec.describe "JSON Methods SQL Queries" do
|
|
27
27
|
context "When adding cast_with: option" do
|
28
28
|
it "should wrap the row_to_json expression with to_jsonb" do
|
29
29
|
query = User.select_row_to_json(User.where(id: 10), cast_with: :to_jsonb, key: :convert_this, as: :results).to_sql
|
30
|
-
puts query
|
31
30
|
expect(query).to match_regex(/SELECT \(SELECT TO_JSONB\(ROW_TO_JSON\("convert_this"\)\) FROM \(.+\).+\) AS "results"/)
|
32
31
|
end
|
33
32
|
|
@@ -74,7 +74,7 @@ RSpec.describe "Union SQL Queries" do
|
|
74
74
|
|
75
75
|
it "should alias the union from clause to 'happy_users'" do
|
76
76
|
expect(described_method).to match_regex(/FROM \(+.+\) UNION \(.+\)+ happy_users$/)
|
77
|
-
expect(described_method).to match_regex(/^SELECT happy_users\.id FROM.+happy_users$/)
|
77
|
+
expect(described_method).to match_regex(/^SELECT (happy_users\.id|"happy_users"\."id") FROM.+happy_users$/)
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
@@ -83,7 +83,7 @@ RSpec.describe "Union SQL Queries" do
|
|
83
83
|
|
84
84
|
it "should retain the actual class calling table name as the union alias" do
|
85
85
|
expect(described_method).to match_regex(/FROM \(+.+\) UNION \(.+\)+ users$/)
|
86
|
-
expect(described_method).to match_regex(/^SELECT
|
86
|
+
expect(described_method).to match_regex(/^SELECT "users"\."id" FROM.+users$/)
|
87
87
|
end
|
88
88
|
end
|
89
89
|
end
|
@@ -82,5 +82,17 @@ RSpec.describe "Active Record WINDOW Query inspection" do
|
|
82
82
|
end
|
83
83
|
end
|
84
84
|
end
|
85
|
+
|
86
|
+
context "when not providing a partition by value" do
|
87
|
+
it "should construct a window function" do
|
88
|
+
query =
|
89
|
+
Tag
|
90
|
+
.define_window(:no_args).partition_by(order_by: { tag_number: :desc })
|
91
|
+
.select_window(:row_number, over: :no_args, as: :my_row)
|
92
|
+
.to_sql
|
93
|
+
|
94
|
+
expect(query).to eq("SELECT (ROW_NUMBER() OVER no_args) AS \"my_row\" FROM \"tags\" WINDOW no_args AS (ORDER BY tag_number DESC)")
|
95
|
+
end
|
96
|
+
end
|
85
97
|
end
|
86
98
|
end
|
@@ -21,6 +21,20 @@ RSpec.describe "Active Record WITH CTE tables" do
|
|
21
21
|
expect(query).to match_regex(with_personal_query)
|
22
22
|
end
|
23
23
|
|
24
|
+
it "will pipe Children CTE's into the Parent relation" do
|
25
|
+
personal_id_one_query = User.where(personal_id: 1)
|
26
|
+
personal_id_two_query = User.where(personal_id: 2)
|
27
|
+
|
28
|
+
sub_query = personal_id_two_query.with(personal_id_one: personal_id_one_query)
|
29
|
+
query = User.all.with(personal_id_two: sub_query)
|
30
|
+
expected_order = User.with(
|
31
|
+
personal_id_one: personal_id_one_query,
|
32
|
+
personal_id_two: personal_id_two_query
|
33
|
+
)
|
34
|
+
|
35
|
+
expect(query.to_sql).to eq(expected_order.to_sql)
|
36
|
+
end
|
37
|
+
|
24
38
|
context "when multiple CTE's" do
|
25
39
|
let(:chained_with) do
|
26
40
|
User.with(personal_id_one: User.where(personal_id: 1))
|
@@ -36,6 +50,7 @@ RSpec.describe "Active Record WITH CTE tables" do
|
|
36
50
|
.joins("JOIN personal_id_two ON personal_id_two.id = users.id")
|
37
51
|
.to_sql
|
38
52
|
end
|
53
|
+
|
39
54
|
it "Should only contain a single WITH statement" do
|
40
55
|
expect(with_arguments.scan(/WITH/).count).to eq(1)
|
41
56
|
expect(with_arguments.scan(/AS/).count).to eq(2)
|
@@ -60,7 +75,21 @@ RSpec.describe "Active Record WITH CTE tables" do
|
|
60
75
|
end
|
61
76
|
|
62
77
|
it "generates an expression with recursive" do
|
63
|
-
|
78
|
+
query = User.with
|
79
|
+
.recursive(personal_id_one: User.where(personal_id: 1))
|
80
|
+
.joins("JOIN personal_id_one ON personal_id_one.id = users.id")
|
81
|
+
.to_sql
|
82
|
+
|
83
|
+
expect(query).to match_regex(with_recursive_personal_query)
|
84
|
+
end
|
85
|
+
|
86
|
+
it "will maintain the CTE table when merging" do
|
87
|
+
sub_query = User.with.recursive(personal_id_one: User.where(personal_id: 1))
|
88
|
+
query = User.merge(sub_query)
|
89
|
+
.joins("JOIN personal_id_one ON personal_id_one.id = users.id")
|
90
|
+
.to_sql
|
91
|
+
|
92
|
+
expect(query).to match_regex(with_recursive_personal_query)
|
64
93
|
end
|
65
94
|
end
|
66
95
|
end
|
metadata
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_record_extended
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- George Protacio-Karaszi
|
8
8
|
- Dan McClain
|
9
9
|
- Olivier El Mekki
|
10
|
-
autorequire:
|
10
|
+
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2020-12-22 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
@@ -18,20 +18,20 @@ dependencies:
|
|
18
18
|
requirements:
|
19
19
|
- - ">="
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: '5.
|
21
|
+
version: '5.1'
|
22
22
|
- - "<"
|
23
23
|
- !ruby/object:Gem::Version
|
24
|
-
version: '6.
|
24
|
+
version: '6.2'
|
25
25
|
type: :runtime
|
26
26
|
prerelease: false
|
27
27
|
version_requirements: !ruby/object:Gem::Requirement
|
28
28
|
requirements:
|
29
29
|
- - ">="
|
30
30
|
- !ruby/object:Gem::Version
|
31
|
-
version: '5.
|
31
|
+
version: '5.1'
|
32
32
|
- - "<"
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: '6.
|
34
|
+
version: '6.2'
|
35
35
|
- !ruby/object:Gem::Dependency
|
36
36
|
name: ar_outer_joins
|
37
37
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,14 +52,14 @@ dependencies:
|
|
52
52
|
requirements:
|
53
53
|
- - "<"
|
54
54
|
- !ruby/object:Gem::Version
|
55
|
-
version: '
|
55
|
+
version: '3.0'
|
56
56
|
type: :runtime
|
57
57
|
prerelease: false
|
58
58
|
version_requirements: !ruby/object:Gem::Requirement
|
59
59
|
requirements:
|
60
60
|
- - "<"
|
61
61
|
- !ruby/object:Gem::Version
|
62
|
-
version: '
|
62
|
+
version: '3.0'
|
63
63
|
- !ruby/object:Gem::Dependency
|
64
64
|
name: bundler
|
65
65
|
requirement: !ruby/object:Gem::Requirement
|
@@ -69,7 +69,7 @@ dependencies:
|
|
69
69
|
version: '1.16'
|
70
70
|
- - "<"
|
71
71
|
- !ruby/object:Gem::Version
|
72
|
-
version: '
|
72
|
+
version: '3.0'
|
73
73
|
type: :development
|
74
74
|
prerelease: false
|
75
75
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -79,7 +79,7 @@ dependencies:
|
|
79
79
|
version: '1.16'
|
80
80
|
- - "<"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
82
|
+
version: '3.0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: database_cleaner
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -98,14 +98,14 @@ dependencies:
|
|
98
98
|
name: rake
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- - "
|
101
|
+
- - ">="
|
102
102
|
- !ruby/object:Gem::Version
|
103
103
|
version: '10.0'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- - "
|
108
|
+
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '10.0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
@@ -153,9 +153,8 @@ files:
|
|
153
153
|
- lib/active_record_extended/arel/aggregate_function_name.rb
|
154
154
|
- lib/active_record_extended/arel/nodes.rb
|
155
155
|
- lib/active_record_extended/arel/predications.rb
|
156
|
+
- lib/active_record_extended/arel/sql_literal.rb
|
156
157
|
- lib/active_record_extended/arel/visitors/postgresql_decorator.rb
|
157
|
-
- lib/active_record_extended/patch/5_0/predicate_builder_decorator.rb
|
158
|
-
- lib/active_record_extended/patch/5_0/regex_match.rb
|
159
158
|
- lib/active_record_extended/patch/5_1/where_clause.rb
|
160
159
|
- lib/active_record_extended/patch/5_2/where_clause.rb
|
161
160
|
- lib/active_record_extended/predicate_builder/array_handler_decorator.rb
|
@@ -199,7 +198,7 @@ homepage: https://github.com/georgekaraszi/ActiveRecordExtended
|
|
199
198
|
licenses:
|
200
199
|
- MIT
|
201
200
|
metadata: {}
|
202
|
-
post_install_message:
|
201
|
+
post_install_message:
|
203
202
|
rdoc_options: []
|
204
203
|
require_paths:
|
205
204
|
- lib
|
@@ -207,16 +206,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
207
206
|
requirements:
|
208
207
|
- - ">="
|
209
208
|
- !ruby/object:Gem::Version
|
210
|
-
version: '
|
209
|
+
version: '2.4'
|
211
210
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
212
211
|
requirements:
|
213
212
|
- - ">="
|
214
213
|
- !ruby/object:Gem::Version
|
215
214
|
version: '0'
|
216
215
|
requirements: []
|
217
|
-
|
218
|
-
|
219
|
-
signing_key:
|
216
|
+
rubygems_version: 3.0.6
|
217
|
+
signing_key:
|
220
218
|
specification_version: 4
|
221
219
|
summary: Adds extended functionality to Activerecord Postgres implementation
|
222
220
|
test_files:
|
@@ -1,87 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Stripped from Rails 5.1.x
|
4
|
-
# This patch is used so that when querying with hash elements that do not belong to an association,
|
5
|
-
# but instead a data attribute. It returns the corrected attribute binds back to the query builder.
|
6
|
-
#
|
7
|
-
# Without joins
|
8
|
-
# Before:
|
9
|
-
# User.where.contains(data: { nickname: "george" })
|
10
|
-
# #=> "SELECT \"people\".* FROM \"people\" WHERE (\"data\".\"nickname\" @> 'george')"
|
11
|
-
#
|
12
|
-
# After:
|
13
|
-
# User.where.contains(data: { nickname: "george" })
|
14
|
-
# #=> "SELECT \"people\".* FROM \"people\" WHERE (\"people\".\"data\" @> '\"nickname\"=>\"george\"')"
|
15
|
-
#
|
16
|
-
# With Joins
|
17
|
-
# Before:
|
18
|
-
# Tag.joins(:user).where.contains(people: { data: { nickname: "george" } })
|
19
|
-
# #=> NoMethodError: undefined method `type' for nil:NilClass
|
20
|
-
#
|
21
|
-
# After:
|
22
|
-
# Tag.joins(:user).where.contains(people: { data: { nickname: "george" } })
|
23
|
-
# #=> "SELECT \"tags\".* FROM \"tags\" INNER JOIN \"people\" ON \"people\".\"id\" = \"tags\".\"person_id\"
|
24
|
-
# WHERE (\"people\".\"data\" @> '\"nickname\"=>\"george\"')"
|
25
|
-
#
|
26
|
-
module ActiveRecord
|
27
|
-
class TableMetadata
|
28
|
-
def has_column?(column_name) # rubocop:disable Naming/PredicateName
|
29
|
-
klass&.columns_hash&.key?(column_name.to_s)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
class PredicateBuilder
|
34
|
-
def create_binds_for_hash(attributes) # rubocop:disable Metrics/PerceivedComplexity, Metrics/AbcSize
|
35
|
-
result = attributes.dup
|
36
|
-
binds = []
|
37
|
-
|
38
|
-
attributes.each do |column_name, value| # rubocop:disable Metrics/BlockLength
|
39
|
-
if value.is_a?(Hash) && !table.has_column?(column_name)
|
40
|
-
attrs, bvs = associated_predicate_builder(column_name).create_binds_for_hash(value)
|
41
|
-
result[column_name] = attrs
|
42
|
-
binds += bvs
|
43
|
-
next
|
44
|
-
elsif value.is_a?(Relation)
|
45
|
-
binds += value.bound_attributes
|
46
|
-
elsif value.is_a?(Range) && !table.type(column_name).respond_to?(:subtype)
|
47
|
-
first = value.begin
|
48
|
-
last = value.end
|
49
|
-
unless first.respond_to?(:infinite?) && first.infinite?
|
50
|
-
binds << build_bind_param(column_name, first)
|
51
|
-
first = Arel::Nodes::BindParam.new
|
52
|
-
end
|
53
|
-
unless last.respond_to?(:infinite?) && last.infinite?
|
54
|
-
binds << build_bind_param(column_name, last)
|
55
|
-
last = Arel::Nodes::BindParam.new
|
56
|
-
end
|
57
|
-
|
58
|
-
result[column_name] = RangeHandler::RangeWithBinds.new(first, last, value.exclude_end?)
|
59
|
-
elsif can_be_bound?(column_name, value)
|
60
|
-
result[column_name] = Arel::Nodes::BindParam.new
|
61
|
-
binds << build_bind_param(column_name, value)
|
62
|
-
end
|
63
|
-
|
64
|
-
# Find the foreign key when using queries such as:
|
65
|
-
# Post.where(author: author)
|
66
|
-
#
|
67
|
-
# For polymorphic relationships, find the foreign key and type:
|
68
|
-
# PriceEstimate.where(estimate_of: treasure)
|
69
|
-
if table.associated_with?(column_name)
|
70
|
-
result[column_name] = AssociationQueryHandler.value_for(table, column_name, value)
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
[result, binds]
|
75
|
-
end
|
76
|
-
|
77
|
-
def can_be_bound?(column_name, value)
|
78
|
-
return if table.associated_with?(column_name)
|
79
|
-
case value
|
80
|
-
when Array, Range
|
81
|
-
table.type(column_name).respond_to?(:subtype)
|
82
|
-
else
|
83
|
-
!value.nil? && handler_for(value).is_a?(BasicObjectHandler)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|