active_record_union 1.1.0 → 1.3.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/.gitignore +1 -0
- data/.travis.yml +8 -5
- data/README.md +33 -7
- data/Rakefile +57 -4
- data/lib/active_record_union/active_record/relation/union.rb +57 -24
- data/lib/active_record_union/version.rb +1 -1
- data/rails_4_2.gemfile +11 -0
- data/rails_5_0.gemfile +11 -0
- data/rails_5_1.gemfile +11 -0
- data/rails_5_2.gemfile +7 -0
- data/spec/support/databases.rb +2 -0
- data/spec/support/models.rb +2 -0
- data/spec/union_spec.rb +53 -18
- metadata +7 -4
- data/Gemfile +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b6908e8b32e304532e3b8405712ade67fab7e52
|
4
|
+
data.tar.gz: 937298c6d470715b18510f2e317a7f3d735b2cf6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8466c15bb3c268bbea28f49b3a85915e91f72bd43997462a4bd09a1da14ec9c85d5ca9621a6b7316ac04fe6949ccf4284bce854ed5b33c71bc492ca9bbd0cc9f
|
7
|
+
data.tar.gz: a99a9d9c0c19e332d9b0c357310d886c72c5b7f629be97a0d8e2dcdc8d90dcb1605960f3a44a8dacdfe0749dc7dfe6f2571ebe27d63a6bcb1970e7798447a88f
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
language: ruby
|
2
2
|
addons:
|
3
|
-
postgresql: "9.
|
4
|
-
before_script:
|
5
|
-
- psql -c 'create database travis;' -U postgres # "travis" is the UNIX username, and therefore the default database name on connection
|
3
|
+
postgresql: "9.4"
|
6
4
|
rvm:
|
7
|
-
- 2.
|
8
|
-
|
5
|
+
- 2.3.6
|
6
|
+
gemfile:
|
7
|
+
- rails_4_2.gemfile
|
8
|
+
- rails_5_0.gemfile
|
9
|
+
- rails_5_1.gemfile
|
10
|
+
- rails_5_2.gemfile
|
11
|
+
script: bundle exec rspec
|
data/README.md
CHANGED
@@ -7,6 +7,18 @@ Use unions on ActiveRecord scopes without ugliness.
|
|
7
7
|
|
8
8
|
If you find yourself writing `pluck(:id)` and then feeding that into another query, you may be able to reduce the number of database requests by using a nested query or a UNION without writing crazy JOIN statements.
|
9
9
|
|
10
|
+
Quick usage examples:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
current_user.posts.union(Post.published)
|
14
|
+
current_user.posts.union(Post.published).where(id: [6, 7])
|
15
|
+
current_user.posts.union("published_at < ?", Time.now)
|
16
|
+
user_1.posts.union(user_2.posts).union(Post.published)
|
17
|
+
user_1.posts.union_all(user_2.posts)
|
18
|
+
```
|
19
|
+
|
20
|
+
ActiveRecordUnion is tested against Rails 4.2 and Rails 5.0. It may or may not work on Rails 4.0/4.1.
|
21
|
+
|
10
22
|
## Installation
|
11
23
|
|
12
24
|
Add this line to your application's Gemfile:
|
@@ -55,7 +67,7 @@ SELECT "posts".* FROM (
|
|
55
67
|
SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = 1
|
56
68
|
UNION
|
57
69
|
SELECT "posts".* FROM "posts" WHERE (published_at < '2014-07-19 16:04:21.918366')
|
58
|
-
) posts
|
70
|
+
) "posts"
|
59
71
|
```
|
60
72
|
|
61
73
|
Because the `union` method returns another `ActiveRecord::Relation`, we can run further queries on the union.
|
@@ -68,7 +80,7 @@ SELECT "posts".* FROM (
|
|
68
80
|
SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = 1
|
69
81
|
UNION
|
70
82
|
SELECT "posts".* FROM "posts" WHERE (published_at < '2014-07-19 16:06:04.460771')
|
71
|
-
) posts WHERE "posts"."id" IN (6, 7)
|
83
|
+
) "posts" WHERE "posts"."id" IN (6, 7)
|
72
84
|
```
|
73
85
|
|
74
86
|
The `union` method can also accept anything that `where` does.
|
@@ -92,10 +104,10 @@ SELECT "posts".* FROM (
|
|
92
104
|
SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = 1
|
93
105
|
UNION
|
94
106
|
SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = 2
|
95
|
-
) posts
|
107
|
+
) "posts"
|
96
108
|
UNION
|
97
109
|
SELECT "posts".* FROM "posts" WHERE (published_at < '2014-07-19 16:12:45.882648')
|
98
|
-
) posts
|
110
|
+
) "posts"
|
99
111
|
```
|
100
112
|
|
101
113
|
### UNION ALL
|
@@ -110,14 +122,14 @@ SELECT "posts".* FROM (
|
|
110
122
|
SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = 1
|
111
123
|
UNION ALL
|
112
124
|
SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = 2
|
113
|
-
) posts
|
125
|
+
) "posts"
|
114
126
|
```
|
115
127
|
|
116
128
|
## Caveats
|
117
129
|
|
118
130
|
There's a couple things to be aware of when using ActiveRecordUnion:
|
119
131
|
|
120
|
-
1. ActiveRecordUnion
|
132
|
+
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.
|
121
133
|
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.
|
122
134
|
|
123
135
|
## Another nifty way to reduce extra queries
|
@@ -174,6 +186,16 @@ This is a gem not a Rails pull request because the standard of code quality for
|
|
174
186
|
|
175
187
|
## Changelog
|
176
188
|
|
189
|
+
**1.3.0** - January 14, 2018
|
190
|
+
- Ready for Rails 5.2! Updates provided by [@glebm](https://github.com/glebm).
|
191
|
+
|
192
|
+
**1.2.0** - June 26, 2016
|
193
|
+
- Ready for Rails 5.0! Updates provided by [@glebm](https://github.com/glebm).
|
194
|
+
|
195
|
+
**1.1.1** - Mar 19, 2016
|
196
|
+
- Fix broken polymorphic associations and joins due to improper handling of bind values. Fix by [@efradelos](https://github.com/efradelos), reported by [@Machiaweliczny](https://github.com/Machiaweliczny) and [@seandougall](https://github.com/seandougall).
|
197
|
+
- Quote table name aliases properly. Reported by [@odedniv](https://github.com/odedniv).
|
198
|
+
|
177
199
|
**1.1.0** - Mar 29, 2015 - Add UNION ALL support, courtesy of [@pic](https://github.com/pic).
|
178
200
|
|
179
201
|
**1.0.1** - Sept 2, 2014 - Allow ORDER BY in UNION subselects for databases that support it (not SQLite).
|
@@ -190,7 +212,11 @@ This public domain dedication follows the the CC0 1.0 at https://creativecommons
|
|
190
212
|
|
191
213
|
1. Fork it ( https://github.com/brianhempel/active_record_union/fork )
|
192
214
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
193
|
-
3. Run the tests
|
215
|
+
3. Run the tests:
|
216
|
+
1. Install MySQL and PostgreSQL.
|
217
|
+
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. From a vanilla install of MariaDB from Homebrew, this just works. For Postgres installed by Homebrew, you may need to run `$ echo "create database my_computer_user_name;" | psql postgres` since the initial database created by Homebrew is named "postgres" but PG defaults to connecting to a database named after your username.
|
218
|
+
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).
|
219
|
+
4. Run `rake test_rails_4_2` or `rake test_rails_5_2` etc. to test a specific Rails version.
|
194
220
|
4. There is also a `bin/console` command to load up a REPL for playing around
|
195
221
|
5. Commit your changes (`git commit -am 'Add some feature'`)
|
196
222
|
6. Push to the branch (`git push origin my-new-feature`)
|
data/Rakefile
CHANGED
@@ -1,8 +1,61 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
|
3
|
-
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
RSpec::Core::RakeTask.new(:spec)
|
5
|
+
task :default => :test_all_gemfiles
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
-
|
7
|
+
module TestTasks
|
8
|
+
module_function
|
9
|
+
|
10
|
+
TEST_CMD = 'bundle exec rspec'
|
11
|
+
|
12
|
+
def run_all(envs, cmd = "bundle install && #{TEST_CMD}", success_message)
|
13
|
+
statuses = envs.map { |env| run(env, cmd) }
|
14
|
+
failed = statuses.reject(&:first).map(&:last)
|
15
|
+
if failed.empty?
|
16
|
+
$stderr.puts success_message
|
17
|
+
else
|
18
|
+
$stderr.puts "❌ FAILING (#{failed.size}):\n#{failed.map { |env| to_bash_cmd_with_env(cmd, env) } * "\n"}"
|
19
|
+
exit 1
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def run_one(env, cmd = "bundle install && #{TEST_CMD}")
|
24
|
+
full_cmd = to_bash_cmd_with_env(cmd, env)
|
25
|
+
exec(full_cmd)
|
26
|
+
end
|
27
|
+
|
28
|
+
def run(env, cmd)
|
29
|
+
Bundler.with_clean_env do
|
30
|
+
full_cmd = to_bash_cmd_with_env(cmd, env)
|
31
|
+
$stderr.puts full_cmd
|
32
|
+
isSuccess = system(full_cmd)
|
33
|
+
[isSuccess, env]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def gemfiles
|
38
|
+
Dir.glob('*.gemfile').sort
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_bash_cmd_with_env(cmd, env)
|
42
|
+
"(export #{env.map { |k, v| "#{k}=#{v}" }.join(' ')}; #{cmd})"
|
43
|
+
end
|
8
44
|
end
|
45
|
+
|
46
|
+
desc 'Test all Gemfiles'
|
47
|
+
task :test_all_gemfiles do
|
48
|
+
envs = TestTasks.gemfiles.map { |gemfile| { 'BUNDLE_GEMFILE' => gemfile } }
|
49
|
+
TestTasks.run_all envs, "✓ Tests pass with all #{envs.size} gemfiles"
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
TestTasks.gemfiles.each do |gemfile|
|
54
|
+
rails_version_underscored = gemfile[/rails_(.+)\.gemfile/, 1]
|
55
|
+
|
56
|
+
desc "Test Rails #{rails_version_underscored.gsub("_", ".")}"
|
57
|
+
task :"test_rails_#{rails_version_underscored}" do
|
58
|
+
env = { 'BUNDLE_GEMFILE' => gemfile }
|
59
|
+
TestTasks.run_one(env)
|
60
|
+
end
|
61
|
+
end
|
@@ -3,46 +3,79 @@ module ActiveRecord
|
|
3
3
|
module Union
|
4
4
|
|
5
5
|
SET_OPERATION_TO_AREL_CLASS = {
|
6
|
-
|
7
|
-
|
6
|
+
union: Arel::Nodes::Union,
|
7
|
+
union_all: Arel::Nodes::UnionAll
|
8
8
|
}
|
9
9
|
|
10
|
-
def
|
11
|
-
set_operation(
|
10
|
+
def union(relation_or_where_arg, *args)
|
11
|
+
set_operation(:union, relation_or_where_arg, *args)
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
15
|
-
set_operation(
|
14
|
+
def union_all(relation_or_where_arg, *args)
|
15
|
+
set_operation(:union_all, relation_or_where_arg, *args)
|
16
16
|
end
|
17
17
|
|
18
18
|
private
|
19
19
|
|
20
20
|
def set_operation(operation, relation_or_where_arg, *args)
|
21
|
-
other = if args.
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
other = if args.empty? && relation_or_where_arg.is_a?(Relation)
|
22
|
+
relation_or_where_arg
|
23
|
+
else
|
24
|
+
@klass.where(relation_or_where_arg, *args)
|
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
|
-
set
|
38
|
-
from = Arel::Nodes::TableAlias.new(
|
39
|
-
|
40
|
-
|
41
|
-
)
|
39
|
+
set = SET_OPERATION_TO_AREL_CLASS[operation].new(left, right)
|
40
|
+
from = Arel::Nodes::TableAlias.new(set, @klass.arel_table.name)
|
41
|
+
build_union_relation(from, other)
|
42
|
+
end
|
42
43
|
|
43
|
-
|
44
|
-
|
45
|
-
|
44
|
+
if ActiveRecord.gem_version >= Gem::Version.new('5.2.0.beta2')
|
45
|
+
# Since Rails 5.2, binds are maintained only in the Arel AST.
|
46
|
+
def build_union_relation(arel_table_alias, _other)
|
47
|
+
@klass.unscoped.from(arel_table_alias)
|
48
|
+
end
|
49
|
+
elsif ActiveRecord::VERSION::MAJOR >= 5
|
50
|
+
# In Rails >= 5.0, < 5.2, binds are maintained only in ActiveRecord
|
51
|
+
# relations and clauses.
|
52
|
+
def build_union_relation(arel_table_alias, other)
|
53
|
+
relation = @klass.unscoped.spawn
|
54
|
+
relation.from_clause =
|
55
|
+
UnionFromClause.new(arel_table_alias, nil,
|
56
|
+
self.bound_attributes + other.bound_attributes)
|
57
|
+
relation
|
58
|
+
end
|
59
|
+
|
60
|
+
class UnionFromClause < ActiveRecord::Relation::FromClause
|
61
|
+
def initialize(value, name, bound_attributes)
|
62
|
+
super(value, name)
|
63
|
+
@bound_attributes = bound_attributes
|
64
|
+
end
|
65
|
+
|
66
|
+
def binds
|
67
|
+
@bound_attributes
|
68
|
+
end
|
69
|
+
end
|
70
|
+
else
|
71
|
+
# In Rails 4.x, binds are maintained in both ActiveRecord relations and
|
72
|
+
# clauses and also in their Arel ASTs.
|
73
|
+
def build_union_relation(arel_table_alias, other)
|
74
|
+
relation = @klass.unscoped.from(arel_table_alias)
|
75
|
+
relation.bind_values = self.arel.bind_values + self.bind_values +
|
76
|
+
other.arel.bind_values + other.bind_values
|
77
|
+
relation
|
78
|
+
end
|
46
79
|
end
|
47
80
|
|
48
81
|
def verify_relations_for_set_operation!(operation, *relations)
|
data/rails_4_2.gemfile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in active_record_union.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
gem 'rails', '~> 4.2.7'
|
7
|
+
|
8
|
+
# On Rails < 5.2, only pg < v1 is supported. See:
|
9
|
+
# https://github.com/rails/rails/pull/31671
|
10
|
+
# https://bitbucket.org/ged/ruby-pg/issues/270/pg-100-x64-mingw32-rails-server-not-start
|
11
|
+
gem 'pg', '~> 0.21'
|
data/rails_5_0.gemfile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in active_record_union.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
gem 'rails', '~> 5.0.0'
|
7
|
+
|
8
|
+
# On Rails < 5.2, only pg < v1 is supported. See:
|
9
|
+
# https://github.com/rails/rails/pull/31671
|
10
|
+
# https://bitbucket.org/ged/ruby-pg/issues/270/pg-100-x64-mingw32-rails-server-not-start
|
11
|
+
gem 'pg', '~> 0.21'
|
data/rails_5_1.gemfile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in active_record_union.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
gem 'rails', '~> 5.1.0'
|
7
|
+
|
8
|
+
# On Rails < 5.2, only pg < v1 is supported. See:
|
9
|
+
# https://github.com/rails/rails/pull/31671
|
10
|
+
# https://bitbucket.org/ged/ruby-pg/issues/270/pg-100-x64-mingw32-rails-server-not-start
|
11
|
+
gem 'pg', '~> 0.21'
|
data/rails_5_2.gemfile
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in active_record_union.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
# pg v1.0+ compatibility, https://github.com/rails/rails/pull/31671:
|
7
|
+
gem 'rails', '~> 5.2.0.beta2', git: 'https://github.com/rails/rails', ref: 'f1af27fd9d9101684b26d0dcf2028859d67bec1f'
|
data/spec/support/databases.rb
CHANGED
data/spec/support/models.rb
CHANGED
@@ -3,10 +3,12 @@ end
|
|
3
3
|
|
4
4
|
class User < ActiveRecord::Base
|
5
5
|
has_many :posts
|
6
|
+
has_many :drafts, -> { where draft: true }, class_name: "Post"
|
6
7
|
end unless defined?(User)
|
7
8
|
|
8
9
|
ActiveRecord::Base.connection.create_table :posts, force: true do |t|
|
9
10
|
t.integer :user_id
|
11
|
+
t.boolean :draft
|
10
12
|
t.timestamp :published_at
|
11
13
|
t.timestamps null: false
|
12
14
|
end
|
data/spec/union_spec.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
-
require
|
1
|
+
require "spec_helper"
|
2
2
|
|
3
3
|
describe ActiveRecord::Relation do
|
4
|
+
TIME = Time.utc(2014, 7, 19, 0, 0, 0)
|
5
|
+
SQL_TIME = ActiveRecord::VERSION::MAJOR >= 5 ? "2014-07-19 00:00:00" : "2014-07-19 00:00:00.000000"
|
6
|
+
|
4
7
|
describe ".union" do
|
5
8
|
it "returns an ActiveRecord::Relation" do
|
6
9
|
expect(User.all.union(User.all)).to be_kind_of(ActiveRecord::Relation)
|
@@ -26,55 +29,84 @@ describe ActiveRecord::Relation do
|
|
26
29
|
end
|
27
30
|
|
28
31
|
it "works" do
|
29
|
-
union = User.new(id: 1).posts.union(Post.where("created_at > ?",
|
32
|
+
union = User.new(id: 1).posts.union(Post.where("created_at > ?", TIME))
|
30
33
|
|
31
34
|
expect(union.to_sql.squish).to eq(
|
32
|
-
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 UNION SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '
|
35
|
+
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 UNION SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '#{SQL_TIME}') ) \"posts\""
|
33
36
|
)
|
34
37
|
expect(union.arel.to_sql.squish).to eq(
|
35
|
-
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = ? UNION SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '
|
38
|
+
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = ? UNION SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '#{SQL_TIME}') ) \"posts\""
|
36
39
|
)
|
37
40
|
expect{union.to_a}.to_not raise_error
|
38
41
|
end
|
39
42
|
|
43
|
+
def bind_values_from_relation(relation)
|
44
|
+
if ActiveRecord.gem_version >= Gem::Version.new('5.2.0.beta2')
|
45
|
+
relation.arel_table.class.engine.connection.visitor.accept(
|
46
|
+
relation.arel.ast, Arel::Collectors::Bind.new
|
47
|
+
).value.map(&:value)
|
48
|
+
elsif ActiveRecord::VERSION::MAJOR >= 5
|
49
|
+
relation.bound_attributes.map { |a| a.value_for_database }
|
50
|
+
else
|
51
|
+
(relation.arel.bind_values + relation.bind_values).map { |_column, value| value }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
40
55
|
it "binds values properly" do
|
41
56
|
user1 = User.new(id: 1)
|
42
57
|
user2 = User.new(id: 2)
|
43
58
|
user3 = User.new(id: 3)
|
44
59
|
|
45
60
|
union = user1.posts.union(user2.posts).where.not(id: user3.posts)
|
46
|
-
|
61
|
+
|
62
|
+
# Inside ActiveRecord the bind value list is
|
63
|
+
# (union.arel.bind_values + union.bind_values)
|
64
|
+
bind_values = bind_values_from_relation union
|
47
65
|
|
48
66
|
expect(bind_values).to eq([1, 2, 3])
|
49
67
|
end
|
50
68
|
|
69
|
+
it "binds values properly on joins" do
|
70
|
+
union = User.joins(:drafts).union(User.where(id: 11))
|
71
|
+
|
72
|
+
bind_values = bind_values_from_relation union
|
73
|
+
expect(bind_values).to eq([true, 11])
|
74
|
+
|
75
|
+
|
76
|
+
expect(union.to_sql.squish).to eq(
|
77
|
+
"SELECT \"users\".* FROM ( SELECT \"users\".* FROM \"users\" INNER JOIN \"posts\" ON \"posts\".\"user_id\" = \"users\".\"id\" AND \"posts\".\"draft\" = 't' UNION SELECT \"users\".* FROM \"users\" WHERE \"users\".\"id\" = 11 ) \"users\""
|
78
|
+
)
|
79
|
+
expect{union.to_a}.to_not raise_error
|
80
|
+
end
|
81
|
+
|
51
82
|
it "doesn't repeat default scopes" do
|
52
83
|
expect(Time).to receive(:now) { Time.utc(2014, 7, 24, 0, 0, 0) }
|
84
|
+
sql_now = "2014-07-24 00:00:00#{".000000" if ActiveRecord::VERSION::MAJOR < 5}"
|
53
85
|
|
54
86
|
class PublishedPost < ActiveRecord::Base
|
55
87
|
self.table_name = "posts"
|
56
88
|
default_scope { where("published_at < ?", Time.now) }
|
57
89
|
end
|
58
90
|
|
59
|
-
union = PublishedPost.where("created_at > ?",
|
91
|
+
union = PublishedPost.where("created_at > ?", TIME).union(User.new(id: 1).posts)
|
60
92
|
|
61
93
|
expect(union.to_sql.squish).to eq(
|
62
|
-
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE (published_at < '
|
94
|
+
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE (published_at < '#{sql_now}') AND (created_at > '#{SQL_TIME}') UNION SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 ) \"posts\""
|
63
95
|
)
|
64
96
|
expect{union.to_a}.to_not raise_error
|
65
97
|
end
|
66
98
|
|
67
99
|
context "with ORDER BY in subselects" do
|
68
|
-
|
100
|
+
let :union do
|
69
101
|
User.new(id: 1).posts.order(:created_at).union(
|
70
|
-
Post.where("created_at > ?",
|
102
|
+
Post.where("created_at > ?", TIME).order(:created_at)
|
71
103
|
).order(:created_at)
|
72
104
|
end
|
73
105
|
|
74
106
|
context "in SQLite" do
|
75
107
|
it "lets ORDER BY in query subselects throw a syntax error" do
|
76
108
|
expect(union.to_sql.squish).to eq(
|
77
|
-
"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 > '
|
109
|
+
"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 > '#{SQL_TIME}') ORDER BY \"posts\".\"created_at\" ASC ) \"posts\" ORDER BY \"posts\".\"created_at\" ASC"
|
78
110
|
)
|
79
111
|
expect{union.to_a}.to raise_error(ActiveRecord::StatementInvalid)
|
80
112
|
end
|
@@ -84,7 +116,7 @@ describe ActiveRecord::Relation do
|
|
84
116
|
it "wraps query subselects in parentheses to allow ORDER BY clauses" do
|
85
117
|
Databases.with_postgres do
|
86
118
|
expect(union.to_sql.squish).to eq(
|
87
|
-
"SELECT \"posts\".* FROM ( (SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" =
|
119
|
+
"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 > '#{SQL_TIME}') ORDER BY \"posts\".\"created_at\" ASC) ) \"posts\" ORDER BY \"posts\".\"created_at\" ASC"
|
88
120
|
)
|
89
121
|
expect{union.to_a}.to_not raise_error
|
90
122
|
end
|
@@ -95,7 +127,7 @@ describe ActiveRecord::Relation do
|
|
95
127
|
it "wraps query subselects in parentheses to allow ORDER BY clauses" do
|
96
128
|
Databases.with_mysql do
|
97
129
|
expect(union.to_sql.squish).to eq(
|
98
|
-
"SELECT
|
130
|
+
"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 > '#{SQL_TIME}') ORDER BY `posts`.`created_at` ASC) ) `posts` ORDER BY `posts`.`created_at` ASC"
|
99
131
|
)
|
100
132
|
expect{union.to_a}.to_not raise_error
|
101
133
|
end
|
@@ -108,34 +140,37 @@ describe ActiveRecord::Relation do
|
|
108
140
|
union = User.new(id: 1).posts.union(id: 2)
|
109
141
|
|
110
142
|
expect(union.to_sql.squish).to eq(
|
111
|
-
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 UNION SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"id\" = 2 ) posts"
|
143
|
+
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 UNION SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"id\" = 2 ) \"posts\""
|
112
144
|
)
|
145
|
+
expect{union.to_a}.to_not raise_error
|
113
146
|
end
|
114
147
|
|
115
148
|
it "multiple arguments" do
|
116
|
-
union = User.new(id: 1).posts.union("created_at > ?",
|
149
|
+
union = User.new(id: 1).posts.union("created_at > ?", TIME)
|
117
150
|
|
118
151
|
expect(union.to_sql.squish).to eq(
|
119
|
-
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 UNION SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '
|
152
|
+
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 UNION SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '#{SQL_TIME}') ) \"posts\""
|
120
153
|
)
|
154
|
+
expect{union.to_a}.to_not raise_error
|
121
155
|
end
|
122
156
|
|
123
157
|
it "arel" do
|
124
158
|
union = User.new(id: 1).posts.union(Post.arel_table[:id].eq(2).or(Post.arel_table[:id].eq(3)))
|
125
159
|
|
126
160
|
expect(union.to_sql.squish).to eq(
|
127
|
-
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 UNION SELECT \"posts\".* FROM \"posts\" WHERE (\"posts\".\"id\" = 2 OR \"posts\".\"id\" = 3) ) posts"
|
161
|
+
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 UNION SELECT \"posts\".* FROM \"posts\" WHERE (\"posts\".\"id\" = 2 OR \"posts\".\"id\" = 3) ) \"posts\""
|
128
162
|
)
|
163
|
+
expect{union.to_a}.to_not raise_error
|
129
164
|
end
|
130
165
|
end
|
131
166
|
end
|
132
167
|
|
133
168
|
describe ".union_all" do
|
134
169
|
it "works" do
|
135
|
-
union = User.new(id: 1).posts.union_all(Post.where("created_at > ?",
|
170
|
+
union = User.new(id: 1).posts.union_all(Post.where("created_at > ?", TIME))
|
136
171
|
|
137
172
|
expect(union.to_sql.squish).to eq(
|
138
|
-
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 UNION ALL SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '
|
173
|
+
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = 1 UNION ALL SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '#{SQL_TIME}') ) \"posts\""
|
139
174
|
)
|
140
175
|
expect{union.to_a}.to_not raise_error
|
141
176
|
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.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Hempel
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-01-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -131,7 +131,6 @@ extra_rdoc_files: []
|
|
131
131
|
files:
|
132
132
|
- ".gitignore"
|
133
133
|
- ".travis.yml"
|
134
|
-
- Gemfile
|
135
134
|
- LICENSE.txt
|
136
135
|
- README.md
|
137
136
|
- Rakefile
|
@@ -140,6 +139,10 @@ files:
|
|
140
139
|
- lib/active_record_union.rb
|
141
140
|
- lib/active_record_union/active_record/relation/union.rb
|
142
141
|
- lib/active_record_union/version.rb
|
142
|
+
- rails_4_2.gemfile
|
143
|
+
- rails_5_0.gemfile
|
144
|
+
- rails_5_1.gemfile
|
145
|
+
- rails_5_2.gemfile
|
143
146
|
- spec/spec_helper.rb
|
144
147
|
- spec/support/databases.rb
|
145
148
|
- spec/support/models.rb
|
@@ -164,7 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
164
167
|
version: '0'
|
165
168
|
requirements: []
|
166
169
|
rubyforge_project:
|
167
|
-
rubygems_version: 2.
|
170
|
+
rubygems_version: 2.6.8
|
168
171
|
signing_key:
|
169
172
|
specification_version: 4
|
170
173
|
summary: UNIONs in ActiveRecord! Adds proper union and union_all methods to ActiveRecord::Relation.
|
data/Gemfile
DELETED