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