arel-helpers 1.2.0 → 2.0.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/History.txt +5 -1
- data/README.md +21 -9
- data/arel-helpers.gemspec +1 -1
- data/lib/arel-helpers/join_association.rb +49 -40
- data/lib/arel-helpers/query_builder.rb +5 -0
- data/lib/arel-helpers/version.rb +1 -1
- data/spec/join_association_spec.rb +19 -10
- data/spec/query_builder_spec.rb +7 -0
- metadata +13 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 74fd32a2a3bd64b02be9dbd561afe2add390752f
|
4
|
+
data.tar.gz: 78f91129ab6b88b3b93675de7a0e96f7ac9ef716
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a8082544e84ddca08e248276682473fa8d2af08cd666fb2b76c2588cbf52546c8f9345805693dbd6929ec0a58f64e130f21a7fdb973374feb81f263a2b6fd65c
|
7
|
+
data.tar.gz: 0fa70969de2f89d8fa03c2b0747ca3da4c011e9399f74b4f9222611401df399bbd24ecd7e2e577e9600de21291a5f99d06559ca0648be1c1770b27ff08f902fa
|
data/History.txt
CHANGED
data/README.md
CHANGED
@@ -39,7 +39,7 @@ Post.where(Post[:id].eq(1))
|
|
39
39
|
|
40
40
|
### JoinAssociation Helper
|
41
41
|
|
42
|
-
Using pure Arel is
|
42
|
+
Using pure Arel is one of the only ways to do an outer join with ActiveRecord. For example, let's say we have these two models:
|
43
43
|
|
44
44
|
```ruby
|
45
45
|
class Post < ActiveRecord::Base
|
@@ -59,7 +59,7 @@ Post.joins(:comments)
|
|
59
59
|
|
60
60
|
ActiveRecord introspects the association between posts and comments and automatically chooses the right columns to use in the join conditions.
|
61
61
|
|
62
|
-
Things start to get messy however if you
|
62
|
+
Things start to get messy however if you want to do an outer join instead of the default inner join. Your query might look like this:
|
63
63
|
|
64
64
|
```ruby
|
65
65
|
Post
|
@@ -70,25 +70,37 @@ Post
|
|
70
70
|
)
|
71
71
|
```
|
72
72
|
|
73
|
-
Such verbose. Much code. Very bloat. Wow. We've lost all the awesome association introspection that ActiveRecord would otherwise have given us. Enter `ArelHelpers
|
73
|
+
Such verbose. Much code. Very bloat. Wow. We've lost all the awesome association introspection that ActiveRecord would otherwise have given us. Enter `ArelHelpers.join_association`:
|
74
74
|
|
75
75
|
```ruby
|
76
|
-
|
77
|
-
Post.joins(join_association(Post, :comments, Arel::OuterJoin))
|
76
|
+
Post.joins(ArelHelpers.join_association(Post, :comments, Arel::OuterJoin))
|
78
77
|
```
|
79
78
|
|
80
79
|
Easy peasy.
|
81
80
|
|
82
|
-
`
|
81
|
+
`#join_association` also allows you to customize the join conditions via a block:
|
83
82
|
|
84
83
|
```ruby
|
85
84
|
Post.joins(
|
86
|
-
join_association(Post, :comments, Arel::OuterJoin) do |assoc_name, join_conditions|
|
85
|
+
ArelHelpers.join_association(Post, :comments, Arel::OuterJoin) do |assoc_name, join_conditions|
|
87
86
|
join_conditions.and(Post[:author_id].eq(4))
|
88
87
|
end
|
89
88
|
)
|
90
89
|
```
|
91
90
|
|
91
|
+
But wait, there's more! Include the `ArelHelpers::JoinAssociation` concern into your models to have access to the `join_association` method directly from the model's class:
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
include ArelHelpers::JoinAssociation
|
95
|
+
|
96
|
+
Post
|
97
|
+
.joins(
|
98
|
+
Post.join_association(:comments, Arel::OuterJoin) do |assoc_name, join_conditions|
|
99
|
+
join_conditions.and(Post[:author_id].eq(4))
|
100
|
+
end
|
101
|
+
)
|
102
|
+
```
|
103
|
+
|
92
104
|
### Query Builders
|
93
105
|
|
94
106
|
ArelHelpers also contains a very simple class that's designed to provide a light framework for constructing queries using the builder pattern. For example, let's write a class that encapsulates generating queries for blog posts:
|
@@ -110,7 +122,7 @@ class PostQueryBuilder < ArelHelpers::QueryBuilder
|
|
110
122
|
reflect(
|
111
123
|
query
|
112
124
|
.joins(:comments => :author)
|
113
|
-
.where(
|
125
|
+
.where(author[:username].in(usernames))
|
114
126
|
)
|
115
127
|
end
|
116
128
|
|
@@ -143,7 +155,7 @@ PostQueryBuilder.new
|
|
143
155
|
|
144
156
|
## Requirements
|
145
157
|
|
146
|
-
|
158
|
+
Requires ActiveRecord >= 3.1.0, <= 4.1.0, tested with Ruby 1.9.3, 2.0.0, and 2.1.0. Depends on SQLite for testing purposes.
|
147
159
|
|
148
160
|
## Running Tests
|
149
161
|
|
data/arel-helpers.gemspec
CHANGED
@@ -1,58 +1,67 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
3
|
module ArelHelpers
|
4
|
+
|
4
5
|
module JoinAssociation
|
6
|
+
extend ActiveSupport::Concern
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
# This method encapsulates that functionality and yields an intermediate object for chaining.
|
10
|
-
# It also allows you to use an outer join instead of the default inner via the join_type arg.
|
11
|
-
def join_association(table, association, join_type = Arel::InnerJoin, &block)
|
12
|
-
if ActiveRecord::VERSION::STRING >= '4.1.0'
|
13
|
-
join_association_4_1(table, association, join_type, &block)
|
14
|
-
else
|
15
|
-
join_association_3_1(table, association, join_type, &block)
|
8
|
+
included do
|
9
|
+
def self.join_association(association, join_type = Arel::InnerJoin, &block)
|
10
|
+
ArelHelpers.join_association(self, association, join_type, &block)
|
16
11
|
end
|
17
12
|
end
|
18
13
|
|
19
|
-
|
14
|
+
end
|
15
|
+
|
16
|
+
# activerecord uses JoinDependency to automagically generate inner join statements for
|
17
|
+
# any type of association (belongs_to, has_many, and has_and_belongs_to_many).
|
18
|
+
# For example, for HABTM associations, two join statements are required.
|
19
|
+
# This method encapsulates that functionality and yields an intermediate object for chaining.
|
20
|
+
# It also allows you to use an outer join instead of the default inner via the join_type arg.
|
21
|
+
def self.join_association(table, association, join_type = Arel::InnerJoin, &block)
|
22
|
+
if ActiveRecord::VERSION::STRING >= '4.1.0'
|
23
|
+
join_association_4_1(table, association, join_type, &block)
|
24
|
+
else
|
25
|
+
join_association_3_1(table, association, join_type, &block)
|
26
|
+
end
|
27
|
+
end
|
20
28
|
|
21
|
-
|
22
|
-
associations = association.is_a?(Array) ? association : [association]
|
23
|
-
join_dependency = ActiveRecord::Associations::JoinDependency.new(table, associations, [])
|
24
|
-
manager = Arel::SelectManager.new(table)
|
29
|
+
private
|
25
30
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
31
|
+
def self.join_association_3_1(table, association, join_type)
|
32
|
+
associations = association.is_a?(Array) ? association : [association]
|
33
|
+
join_dependency = ActiveRecord::Associations::JoinDependency.new(table, associations, [])
|
34
|
+
manager = Arel::SelectManager.new(table)
|
30
35
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
right = yield assoc.left.name.to_sym, assoc.right
|
35
|
-
assoc.class.new(assoc.left, right)
|
36
|
-
else
|
37
|
-
assoc
|
38
|
-
end
|
39
|
-
end
|
36
|
+
join_dependency.join_associations.each do |assoc|
|
37
|
+
assoc.join_type = join_type
|
38
|
+
assoc.join_to(manager)
|
40
39
|
end
|
41
40
|
|
42
|
-
|
43
|
-
|
44
|
-
|
41
|
+
manager.join_sources.map do |assoc|
|
42
|
+
if block_given?
|
43
|
+
# yield |assoc_name, join_conditions|
|
44
|
+
right = yield assoc.left.name.to_sym, assoc.right
|
45
|
+
assoc.class.new(assoc.left, right)
|
46
|
+
else
|
47
|
+
assoc
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
45
51
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
else
|
50
|
-
constraint.right
|
51
|
-
end
|
52
|
+
def self.join_association_4_1(table, association, join_type)
|
53
|
+
associations = association.is_a?(Array) ? association : [association]
|
54
|
+
join_dependency = ActiveRecord::Associations::JoinDependency.new(table, associations, [])
|
52
55
|
|
53
|
-
|
56
|
+
join_dependency.join_constraints([]).map do |constraint|
|
57
|
+
right = if block_given?
|
58
|
+
yield constraint.left.name.to_sym, constraint.right
|
59
|
+
else
|
60
|
+
constraint.right
|
54
61
|
end
|
55
|
-
end
|
56
62
|
|
63
|
+
join_type.new(constraint.left, right)
|
64
|
+
end
|
57
65
|
end
|
58
|
-
|
66
|
+
|
67
|
+
end
|
@@ -11,6 +11,11 @@ module ArelHelpers
|
|
11
11
|
attr_reader :query
|
12
12
|
def_delegators :@query, :to_a, :to_sql, :each
|
13
13
|
|
14
|
+
TERMINAL_METHODS = [:count, :first, :last]
|
15
|
+
TERMINAL_METHODS << :pluck if ActiveRecord::VERSION::MAJOR >= 4
|
16
|
+
|
17
|
+
def_delegators :@query, *TERMINAL_METHODS
|
18
|
+
|
14
19
|
def initialize(query)
|
15
20
|
@query = query
|
16
21
|
end
|
data/lib/arel-helpers/version.rb
CHANGED
@@ -2,22 +2,20 @@
|
|
2
2
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
|
-
describe ArelHelpers
|
6
|
-
include ArelHelpers::JoinAssociation
|
7
|
-
|
5
|
+
describe ArelHelpers do
|
8
6
|
describe "#join_association" do
|
9
7
|
it "should work for a directly associated model" do
|
10
|
-
Post.joins(join_association(Post, :comments)).to_sql.should ==
|
8
|
+
Post.joins(ArelHelpers.join_association(Post, :comments)).to_sql.should ==
|
11
9
|
'SELECT "posts".* FROM "posts" INNER JOIN "comments" ON "comments"."post_id" = "posts"."id"'
|
12
10
|
end
|
13
11
|
|
14
12
|
it "should work with an outer join" do
|
15
|
-
Post.joins(join_association(Post, :comments, Arel::OuterJoin)).to_sql.should ==
|
13
|
+
Post.joins(ArelHelpers.join_association(Post, :comments, Arel::OuterJoin)).to_sql.should ==
|
16
14
|
'SELECT "posts".* FROM "posts" LEFT OUTER JOIN "comments" ON "comments"."post_id" = "posts"."id"'
|
17
15
|
end
|
18
16
|
|
19
17
|
it "should allow adding additional join conditions" do
|
20
|
-
Post.joins(join_association(Post, :comments) do |assoc_name, join_conditions|
|
18
|
+
Post.joins(ArelHelpers.join_association(Post, :comments) do |assoc_name, join_conditions|
|
21
19
|
join_conditions.and(Comment[:id].eq(10))
|
22
20
|
end).to_sql.should ==
|
23
21
|
'SELECT "posts".* FROM "posts" INNER JOIN "comments" ON "comments"."post_id" = "posts"."id" AND "comments"."id" = 10'
|
@@ -25,19 +23,19 @@ describe ArelHelpers::JoinAssociation do
|
|
25
23
|
|
26
24
|
it "should work for two models, one directly associated and the other indirectly" do
|
27
25
|
Post
|
28
|
-
.joins(join_association(Post, :comments))
|
29
|
-
.joins(join_association(Comment, :author))
|
26
|
+
.joins(ArelHelpers.join_association(Post, :comments))
|
27
|
+
.joins(ArelHelpers.join_association(Comment, :author))
|
30
28
|
.to_sql.should ==
|
31
29
|
'SELECT "posts".* FROM "posts" INNER JOIN "comments" ON "comments"."post_id" = "posts"."id" INNER JOIN "authors" ON "authors"."comment_id" = "comments"."id"'
|
32
30
|
end
|
33
31
|
|
34
32
|
it "should be able to handle multiple associations" do
|
35
|
-
Post.joins(join_association(Post, [:comments, :favorites])).to_sql.should ==
|
33
|
+
Post.joins(ArelHelpers.join_association(Post, [:comments, :favorites])).to_sql.should ==
|
36
34
|
'SELECT "posts".* FROM "posts" INNER JOIN "comments" ON "comments"."post_id" = "posts"."id" INNER JOIN "favorites" ON "favorites"."post_id" = "posts"."id"'
|
37
35
|
end
|
38
36
|
|
39
37
|
it "should yield once for each association" do
|
40
|
-
Post.joins(join_association(Post, [:comments, :favorites]) do |assoc_name, join_conditions|
|
38
|
+
Post.joins(ArelHelpers.join_association(Post, [:comments, :favorites]) do |assoc_name, join_conditions|
|
41
39
|
case assoc_name
|
42
40
|
when :favorites
|
43
41
|
join_conditions.or(Favorite[:amount].eq("lots"))
|
@@ -49,3 +47,14 @@ describe ArelHelpers::JoinAssociation do
|
|
49
47
|
end
|
50
48
|
end
|
51
49
|
end
|
50
|
+
|
51
|
+
describe ArelHelpers::JoinAssociation do
|
52
|
+
class AssocPost < Post
|
53
|
+
include ArelHelpers::JoinAssociation
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should provide the join_association method and use the parent class as the model to join on" do
|
57
|
+
AssocPost.joins(AssocPost.join_association(:comments)).to_sql.should ==
|
58
|
+
'SELECT "posts".* FROM "posts" INNER JOIN "comments" ON "comments"."post_id" = "posts"."id"'
|
59
|
+
end
|
60
|
+
end
|
data/spec/query_builder_spec.rb
CHANGED
@@ -44,4 +44,11 @@ describe ArelHelpers::QueryBuilder do
|
|
44
44
|
Post.create(title: "Foobar")
|
45
45
|
builder.map(&:title).should == ["Foobar"]
|
46
46
|
end
|
47
|
+
|
48
|
+
ArelHelpers::QueryBuilder::TERMINAL_METHODS.each do |method|
|
49
|
+
it "does not enumerate records for #{method}" do
|
50
|
+
mock(builder).each.never
|
51
|
+
builder.public_send(method)
|
52
|
+
end
|
53
|
+
end
|
47
54
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: arel-helpers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cameron Dutro
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-06-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -17,9 +17,9 @@ dependencies:
|
|
17
17
|
- - '>='
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: 3.1.0
|
20
|
-
- -
|
20
|
+
- - <
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version:
|
22
|
+
version: '5'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -27,9 +27,9 @@ dependencies:
|
|
27
27
|
- - '>='
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: 3.1.0
|
30
|
-
- -
|
30
|
+
- - <
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version:
|
32
|
+
version: '5'
|
33
33
|
description: Useful tools to help construct database queries with ActiveRecord and
|
34
34
|
Arel.
|
35
35
|
email:
|
@@ -38,20 +38,20 @@ executables: []
|
|
38
38
|
extensions: []
|
39
39
|
extra_rdoc_files: []
|
40
40
|
files:
|
41
|
+
- Gemfile
|
42
|
+
- History.txt
|
43
|
+
- README.md
|
44
|
+
- Rakefile
|
45
|
+
- arel-helpers.gemspec
|
46
|
+
- lib/arel-helpers.rb
|
41
47
|
- lib/arel-helpers/arel_table.rb
|
42
48
|
- lib/arel-helpers/join_association.rb
|
43
49
|
- lib/arel-helpers/query_builder.rb
|
44
50
|
- lib/arel-helpers/version.rb
|
45
|
-
- lib/arel-helpers.rb
|
46
51
|
- spec/arel_table_spec.rb
|
47
52
|
- spec/join_association_spec.rb
|
48
53
|
- spec/query_builder_spec.rb
|
49
54
|
- spec/spec_helper.rb
|
50
|
-
- Gemfile
|
51
|
-
- History.txt
|
52
|
-
- README.md
|
53
|
-
- Rakefile
|
54
|
-
- arel-helpers.gemspec
|
55
55
|
homepage: http://github.com/camertron
|
56
56
|
licenses: []
|
57
57
|
metadata: {}
|
@@ -71,7 +71,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
71
71
|
version: '0'
|
72
72
|
requirements: []
|
73
73
|
rubyforge_project:
|
74
|
-
rubygems_version: 2.
|
74
|
+
rubygems_version: 2.2.2
|
75
75
|
signing_key:
|
76
76
|
specification_version: 4
|
77
77
|
summary: Useful tools to help construct database queries with ActiveRecord and Arel.
|