dase 3.2.1 → 3.2.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,29 +1,16 @@
1
1
  # Dase
2
2
 
3
- The gem is named by the german mathematician [Johann Dase](http://en.wikipedia.org/wiki/Zacharias_Dase),
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
- Active Record has a built-in solution for efficient fetching of
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
+ ![Dase example](https://vovayartsev-home.s3.amazonaws.com/dase-mockup.png)
22
8
 
23
- The Dase gem provides a similar solution for the efficient counting of associated records:
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(:books) # => will cause only 2 queries
26
- authors.first.books_count
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(:books).find_each do |author|
57
- puts "#{author.name} has #{author.books_count} books published"
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(:books, :conditions => {:year => 1990})
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(:books) # this will not work!!!
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
- The equivalent code would look something like this:
64
+ Here's a pseudo-code that gives an idea on how it works internally
85
65
  ```
86
- counters_hash = Book.count(:group => :author_id)
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} books published"
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{A solution for N+1 querying problem in Active Record associated records counting}
12
- gem.summary = %q{Really fast associated records counting}
13
- gem.homepage = "https://github.com/vovayartsev"
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 { |a| relation.dase_values[a] = options}
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 |associations, options|
19
- Dase::Preloader.new(@records, associations, options).run
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
@@ -1,3 +1,3 @@
1
1
  module Dase
2
- VERSION = "3.2.1"
2
+ VERSION = "3.2.2"
3
3
  end
@@ -6,4 +6,6 @@ class Author < ActiveRecord::Base
6
6
 
7
7
  has_many :quotes, :through => :books
8
8
 
9
+ scope :with_count_of_books, lambda { includes_count_of(:books) }
10
+
9
11
  end
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.1
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-02 00:00:00.000000000 Z
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: &70291652811140 !ruby/object:Gem::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: *70291652811140
24
+ version_requirements: *70318116217480
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: activesupport
27
- requirement: &70291652810620 !ruby/object:Gem::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: *70291652810620
35
+ version_requirements: *70318116214540
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: shoulda
38
- requirement: &70291652810220 !ruby/object:Gem::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: *70291652810220
46
+ version_requirements: *70318116213300
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: sqlite3
49
- requirement: &70291652808960 !ruby/object:Gem::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: *70291652808960
57
+ version_requirements: *70318116211980
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: debugger
60
- requirement: &70291652808360 !ruby/object:Gem::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: *70291652808360
69
- description: A solution for N+1 querying problem in Active Record associated records
70
- counting
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: Really fast associated records counting
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