active_record-union_relation 0.1.0 → 0.2.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/.github/dependabot.yml +16 -0
- data/.github/workflows/auto-merge.yml +22 -0
- data/.github/workflows/main.yml +72 -17
- data/CHANGELOG.md +17 -2
- data/CODE_OF_CONDUCT.md +1 -1
- data/Gemfile +5 -1
- data/Gemfile.lock +174 -113
- data/LICENSE +1 -1
- data/README.md +23 -8
- data/Rakefile +14 -5
- data/active_record-union_relation.gemspec +32 -22
- data/gemfiles/mysql/Gemfile +7 -0
- data/gemfiles/mysql/Gemfile.lock +202 -0
- data/gemfiles/postgresql/Gemfile +7 -0
- data/gemfiles/postgresql/Gemfile.lock +202 -0
- data/gemfiles/sqlite/Gemfile +7 -0
- data/gemfiles/sqlite/Gemfile.lock +203 -0
- data/lib/active_record/union_relation/version.rb +1 -1
- data/lib/active_record/union_relation.rb +55 -24
- metadata +41 -31
- data/.mergify.yml +0 -10
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "active_record"
|
4
|
+
require "active_record/union_relation/version"
|
5
5
|
|
6
6
|
module ActiveRecord
|
7
7
|
class UnionRelation
|
@@ -21,7 +21,7 @@ module ActiveRecord
|
|
21
21
|
# raise this error so there's not some weird undefined method behavior.
|
22
22
|
class NoConfiguredSubqueriesError < Error
|
23
23
|
def initialize
|
24
|
-
super(
|
24
|
+
super("No subqueries have been configured for this union")
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
@@ -31,7 +31,7 @@ module ActiveRecord
|
|
31
31
|
# Sometimes you need some columns in some subqeries that you don't need in
|
32
32
|
# others. In order to accomplish that and still maintain the matching
|
33
33
|
# number of columns, you can put a null in space of a column instead.
|
34
|
-
NULL = Arel.sql(
|
34
|
+
NULL = Arel.sql("NULL")
|
35
35
|
|
36
36
|
attr_reader :relation, :model_name, :sources
|
37
37
|
|
@@ -42,18 +42,20 @@ module ActiveRecord
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def to_arel(columns, discriminator)
|
45
|
-
relation
|
46
|
-
.
|
47
|
-
|
48
|
-
|
45
|
+
relation.select(
|
46
|
+
Arel.sql("'#{model_name}'").as(quote_column_name(discriminator)),
|
47
|
+
*sources
|
48
|
+
.zip(columns)
|
49
|
+
.map do |(source, column)|
|
49
50
|
Arel.sql(source.to_s).as(quote_column_name(column))
|
50
51
|
end
|
51
|
-
|
52
|
-
.arel
|
52
|
+
).arel
|
53
53
|
end
|
54
54
|
|
55
55
|
def to_mapping(columns)
|
56
|
-
|
56
|
+
# Remove the scope_name/table_name when using table_name.column
|
57
|
+
sources_without_scope = sources.map { _1.split(".").last }
|
58
|
+
[model_name, columns.zip(sources_without_scope).to_h]
|
57
59
|
end
|
58
60
|
|
59
61
|
private
|
@@ -96,16 +98,45 @@ module ActiveRecord
|
|
96
98
|
mappings = subqueries.to_h { |subquery| subquery.to_mapping(columns) }
|
97
99
|
|
98
100
|
Class.new(model) do
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
101
|
+
# Set the inheritance column and register the discriminator as a string
|
102
|
+
# column so that Active Record will instantiate the right subclass.
|
103
|
+
self.inheritance_column = discriminator
|
104
|
+
attribute inheritance_column, :string
|
105
|
+
|
106
|
+
define_singleton_method(:instantiate) do |attrs, columns = {}, &block|
|
107
|
+
mapped = {}
|
108
|
+
mapping = mappings[attrs[inheritance_column]]
|
109
|
+
|
110
|
+
# Map the result set columns back to their original source column
|
111
|
+
# names. This ensures that even though the UNION saw them as the same
|
112
|
+
# columns our resulting records see them as their original names.
|
113
|
+
attrs.each do |key, value|
|
114
|
+
case mapping[key]
|
115
|
+
when Subquery::NULL
|
116
|
+
# Ignore columns that didn't have a value.
|
117
|
+
when nil
|
118
|
+
# If we don't have a mapping for this column, then it's the
|
119
|
+
# discriminator. Map that column directly.
|
120
|
+
mapped[key] = value
|
121
|
+
else
|
122
|
+
# Otherwise, use the mapping to map the column back to its
|
123
|
+
# original name.
|
124
|
+
mapped[mapping[key]] = value
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# Now that we've mapped all of the columns, we can call super with the
|
129
|
+
# mapped values.
|
130
|
+
super(mapped, columns, &block)
|
131
|
+
end
|
132
|
+
|
133
|
+
# Override the default find_sti_class method because it does sanity
|
134
|
+
# checks to ensure that the class you're trying to instantiate is a
|
135
|
+
# subclass of the current class. Since we want to explicitly _not_ do
|
136
|
+
# that, we will instead just check that it is a valid model class.
|
137
|
+
define_singleton_method(:find_sti_class) do |type_name|
|
138
|
+
type = type_name.constantize
|
139
|
+
type < ActiveRecord::Base ? type : super(type_name)
|
109
140
|
end
|
110
141
|
end
|
111
142
|
end
|
@@ -113,9 +144,9 @@ module ActiveRecord
|
|
113
144
|
def union_for(model)
|
114
145
|
Arel::Nodes::As.new(
|
115
146
|
subqueries
|
116
|
-
.map { |subquery| subquery.to_arel(columns, discriminator) }
|
147
|
+
.map { |subquery| subquery.to_arel(columns, discriminator).ast }
|
117
148
|
.inject { |left, right| Arel::Nodes::Union.new(left, right) },
|
118
|
-
Arel.sql(model.connection.quote_table_name(
|
149
|
+
Arel.sql(model.connection.quote_table_name("union"))
|
119
150
|
)
|
120
151
|
end
|
121
152
|
end
|
@@ -129,7 +160,7 @@ module ActiveRecord
|
|
129
160
|
# One additional column will be added to the query in order to discriminate
|
130
161
|
# between all of the unioned types. Then when the objects are going to be
|
131
162
|
# instantiated, we map the columns back to their original names.
|
132
|
-
def self.union(*columns, discriminator:
|
163
|
+
def self.union(*columns, discriminator: "discriminator")
|
133
164
|
UnionRelation.new(columns, discriminator).tap { |union| yield union }.all
|
134
165
|
end
|
135
166
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_record-union_relation
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
- Kevin
|
8
|
-
autorequire:
|
7
|
+
- Kevin Newton
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-02-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -28,69 +28,69 @@ dependencies:
|
|
28
28
|
name: minitest
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: rails
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
61
|
+
version: '0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - "
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
68
|
+
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: syntax_tree
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- - "
|
73
|
+
- - ">="
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
75
|
+
version: '0'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- - "
|
80
|
+
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
83
|
-
description:
|
82
|
+
version: '0'
|
83
|
+
description:
|
84
84
|
email:
|
85
|
-
-
|
85
|
+
- kddnewton@gmail.com
|
86
86
|
executables: []
|
87
87
|
extensions: []
|
88
88
|
extra_rdoc_files: []
|
89
89
|
files:
|
90
90
|
- ".github/dependabot.yml"
|
91
|
+
- ".github/workflows/auto-merge.yml"
|
91
92
|
- ".github/workflows/main.yml"
|
92
93
|
- ".gitignore"
|
93
|
-
- ".mergify.yml"
|
94
94
|
- CHANGELOG.md
|
95
95
|
- CODE_OF_CONDUCT.md
|
96
96
|
- Gemfile
|
@@ -101,13 +101,23 @@ files:
|
|
101
101
|
- active_record-union_relation.gemspec
|
102
102
|
- bin/console
|
103
103
|
- bin/setup
|
104
|
+
- gemfiles/mysql/Gemfile
|
105
|
+
- gemfiles/mysql/Gemfile.lock
|
106
|
+
- gemfiles/postgresql/Gemfile
|
107
|
+
- gemfiles/postgresql/Gemfile.lock
|
108
|
+
- gemfiles/sqlite/Gemfile
|
109
|
+
- gemfiles/sqlite/Gemfile.lock
|
104
110
|
- lib/active_record/union_relation.rb
|
105
111
|
- lib/active_record/union_relation/version.rb
|
106
|
-
homepage: https://github.com/
|
112
|
+
homepage: https://github.com/kddnewton/active_record-union_relation
|
107
113
|
licenses:
|
108
114
|
- MIT
|
109
|
-
metadata:
|
110
|
-
|
115
|
+
metadata:
|
116
|
+
bug_tracker_uri: https://github.com/kddnewton/active_record-union_relation/issues
|
117
|
+
changelog_uri: https://github.com/kddnewton/active_record-union_relation/blob/v0.2.0/CHANGELOG.md
|
118
|
+
source_code_uri: https://github.com/kddnewton/active_record-union_relation
|
119
|
+
rubygems_mfa_required: 'true'
|
120
|
+
post_install_message:
|
111
121
|
rdoc_options: []
|
112
122
|
require_paths:
|
113
123
|
- lib
|
@@ -122,8 +132,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
122
132
|
- !ruby/object:Gem::Version
|
123
133
|
version: '0'
|
124
134
|
requirements: []
|
125
|
-
rubygems_version: 3.1
|
126
|
-
signing_key:
|
135
|
+
rubygems_version: 3.4.1
|
136
|
+
signing_key:
|
127
137
|
specification_version: 4
|
128
138
|
summary: Create ActiveRecord relations from UNIONs
|
129
139
|
test_files: []
|