active_record_union 1.2.0 → 1.4.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 +5 -5
- data/.github/workflows/testing.yml +70 -0
- data/Gemfile +4 -0
- data/README.md +14 -4
- data/Rakefile +4 -4
- data/active_record_union.gemspec +2 -5
- data/bin/create-db-users +44 -0
- data/lib/active_record_union/active_record/relation/union.rb +15 -29
- data/lib/active_record_union/version.rb +1 -1
- data/rails_6.0.gemfile +16 -0
- data/rails_6.1.gemfile +16 -0
- data/rails_7.0.gemfile +17 -0
- data/rails_7.1.gemfile +17 -0
- data/rails_7.2.gemfile +17 -0
- data/rails_8.0.gemfile +17 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/support/databases.rb +28 -5
- data/spec/union_spec.rb +38 -18
- metadata +22 -58
- data/.travis.yml +0 -19
- data/rails_4_2.gemfile +0 -6
- data/rails_5_0.gemfile +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b321a0fac09cf1cc331b5526f907b8dd78844796ed473093396dc08297997415
|
4
|
+
data.tar.gz: 020d7aca83f28ecfbf9455c48049f70c7fa0389ec72904c743fd724fd314ad05
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 35cb47fc2d57a16a74dbad2995cd90b87eecdea1a5aca340d8543cb504b73cd2a9cbbdb0e0c6cc4b71b416fcc04f3aa884f588067c316a0cc03dd60e9b5b376d
|
7
|
+
data.tar.gz: d0dff7b05b27f31b82d00ffe2f8cb5537b5c91a345ec31b02015cb31d020ace156277f8a9f6a8bac3c326f3890f5202213c909ec94435aac46a8be7a169b0ba9
|
@@ -0,0 +1,70 @@
|
|
1
|
+
name: RSpec Test Matrix
|
2
|
+
on:
|
3
|
+
push:
|
4
|
+
pull_request:
|
5
|
+
|
6
|
+
jobs:
|
7
|
+
test:
|
8
|
+
runs-on: ubuntu-latest
|
9
|
+
|
10
|
+
services:
|
11
|
+
postgresql:
|
12
|
+
image: postgres
|
13
|
+
ports:
|
14
|
+
- 5432:5432
|
15
|
+
options: >-
|
16
|
+
--health-cmd pg_isready
|
17
|
+
--health-interval 10s
|
18
|
+
--health-timeout 5s
|
19
|
+
--health-retries 5
|
20
|
+
env:
|
21
|
+
POSTGRES_DB: active_record_union
|
22
|
+
POSTGRES_USER: active_record_union
|
23
|
+
POSTGRES_PASSWORD: active_record_union
|
24
|
+
|
25
|
+
mysql2:
|
26
|
+
image: mysql:8.0
|
27
|
+
env:
|
28
|
+
MYSQL_DATABASE: active_record_union
|
29
|
+
MYSQL_ROOT_PASSWORD: active_record_union
|
30
|
+
options: >-
|
31
|
+
--health-cmd "mysqladmin ping"
|
32
|
+
--health-interval 10s
|
33
|
+
--health-timeout 5s
|
34
|
+
ports:
|
35
|
+
- "3306:3306"
|
36
|
+
|
37
|
+
strategy:
|
38
|
+
fail-fast: false
|
39
|
+
matrix:
|
40
|
+
# just define specific versions for each rails version
|
41
|
+
include:
|
42
|
+
- ruby: 2.6
|
43
|
+
rails: "6.0"
|
44
|
+
- ruby: "3.0"
|
45
|
+
rails: 6.1
|
46
|
+
- ruby: 3.1
|
47
|
+
rails: "7.0"
|
48
|
+
- ruby: 3.2
|
49
|
+
rails: 7.1
|
50
|
+
- ruby: 3.2
|
51
|
+
rails: 7.2
|
52
|
+
- ruby: 3.3
|
53
|
+
rails: "8.0"
|
54
|
+
|
55
|
+
env:
|
56
|
+
BUNDLE_GEMFILE: "rails_${{ matrix.rails }}.gemfile"
|
57
|
+
DB_HOST: 127.0.0.1
|
58
|
+
MYSQL_ROOT_HOST: "%"
|
59
|
+
MYSQL_DB: active_record_union
|
60
|
+
MYSQL_USER: root
|
61
|
+
MYSQL_PASSWORD: active_record_union
|
62
|
+
steps:
|
63
|
+
- uses: actions/checkout@v4
|
64
|
+
|
65
|
+
- uses: ruby/setup-ruby@v1
|
66
|
+
with:
|
67
|
+
ruby-version: ${{ matrix.ruby }}
|
68
|
+
bundler-cache: true # install gems and cache
|
69
|
+
|
70
|
+
- run: bundle exec rspec --force-color --format d
|
data/Gemfile
ADDED
data/README.md
CHANGED
@@ -17,6 +17,10 @@ user_1.posts.union(user_2.posts).union(Post.published)
|
|
17
17
|
user_1.posts.union_all(user_2.posts)
|
18
18
|
```
|
19
19
|
|
20
|
+
ActiveRecordUnion is tested against Rails 6.0, 6.1, 7.0, 7.1, 7.2 and 8.0.
|
21
|
+
|
22
|
+
If you are using Postgres, you might alternatively check out [ActiveRecordExtended](https://github.com/georgekaraszi/ActiveRecordExtended) which includes support for unions as well as other goodies.
|
23
|
+
|
20
24
|
## Installation
|
21
25
|
|
22
26
|
Add this line to your application's Gemfile:
|
@@ -127,7 +131,7 @@ SELECT "posts".* FROM (
|
|
127
131
|
|
128
132
|
There's a couple things to be aware of when using ActiveRecordUnion:
|
129
133
|
|
130
|
-
1. ActiveRecordUnion
|
134
|
+
1. ActiveRecordUnion will raise an error if you try to UNION any relations that do any preloading/eager-loading. There's no sensible way to do the preloading in the subselects. If enough people complain, maybe, we can change ActiveRecordUnion to let the queries run anyway but without preloading any records.
|
131
135
|
2. There's no easy way to get SQLite to allow ORDER BY in the UNION subselects. If you get a syntax error, you can either write `my_relation.reorder(nil).union(other.reorder(nil))` or switch to Postgres.
|
132
136
|
|
133
137
|
## Another nifty way to reduce extra queries
|
@@ -184,6 +188,12 @@ This is a gem not a Rails pull request because the standard of code quality for
|
|
184
188
|
|
185
189
|
## Changelog
|
186
190
|
|
191
|
+
**1.4.0** - October 1, 2025
|
192
|
+
- Ready for Rails 8.0! Support for Rails < 6 dropped. Updates provided by [@timdiggins](https://github.com/frederikspang) and [@frederikspang](https://github.com/frederikspang).
|
193
|
+
|
194
|
+
**1.3.0** - January 14, 2018
|
195
|
+
- Ready for Rails 5.2! Updates provided by [@glebm](https://github.com/glebm).
|
196
|
+
|
187
197
|
**1.2.0** - June 26, 2016
|
188
198
|
- Ready for Rails 5.0! Updates provided by [@glebm](https://github.com/glebm).
|
189
199
|
|
@@ -209,9 +219,9 @@ This public domain dedication follows the the CC0 1.0 at https://creativecommons
|
|
209
219
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
210
220
|
3. Run the tests:
|
211
221
|
1. Install MySQL and PostgreSQL.
|
212
|
-
2. You
|
213
|
-
3. Run `rake` to test with all supported Rails versions.
|
214
|
-
4. Run `rake
|
222
|
+
2. You need to be able to connect to a local MySQL and Postgres database as the default user, so the specs can create a `test_active_record_union` database. To set up the users this test expects, execute `bin/create-db-users` (or set the environment variables referenced in `spec/support/databases.rb`).
|
223
|
+
3. Run `rake` to test with all supported Rails versions. All needed dependencies will be installed via Bundler (`gem install bundler` if you happen not to have Bundler yet).
|
224
|
+
4. Run `rake test_rails_8_0` or `rake test_rails_7_2` etc. to test a specific Rails version.
|
215
225
|
4. There is also a `bin/console` command to load up a REPL for playing around
|
216
226
|
5. Commit your changes (`git commit -am 'Add some feature'`)
|
217
227
|
6. Push to the branch (`git push origin my-new-feature`)
|
data/Rakefile
CHANGED
@@ -51,11 +51,11 @@ end
|
|
51
51
|
|
52
52
|
|
53
53
|
TestTasks.gemfiles.each do |gemfile|
|
54
|
-
|
54
|
+
rails_version = gemfile[/rails_(.+)\.gemfile/, 1]
|
55
55
|
|
56
|
-
desc "Test Rails #{
|
57
|
-
task :"test_rails_#{
|
56
|
+
desc "Test Rails #{rails_version}"
|
57
|
+
task :"test_rails_#{rails_version.gsub(".", "_")}" do
|
58
58
|
env = { 'BUNDLE_GEMFILE' => gemfile }
|
59
59
|
TestTasks.run_one(env)
|
60
60
|
end
|
61
|
-
end
|
61
|
+
end
|
data/active_record_union.gemspec
CHANGED
@@ -18,13 +18,10 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) + spec.files.grep(%r{^bin/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_dependency "activerecord", ">=
|
21
|
+
spec.add_dependency "activerecord", ">= 6.0"
|
22
22
|
|
23
|
-
spec.add_development_dependency "bundler"
|
23
|
+
spec.add_development_dependency "bundler"
|
24
24
|
spec.add_development_dependency "rake"
|
25
25
|
spec.add_development_dependency "rspec", "~> 3.0"
|
26
26
|
spec.add_development_dependency "pry"
|
27
|
-
spec.add_development_dependency "sqlite3"
|
28
|
-
spec.add_development_dependency "pg"
|
29
|
-
spec.add_development_dependency "mysql2"
|
30
27
|
end
|
data/bin/create-db-users
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
GREEN='\033[0;32m'
|
4
|
+
RESET_COLOR='\033[0m'
|
5
|
+
|
6
|
+
if [ -n "$1" ]; then cat <<'HELP'; exit; fi
|
7
|
+
Usage: bin/create-db-users
|
8
|
+
Create the active_record_union database users for all the supported databases.
|
9
|
+
If the `DB` environment variable is set, do the above only for that database.
|
10
|
+
HELP
|
11
|
+
|
12
|
+
USER='active_record_union'
|
13
|
+
PASS='active_record_union'
|
14
|
+
|
15
|
+
set -e
|
16
|
+
log() { if [ -t 1 ]; then echo -e >&2 "${GREEN}create-db-users: $@${RESET_COLOR}"; else echo >&2 "$@"; fi }
|
17
|
+
|
18
|
+
create_mysql_user() {
|
19
|
+
if mysql -s -u"$USER" -p"$PASS" -e '' 2>/dev/null; then return; fi
|
20
|
+
log "Creating MySQL '$USER' user. MySQL root password required."
|
21
|
+
mysql --verbose -uroot -p <<SQL
|
22
|
+
CREATE USER '$USER'@'localhost' IDENTIFIED BY '$PASS';
|
23
|
+
GRANT ALL PRIVILEGES ON \`test_active_record_union\`.* TO '$USER'@'localhost';
|
24
|
+
SQL
|
25
|
+
}
|
26
|
+
|
27
|
+
create_postgresql_user() {
|
28
|
+
if PGPASSWORD="$PASS" psql -h 127.0.0.1 postgres -U $USER -c ''; then return; fi
|
29
|
+
log "Creating Postgres '$USER' user."
|
30
|
+
local cmd='psql postgres'
|
31
|
+
if ! $cmd -c '' 2>/dev/null; then
|
32
|
+
log "sudo required:"
|
33
|
+
cmd="sudo -u ${PG_DAEMON_USER:-postgres} psql postgres"
|
34
|
+
fi
|
35
|
+
# need to also create database first time
|
36
|
+
$cmd --echo-all <<SQL
|
37
|
+
CREATE ROLE $USER LOGIN PASSWORD '$PASS';
|
38
|
+
ALTER ROLE $USER CREATEDB;
|
39
|
+
CREATE DATABASE active_record_union;
|
40
|
+
SQL
|
41
|
+
}
|
42
|
+
|
43
|
+
[ -z "$DB" -o "$DB" = 'mysql2' ] && create_mysql_user
|
44
|
+
[ -z "$DB" -o "$DB" = 'postgresql' ] && create_postgresql_user
|
@@ -18,32 +18,31 @@ module ActiveRecord
|
|
18
18
|
private
|
19
19
|
|
20
20
|
def set_operation(operation, relation_or_where_arg, *args)
|
21
|
-
other = if args.
|
21
|
+
other = if args.empty? && relation_or_where_arg.is_a?(Relation)
|
22
22
|
relation_or_where_arg
|
23
23
|
else
|
24
|
-
|
24
|
+
self.klass.where(relation_or_where_arg, *args)
|
25
25
|
end
|
26
26
|
|
27
27
|
verify_relations_for_set_operation!(operation, self, other)
|
28
28
|
|
29
|
+
left = self.arel.ast
|
30
|
+
right = other.arel.ast
|
31
|
+
|
29
32
|
# Postgres allows ORDER BY in the UNION subqueries if each subquery is surrounded by parenthesis
|
30
|
-
# but SQLite does not allow parens around the subqueries
|
31
|
-
|
32
|
-
left
|
33
|
-
|
34
|
-
left, right = Arel::Nodes::Grouping.new(self.ast), Arel::Nodes::Grouping.new(other.ast)
|
33
|
+
# but SQLite does not allow parens around the subqueries
|
34
|
+
unless self.connection.visitor.is_a?(Arel::Visitors::SQLite)
|
35
|
+
left = Arel::Nodes::Grouping.new(left)
|
36
|
+
right = Arel::Nodes::Grouping.new(right)
|
35
37
|
end
|
36
38
|
|
37
39
|
set = SET_OPERATION_TO_AREL_CLASS[operation].new(left, right)
|
38
|
-
from = Arel::Nodes::TableAlias.new(set,
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
relation.bind_values = self.arel.bind_values + self.bind_values + other.arel.bind_values + other.bind_values
|
45
|
-
end
|
46
|
-
relation
|
40
|
+
from = Arel::Nodes::TableAlias.new(set, self.klass.arel_table.name)
|
41
|
+
build_union_relation(from, other)
|
42
|
+
end
|
43
|
+
|
44
|
+
def build_union_relation(arel_table_alias, _other)
|
45
|
+
self.klass.unscoped.from(arel_table_alias)
|
47
46
|
end
|
48
47
|
|
49
48
|
def verify_relations_for_set_operation!(operation, *relations)
|
@@ -63,19 +62,6 @@ module ActiveRecord
|
|
63
62
|
raise ArgumentError.new("Cannot #{operation} relation with eager load.")
|
64
63
|
end
|
65
64
|
end
|
66
|
-
|
67
|
-
if ActiveRecord::VERSION::MAJOR >= 5
|
68
|
-
class UnionFromClause < ActiveRecord::Relation::FromClause
|
69
|
-
def initialize(value, name, bound_attributes)
|
70
|
-
super(value, name)
|
71
|
-
@bound_attributes = bound_attributes
|
72
|
-
end
|
73
|
-
|
74
|
-
def binds
|
75
|
-
@bound_attributes
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
65
|
end
|
80
66
|
end
|
81
67
|
end
|
data/rails_6.0.gemfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in active_record_union.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
gem 'rails', '~> 6.0.0'
|
7
|
+
|
8
|
+
# https://github.com/rails/rails/blob/v6.0.6.1/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
|
9
|
+
gem "pg", ">= 0.18", "< 2.0"
|
10
|
+
|
11
|
+
# https://github.com/rails/rails/blob/v6.0.2/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb#L13
|
12
|
+
gem "sqlite3", "~> 1.4"
|
13
|
+
|
14
|
+
# https://github.com/rails/rails/blob/v6.0.6.1/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
|
15
|
+
gem "mysql2", ">= 0.4.4"
|
16
|
+
|
data/rails_6.1.gemfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in active_record_union.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
gem 'rails', '~> 6.1.0'
|
7
|
+
|
8
|
+
# https://github.com/rails/rails/blob/v6.1.7.10/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
|
9
|
+
gem "pg", "~> 1.1"
|
10
|
+
|
11
|
+
# https://github.com/rails/rails/blob/v6.1.2/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb#L13
|
12
|
+
gem "sqlite3", "~> 1.4"
|
13
|
+
|
14
|
+
# https://github.com/rails/rails/blob/v6.1.7.10/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
|
15
|
+
gem "mysql2", "~> 0.5"
|
16
|
+
|
data/rails_7.0.gemfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source 'https://rubygems.org'
|
4
|
+
|
5
|
+
# Specify your gem's dependencies in active_record_union.gemspec
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
gem 'rails', '~> 7.0.0'
|
9
|
+
|
10
|
+
# https://github.com/rails/rails/blob/v7.0.2/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb#L13
|
11
|
+
gem 'sqlite3', '~> 1.4'
|
12
|
+
|
13
|
+
# https://github.com/rails/rails/blob/v7.0.2/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L4
|
14
|
+
gem 'pg', '~> 1.1'
|
15
|
+
|
16
|
+
# https://github.com/rails/rails/blob/v7.0.2/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb#L6
|
17
|
+
gem 'mysql2', '~> 0.5'
|
data/rails_7.1.gemfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source 'https://rubygems.org'
|
4
|
+
|
5
|
+
# Specify your gem's dependencies in active_record_union.gemspec
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
gem 'rails', '~> 7.1.0'
|
9
|
+
|
10
|
+
# https://github.com/rails/rails/blob/v7.1.2/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb#L13
|
11
|
+
gem 'sqlite3', '~> 1.4'
|
12
|
+
|
13
|
+
# https://github.com/rails/rails/blob/v7.1.2/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L4
|
14
|
+
gem 'pg', '~> 1.1'
|
15
|
+
|
16
|
+
# https://github.com/rails/rails/blob/v7.1.2/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb#L6
|
17
|
+
gem 'mysql2', '~> 0.5'
|
data/rails_7.2.gemfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source 'https://rubygems.org'
|
4
|
+
|
5
|
+
# Specify your gem's dependencies in active_record_union.gemspec
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
gem 'rails', '~> 7.2.0'
|
9
|
+
|
10
|
+
# https://github.com/rails/rails/blob/v7.2.0/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb#L13
|
11
|
+
gem 'sqlite3', '>= 1.4'
|
12
|
+
|
13
|
+
# https://github.com/rails/rails/blob/v7.2.0/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L4
|
14
|
+
gem 'pg', '~> 1.1'
|
15
|
+
|
16
|
+
# https://github.com/rails/rails/blob/v7.2.0/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb#L6
|
17
|
+
gem 'mysql2', '~> 0.5'
|
data/rails_8.0.gemfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source 'https://rubygems.org'
|
4
|
+
|
5
|
+
# Specify your gem's dependencies in active_record_union.gemspec
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
gem 'rails', '~> 8.0.0'
|
9
|
+
|
10
|
+
# https://github.com/rails/rails/blob/main/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb#L13
|
11
|
+
gem 'sqlite3', '>= 2.1'
|
12
|
+
|
13
|
+
# https://github.com/rails/rails/blob/main/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb#L4
|
14
|
+
gem 'pg', '~> 1.1'
|
15
|
+
|
16
|
+
# https://github.com/rails/rails/blob/main/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb#L6
|
17
|
+
gem 'mysql2', '~> 0.5'
|
data/spec/spec_helper.rb
CHANGED
@@ -9,6 +9,7 @@ Databases.connect_to_sqlite
|
|
9
9
|
|
10
10
|
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
11
11
|
RSpec.configure do |config|
|
12
|
+
config.backtrace_inclusion_patterns << %r{gems/([0-9.])+/gems/(?!rspec|capybara)} if ENV['BACKTRACE']
|
12
13
|
# Run specs in random order to surface order dependencies. If you find an
|
13
14
|
# order dependency and want to debug it, you can fix the order by providing
|
14
15
|
# the seed, which is printed after each run.
|
data/spec/support/databases.rb
CHANGED
@@ -11,24 +11,47 @@ module Databases
|
|
11
11
|
|
12
12
|
def connect_to_postgres
|
13
13
|
ActiveRecord::Base.establish_connection(
|
14
|
-
adapter: "postgresql"
|
14
|
+
adapter: "postgresql",
|
15
|
+
host: ENV.fetch('DB_HOST', 'localhost'),
|
16
|
+
username: ENV.fetch("POSTGRES_USER", 'active_record_union'),
|
17
|
+
password: ENV.fetch("POSTGRES_PASSWORD", 'active_record_union')
|
15
18
|
)
|
16
|
-
|
19
|
+
try_to_drop_database
|
20
|
+
ActiveRecord::Base.connection.create_database("test_active_record_union")
|
17
21
|
ActiveRecord::Base.establish_connection(
|
18
22
|
adapter: "postgresql",
|
19
|
-
|
23
|
+
host: ENV.fetch('DB_HOST', 'localhost'),
|
24
|
+
username: ENV.fetch("POSTGRES_USER", 'active_record_union'),
|
25
|
+
password: ENV.fetch("POSTGRES_PASSWORD", 'active_record_union'),
|
26
|
+
database: ENV.fetch("POSTGRES_DB", "test_active_record_union")
|
20
27
|
)
|
21
28
|
load("support/models.rb")
|
22
29
|
end
|
23
30
|
|
31
|
+
def try_to_drop_database
|
32
|
+
ActiveRecord::Base.connection.drop_database("test_active_record_union")
|
33
|
+
rescue ActiveRecord::NoDatabaseError
|
34
|
+
$stderr.puts "Can't drop database 'test_active_record_union' as it doesn't exist"
|
35
|
+
rescue ActiveRecord::ActiveRecordError => e
|
36
|
+
$stderr.puts "Can't drop database 'test_active_record_union' (but continuing anyway): #{e}"
|
37
|
+
rescue => e
|
38
|
+
$stderr.puts "Other error (#{e.class.name}) dropping database 'test_active_record_union' (but continuing anyway): #{e}"
|
39
|
+
end
|
40
|
+
|
24
41
|
def connect_to_mysql
|
25
42
|
ActiveRecord::Base.establish_connection(
|
26
|
-
adapter: "mysql2"
|
43
|
+
adapter: "mysql2",
|
44
|
+
host: ENV.fetch('DB_HOST', 'localhost'),
|
45
|
+
username: ENV.fetch("MYSQL_USER", "active_record_union"),
|
46
|
+
password: ENV.fetch("MYSQL_PASSWORD", "active_record_union")
|
27
47
|
)
|
28
48
|
ActiveRecord::Base.connection.recreate_database("test_active_record_union")
|
29
49
|
ActiveRecord::Base.establish_connection(
|
30
50
|
adapter: "mysql2",
|
31
|
-
|
51
|
+
host: ENV.fetch('DB_HOST', 'localhost'),
|
52
|
+
username: ENV.fetch("MYSQL_USER", "active_record_union"),
|
53
|
+
password: ENV.fetch("MYSQL_PASSWORD", "active_record_union"),
|
54
|
+
database: ENV.fetch("MYSQL_DB", "test_active_record_union")
|
32
55
|
)
|
33
56
|
load("support/models.rb")
|
34
57
|
end
|
data/spec/union_spec.rb
CHANGED
@@ -2,7 +2,7 @@ require "spec_helper"
|
|
2
2
|
|
3
3
|
describe ActiveRecord::Relation do
|
4
4
|
TIME = Time.utc(2014, 7, 19, 0, 0, 0)
|
5
|
-
SQL_TIME =
|
5
|
+
SQL_TIME = "2014-07-19 00:00:00"
|
6
6
|
|
7
7
|
describe ".union" do
|
8
8
|
it "returns an ActiveRecord::Relation" do
|
@@ -34,18 +34,30 @@ describe ActiveRecord::Relation do
|
|
34
34
|
expect(union.to_sql.squish).to eq(
|
35
35
|
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 UNION SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '#{SQL_TIME}') ) \"posts\""
|
36
36
|
)
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
if ActiveRecord.version >= Gem::Version.new("7.2.0")
|
38
|
+
expect(union.arel.to_sql.squish).to eq(
|
39
|
+
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = ? UNION SELECT \"posts\".* FROM \"posts\" WHERE (created_at > ?) ) \"posts\""
|
40
|
+
)
|
41
|
+
expect(bind_values_from_arel(union.arel, Post.arel_table)).to eq([1, TIME])
|
42
|
+
else
|
43
|
+
expect(union.arel.to_sql.squish).to eq(
|
44
|
+
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = ? UNION SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '#{SQL_TIME}') ) \"posts\""
|
45
|
+
)
|
46
|
+
expect(bind_values_from_arel(union.arel, Post.arel_table)).to eq([1])
|
47
|
+
end
|
48
|
+
expect { union.to_a }.to_not raise_error
|
41
49
|
end
|
42
50
|
|
43
51
|
def bind_values_from_relation(relation)
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
52
|
+
bind_values_from_arel(relation.arel, relation.arel_table)
|
53
|
+
end
|
54
|
+
|
55
|
+
def bind_values_from_arel(arel, arel_table)
|
56
|
+
collector = Arel::Collectors::Bind.new
|
57
|
+
collector.define_singleton_method(:preparable=) { |_preparable| } if ActiveRecord.version.between?(Gem::Version.new("6.1.0"), Gem::Version.new("7.2.99"))
|
58
|
+
arel_table.class.engine.connection.visitor.accept(
|
59
|
+
arel.ast, collector
|
60
|
+
).value.map { |v| v.try(:value) || v }
|
49
61
|
end
|
50
62
|
|
51
63
|
it "binds values properly" do
|
@@ -68,16 +80,16 @@ describe ActiveRecord::Relation do
|
|
68
80
|
bind_values = bind_values_from_relation union
|
69
81
|
expect(bind_values).to eq([true, 11])
|
70
82
|
|
71
|
-
|
72
83
|
expect(union.to_sql.squish).to eq(
|
73
|
-
"SELECT \"users\".* FROM ( SELECT \"users\".* FROM \"users\" INNER JOIN \"posts\" ON \"posts\".\"
|
84
|
+
"SELECT \"users\".* FROM ( SELECT \"users\".* FROM \"users\" INNER JOIN \"posts\" ON \"posts\".\"draft\" = 1 AND \"posts\".\"user_id\" = \"users\".\"id\" UNION SELECT \"users\".* FROM \"users\" WHERE \"users\".\"id\" = 11 ) \"users\""
|
74
85
|
)
|
75
86
|
expect{union.to_a}.to_not raise_error
|
76
87
|
end
|
77
88
|
|
78
89
|
it "doesn't repeat default scopes" do
|
79
90
|
expect(Time).to receive(:now) { Time.utc(2014, 7, 24, 0, 0, 0) }
|
80
|
-
|
91
|
+
|
92
|
+
sql_now = "2014-07-24 00:00:00"
|
81
93
|
|
82
94
|
class PublishedPost < ActiveRecord::Base
|
83
95
|
self.table_name = "posts"
|
@@ -101,9 +113,15 @@ describe ActiveRecord::Relation do
|
|
101
113
|
|
102
114
|
context "in SQLite" do
|
103
115
|
it "lets ORDER BY in query subselects throw a syntax error" do
|
104
|
-
|
105
|
-
|
106
|
-
|
116
|
+
if ActiveRecord.version >= Gem::Version.new("7.2.0")
|
117
|
+
expect(union.to_sql.squish).to eq(
|
118
|
+
"SELECT \"posts\".* FROM ( (SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 ORDER BY \"posts\".\"created_at\" ASC) UNION (SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '2014-07-19 00:00:00') ORDER BY \"posts\".\"created_at\" ASC) ) \"posts\" ORDER BY \"created_at\" ASC"
|
119
|
+
)
|
120
|
+
else
|
121
|
+
expect(union.to_sql.squish).to eq(
|
122
|
+
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 ORDER BY \"posts\".\"created_at\" ASC UNION SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '2014-07-19 00:00:00') ORDER BY \"posts\".\"created_at\" ASC ) \"posts\" ORDER BY \"created_at\" ASC"
|
123
|
+
)
|
124
|
+
end
|
107
125
|
expect{union.to_a}.to raise_error(ActiveRecord::StatementInvalid)
|
108
126
|
end
|
109
127
|
end
|
@@ -112,8 +130,9 @@ describe ActiveRecord::Relation do
|
|
112
130
|
it "wraps query subselects in parentheses to allow ORDER BY clauses" do
|
113
131
|
Databases.with_postgres do
|
114
132
|
expect(union.to_sql.squish).to eq(
|
115
|
-
"SELECT \"posts\".* FROM ( (SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 ORDER BY \"posts\".\"created_at\" ASC) UNION (SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '
|
133
|
+
"SELECT \"posts\".* FROM ( (SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 ORDER BY \"posts\".\"created_at\" ASC) UNION (SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '2014-07-19 00:00:00') ORDER BY \"posts\".\"created_at\" ASC) ) \"posts\" ORDER BY \"created_at\" ASC"
|
116
134
|
)
|
135
|
+
|
117
136
|
expect{union.to_a}.to_not raise_error
|
118
137
|
end
|
119
138
|
end
|
@@ -123,8 +142,9 @@ describe ActiveRecord::Relation do
|
|
123
142
|
it "wraps query subselects in parentheses to allow ORDER BY clauses" do
|
124
143
|
Databases.with_mysql do
|
125
144
|
expect(union.to_sql.squish).to eq(
|
126
|
-
"SELECT `posts`.* FROM ( (SELECT `posts`.* FROM `posts` WHERE `posts`.`user_id` = 1 ORDER BY `posts`.`created_at` ASC) UNION (SELECT `posts`.* FROM `posts` WHERE (created_at > '
|
145
|
+
"SELECT `posts`.* FROM ( (SELECT `posts`.* FROM `posts` WHERE `posts`.`user_id` = 1 ORDER BY `posts`.`created_at` ASC) UNION (SELECT `posts`.* FROM `posts` WHERE (created_at > '2014-07-19 00:00:00') ORDER BY `posts`.`created_at` ASC) ) `posts` ORDER BY `created_at` ASC"
|
127
146
|
)
|
147
|
+
|
128
148
|
expect{union.to_a}.to_not raise_error
|
129
149
|
end
|
130
150
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_record_union
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Hempel
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-10-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '6.0'
|
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: '6.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
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
42
|
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,48 +80,6 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: sqlite3
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - ">="
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - ">="
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: pg
|
99
|
-
requirement: !ruby/object:Gem::Requirement
|
100
|
-
requirements:
|
101
|
-
- - ">="
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: '0'
|
104
|
-
type: :development
|
105
|
-
prerelease: false
|
106
|
-
version_requirements: !ruby/object:Gem::Requirement
|
107
|
-
requirements:
|
108
|
-
- - ">="
|
109
|
-
- !ruby/object:Gem::Version
|
110
|
-
version: '0'
|
111
|
-
- !ruby/object:Gem::Dependency
|
112
|
-
name: mysql2
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
114
|
-
requirements:
|
115
|
-
- - ">="
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: '0'
|
118
|
-
type: :development
|
119
|
-
prerelease: false
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
requirements:
|
122
|
-
- - ">="
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
version: '0'
|
125
83
|
description: UNIONs in ActiveRecord! Adds proper union and union_all methods to ActiveRecord::Relation.
|
126
84
|
email:
|
127
85
|
- plasticchicken@gmail.com
|
@@ -129,18 +87,24 @@ executables: []
|
|
129
87
|
extensions: []
|
130
88
|
extra_rdoc_files: []
|
131
89
|
files:
|
90
|
+
- ".github/workflows/testing.yml"
|
132
91
|
- ".gitignore"
|
133
|
-
-
|
92
|
+
- Gemfile
|
134
93
|
- LICENSE.txt
|
135
94
|
- README.md
|
136
95
|
- Rakefile
|
137
96
|
- active_record_union.gemspec
|
138
97
|
- bin/console
|
98
|
+
- bin/create-db-users
|
139
99
|
- lib/active_record_union.rb
|
140
100
|
- lib/active_record_union/active_record/relation/union.rb
|
141
101
|
- lib/active_record_union/version.rb
|
142
|
-
-
|
143
|
-
-
|
102
|
+
- rails_6.0.gemfile
|
103
|
+
- rails_6.1.gemfile
|
104
|
+
- rails_7.0.gemfile
|
105
|
+
- rails_7.1.gemfile
|
106
|
+
- rails_7.2.gemfile
|
107
|
+
- rails_8.0.gemfile
|
144
108
|
- spec/spec_helper.rb
|
145
109
|
- spec/support/databases.rb
|
146
110
|
- spec/support/models.rb
|
@@ -149,7 +113,7 @@ homepage: https://github.com/brianhempel/active_record_union
|
|
149
113
|
licenses:
|
150
114
|
- Public Domain
|
151
115
|
metadata: {}
|
152
|
-
post_install_message:
|
116
|
+
post_install_message:
|
153
117
|
rdoc_options: []
|
154
118
|
require_paths:
|
155
119
|
- lib
|
@@ -164,9 +128,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
164
128
|
- !ruby/object:Gem::Version
|
165
129
|
version: '0'
|
166
130
|
requirements: []
|
167
|
-
|
168
|
-
|
169
|
-
signing_key:
|
131
|
+
rubygems_version: 3.4.10
|
132
|
+
signing_key:
|
170
133
|
specification_version: 4
|
171
134
|
summary: UNIONs in ActiveRecord! Adds proper union and union_all methods to ActiveRecord::Relation.
|
172
135
|
test_files:
|
@@ -175,3 +138,4 @@ test_files:
|
|
175
138
|
- spec/support/models.rb
|
176
139
|
- spec/union_spec.rb
|
177
140
|
- bin/console
|
141
|
+
- bin/create-db-users
|
data/.travis.yml
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
language: ruby
|
2
|
-
addons:
|
3
|
-
postgresql: "9.4"
|
4
|
-
rvm:
|
5
|
-
- 2.3.1
|
6
|
-
- 2.2.5
|
7
|
-
- 2.1.8
|
8
|
-
- 2.0.0
|
9
|
-
gemfile:
|
10
|
-
- rails_4_2.gemfile
|
11
|
-
- rails_5_0.gemfile
|
12
|
-
matrix:
|
13
|
-
exclude:
|
14
|
-
# Rails 5 requires Ruby 2.2+:
|
15
|
-
- rvm: 2.1.8
|
16
|
-
gemfile: rails_5_0.gemfile
|
17
|
-
- rvm: 2.0.0
|
18
|
-
gemfile: rails_5_0.gemfile
|
19
|
-
script: bundle exec rspec
|
data/rails_4_2.gemfile
DELETED