active_record_union 1.0.0 → 1.0.1

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: 3eba1bb2ac0137d8d84b62bb48f45e8c90f78efe
4
- data.tar.gz: ef18422b1efeabda133e5e2d86985c918759f1b8
3
+ metadata.gz: de7279017756c9ab6fa65a13d457b671d4d3fc1f
4
+ data.tar.gz: a059566a08659d432635c1ad707431a3b8c163a1
5
5
  SHA512:
6
- metadata.gz: e318f5d727ea3e1483c67638b824293840ba75f7d0191bf7490d62a32bc17c9dbd8fd7403f652f1fd51294f97b8bd8e33e4445cd83ac36e3956ded6b978efc12
7
- data.tar.gz: 961b39db5ef83a66708c6424356189e60837d3eb2068a1c0464da7d7ddc2154cb116bf596f7f73b8b3c46bdffbafafa0774a20c4c0798b5774df71e12b246717
6
+ metadata.gz: bc0aae94fd243b66458b8ad719833bea64c69be694eaa5a73e370c3aea1dd918b74e15463b8c03b7d5c7d808db526bc0de3b06f27e8d27087e1678c0d0441b70
7
+ data.tar.gz: 0481dcf3092a0e166bcca93da7e9b1634d999cdecfd7ef14d108ab95ab2c291c9d8cf8e77f232b946da5beba0aec78ecb2036abaa5536668656655af6f908a0e
@@ -1,4 +1,8 @@
1
1
  language: ruby
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
2
6
  rvm:
3
7
  - 2.1.2
4
8
  - 2.0.0
data/README.md CHANGED
@@ -100,6 +100,13 @@ SELECT "posts".* FROM (
100
100
 
101
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
102
 
103
+ ## Caveats
104
+
105
+ There's a couple things to be aware of when using ActiveRecordUnion:
106
+
107
+ 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.
108
+ 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.
109
+
103
110
  ## Another nifty way to reduce extra queries
104
111
 
105
112
  ActiveRecord already supports turning scopes into nested queries in WHERE clauses. The nested relation defaults to selecting `id` by default.
@@ -152,6 +159,12 @@ https://github.com/yakaz/rails/commit/29b8ebd187e0888d5e71b2e1e4a12334860bc76c
152
159
 
153
160
  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
161
 
162
+ ## Changelog
163
+
164
+ **1.0.1** - Sept 2, 2014 - Allow ORDER BY in UNION subselects for databases that support it (not SQLite).
165
+
166
+ **1.0.0** - July 24, 2014 - Initial release.
167
+
155
168
  ## License
156
169
 
157
170
  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.
@@ -25,4 +25,6 @@ Gem::Specification.new do |spec|
25
25
  spec.add_development_dependency "rspec", "~> 3.0"
26
26
  spec.add_development_dependency "pry"
27
27
  spec.add_development_dependency "sqlite3"
28
+ spec.add_development_dependency "pg"
29
+ spec.add_development_dependency "mysql2"
28
30
  end
@@ -7,19 +7,27 @@ module ActiveRecord
7
7
 
8
8
  verify_union_relations!(self, other)
9
9
 
10
- union = self.arel.union(other)
10
+ # Postgres allows ORDER BY in the UNION subqueries if each subquery is surrounded by parenthesis
11
+ # but SQLite does not allow parens around the subqueries; you will have to explicitly do `relation.reorder(nil)` in SQLite
12
+ if Arel::Visitors::SQLite === self.visitor
13
+ left, right = self.ast, other.ast
14
+ else
15
+ left, right = Arel::Nodes::Grouping.new(self.ast), Arel::Nodes::Grouping.new(other.ast)
16
+ end
17
+
18
+ union = Arel::Nodes::Union.new(left, right)
11
19
  from = Arel::Nodes::TableAlias.new(
12
20
  union,
13
21
  Arel::Nodes::SqlLiteral.new(@klass.arel_table.name)
14
22
  )
15
23
 
16
24
  relation = @klass.unscoped.from(from)
17
- relation.bind_values = self.bind_values + other.bind_values + relation.bind_values
25
+ relation.bind_values = self.bind_values + other.bind_values
18
26
  relation
19
27
  end
20
28
 
21
29
  private
22
-
30
+
23
31
  def verify_union_relations!(*args)
24
32
  includes_relations = args.select { |r| r.includes_values.any? }
25
33
  if includes_relations.any?
@@ -1,3 +1,3 @@
1
1
  module ActiveRecordUnion
2
- VERSION = "1.0.0"
2
+ VERSION = "1.0.1"
3
3
  end
@@ -1,11 +1,11 @@
1
+ require "bundler/setup"
2
+
3
+ Bundler.require(:development)
1
4
  require "active_record_union"
2
5
 
3
- ActiveRecord::Base.establish_connection(
4
- adapter: "sqlite3",
5
- database: ":memory:"
6
- )
6
+ require "support/databases"
7
7
 
8
- require "support/models"
8
+ Databases.connect_to_sqlite
9
9
 
10
10
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
11
11
  RSpec.configure do |config|
@@ -0,0 +1,47 @@
1
+ module Databases
2
+ extend self
3
+
4
+ def connect_to_sqlite
5
+ ActiveRecord::Base.establish_connection(
6
+ adapter: "sqlite3",
7
+ database: ":memory:"
8
+ )
9
+ load("support/models.rb")
10
+ end
11
+
12
+ def connect_to_postgres
13
+ ActiveRecord::Base.establish_connection(
14
+ adapter: "postgresql"
15
+ )
16
+ ActiveRecord::Base.connection.recreate_database("test_active_record_union")
17
+ ActiveRecord::Base.establish_connection(
18
+ adapter: "postgresql",
19
+ database: "test_active_record_union"
20
+ )
21
+ load("support/models.rb")
22
+ end
23
+
24
+ def connect_to_mysql
25
+ ActiveRecord::Base.establish_connection(
26
+ adapter: "mysql2"
27
+ )
28
+ ActiveRecord::Base.connection.recreate_database("test_active_record_union")
29
+ ActiveRecord::Base.establish_connection(
30
+ adapter: "mysql2",
31
+ database: "test_active_record_union"
32
+ )
33
+ load("support/models.rb")
34
+ end
35
+
36
+ def with_postgres(&block)
37
+ connect_to_postgres
38
+ ensure
39
+ connect_to_sqlite
40
+ end
41
+
42
+ def with_mysql(&block)
43
+ connect_to_mysql
44
+ ensure
45
+ connect_to_sqlite
46
+ end
47
+ end
@@ -1,15 +1,16 @@
1
- class User < ActiveRecord::Base
2
- connection.create_table :users, force: true do |t|
3
- end
1
+ ActiveRecord::Base.connection.create_table :users, force: true do |t|
2
+ end
4
3
 
4
+ class User < ActiveRecord::Base
5
5
  has_many :posts
6
+ end unless defined?(User)
7
+
8
+ ActiveRecord::Base.connection.create_table :posts, force: true do |t|
9
+ t.integer :user_id
10
+ t.timestamp :published_at
11
+ t.timestamps
6
12
  end
7
13
 
8
14
  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
15
  belongs_to :user
15
- end
16
+ end unless defined?(Post)
@@ -31,6 +31,7 @@ describe ActiveRecord::Relation do
31
31
  expect(union.to_sql).to eq(
32
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
33
  )
34
+ expect{union.to_a}.to_not raise_error
34
35
  end
35
36
 
36
37
  it "binds values properly" do
@@ -57,6 +58,46 @@ describe ActiveRecord::Relation do
57
58
  expect(union.to_sql).to eq(
58
59
  "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
  )
61
+ expect{union.to_a}.to_not raise_error
62
+ end
63
+
64
+ context "with ORDER BY in subselects" do
65
+ def union
66
+ User.new.posts.order(:created_at).union(
67
+ Post.where("created_at > ?", Time.utc(2014, 7, 19, 0, 0, 0)).order(:created_at)
68
+ ).order(:created_at)
69
+ end
70
+
71
+ context "in SQLite" do
72
+ it "lets ORDER BY in query subselects throw a syntax error" do
73
+ expect(union.to_sql).to eq(
74
+ "SELECT \"posts\".* FROM ( SELECT \"posts\".* FROM \"posts\" WHERE \"posts\".\"user_id\" = ? 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"
75
+ )
76
+ expect{union.to_a}.to raise_error(ActiveRecord::StatementInvalid)
77
+ end
78
+ end
79
+
80
+ context "in Postgres" do
81
+ it "wraps query subselects in parentheses to allow ORDER BY clauses" do
82
+ Databases.with_postgres do
83
+ expect(union.to_sql).to eq(
84
+ "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"
85
+ )
86
+ expect{union.to_a}.to_not raise_error
87
+ end
88
+ end
89
+ end
90
+
91
+ context "in MySQL" do
92
+ it "wraps query subselects in parentheses to allow ORDER BY clauses" do
93
+ Databases.with_mysql do
94
+ expect(union.to_sql).to eq(
95
+ "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"
96
+ )
97
+ expect{union.to_a}.to_not raise_error
98
+ end
99
+ end
100
+ end
60
101
  end
61
102
 
62
103
  context "builds a scope when given" do
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.0.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Hempel
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-24 00:00:00.000000000 Z
11
+ date: 2014-09-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -94,6 +94,34 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
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'
97
125
  description: UNIONs in ActiveRecord! Adds a proper union method to ActiveRecord::Relation.
98
126
  email:
99
127
  - plasticchicken@gmail.com
@@ -113,6 +141,7 @@ files:
113
141
  - lib/active_record_union/active_record/relation/union.rb
114
142
  - lib/active_record_union/version.rb
115
143
  - spec/spec_helper.rb
144
+ - spec/support/databases.rb
116
145
  - spec/support/models.rb
117
146
  - spec/union_spec.rb
118
147
  homepage: https://github.com/brianhempel/active_record_union
@@ -141,6 +170,7 @@ specification_version: 4
141
170
  summary: UNIONs in ActiveRecord! Adds a proper union method to ActiveRecord::Relation.
142
171
  test_files:
143
172
  - spec/spec_helper.rb
173
+ - spec/support/databases.rb
144
174
  - spec/support/models.rb
145
175
  - spec/union_spec.rb
146
176
  - bin/console