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 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.