includes-count 0.1
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/.gitignore +4 -0
- data/Gemfile +4 -0
- data/README.md +4 -0
- data/Rakefile +1 -0
- data/includes-count.gemspec +24 -0
- data/lib/includes-count.rb +137 -0
- data/lib/includes-count/version.rb +3 -0
- metadata +64 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "includes-count/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "includes-count"
|
7
|
+
s.version = IncludesCount::VERSION
|
8
|
+
s.authors = ["Santiago Palladino"]
|
9
|
+
s.email = ["spalladino@manas.com.ar"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Adds includes_count method to active record queries}
|
12
|
+
s.description = %q{The includes_count method executes a SQL count on an association to retrieve its number of records, optionally filtered by a set of conditions.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "includes-count"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
# s.add_development_dependency "rspec"
|
23
|
+
s.add_runtime_dependency "activerecord"
|
24
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require "includes-count/version"
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
|
5
|
+
module Associations
|
6
|
+
|
7
|
+
class CountPreloader < Preloader
|
8
|
+
|
9
|
+
class Count < ActiveRecord::Associations::Preloader::HasOne
|
10
|
+
|
11
|
+
def initialize(klass, owners, reflection, preload_options)
|
12
|
+
super
|
13
|
+
@preload_options[:select] ||= "#{table.name}.#{association_key_name}, COUNT(id) AS #{count_name}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def count_name
|
17
|
+
preload_options[:count_name].try(:to_s) || "#{reflection.name}_count"
|
18
|
+
end
|
19
|
+
|
20
|
+
def preload
|
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
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def build_scope
|
28
|
+
super.group(association_key)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
class CountHasMany < Count
|
34
|
+
end
|
35
|
+
|
36
|
+
class CountHasManyThrough < Count
|
37
|
+
include ThroughAssociation
|
38
|
+
|
39
|
+
def associated_records_by_owner
|
40
|
+
through_records = through_records_by_owner
|
41
|
+
|
42
|
+
ActiveRecord::Associations::CountPreloader.new(
|
43
|
+
through_records.values.flatten,
|
44
|
+
source_reflection.name, options.merge(@preload_options)
|
45
|
+
).run
|
46
|
+
|
47
|
+
through_records
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
def through_records_by_owner
|
52
|
+
ActiveRecord::Associations::Preloader.new(
|
53
|
+
owners, through_reflection.name,
|
54
|
+
through_options
|
55
|
+
).run
|
56
|
+
|
57
|
+
Hash[owners.map do |owner|
|
58
|
+
through_records = Array.wrap(owner.send(through_reflection.name))
|
59
|
+
|
60
|
+
# Dont cache the association - we would only be caching a subset
|
61
|
+
if reflection.options[:source_type] && through_reflection.collection?
|
62
|
+
owner.association(through_reflection.name).reset
|
63
|
+
end
|
64
|
+
|
65
|
+
[owner, through_records]
|
66
|
+
end]
|
67
|
+
end
|
68
|
+
|
69
|
+
def through_options
|
70
|
+
through_options = {}
|
71
|
+
|
72
|
+
if options[:source_type]
|
73
|
+
through_options[:conditions] = { reflection.foreign_type => options[:source_type] }
|
74
|
+
else
|
75
|
+
if options[:conditions]
|
76
|
+
through_options[:include] = options[:include] || options[:source]
|
77
|
+
through_options[:conditions] = options[:conditions]
|
78
|
+
end
|
79
|
+
|
80
|
+
through_options[:order] = options[:order]
|
81
|
+
end
|
82
|
+
|
83
|
+
if @preload_options[:through_options]
|
84
|
+
through_preload_options = @preload_options[:through_options][through_reflection.name.to_sym] || {}
|
85
|
+
through_options.merge!(through_preload_options)
|
86
|
+
end
|
87
|
+
|
88
|
+
through_options
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
def preloader_for(reflection)
|
94
|
+
case reflection.macro
|
95
|
+
when :has_many
|
96
|
+
reflection.options[:through] ? CountHasManyThrough : CountHasMany
|
97
|
+
else
|
98
|
+
raise "unsupported association kind #{reflection.macro}"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
class Relation
|
107
|
+
|
108
|
+
attr_accessor :includes_counts_values
|
109
|
+
|
110
|
+
def includes_count(*args)
|
111
|
+
args.reject! {|a| a.blank? }
|
112
|
+
|
113
|
+
return self if args.empty?
|
114
|
+
|
115
|
+
relation = clone
|
116
|
+
(relation.includes_counts_values ||= []) << args
|
117
|
+
relation
|
118
|
+
end
|
119
|
+
|
120
|
+
def to_a_with_includes_count
|
121
|
+
return @records if loaded?
|
122
|
+
|
123
|
+
to_a_without_includes_count
|
124
|
+
|
125
|
+
(includes_counts_values || []).each do |association_with_opts|
|
126
|
+
association, opts = association_with_opts
|
127
|
+
ActiveRecord::Associations::CountPreloader.new(@records, [association], opts).run
|
128
|
+
end
|
129
|
+
|
130
|
+
@records
|
131
|
+
end
|
132
|
+
|
133
|
+
alias_method_chain :to_a, :includes_count
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: includes-count
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Santiago Palladino
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-02-10 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activerecord
|
16
|
+
requirement: &70110664755620 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70110664755620
|
25
|
+
description: The includes_count method executes a SQL count on an association to retrieve
|
26
|
+
its number of records, optionally filtered by a set of conditions.
|
27
|
+
email:
|
28
|
+
- spalladino@manas.com.ar
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- .gitignore
|
34
|
+
- Gemfile
|
35
|
+
- README.md
|
36
|
+
- Rakefile
|
37
|
+
- includes-count.gemspec
|
38
|
+
- lib/includes-count.rb
|
39
|
+
- lib/includes-count/version.rb
|
40
|
+
homepage: ''
|
41
|
+
licenses: []
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
none: false
|
48
|
+
requirements:
|
49
|
+
- - ! '>='
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ! '>='
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
requirements: []
|
59
|
+
rubyforge_project: includes-count
|
60
|
+
rubygems_version: 1.8.10
|
61
|
+
signing_key:
|
62
|
+
specification_version: 3
|
63
|
+
summary: Adds includes_count method to active record queries
|
64
|
+
test_files: []
|