includes-count 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 01664881d5b615683b68c72b023bf901e492fb35
4
+ data.tar.gz: 15a6cba04f0c7a280de2eb6825532665cee4e15c
5
+ SHA512:
6
+ metadata.gz: 1b60c9999656159d056e45ca3a457e597079d3891bc734aa4f7be65caa9515aec046156c79de7c01f5356a4547e9f02bfb2524172fc9d186fb09e6e677dc71a5
7
+ data.tar.gz: 8312170635c24bad30c4ae06c5f5bd290bbb8be175ec112c27ac51e7b8848d76f39071aae08dea7c2940418df8ee8150c2efe8d40470c62c66ca36baa7931d4a
data/README.md CHANGED
@@ -1,4 +1,138 @@
1
- Includes Count Gem
2
- ==================
1
+ Includes Count
2
+ ==============
3
3
 
4
- Tested with ActiveRecord version 3.1.3
4
+ This gem adds an `includes_count` method to active record queries, which adds the count of an association to a relation using a simple `SELECT` SQL query in a similar way as the `includes` method does, only that retrieving counts instead of the full records collection.
5
+
6
+ This gem has been tested with ActiveRecord version 3.1.3.
7
+
8
+ Usage
9
+ =====
10
+
11
+ For example, in the following model:
12
+
13
+ ```ruby
14
+ class Blog
15
+ has_many :posts
16
+ has_many :comments, :through => :posts
17
+ end
18
+
19
+ class Post
20
+ belongs_to :blog
21
+ has_many :comments
22
+ end
23
+
24
+ class Comment
25
+ belongs_to :post
26
+ end
27
+ ```
28
+
29
+ It is possible to retrieve the number of posts in every blog with the command:
30
+
31
+ ```ruby
32
+ blogs_with_posts_count = Blog.scoped.includes_count(:posts)
33
+ ```
34
+
35
+ This will issue a simple `SELECT` query retrieving all counts and assigning them in memory, thus not requiring an `INNER JOIN` that could be expensive to handle in the database:
36
+
37
+ ```sql
38
+ SELECT SQL_NO_CACHE posts.blog_id, COUNT(id) AS posts_count
39
+ FROM `posts`
40
+ WHERE `posts`.`blog_id` IN (1, 2, 3, 4, 5, 6, 7, 8)
41
+ GROUP BY `posts`.`blog_id`
42
+ ```
43
+
44
+ The count is projected to a field named by default `association_name_count`:
45
+
46
+ ```ruby
47
+ blogs_with_posts_count.map(&:posts_count)
48
+ ```
49
+
50
+ The name of the method can be changed by supplying the `count_name` option:
51
+
52
+ ```ruby
53
+ blogs_with_posts_count = Blog.scoped.includes_count(:posts, :count_name => 'number_of_posts')
54
+ blogs_with_posts_count.map(&:posts_count)
55
+ ```
56
+
57
+ The execution of the count is delayed until execution of the query, as happens with the `includes` clause, so further clauses, such as `where`, can be set to the relation:
58
+
59
+ ```ruby
60
+ latest_blogs_with_posts_count = Blog.scoped.includes_count(:posts).where('updated_at > ?', 1.week.ago)
61
+ ```
62
+
63
+ This will retrieve only the blogs that have been updated since 1 week ago, along with their counts. Supposing there are only two blogs that match that condition (ids 3 and 5), the `SELECT` query issued will be the following:
64
+
65
+ ```sql
66
+ SELECT SQL_NO_CACHE posts.blog_id, COUNT(id) AS posts_count
67
+ FROM `posts`
68
+ WHERE `posts`.`blog_id` IN (3, 5)
69
+ GROUP BY `posts`.`blog_id`
70
+ ```
71
+
72
+ Conditions can be specified on the included association (using a string, a hash or a proc), in order to filter which records are to be counted:
73
+
74
+ ```ruby
75
+ blogs_with_rails_posts_count = Blog.scoped.includes_count(:posts, :count_name => 'rails_posts_count', :conditions => "category = 'rails'")
76
+ ```
77
+
78
+ ```sql
79
+ SELECT SQL_NO_CACHE posts.blog_id, COUNT(id) AS posts_count
80
+ FROM `posts`
81
+ WHERE `posts`.`blog_id` IN (3, 5)
82
+ AND `posts`.`category` = 'rails'
83
+ GROUP BY `posts`.`blog_id`
84
+ ```
85
+
86
+ Through Associations
87
+ --------------------
88
+
89
+ The `includes_count` method also supports through associations, and issues as many `SELECT` queries as needed to navigate the hierarchy and obtain the specified counts.
90
+
91
+ ```ruby
92
+ blogs_with_comments_count = Blog.scoped.includes_count(:comments)
93
+ ```
94
+
95
+ ```sql
96
+ SELECT SQL_NO_CACHE `posts`.*
97
+ FROM `posts`
98
+ WHERE `posts`.`blog_id` IN (1, 2, 3, 4, 5, 6, 7, 8)
99
+ ```
100
+
101
+ ```sql
102
+ SELECT SQL_NO_CACHE `comments`.post_id, COUNT(id) AS comments_count
103
+ FROM `comments`
104
+ WHERE `comments`.`post_id` IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
105
+ GROUP BY `comments`.`post_id`
106
+ ```
107
+
108
+ As usual, the value can be accessed via the method named after the association, and overridden via `count_name`:
109
+
110
+ ```ruby
111
+ blogs_with_posts_count.map(&:comments_count)
112
+ ```
113
+
114
+ It is also possible to specify conditions at any of the intermediate associations in the `through` association:
115
+
116
+ ```ruby
117
+ blogs_with_comments_count_from_rails_posts = Blog.scoped.includes_count(:comments, :through_options => { :posts => { :conditions => "category = 'rails'"} })
118
+ ```
119
+
120
+ ```sql
121
+ SELECT SQL_NO_CACHE `posts`.*
122
+ FROM `posts`
123
+ WHERE `posts`.`blog_id` IN (1, 2, 3, 4, 5, 6, 7, 8)
124
+ AND `posts`.`category` = 'rails'
125
+ ```
126
+
127
+ ```sql
128
+ SELECT SQL_NO_CACHE `comments`.post_id, COUNT(id) AS comments_count
129
+ FROM `comments`
130
+ WHERE `comments`.`post_id` IN (5, 6, 10, 11, 12)
131
+ GROUP BY `comments`.`post_id`
132
+ ```
133
+
134
+
135
+ Known Issues
136
+ ------------
137
+
138
+ * The `includes_count` method is included only in `ActiveRecord::Relation` objects, which means you cannot execute it straight on a model. As a workaround, supply the method `scoped` before executing `includes_count`: `Blog.scoped.includes_count(:posts)`
@@ -19,8 +19,13 @@ module ActiveRecord
19
19
 
20
20
  def preload
21
21
  associated_records_by_owner.each do |owner, associated_records|
22
- owner[count_name] ||= 0
23
- owner[count_name] += associated_records.map{|r| r[count_name] || 0}.sum
22
+ sum = associated_records.map{|r| r[count_name] || 0}.sum
23
+ owner.instance_eval "
24
+ def #{count_name}
25
+ @#{count_name} ||= 0
26
+ @#{count_name} += #{sum}
27
+ end
28
+ "
24
29
  end
25
30
  end
26
31
 
@@ -1,3 +1,3 @@
1
1
  module IncludesCount
2
- VERSION = "0.1"
2
+ VERSION = "0.2"
3
3
  end
metadata CHANGED
@@ -1,27 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: includes-count
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.1'
5
- prerelease:
4
+ version: '0.2'
6
5
  platform: ruby
7
6
  authors:
8
7
  - Santiago Palladino
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-02-10 00:00:00.000000000 Z
11
+ date: 2014-08-08 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: activerecord
16
- requirement: &70110664755620 !ruby/object:Gem::Requirement
17
- none: false
15
+ requirement: !ruby/object:Gem::Requirement
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :runtime
23
21
  prerelease: false
24
- version_requirements: *70110664755620
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
25
27
  description: The includes_count method executes a SQL count on an association to retrieve
26
28
  its number of records, optionally filtered by a set of conditions.
27
29
  email:
@@ -39,26 +41,25 @@ files:
39
41
  - lib/includes-count/version.rb
40
42
  homepage: ''
41
43
  licenses: []
44
+ metadata: {}
42
45
  post_install_message:
43
46
  rdoc_options: []
44
47
  require_paths:
45
48
  - lib
46
49
  required_ruby_version: !ruby/object:Gem::Requirement
47
- none: false
48
50
  requirements:
49
- - - ! '>='
51
+ - - '>='
50
52
  - !ruby/object:Gem::Version
51
53
  version: '0'
52
54
  required_rubygems_version: !ruby/object:Gem::Requirement
53
- none: false
54
55
  requirements:
55
- - - ! '>='
56
+ - - '>='
56
57
  - !ruby/object:Gem::Version
57
58
  version: '0'
58
59
  requirements: []
59
60
  rubyforge_project: includes-count
60
- rubygems_version: 1.8.10
61
+ rubygems_version: 2.0.14
61
62
  signing_key:
62
- specification_version: 3
63
+ specification_version: 4
63
64
  summary: Adds includes_count method to active record queries
64
65
  test_files: []