arel-helpers 1.2.0 → 2.0.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: a7fcf6cccc8b45ef008f68a1da1ef9e8b468d474
4
- data.tar.gz: dc76542898067426f8715ccb4b91b2b1d48f1787
3
+ metadata.gz: 74fd32a2a3bd64b02be9dbd561afe2add390752f
4
+ data.tar.gz: 78f91129ab6b88b3b93675de7a0e96f7ac9ef716
5
5
  SHA512:
6
- metadata.gz: 3c95d188bfb80db50e13f8c98d7847bca7abc6c0b95502c22efd0eef043f1a5ec0b71b5c71410cfeaeabfb9533ae9ee02099d5fc55644f886fe95599ecd1631a
7
- data.tar.gz: b74df2e5fd0224f8945219944888eea41be7e78532f4e277ae1e17eb3fc5ba792a35e0957e08b1b0dc87cd8a376d4b769c99fb0981c12475ca54efca404e084d
6
+ metadata.gz: a8082544e84ddca08e248276682473fa8d2af08cd666fb2b76c2588cbf52546c8f9345805693dbd6929ec0a58f64e130f21a7fdb973374feb81f263a2b6fd65c
7
+ data.tar.gz: 0fa70969de2f89d8fa03c2b0747ca3da4c011e9399f74b4f9222611401df399bbd24ecd7e2e577e9600de21291a5f99d06559ca0648be1c1770b27ff08f902fa
data/History.txt CHANGED
@@ -8,4 +8,8 @@
8
8
 
9
9
  == 1.2.0
10
10
 
11
- * Adding Rails 4 support.
11
+ * Adding Rails 4 support.
12
+
13
+ == 2.0.0
14
+
15
+ * Turning JoinAssociation into an ActiveSupport::Concern (breaks backwards compatibility).
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 probably one of the only ways to do an outer join with ActiveRecord. For example, let's say we have these two models:
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 wanted to do an outer join instead of the default inner join. Your query might look like this:
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::JoinAssociation`:
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
- include ArelHelpers::JoinAssociation
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
- `JoinAssociation` also allows you to customize the join conditions via a block:
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(Author[:username].in(usernames))
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
- No external requirements. Depends on SQLite for testing purposes.
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
@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
16
16
  if ENV["AR"]
17
17
  s.add_dependency 'activerecord', ENV["AR"]
18
18
  else
19
- s.add_dependency 'activerecord', '>= 3.1.0', '<= 4.1.0'
19
+ s.add_dependency 'activerecord', '>= 3.1.0', '< 5'
20
20
  end
21
21
 
22
22
  s.require_path = 'lib'
@@ -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
- # activerecord uses JoinDependency to automagically generate inner join statements for
7
- # any type of association (belongs_to, has_many, and has_and_belongs_to_many).
8
- # For example, for HABTM associations, two join statements are required.
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
- private
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
- def join_association_3_1(table, association, join_type)
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
- join_dependency.join_associations.each do |assoc|
27
- assoc.join_type = join_type
28
- assoc.join_to(manager)
29
- end
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
- manager.join_sources.map do |assoc|
32
- if block_given?
33
- # yield |assoc_name, join_conditions|
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
- def join_association_4_1(table, association, join_type)
43
- associations = association.is_a?(Array) ? association : [association]
44
- join_dependency = ActiveRecord::Associations::JoinDependency.new(table, associations, [])
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
- join_dependency.join_constraints([]).map do |constraint|
47
- right = if block_given?
48
- yield constraint.left.name.to_sym, constraint.right
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
- join_type.new(constraint.left, right)
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
- end
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
@@ -1,5 +1,5 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  module ArelHelpers
4
- VERSION = "1.2.0"
4
+ VERSION = "2.0.0"
5
5
  end
@@ -2,22 +2,20 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe ArelHelpers::JoinAssociation do
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
@@ -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: 1.2.0
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-05-07 00:00:00.000000000 Z
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: 4.1.0
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: 4.1.0
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.0.3
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.