includes-count 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in includes-count.gemspec
4
+ gemspec
@@ -0,0 +1,4 @@
1
+ Includes Count Gem
2
+ ==================
3
+
4
+ Tested with ActiveRecord version 3.1.3
@@ -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
@@ -0,0 +1,3 @@
1
+ module IncludesCount
2
+ VERSION = "0.1"
3
+ 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: []