dase 3.2.2 → 3.2.4
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 +21 -13
- data/lib/dase/mp_active_record_base.rb +10 -1
- data/lib/dase/mp_active_record_relation.rb +7 -10
- data/lib/dase/preloader.rb +14 -4
- data/lib/dase/preloader_methods.rb +21 -4
- data/lib/dase/version.rb +1 -1
- data/test/fixtures/author.rb +1 -0
- data/test/fixtures/book.rb +2 -0
- data/test/fixtures/like.rb +4 -0
- data/test/fixtures/likes.yml +10 -0
- data/test/fixtures/quote.rb +2 -1
- data/test/fixtures/schema.rb +6 -0
- data/test/test_dase.rb +35 -6
- metadata +16 -12
data/README.md
CHANGED
@@ -37,28 +37,36 @@ Since it's a sort of a "hack", make sure you specified the version number for "d
|
|
37
37
|
end
|
38
38
|
```
|
39
39
|
|
40
|
-
###
|
41
|
-
|
42
|
-
You can specify a hash of options which will be passed to the underlying finder
|
40
|
+
### Using :conditions hash
|
41
|
+
Specify a hash of options which will be passed to the underlying finder
|
43
42
|
which retrieves the association. Valid keys are: :conditions, :group, :having, :joins, :include
|
44
43
|
```
|
45
|
-
Author.includes_count_of(:articles, :conditions => {:year => 2012})
|
44
|
+
Author.includes_count_of(:articles, :conditions => {:year => 2012}) # counts only articles in year 2012
|
46
45
|
```
|
47
46
|
|
47
|
+
### Using scope merging
|
48
|
+
```
|
49
|
+
scope = Article.where(:year => 2012)
|
50
|
+
Author.includes_count_of(:articles, :only => scope) # counts only articles in year 2012
|
51
|
+
```
|
48
52
|
|
49
|
-
###
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
+
### Using block syntax
|
54
|
+
```
|
55
|
+
Author.includes_count_of(:articles){ where(:year => 2012) } # in the block, 'self' is a Relation instance
|
56
|
+
Author.includes_count_of(:articles){ |scope| scope.where(:year => 2012) } # 'self' is the same inside and outside the block
|
57
|
+
```
|
53
58
|
|
59
|
+
### Renaming counter column
|
54
60
|
```
|
55
|
-
|
56
|
-
|
57
|
-
includes_count_of(:articles) # this will not work!!!
|
58
|
-
}
|
59
|
-
end
|
61
|
+
sites = WebSite.includes_count_of(:users, :conditions => {:role => 'admin'}, :as => :admins_count)
|
62
|
+
sites.each { |site| puts "Site #{site.url} has #{site.admins_count} admin users" }
|
60
63
|
```
|
61
64
|
|
65
|
+
|
66
|
+
### Known problems
|
67
|
+
|
68
|
+
Dase doesn't support polymorphism.
|
69
|
+
|
62
70
|
## How it works
|
63
71
|
|
64
72
|
Here's a pseudo-code that gives an idea on how it works internally
|
@@ -4,7 +4,7 @@ module Dase
|
|
4
4
|
module ARBaseInstanceMethods
|
5
5
|
def set_dase_counter(name, value)
|
6
6
|
@dase_counters ||= {}
|
7
|
-
@dase_counters[name] = value
|
7
|
+
@dase_counters[name.to_sym] = value
|
8
8
|
end
|
9
9
|
|
10
10
|
def get_dase_counter(name)
|
@@ -23,6 +23,15 @@ module Dase
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
+
# This method uses traditions counting method
|
27
|
+
# and stores the results in dase-compatible form
|
28
|
+
def refresh_dase_counters!(*associations)
|
29
|
+
associations.flatten.each do |assoc|
|
30
|
+
value = self.send(assoc).count # i.e. book.quotes.count
|
31
|
+
set_dase_counter "#{column}_count", value # i.e. book.quotes_count = value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
26
35
|
private
|
27
36
|
|
28
37
|
def self.included(klass)
|
@@ -5,29 +5,26 @@ 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(:as, :conditions, :group, :having, :limit, :offset, :joins, :include, :from, :lock)
|
8
|
+
options.assert_valid_keys(:proc, :as, :only, :conditions, :group, :having, :limit, :offset, :joins, :include, :from, :lock)
|
9
9
|
return self if args.empty?
|
10
10
|
if options.present? and args.many?
|
11
11
|
raise ArgumentError, "includes_count_of takes either multiple associations OR single association + options"
|
12
12
|
end
|
13
13
|
relation = clone
|
14
14
|
relation.dase_values ||= {}
|
15
|
+
#options[:proc] = block if block
|
15
16
|
args.each do |arg|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
else
|
20
|
-
relation.dase_values[arg] = options
|
21
|
-
end
|
17
|
+
options[:association] = arg.to_sym
|
18
|
+
options[:as] = (options[:as] || "#{arg}_count").to_sym
|
19
|
+
relation.dase_values[options[:as]] = options
|
22
20
|
end
|
23
21
|
relation
|
24
22
|
end
|
25
23
|
|
26
24
|
def attach_dase_counters_to_records
|
27
25
|
if dase_values.present? and !@has_dase_counters
|
28
|
-
dase_values.each do |
|
29
|
-
|
30
|
-
Dase::Preloader.new(@records, association, options).run
|
26
|
+
dase_values.each do |name, options|
|
27
|
+
Dase::Preloader.new(@records, options[:association], options).run
|
31
28
|
end
|
32
29
|
@has_dase_counters = true
|
33
30
|
end
|
data/lib/dase/preloader.rb
CHANGED
@@ -10,9 +10,18 @@ module Dase
|
|
10
10
|
end
|
11
11
|
|
12
12
|
# Not implemented yet
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
class HasManyThrough < ::ActiveRecord::Associations::Preloader::HasManyThrough
|
14
|
+
include Dase::PreloaderMethods
|
15
|
+
|
16
|
+
def prefixed_foreign_key
|
17
|
+
"#{reflection.active_record.table_name}.#{reflection.active_record_primary_key}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def records_for(ids)
|
21
|
+
reflection.active_record.joins(reflection.name).
|
22
|
+
where(prefixed_foreign_key => ids)
|
23
|
+
end
|
24
|
+
end
|
16
25
|
|
17
26
|
# an overloaded version of ActiveRecord::Associations::Preloader's preloader_for
|
18
27
|
# which returns a class of a custom preloader for a given association
|
@@ -20,7 +29,8 @@ module Dase
|
|
20
29
|
case reflection.macro
|
21
30
|
when :has_many
|
22
31
|
if reflection.options[:through]
|
23
|
-
|
32
|
+
HasManyThrough
|
33
|
+
#raise NotImplementedError, "The support for HasManyThrough associations is not implemented yet"
|
24
34
|
else
|
25
35
|
HasMany
|
26
36
|
end
|
@@ -4,19 +4,36 @@ module Dase
|
|
4
4
|
def initialize(klass, owners, reflection, preload_options)
|
5
5
|
# grabbing our options
|
6
6
|
preload_options = preload_options.clone
|
7
|
+
@dase_association = preload_options.delete(:association)
|
7
8
|
@dase_counter_name = preload_options.delete(:as)
|
9
|
+
@dase_scope_to_merge = preload_options.delete(:only)
|
10
|
+
#@dase_proc = preload_options.delete(:proc)
|
8
11
|
super(klass, owners, reflection, preload_options)
|
9
12
|
end
|
10
13
|
|
14
|
+
def prefixed_foreign_key
|
15
|
+
"#{scoped.quoted_table_name}.#{reflection.foreign_key}"
|
16
|
+
end
|
17
|
+
|
11
18
|
def preload
|
12
|
-
counter_name = @dase_counter_name || "#{reflection.name}_count".to_sym
|
13
19
|
pk = model.primary_key.to_sym
|
14
20
|
ids = owners.map(&pk)
|
15
|
-
|
16
|
-
|
21
|
+
scope = records_for(ids)
|
22
|
+
scope = scope.merge(@dase_scope_to_merge) if @dase_scope_to_merge
|
23
|
+
#if @dase_proc # support for includes_count_of(...){ where(...) } syntax
|
24
|
+
# case @dase_proc.arity
|
25
|
+
# when 0
|
26
|
+
# scope = scope.instance_eval &@dase_proc
|
27
|
+
# when 1
|
28
|
+
# scope = @dase_proc.call(scope)
|
29
|
+
# else
|
30
|
+
# raise ArgumentError, "The block passed to includes_count_of takes 0 or 1 arguments"
|
31
|
+
# end
|
32
|
+
#end
|
33
|
+
counters_hash = scope.count(:group => prefixed_foreign_key)
|
17
34
|
owners.each do |owner|
|
18
35
|
value = counters_hash[owner[pk]] || 0 # 0 is "default count", when no records found
|
19
|
-
owner.set_dase_counter(
|
36
|
+
owner.set_dase_counter(@dase_counter_name, value)
|
20
37
|
end
|
21
38
|
end
|
22
39
|
|
data/lib/dase/version.rb
CHANGED
data/test/fixtures/author.rb
CHANGED
data/test/fixtures/book.rb
CHANGED
data/test/fixtures/quote.rb
CHANGED
data/test/fixtures/schema.rb
CHANGED
data/test/test_dase.rb
CHANGED
@@ -57,13 +57,42 @@ class TestBase < Test::Unit::TestCase
|
|
57
57
|
compare_counts(traditional_counts, dase_counts, true_counts)
|
58
58
|
end
|
59
59
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
60
|
+
should "count books for year 2012 using :only option" do
|
61
|
+
dase_counts = Author.includes_count_of(:books, :only => Book.year2012).order(:name).map { |a| a.books_count }
|
62
|
+
# the order is: Bobby, Joe, Teddy - due to order(:name)
|
63
|
+
true_counts = [0, 1, 0] # see books.yml
|
64
|
+
assert_equal true_counts, dase_counts, "results mismatch"
|
65
|
+
end
|
66
|
+
|
67
|
+
#should "count using block conditions (arity: 0)" do
|
68
|
+
# dase_counts = Author.includes_count_of(:books){where(:year => 2012)}.order(:name).map { |a| a.books_count }
|
64
69
|
# # the order is: Bobby, Joe, Teddy - due to order(:name)
|
65
|
-
# true_counts = [
|
66
|
-
#
|
70
|
+
# true_counts = [0, 1, 0] # see books.yml
|
71
|
+
# assert_equal true_counts, dase_counts, "results mismatch"
|
67
72
|
#end
|
73
|
+
#
|
74
|
+
#should "count using block conditions (arity: 1)" do
|
75
|
+
# @y = 2012
|
76
|
+
# dase_counts = Author.includes_count_of(:books){ |books| books.where(:year => @y)}.order(:name).map { |a| a.books_count }
|
77
|
+
# # the order is: Bobby, Joe, Teddy - due to order(:name)
|
78
|
+
# true_counts = [0, 1, 0] # see books.yml
|
79
|
+
# assert_equal true_counts, dase_counts, "results mismatch"
|
80
|
+
#end
|
81
|
+
|
82
|
+
should "count likes" do
|
83
|
+
dase_counts = Author.order(:name).includes_count_of(:scores).map { |a| a.scores_count }
|
84
|
+
# the order is: Bobby, Joe, Teddy - due to order(:name)
|
85
|
+
true_counts = [0, 2, 0] # see likes.yml
|
86
|
+
assert_equal true_counts, dase_counts, "results mismatch"
|
87
|
+
end
|
88
|
+
|
89
|
+
should "count quotations" do
|
90
|
+
traditional_counts = Author.order(:name).map { |a| a.quotes.count }
|
91
|
+
dase_counts = Author.order(:name).includes_count_of(:quotes).map { |a| a.quotes_count }
|
92
|
+
# the order is: Bobby, Joe, Teddy - due to order(:name)
|
93
|
+
true_counts = [2, 1, 0] # see quotes.yml
|
94
|
+
compare_counts(traditional_counts, dase_counts, true_counts)
|
95
|
+
end
|
96
|
+
|
68
97
|
end
|
69
98
|
end
|
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.4
|
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-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
16
|
-
requirement: &
|
16
|
+
requirement: &70360982006260 !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: *70360982006260
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: activesupport
|
27
|
-
requirement: &
|
27
|
+
requirement: &70360982005420 !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: *70360982005420
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: shoulda
|
38
|
-
requirement: &
|
38
|
+
requirement: &70360982004720 !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: *70360982004720
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: sqlite3
|
49
|
-
requirement: &
|
49
|
+
requirement: &70360982003420 !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: *70360982003420
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: debugger
|
60
|
-
requirement: &
|
60
|
+
requirement: &70360982001620 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,7 +65,7 @@ dependencies:
|
|
65
65
|
version: '0'
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70360982001620
|
69
69
|
description: ! "Dase gem creates includes_count_of method in ActiveRecord::Relation\n
|
70
70
|
\ to count associated records efficiently. See examples at
|
71
71
|
https://github.com/vovayartsev/dase\n "
|
@@ -91,6 +91,8 @@ files:
|
|
91
91
|
- test/fixtures/authors.yml
|
92
92
|
- test/fixtures/book.rb
|
93
93
|
- test/fixtures/books.yml
|
94
|
+
- test/fixtures/like.rb
|
95
|
+
- test/fixtures/likes.yml
|
94
96
|
- test/fixtures/quote.rb
|
95
97
|
- test/fixtures/quotes.yml
|
96
98
|
- test/fixtures/schema.rb
|
@@ -126,6 +128,8 @@ test_files:
|
|
126
128
|
- test/fixtures/authors.yml
|
127
129
|
- test/fixtures/book.rb
|
128
130
|
- test/fixtures/books.yml
|
131
|
+
- test/fixtures/like.rb
|
132
|
+
- test/fixtures/likes.yml
|
129
133
|
- test/fixtures/quote.rb
|
130
134
|
- test/fixtures/quotes.yml
|
131
135
|
- test/fixtures/schema.rb
|