dase 3.2.1 → 3.2.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.
- data/README.md +23 -35
- data/dase.gemspec +5 -3
- data/lib/dase/mp_active_record_relation.rb +24 -4
- data/lib/dase/preloader_methods.rb +10 -1
- data/lib/dase/version.rb +1 -1
- data/test/fixtures/author.rb +2 -0
- data/test/test_dase.rb +17 -0
- metadata +18 -16
data/README.md
CHANGED
@@ -1,29 +1,16 @@
|
|
1
1
|
# Dase
|
2
2
|
|
3
|
-
|
4
|
-
who was a mental calculator - he could count and multiply numbers very quickly. Dase gem adds extra speed
|
5
|
-
to Active Record whenever you need to calculate the number of associated records.
|
6
|
-
|
7
|
-
Here's an example of the code that will cause N + 1 queries:
|
3
|
+
## Overview
|
8
4
|
|
9
|
-
|
10
|
-
Author.find_each do |author|
|
11
|
-
cnt = author.books.where(year: 1992).count
|
12
|
-
puts "#{author.name} has published #{cnt} books in 1992"
|
13
|
-
end
|
14
|
-
```
|
5
|
+
Dase gem provides 'includes_count_of' method on a relation, which works similar to ActiveRecord's 'includes' method.
|
15
6
|
|
16
|
-
|
17
|
-
associated records - see [Rails Guides](http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations)
|
18
|
-
```
|
19
|
-
authors = Author.includes(:books) # => will cause only 2 queries
|
20
|
-
authors.first
|
21
|
-
```
|
7
|
+

|
22
8
|
|
23
|
-
|
9
|
+
Calling 'includes_count_of(:articles)' on a relation object adds 'articles_count' method to each of the authors:
|
24
10
|
```
|
25
|
-
authors = Author.includes_count_of(:
|
26
|
-
authors.first.
|
11
|
+
authors = Author.includes(:publisher).includes_count_of(:articles, :conditions => {:year => 2012})
|
12
|
+
authors.first.name # => 'Billy'
|
13
|
+
authors.first.articles_count # => 2
|
27
14
|
```
|
28
15
|
|
29
16
|
|
@@ -33,14 +20,6 @@ Add this line to your application's Gemfile:
|
|
33
20
|
|
34
21
|
gem 'dase', "~> 3.2.0"
|
35
22
|
|
36
|
-
And then execute:
|
37
|
-
|
38
|
-
$ bundle
|
39
|
-
|
40
|
-
Or install it yourself as:
|
41
|
-
|
42
|
-
$ gem install dase
|
43
|
-
|
44
23
|
### Note on version numbers
|
45
24
|
|
46
25
|
Dase version number correlates with the Active Record's versions number,
|
@@ -53,8 +32,8 @@ Since it's a sort of a "hack", make sure you specified the version number for "d
|
|
53
32
|
### Basic usage:
|
54
33
|
|
55
34
|
```
|
56
|
-
Author.includes_count_of(:
|
57
|
-
puts "#{author.name} has #{author.
|
35
|
+
Author.includes_count_of(:articles).find_each do |author|
|
36
|
+
puts "#{author.name} has #{author.articles_count} articles published"
|
58
37
|
end
|
59
38
|
```
|
60
39
|
|
@@ -63,9 +42,10 @@ Since it's a sort of a "hack", make sure you specified the version number for "d
|
|
63
42
|
You can specify a hash of options which will be passed to the underlying finder
|
64
43
|
which retrieves the association. Valid keys are: :conditions, :group, :having, :joins, :include
|
65
44
|
```
|
66
|
-
Author.includes_count_of(:
|
45
|
+
Author.includes_count_of(:articles, :conditions => {:year => 2012})
|
67
46
|
```
|
68
47
|
|
48
|
+
|
69
49
|
### Known problems
|
70
50
|
|
71
51
|
1. Dase doesn't support :through option on associations
|
@@ -74,21 +54,29 @@ Author.includes_count_of(:books, :conditions => {:year => 1990})
|
|
74
54
|
```
|
75
55
|
class Author
|
76
56
|
scope :with_counters, lambda {
|
77
|
-
includes_count_of(:
|
57
|
+
includes_count_of(:articles) # this will not work!!!
|
78
58
|
}
|
79
59
|
end
|
80
60
|
```
|
81
61
|
|
82
62
|
## How it works
|
83
63
|
|
84
|
-
|
64
|
+
Here's a pseudo-code that gives an idea on how it works internally
|
85
65
|
```
|
86
|
-
counters_hash =
|
66
|
+
counters_hash = Article.where(:year => 2012).count(:group => :author_id)
|
87
67
|
Author.find_each do |author|
|
88
|
-
puts "#{author.name} has #{counters_hash[author.id] || 0}
|
68
|
+
puts "#{author.name} has #{counters_hash[author.id] || 0} articles published"
|
89
69
|
end
|
90
70
|
```
|
91
71
|
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
## Name origin
|
76
|
+
|
77
|
+
The gem is named by the german mathematician [Johann Dase](http://en.wikipedia.org/wiki/Zacharias_Dase),
|
78
|
+
who was a mental calculator - he could count and multiply numbers very quickly.
|
79
|
+
|
92
80
|
## Contributing
|
93
81
|
|
94
82
|
1. Fork it
|
data/dase.gemspec
CHANGED
@@ -8,9 +8,11 @@ Gem::Specification.new do |gem|
|
|
8
8
|
gem.version = Dase::VERSION
|
9
9
|
gem.authors = ["Vladimir Yartsev"]
|
10
10
|
gem.email = ["vovayartsev@gmail.com"]
|
11
|
-
gem.description = %q{
|
12
|
-
|
13
|
-
|
11
|
+
gem.description = %q{Dase gem creates includes_count_of method in ActiveRecord::Relation
|
12
|
+
to count associated records efficiently. See examples at https://github.com/vovayartsev/dase
|
13
|
+
}
|
14
|
+
gem.summary = %q{Provides includes_count_of method on ActiveRecord::Relation to count associated records efficiently}
|
15
|
+
gem.homepage = "https://github.com/vovayartsev/dase"
|
14
16
|
|
15
17
|
gem.add_runtime_dependency "activerecord", "~> 3.2.0"
|
16
18
|
gem.add_runtime_dependency "activesupport", "~> 3.2.0"
|
@@ -5,22 +5,42 @@ module Dase
|
|
5
5
|
def includes_count_of(*args)
|
6
6
|
args.reject! { |a| a.blank? }
|
7
7
|
options = args.extract_options!
|
8
|
-
options.assert_valid_keys(:conditions, :group, :having, :limit, :offset, :joins, :include, :from, :lock)
|
8
|
+
options.assert_valid_keys(:as, :conditions, :group, :having, :limit, :offset, :joins, :include, :from, :lock)
|
9
9
|
return self if args.empty?
|
10
|
+
if options.present? and args.many?
|
11
|
+
raise ArgumentError, "includes_count_of takes either multiple associations OR single association + options"
|
12
|
+
end
|
10
13
|
relation = clone
|
11
14
|
relation.dase_values ||= {}
|
12
|
-
args.each
|
15
|
+
args.each do |arg|
|
16
|
+
if options[:as].present?
|
17
|
+
options[:association] = arg
|
18
|
+
relation.dase_values[options[:as].to_sym] = options
|
19
|
+
else
|
20
|
+
relation.dase_values[arg] = options
|
21
|
+
end
|
22
|
+
end
|
13
23
|
relation
|
14
24
|
end
|
15
25
|
|
16
26
|
def attach_dase_counters_to_records
|
17
27
|
if dase_values.present? and !@has_dase_counters
|
18
|
-
dase_values.each do |
|
19
|
-
|
28
|
+
dase_values.each do |association, options|
|
29
|
+
association = options.delete(:association) if options[:association]
|
30
|
+
Dase::Preloader.new(@records, association, options).run
|
20
31
|
end
|
21
32
|
@has_dase_counters = true
|
22
33
|
end
|
23
34
|
end
|
35
|
+
|
36
|
+
def merge(r)
|
37
|
+
super(r).tap do |result|
|
38
|
+
if r.dase_values.present?
|
39
|
+
result.dase_values ||= {}
|
40
|
+
result.dase_values.merge!(r.dase_values)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
24
44
|
end
|
25
45
|
end
|
26
46
|
|
@@ -1,7 +1,15 @@
|
|
1
1
|
module Dase
|
2
2
|
module PreloaderMethods
|
3
|
+
|
4
|
+
def initialize(klass, owners, reflection, preload_options)
|
5
|
+
# grabbing our options
|
6
|
+
preload_options = preload_options.clone
|
7
|
+
@dase_counter_name = preload_options.delete(:as)
|
8
|
+
super(klass, owners, reflection, preload_options)
|
9
|
+
end
|
10
|
+
|
3
11
|
def preload
|
4
|
-
counter_name = "#{reflection.name}_count".to_sym
|
12
|
+
counter_name = @dase_counter_name || "#{reflection.name}_count".to_sym
|
5
13
|
pk = model.primary_key.to_sym
|
6
14
|
ids = owners.map(&pk)
|
7
15
|
fk = "#{scoped.quoted_table_name}.#{reflection.foreign_key}"
|
@@ -11,5 +19,6 @@ module Dase
|
|
11
19
|
owner.set_dase_counter(counter_name, value)
|
12
20
|
end
|
13
21
|
end
|
22
|
+
|
14
23
|
end
|
15
24
|
end
|
data/lib/dase/version.rb
CHANGED
data/test/fixtures/author.rb
CHANGED
data/test/test_dase.rb
CHANGED
@@ -24,6 +24,23 @@ class TestBase < Test::Unit::TestCase
|
|
24
24
|
assert_equal true, Author.includes_count_of(:books).first.respond_to?(:books_count), "doesn't respond'"
|
25
25
|
end
|
26
26
|
|
27
|
+
should "sneeze through scope definitions" do
|
28
|
+
assert_equal true, Author.with_count_of_books.first.respond_to?(:books_count), "doesn't respond'"
|
29
|
+
end
|
30
|
+
|
31
|
+
should "support :as option" do
|
32
|
+
assert_equal true, Author.includes_count_of(:books, :as => :my_count).first.respond_to?(:my_count), "doesn't respond'"
|
33
|
+
end
|
34
|
+
|
35
|
+
should "support old_books_count and new_books_count simultaneously using :as option" do
|
36
|
+
scope = Author.includes_count_of(:books, :conditions => {:year => 2012}, :as => :new_books_count).
|
37
|
+
includes_count_of(:books, :conditions => {:year => 1990}, :as => :old_books_count)
|
38
|
+
dase_counts = scope.order(:name).map{ |a| [a.old_books_count, a.new_books_count] }
|
39
|
+
# the order is Bobby[:old, :new], Joe[:old, :new], Teddy[:old, :new]
|
40
|
+
true_counts = [[1, 0], [2, 1], [0, 0]] # see books.yml
|
41
|
+
assert_equal true_counts, dase_counts, "results mismatch"
|
42
|
+
end
|
43
|
+
|
27
44
|
should "count old books" do
|
28
45
|
traditional_counts = Author.order(:name).map { |a| a.old_books.count }
|
29
46
|
dase_counts = Author.includes_count_of(:old_books).order(:name).map { |a| a.old_books_count }
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dase
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.2.
|
4
|
+
version: 3.2.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-09-
|
12
|
+
date: 2012-09-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
16
|
-
requirement: &
|
16
|
+
requirement: &70318116217480 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 3.2.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70318116217480
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: activesupport
|
27
|
-
requirement: &
|
27
|
+
requirement: &70318116214540 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 3.2.0
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70318116214540
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: shoulda
|
38
|
-
requirement: &
|
38
|
+
requirement: &70318116213300 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70318116213300
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: sqlite3
|
49
|
-
requirement: &
|
49
|
+
requirement: &70318116211980 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: 1.3.3
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70318116211980
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: debugger
|
60
|
-
requirement: &
|
60
|
+
requirement: &70318116211100 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,9 +65,10 @@ dependencies:
|
|
65
65
|
version: '0'
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
69
|
-
description:
|
70
|
-
|
68
|
+
version_requirements: *70318116211100
|
69
|
+
description: ! "Dase gem creates includes_count_of method in ActiveRecord::Relation\n
|
70
|
+
\ to count associated records efficiently. See examples at
|
71
|
+
https://github.com/vovayartsev/dase\n "
|
71
72
|
email:
|
72
73
|
- vovayartsev@gmail.com
|
73
74
|
executables: []
|
@@ -95,7 +96,7 @@ files:
|
|
95
96
|
- test/fixtures/schema.rb
|
96
97
|
- test/helper.rb
|
97
98
|
- test/test_dase.rb
|
98
|
-
homepage: https://github.com/vovayartsev
|
99
|
+
homepage: https://github.com/vovayartsev/dase
|
99
100
|
licenses: []
|
100
101
|
post_install_message:
|
101
102
|
rdoc_options: []
|
@@ -118,7 +119,8 @@ rubyforge_project:
|
|
118
119
|
rubygems_version: 1.8.11
|
119
120
|
signing_key:
|
120
121
|
specification_version: 3
|
121
|
-
summary:
|
122
|
+
summary: Provides includes_count_of method on ActiveRecord::Relation to count associated
|
123
|
+
records efficiently
|
122
124
|
test_files:
|
123
125
|
- test/fixtures/author.rb
|
124
126
|
- test/fixtures/authors.yml
|