active_record_union 1.1.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0e5aaf90d66c3e15eb7e2c225283b0c2790b146b
4
- data.tar.gz: fb36ac1c86da58e19de86409e9bd3c35897e247f
3
+ metadata.gz: 3b6908e8b32e304532e3b8405712ade67fab7e52
4
+ data.tar.gz: 937298c6d470715b18510f2e317a7f3d735b2cf6
5
5
  SHA512:
6
- metadata.gz: 3b0140dffec59d1214dc18c456dad0ff28c24f1262704ab7628af0f679dc08c5e30d9d8effcbff29fa4f6800894579b82f2663d15a1341b895512546f366c109
7
- data.tar.gz: 9683ecd6bd506d3226a04ddb3abcd461a7a3adb0088d0f577069569965bac460480bf35f5dd26fcf966779a24a0f1cb32cf531e5c8600dea86769ec540416c26
6
+ metadata.gz: 8466c15bb3c268bbea28f49b3a85915e91f72bd43997462a4bd09a1da14ec9c85d5ca9621a6b7316ac04fe6949ccf4284bce854ed5b33c71bc492ca9bbd0cc9f
7
+ data.tar.gz: a99a9d9c0c19e332d9b0c357310d886c72c5b7f629be97a0d8e2dcdc8d90dcb1605960f3a44a8dacdfe0749dc7dfe6f2571ebe27d63a6bcb1970e7798447a88f
data/.gitignore CHANGED
@@ -4,6 +4,7 @@
4
4
  .config
5
5
  .yardoc
6
6
  Gemfile.lock
7
+ *.gemfile.lock
7
8
  InstalledFiles
8
9
  _yardoc
9
10
  coverage
@@ -1,8 +1,11 @@
1
1
  language: ruby
2
2
  addons:
3
- postgresql: "9.3"
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.1.2
8
- - 2.0.0
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 with 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.
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 with `rspec`
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
- task :default => :spec
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec)
5
+ task :default => :test_all_gemfiles
4
6
 
5
- desc "Run the tests"
6
- task :spec do
7
- exec("bundle exec rspec")
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
- "UNION" => Arel::Nodes::Union,
7
- "UNION ALL" => Arel::Nodes::UnionAll
6
+ union: Arel::Nodes::Union,
7
+ union_all: Arel::Nodes::UnionAll
8
8
  }
9
9
 
10
- def union(relation_or_where_arg, *args)
11
- set_operation("UNION", relation_or_where_arg, *args)
10
+ def union(relation_or_where_arg, *args)
11
+ set_operation(:union, relation_or_where_arg, *args)
12
12
  end
13
13
 
14
- def union_all(relation_or_where_arg, *args)
15
- set_operation("UNION ALL", relation_or_where_arg, *args)
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.size == 0 && Relation === relation_or_where_arg
22
- relation_or_where_arg
23
- else
24
- @klass.where(relation_or_where_arg, *args)
25
- end
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; you will have to explicitly do `relation.reorder(nil)` in SQLite
31
- if Arel::Visitors::SQLite === self.visitor
32
- left, right = self.ast, other.ast
33
- else
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 = SET_OPERATION_TO_AREL_CLASS[operation].new(left, right)
38
- from = Arel::Nodes::TableAlias.new(
39
- set,
40
- Arel::Nodes::SqlLiteral.new(@klass.arel_table.name)
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
- relation = @klass.unscoped.from(from)
44
- relation.bind_values = self.bind_values + other.bind_values
45
- relation
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)
@@ -1,3 +1,3 @@
1
1
  module ActiveRecordUnion
2
- VERSION = "1.1.0"
2
+ VERSION = "1.3.0"
3
3
  end
@@ -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'
@@ -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'
@@ -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'
@@ -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'
@@ -35,12 +35,14 @@ module Databases
35
35
 
36
36
  def with_postgres(&block)
37
37
  connect_to_postgres
38
+ yield
38
39
  ensure
39
40
  connect_to_sqlite
40
41
  end
41
42
 
42
43
  def with_mysql(&block)
43
44
  connect_to_mysql
45
+ yield
44
46
  ensure
45
47
  connect_to_sqlite
46
48
  end
@@ -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
@@ -1,6 +1,9 @@
1
- require 'spec_helper'
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 > ?", Time.utc(2014, 7, 19, 0, 0, 0)))
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 > '2014-07-19 00:00:00.000000') ) posts"
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 > '2014-07-19 00:00:00.000000') ) posts"
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
- bind_values = union.bind_values.map { |column, value| value }
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 > ?", Time.utc(2014, 7, 19, 0, 0, 0)).union(User.new(id: 1).posts)
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 < '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\" = 1 ) posts"
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
- def union
100
+ let :union do
69
101
  User.new(id: 1).posts.order(:created_at).union(
70
- Post.where("created_at > ?", Time.utc(2014, 7, 19, 0, 0, 0)).order(: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 > '2014-07-19 00:00:00.000000') ORDER BY \"posts\".\"created_at\" ASC ) posts ORDER BY \"posts\".\"created_at\" ASC"
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\" = $1 ORDER BY \"posts\".\"created_at\" ASC) UNION (SELECT \"posts\".* FROM \"posts\" WHERE (created_at > '2014-07-19 00:00:00.000000') ORDER BY \"posts\".\"created_at\" ASC) ) posts ORDER BY \"posts\".\"created_at\" ASC"
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 \"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.000000') ORDER BY \"posts\".\"created_at\" ASC) ) posts ORDER BY \"posts\".\"created_at\" ASC"
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 > ?", Time.utc(2014, 7, 19, 0, 0, 0))
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 > '2014-07-19 00:00:00.000000') ) posts"
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 > ?", Time.utc(2014, 7, 19, 0, 0, 0)))
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 > '2014-07-19 00:00:00.000000') ) posts"
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.1.0
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: 2015-03-30 00:00:00.000000000 Z
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.4.5
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
@@ -1,4 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in active_record_union.gemspec
4
- gemspec