includes-count 0.1 → 0.2

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.
@@ -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: []