active_record_extended 1.4.0 → 2.1.1
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 +11 -10
- data/lib/active_record_extended/active_record/relation_patch.rb +16 -1
- data/lib/active_record_extended/active_record.rb +2 -11
- data/lib/active_record_extended/arel/nodes.rb +24 -21
- data/lib/active_record_extended/arel/predications.rb +3 -2
- data/lib/active_record_extended/arel/sql_literal.rb +16 -0
- data/lib/active_record_extended/arel/visitors/postgresql_decorator.rb +1 -1
- data/lib/active_record_extended/arel.rb +1 -0
- data/lib/active_record_extended/query_methods/any_of.rb +5 -4
- data/lib/active_record_extended/query_methods/either.rb +5 -2
- 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 +12 -6
- data/lib/active_record_extended/query_methods/where_chain.rb +19 -8
- data/lib/active_record_extended/query_methods/window.rb +4 -3
- data/lib/active_record_extended/query_methods/with_cte.rb +104 -37
- 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/either_spec.rb +11 -0
- 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 +6 -6
- data/spec/query_methods/with_cte_spec.rb +14 -4
- 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 +19 -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
- data/spec/support/database_cleaner.rb +1 -1
- data/spec/support/models.rb +12 -0
- 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
@@ -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
|
data/spec/support/models.rb
CHANGED
@@ -5,6 +5,8 @@ class ApplicationRecord < ActiveRecord::Base
|
|
5
5
|
end
|
6
6
|
|
7
7
|
class User < ApplicationRecord
|
8
|
+
has_many :groups_users, class_name: "GroupsUser"
|
9
|
+
has_many :groups, through: :groups_users, dependent: :destroy
|
8
10
|
has_many :hm_tags, class_name: "Tag"
|
9
11
|
has_one :profile_l, class_name: "ProfileL"
|
10
12
|
has_one :profile_r, class_name: "ProfileR"
|
@@ -66,3 +68,13 @@ class VersionControl < ApplicationRecord
|
|
66
68
|
# t.jsonb :source, default: {}, null: false
|
67
69
|
#
|
68
70
|
end
|
71
|
+
|
72
|
+
class Group < ApplicationRecord
|
73
|
+
has_many :groups_users, class_name: "GroupsUser"
|
74
|
+
has_many :users, through: :groups_users, dependent: :destroy
|
75
|
+
end
|
76
|
+
|
77
|
+
class GroupsUser < ApplicationRecord
|
78
|
+
belongs_to :user
|
79
|
+
belongs_to :group
|
80
|
+
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: 1.
|
4
|
+
version: 2.1.1
|
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: 2022-02-14 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:
|
24
|
+
version: 7.1.0
|
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:
|
34
|
+
version: 7.1.0
|
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.2.15
|
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
|