dase 3.2.1 → 3.2.2

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 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