active_record_union 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +3 -0
- data/README.md +169 -0
- data/Rakefile +8 -0
- data/active_record_union.gemspec +28 -0
- data/bin/console +36 -0
- data/lib/active_record_union.rb +9 -0
- data/lib/active_record_union/active_record/relation/union.rb +41 -0
- data/lib/active_record_union/version.rb +3 -0
- data/spec/spec_helper.rb +41 -0
- data/spec/support/models.rb +15 -0
- data/spec/union_spec.rb +88 -0
- metadata +146 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3eba1bb2ac0137d8d84b62bb48f45e8c90f78efe
|
4
|
+
data.tar.gz: ef18422b1efeabda133e5e2d86985c918759f1b8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e318f5d727ea3e1483c67638b824293840ba75f7d0191bf7490d62a32bc17c9dbd8fd7403f652f1fd51294f97b8bd8e33e4445cd83ac36e3956ded6b978efc12
|
7
|
+
data.tar.gz: 961b39db5ef83a66708c6424356189e60837d3eb2068a1c0464da7d7ddc2154cb116bf596f7f73b8b3c46bdffbafafa0774a20c4c0798b5774df71e12b246717
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,3 @@
|
|
1
|
+
ActiveRecordUnion is dedicated to the public domain by its author, Brian Hempel. No rights are reserved. No restrictions are placed on the use of ActiveRecordUnion. That freedom also means, of course, that no warrenty of fitness is claimed; use ActiveRecordUnion at your own risk.
|
2
|
+
|
3
|
+
Public domain dedication is explained by the CC0 1.0 summary (and only the summary) at https://creativecommons.org/publicdomain/zero/1.0/
|
data/README.md
ADDED
@@ -0,0 +1,169 @@
|
|
1
|
+
# ActiveRecordUnion
|
2
|
+
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/active_record_union.svg)](http://badge.fury.io/rb/active_record_union)
|
4
|
+
[![Build Status](https://travis-ci.org/brianhempel/active_record_union.svg)](https://travis-ci.org/brianhempel/active_record_union)
|
5
|
+
|
6
|
+
Use unions on ActiveRecord scopes without ugliness.
|
7
|
+
|
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
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
gem 'active_record_union'
|
16
|
+
```
|
17
|
+
|
18
|
+
And then execute:
|
19
|
+
|
20
|
+
$ bundle
|
21
|
+
|
22
|
+
Or install it yourself as:
|
23
|
+
|
24
|
+
$ gem install active_record_union
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
|
28
|
+
ActiveRecordUnion adds a `union` method to `ActiveRecord::Relation` so we can easily gather together queries on mutiple scopes.
|
29
|
+
|
30
|
+
Consider some users with posts:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
class User < ActiveRecord::Base
|
34
|
+
has_many :posts
|
35
|
+
end
|
36
|
+
|
37
|
+
class Post < ActiveRecord::Base
|
38
|
+
belongs_to :user
|
39
|
+
|
40
|
+
scope :published, -> { where("published_at < ?", Time.now) }
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
With ActiveRecordUnion, we can do:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
# the current user's (draft) posts and all published posts from anyone
|
48
|
+
current_user.posts.union(Post.published)
|
49
|
+
```
|
50
|
+
|
51
|
+
Which is equivalent to the following SQL: [<a href="#footnote-1">1</a>]
|
52
|
+
|
53
|
+
```sql
|
54
|
+
SELECT "posts".* FROM (
|
55
|
+
SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = ?
|
56
|
+
UNION
|
57
|
+
SELECT "posts".* FROM "posts" WHERE (published_at < '2014-07-19 16:04:21.918366')
|
58
|
+
) posts
|
59
|
+
```
|
60
|
+
|
61
|
+
Because the `union` method returns another `ActiveRecord::Relation`, we can run further queries on the union.
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
current_user.posts.union(Post.published).where(id: [6, 7])
|
65
|
+
```
|
66
|
+
```sql
|
67
|
+
SELECT "posts".* FROM (
|
68
|
+
SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = ?
|
69
|
+
UNION
|
70
|
+
SELECT "posts".* FROM "posts" WHERE (published_at < '2014-07-19 16:06:04.460771')
|
71
|
+
) posts WHERE "posts"."id" IN (6, 7)
|
72
|
+
```
|
73
|
+
|
74
|
+
The `union` method can also accepts anything that `where` does.
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
current_user.posts.union("published_at < ?", Time.now)
|
78
|
+
# equivalent to...
|
79
|
+
current_user.posts.union(Post.where("published_at < ?", Time.now))
|
80
|
+
```
|
81
|
+
|
82
|
+
We can also chain `union` calls to UNION more than two scopes, though the UNIONs will be nested which may not be the prettiest SQL.
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
user_1.posts.union(user_2.posts).union(Post.published)
|
86
|
+
# equivalent to...
|
87
|
+
[user_1.posts, user_2.posts, Post.published].inject(:union)
|
88
|
+
```
|
89
|
+
```sql
|
90
|
+
SELECT "posts".* FROM (
|
91
|
+
SELECT "posts".* FROM (
|
92
|
+
SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = ?
|
93
|
+
UNION
|
94
|
+
SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = ?
|
95
|
+
) posts
|
96
|
+
UNION
|
97
|
+
SELECT "posts".* FROM "posts" WHERE (published_at < '2014-07-19 16:12:45.882648')
|
98
|
+
) posts
|
99
|
+
```
|
100
|
+
|
101
|
+
<a name="footnote-1"></a>[1] Note: the `?` in the SQL is bound to the correct value when ActiveRecord executes the query. Also, the SQL examples here were generated for a SQLite database. The syntax generated for other databases may vary slightly.
|
102
|
+
|
103
|
+
## Another nifty way to reduce extra queries
|
104
|
+
|
105
|
+
ActiveRecord already supports turning scopes into nested queries in WHERE clauses. The nested relation defaults to selecting `id` by default.
|
106
|
+
|
107
|
+
For example, if a user `has_and_belongs_to_many :favorited_posts`, we can quickly find which of the current user's posts are liked by a certain other user.
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
current_user.posts.where(id: other_user.favorited_posts)
|
111
|
+
```
|
112
|
+
```sql
|
113
|
+
SELECT "posts".* FROM "posts"
|
114
|
+
WHERE "posts"."user_id" = ?
|
115
|
+
AND "posts"."id" IN (
|
116
|
+
SELECT "posts"."id"
|
117
|
+
FROM "posts" INNER JOIN "user_favorited_posts" ON "posts"."id" = "user_favorited_posts"."post_id"
|
118
|
+
WHERE "user_favorited_posts"."user_id" = ?
|
119
|
+
)
|
120
|
+
```
|
121
|
+
|
122
|
+
If we want to select something other than `id`, we use `select` to specify. The following is equivalent to the above, but the query is done against the join table.
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
current_user.posts.where(id: UserFavoritedPost.where(user_id: other_user.id).select(:post_id))
|
126
|
+
```
|
127
|
+
```sql
|
128
|
+
SELECT "posts".* FROM "posts"
|
129
|
+
WHERE "posts"."user_id" = ?
|
130
|
+
AND "posts"."id" IN (
|
131
|
+
SELECT "user_favorited_posts"."post_id"
|
132
|
+
FROM "user_favorited_posts"
|
133
|
+
WHERE "user_favorited_posts"."user_id" = 2
|
134
|
+
)
|
135
|
+
```
|
136
|
+
|
137
|
+
(The above example is illustrative only. It might be better with a JOIN.)
|
138
|
+
|
139
|
+
## State of the Union in ActiveRecord
|
140
|
+
|
141
|
+
Why does this gem exist?
|
142
|
+
|
143
|
+
Right now in ActiveRecord, if we call `scope.union` we get an `Arel::Nodes::Union` object instead of an `ActiveRecord::Relation`.
|
144
|
+
|
145
|
+
We could call `to_sql` on the Arel object and then use `find_by_sql`, but that's not super clean and if the original scopes included an association, then the `to_sql` may produce a query with values that need to be bound (represented by `?`s in the SQL) and we have to provide those ourselves. (E.g. `user.posts.to_sql` produces `SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = ?`.)
|
146
|
+
|
147
|
+
While ActiveRecord may eventually have the ability to cleanly perform UNIONs, it's currently stalled. If you're interested, the relevant URLs as of July 2014 are:
|
148
|
+
|
149
|
+
https://github.com/rails/rails/issues/939 and
|
150
|
+
https://github.com/rails/arel/pull/239 and
|
151
|
+
https://github.com/yakaz/rails/commit/29b8ebd187e0888d5e71b2e1e4a12334860bc76c
|
152
|
+
|
153
|
+
This is a gem not a Rails pull request because the standard of code quality for a PR is a bit higher, and we'd have to wait for the PR to be merged and relased to use UNIONs. That said, the code here is fairly clean and it may end up in a PR sometime.
|
154
|
+
|
155
|
+
## License
|
156
|
+
|
157
|
+
ActiveRecordUnion is dedicated to the public domain by its author, Brian Hempel. No rights are reserved. No restrictions are placed on the use of ActiveRecordUnion. That freedom also means, of course, that no warrenty of fitness is claimed; use ActiveRecordUnion at your own risk.
|
158
|
+
|
159
|
+
Public domain dedication is explained by the CC0 1.0 summary (and only the summary) at https://creativecommons.org/publicdomain/zero/1.0/
|
160
|
+
|
161
|
+
## Contributing
|
162
|
+
|
163
|
+
1. Fork it ( https://github.com/[my-github-username]/active_record_union/fork )
|
164
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
165
|
+
3. Run the tests with `rspec`
|
166
|
+
4. There is also a `bin/console` command to load up a REPL for playing around
|
167
|
+
5. Commit your changes (`git commit -am 'Add some feature'`)
|
168
|
+
6. Push to the branch (`git push origin my-new-feature`)
|
169
|
+
7. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'active_record_union/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "active_record_union"
|
8
|
+
spec.version = ActiveRecordUnion::VERSION
|
9
|
+
spec.authors = ["Brian Hempel"]
|
10
|
+
spec.email = ["plasticchicken@gmail.com"]
|
11
|
+
spec.summary = %q{UNIONs in ActiveRecord! Adds a proper union method to ActiveRecord::Relation.}
|
12
|
+
spec.description = spec.summary
|
13
|
+
spec.homepage = "https://github.com/brianhempel/active_record_union"
|
14
|
+
spec.license = "Public Domain"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
# spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) + spec.files.grep(%r{^bin/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "activerecord", ">= 4.0"
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
26
|
+
spec.add_development_dependency "pry"
|
27
|
+
spec.add_development_dependency "sqlite3"
|
28
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# This is for development only.
|
4
|
+
|
5
|
+
require "bundler/setup"
|
6
|
+
|
7
|
+
Bundler.require(:development)
|
8
|
+
require "active_record_union"
|
9
|
+
|
10
|
+
ActiveRecord::Base.establish_connection(
|
11
|
+
adapter: "sqlite3",
|
12
|
+
database: ":memory:"
|
13
|
+
)
|
14
|
+
|
15
|
+
require File.expand_path("../../spec/support/models.rb", __FILE__)
|
16
|
+
|
17
|
+
# extensions over models.rb to help with making the README
|
18
|
+
|
19
|
+
class User
|
20
|
+
has_and_belongs_to_many :favorited_posts,
|
21
|
+
class_name: "Post",
|
22
|
+
join_table: "user_favorited_posts"
|
23
|
+
end
|
24
|
+
|
25
|
+
class UserFavoritedPost < ActiveRecord::Base
|
26
|
+
connection.create_table :user_favorited_posts, force: true do |t|
|
27
|
+
t.integer :post_id
|
28
|
+
t.integer :user_id
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Post
|
33
|
+
scope :published, -> { where("published_at < ?", Time.now) }
|
34
|
+
end
|
35
|
+
|
36
|
+
binding.pry
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class Relation
|
3
|
+
module Union
|
4
|
+
def union(relation_or_where_arg, *args)
|
5
|
+
other = relation_or_where_arg if args.size == 0 && Relation === relation_or_where_arg
|
6
|
+
other ||= @klass.where(relation_or_where_arg, *args)
|
7
|
+
|
8
|
+
verify_union_relations!(self, other)
|
9
|
+
|
10
|
+
union = self.arel.union(other)
|
11
|
+
from = Arel::Nodes::TableAlias.new(
|
12
|
+
union,
|
13
|
+
Arel::Nodes::SqlLiteral.new(@klass.arel_table.name)
|
14
|
+
)
|
15
|
+
|
16
|
+
relation = @klass.unscoped.from(from)
|
17
|
+
relation.bind_values = self.bind_values + other.bind_values + relation.bind_values
|
18
|
+
relation
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def verify_union_relations!(*args)
|
24
|
+
includes_relations = args.select { |r| r.includes_values.any? }
|
25
|
+
if includes_relations.any?
|
26
|
+
raise ArgumentError.new("Cannot union relation with includes.")
|
27
|
+
end
|
28
|
+
|
29
|
+
preload_relations = args.select { |r| r.preload_values.any? }
|
30
|
+
if preload_relations.any?
|
31
|
+
raise ArgumentError.new("Cannot union relation with preload.")
|
32
|
+
end
|
33
|
+
|
34
|
+
eager_load_relations = args.select { |r| r.eager_load_values.any? }
|
35
|
+
if eager_load_relations.any?
|
36
|
+
raise ArgumentError.new("Cannot union relation with eager load.")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require "active_record_union"
|
2
|
+
|
3
|
+
ActiveRecord::Base.establish_connection(
|
4
|
+
adapter: "sqlite3",
|
5
|
+
database: ":memory:"
|
6
|
+
)
|
7
|
+
|
8
|
+
require "support/models"
|
9
|
+
|
10
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
11
|
+
RSpec.configure do |config|
|
12
|
+
# Run specs in random order to surface order dependencies. If you find an
|
13
|
+
# order dependency and want to debug it, you can fix the order by providing
|
14
|
+
# the seed, which is printed after each run.
|
15
|
+
# --seed 1234
|
16
|
+
config.order = :random
|
17
|
+
|
18
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
19
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
20
|
+
# test failures related to randomization by passing the same `--seed` value
|
21
|
+
# as the one that triggered the failure.
|
22
|
+
Kernel.srand config.seed
|
23
|
+
|
24
|
+
config.expect_with :rspec do |expectations|
|
25
|
+
# Enable only the newer, non-monkey-patching expect syntax.
|
26
|
+
# For more details, see:
|
27
|
+
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
28
|
+
expectations.syntax = :expect
|
29
|
+
end
|
30
|
+
|
31
|
+
config.mock_with :rspec do |mocks|
|
32
|
+
# Enable only the newer, non-monkey-patching expect syntax.
|
33
|
+
# For more details, see:
|
34
|
+
# - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
35
|
+
mocks.syntax = :expect
|
36
|
+
|
37
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
38
|
+
# a real object. This is generally recommended.
|
39
|
+
mocks.verify_partial_doubles = true
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class User < ActiveRecord::Base
|
2
|
+
connection.create_table :users, force: true do |t|
|
3
|
+
end
|
4
|
+
|
5
|
+
has_many :posts
|
6
|
+
end
|
7
|
+
|
8
|
+
class Post < ActiveRecord::Base
|
9
|
+
connection.create_table :posts, force: true do |t|
|
10
|
+
t.integer :user_id
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
|
14
|
+
belongs_to :user
|
15
|
+
end
|
data/spec/union_spec.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveRecord::Relation do
|
4
|
+
describe ".union" do
|
5
|
+
it "returns an ActiveRecord::Relation" do
|
6
|
+
expect(User.all.union(User.all)).to be_kind_of(ActiveRecord::Relation)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "requires an argument" do
|
10
|
+
expect{User.all.union}.to raise_error(ArgumentError)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "explodes if asked to union a relation with includes" do
|
14
|
+
expect{User.all.union(User.includes(:posts))}.to raise_error(ArgumentError)
|
15
|
+
expect{User.includes(:posts).union(User.all)}.to raise_error(ArgumentError)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "explodes if asked to union a relation with preload values" do
|
19
|
+
expect{User.all.union(User.preload(:posts))}.to raise_error(ArgumentError)
|
20
|
+
expect{User.preload(:posts).union(User.all)}.to raise_error(ArgumentError)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "explodes if asked to union a relation with eager loading" do
|
24
|
+
expect{User.all.union(User.eager_load(:posts))}.to raise_error(ArgumentError)
|
25
|
+
expect{User.eager_load(:posts).union(User.all)}.to raise_error(ArgumentError)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "works" do
|
29
|
+
union = User.new.posts.union(Post.where("created_at > ?", Time.utc(2014, 7, 19, 0, 0, 0)))
|
30
|
+
|
31
|
+
expect(union.to_sql).to eq(
|
32
|
+
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = ? UNION SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '2014-07-19 00:00:00.000000') ) posts"
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "binds values properly" do
|
37
|
+
user1 = User.new(id: 1)
|
38
|
+
user2 = User.new(id: 2)
|
39
|
+
user3 = User.new(id: 3)
|
40
|
+
|
41
|
+
union = user1.posts.union(user2.posts).where.not(id: user3.posts)
|
42
|
+
bind_values = union.bind_values.map { |column, value| value }
|
43
|
+
|
44
|
+
expect(bind_values).to eq([1, 2, 3])
|
45
|
+
end
|
46
|
+
|
47
|
+
it "doesn't repeat default scopes" do
|
48
|
+
expect(Time).to receive(:now) { Time.utc(2014, 7, 24, 0, 0, 0) }
|
49
|
+
|
50
|
+
class PublishedPost < ActiveRecord::Base
|
51
|
+
self.table_name = "posts"
|
52
|
+
default_scope { where("published_at < ?", Time.now) }
|
53
|
+
end
|
54
|
+
|
55
|
+
union = PublishedPost.where("created_at > ?", Time.utc(2014, 7, 19, 0, 0, 0)).union(User.new.posts)
|
56
|
+
|
57
|
+
expect(union.to_sql).to eq(
|
58
|
+
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE (published_at < '2014-07-24 00:00:00.000000') AND (created_at > '2014-07-19 00:00:00.000000') UNION SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = ? ) posts"
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
context "builds a scope when given" do
|
63
|
+
it "a hash" do
|
64
|
+
union = User.new.posts.union(id: 1)
|
65
|
+
|
66
|
+
expect(union.to_sql).to eq(
|
67
|
+
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = ? UNION SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"id\" = 1 ) posts"
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "multiple arguments" do
|
72
|
+
union = User.new.posts.union("created_at > ?", Time.utc(2014, 7, 19, 0, 0, 0))
|
73
|
+
|
74
|
+
expect(union.to_sql).to eq(
|
75
|
+
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = ? UNION SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '2014-07-19 00:00:00.000000') ) posts"
|
76
|
+
)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "arel" do
|
80
|
+
union = User.new.posts.union(Post.arel_table[:id].eq(1).or(Post.arel_table[:id].eq(2)))
|
81
|
+
|
82
|
+
expect(union.to_sql).to eq(
|
83
|
+
"SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = ? UNION SELECT \"posts\".* FROM \"posts\" WHERE ((\"posts\".\"id\" = 1 OR \"posts\".\"id\" = 2)) ) posts"
|
84
|
+
)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
metadata
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: active_record_union
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Brian Hempel
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-07-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.6'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.6'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
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
|
+
description: UNIONs in ActiveRecord! Adds a proper union method to ActiveRecord::Relation.
|
98
|
+
email:
|
99
|
+
- plasticchicken@gmail.com
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- ".gitignore"
|
105
|
+
- ".travis.yml"
|
106
|
+
- Gemfile
|
107
|
+
- LICENSE.txt
|
108
|
+
- README.md
|
109
|
+
- Rakefile
|
110
|
+
- active_record_union.gemspec
|
111
|
+
- bin/console
|
112
|
+
- lib/active_record_union.rb
|
113
|
+
- lib/active_record_union/active_record/relation/union.rb
|
114
|
+
- lib/active_record_union/version.rb
|
115
|
+
- spec/spec_helper.rb
|
116
|
+
- spec/support/models.rb
|
117
|
+
- spec/union_spec.rb
|
118
|
+
homepage: https://github.com/brianhempel/active_record_union
|
119
|
+
licenses:
|
120
|
+
- Public Domain
|
121
|
+
metadata: {}
|
122
|
+
post_install_message:
|
123
|
+
rdoc_options: []
|
124
|
+
require_paths:
|
125
|
+
- lib
|
126
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - ">="
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
requirements: []
|
137
|
+
rubyforge_project:
|
138
|
+
rubygems_version: 2.2.2
|
139
|
+
signing_key:
|
140
|
+
specification_version: 4
|
141
|
+
summary: UNIONs in ActiveRecord! Adds a proper union method to ActiveRecord::Relation.
|
142
|
+
test_files:
|
143
|
+
- spec/spec_helper.rb
|
144
|
+
- spec/support/models.rb
|
145
|
+
- spec/union_spec.rb
|
146
|
+
- bin/console
|