dase 3.2.2 → 3.2.4

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
@@ -37,28 +37,36 @@ Since it's a sort of a "hack", make sure you specified the version number for "d
37
37
  end
38
38
  ```
39
39
 
40
- ### Advanced usage:
41
-
42
- You can specify a hash of options which will be passed to the underlying finder
40
+ ### Using :conditions hash
41
+ Specify a hash of options which will be passed to the underlying finder
43
42
  which retrieves the association. Valid keys are: :conditions, :group, :having, :joins, :include
44
43
  ```
45
- Author.includes_count_of(:articles, :conditions => {:year => 2012})
44
+ Author.includes_count_of(:articles, :conditions => {:year => 2012}) # counts only articles in year 2012
46
45
  ```
47
46
 
47
+ ### Using scope merging
48
+ ```
49
+ scope = Article.where(:year => 2012)
50
+ Author.includes_count_of(:articles, :only => scope) # counts only articles in year 2012
51
+ ```
48
52
 
49
- ### Known problems
50
-
51
- 1. Dase doesn't support :through option on associations
52
- 2. You can't put includes_count_of calls into a scope declaration, like this:
53
+ ### Using block syntax
54
+ ```
55
+ Author.includes_count_of(:articles){ where(:year => 2012) } # in the block, 'self' is a Relation instance
56
+ Author.includes_count_of(:articles){ |scope| scope.where(:year => 2012) } # 'self' is the same inside and outside the block
57
+ ```
53
58
 
59
+ ### Renaming counter column
54
60
  ```
55
- class Author
56
- scope :with_counters, lambda {
57
- includes_count_of(:articles) # this will not work!!!
58
- }
59
- end
61
+ sites = WebSite.includes_count_of(:users, :conditions => {:role => 'admin'}, :as => :admins_count)
62
+ sites.each { |site| puts "Site #{site.url} has #{site.admins_count} admin users" }
60
63
  ```
61
64
 
65
+
66
+ ### Known problems
67
+
68
+ Dase doesn't support polymorphism.
69
+
62
70
  ## How it works
63
71
 
64
72
  Here's a pseudo-code that gives an idea on how it works internally
@@ -4,7 +4,7 @@ module Dase
4
4
  module ARBaseInstanceMethods
5
5
  def set_dase_counter(name, value)
6
6
  @dase_counters ||= {}
7
- @dase_counters[name] = value
7
+ @dase_counters[name.to_sym] = value
8
8
  end
9
9
 
10
10
  def get_dase_counter(name)
@@ -23,6 +23,15 @@ module Dase
23
23
  end
24
24
  end
25
25
 
26
+ # This method uses traditions counting method
27
+ # and stores the results in dase-compatible form
28
+ def refresh_dase_counters!(*associations)
29
+ associations.flatten.each do |assoc|
30
+ value = self.send(assoc).count # i.e. book.quotes.count
31
+ set_dase_counter "#{column}_count", value # i.e. book.quotes_count = value
32
+ end
33
+ end
34
+
26
35
  private
27
36
 
28
37
  def self.included(klass)
@@ -5,29 +5,26 @@ 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(:as, :conditions, :group, :having, :limit, :offset, :joins, :include, :from, :lock)
8
+ options.assert_valid_keys(:proc, :as, :only, :conditions, :group, :having, :limit, :offset, :joins, :include, :from, :lock)
9
9
  return self if args.empty?
10
10
  if options.present? and args.many?
11
11
  raise ArgumentError, "includes_count_of takes either multiple associations OR single association + options"
12
12
  end
13
13
  relation = clone
14
14
  relation.dase_values ||= {}
15
+ #options[:proc] = block if block
15
16
  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
17
+ options[:association] = arg.to_sym
18
+ options[:as] = (options[:as] || "#{arg}_count").to_sym
19
+ relation.dase_values[options[:as]] = options
22
20
  end
23
21
  relation
24
22
  end
25
23
 
26
24
  def attach_dase_counters_to_records
27
25
  if dase_values.present? and !@has_dase_counters
28
- dase_values.each do |association, options|
29
- association = options.delete(:association) if options[:association]
30
- Dase::Preloader.new(@records, association, options).run
26
+ dase_values.each do |name, options|
27
+ Dase::Preloader.new(@records, options[:association], options).run
31
28
  end
32
29
  @has_dase_counters = true
33
30
  end
@@ -10,9 +10,18 @@ module Dase
10
10
  end
11
11
 
12
12
  # Not implemented yet
13
- #class HasManyThrough < ::ActiveRecord::Associations::Preloader::HasManyThrough
14
- # include Dase::PreloaderMethods
15
- #end
13
+ class HasManyThrough < ::ActiveRecord::Associations::Preloader::HasManyThrough
14
+ include Dase::PreloaderMethods
15
+
16
+ def prefixed_foreign_key
17
+ "#{reflection.active_record.table_name}.#{reflection.active_record_primary_key}"
18
+ end
19
+
20
+ def records_for(ids)
21
+ reflection.active_record.joins(reflection.name).
22
+ where(prefixed_foreign_key => ids)
23
+ end
24
+ end
16
25
 
17
26
  # an overloaded version of ActiveRecord::Associations::Preloader's preloader_for
18
27
  # which returns a class of a custom preloader for a given association
@@ -20,7 +29,8 @@ module Dase
20
29
  case reflection.macro
21
30
  when :has_many
22
31
  if reflection.options[:through]
23
- raise NotImplementedError, "The support for HasManyThrough associations is not implemented yet"
32
+ HasManyThrough
33
+ #raise NotImplementedError, "The support for HasManyThrough associations is not implemented yet"
24
34
  else
25
35
  HasMany
26
36
  end
@@ -4,19 +4,36 @@ module Dase
4
4
  def initialize(klass, owners, reflection, preload_options)
5
5
  # grabbing our options
6
6
  preload_options = preload_options.clone
7
+ @dase_association = preload_options.delete(:association)
7
8
  @dase_counter_name = preload_options.delete(:as)
9
+ @dase_scope_to_merge = preload_options.delete(:only)
10
+ #@dase_proc = preload_options.delete(:proc)
8
11
  super(klass, owners, reflection, preload_options)
9
12
  end
10
13
 
14
+ def prefixed_foreign_key
15
+ "#{scoped.quoted_table_name}.#{reflection.foreign_key}"
16
+ end
17
+
11
18
  def preload
12
- counter_name = @dase_counter_name || "#{reflection.name}_count".to_sym
13
19
  pk = model.primary_key.to_sym
14
20
  ids = owners.map(&pk)
15
- fk = "#{scoped.quoted_table_name}.#{reflection.foreign_key}"
16
- counters_hash = records_for(ids).count(group: fk)
21
+ scope = records_for(ids)
22
+ scope = scope.merge(@dase_scope_to_merge) if @dase_scope_to_merge
23
+ #if @dase_proc # support for includes_count_of(...){ where(...) } syntax
24
+ # case @dase_proc.arity
25
+ # when 0
26
+ # scope = scope.instance_eval &@dase_proc
27
+ # when 1
28
+ # scope = @dase_proc.call(scope)
29
+ # else
30
+ # raise ArgumentError, "The block passed to includes_count_of takes 0 or 1 arguments"
31
+ # end
32
+ #end
33
+ counters_hash = scope.count(:group => prefixed_foreign_key)
17
34
  owners.each do |owner|
18
35
  value = counters_hash[owner[pk]] || 0 # 0 is "default count", when no records found
19
- owner.set_dase_counter(counter_name, value)
36
+ owner.set_dase_counter(@dase_counter_name, value)
20
37
  end
21
38
  end
22
39
 
data/lib/dase/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Dase
2
- VERSION = "3.2.2"
2
+ VERSION = "3.2.4"
3
3
  end
@@ -5,6 +5,7 @@ class Author < ActiveRecord::Base
5
5
  has_many :old_books, :class_name => "Book", :conditions => {:year => 1990}
6
6
 
7
7
  has_many :quotes, :through => :books
8
+ has_many :scores, :through => :quotes
8
9
 
9
10
  scope :with_count_of_books, lambda { includes_count_of(:books) }
10
11
 
@@ -4,4 +4,6 @@ class Book < ActiveRecord::Base
4
4
  belongs_to :author
5
5
  has_many :quotes
6
6
 
7
+ scope :year2012, lambda{ where(:year => 2012) }
8
+
7
9
  end
@@ -0,0 +1,4 @@
1
+ class Like < ActiveRecord::Base
2
+ belongs_to :quote
3
+ has_one :author, :through => :quote
4
+ end
@@ -0,0 +1,10 @@
1
+ good:
2
+ id: 1
3
+ quote_id: 1
4
+ score: 5
5
+
6
+ bad:
7
+ id: 2
8
+ quote_id: 1
9
+ score: 2
10
+
@@ -1,4 +1,5 @@
1
1
  class Quote < ActiveRecord::Base
2
2
  belongs_to :book
3
-
3
+ #has_one :author, :through => :book
4
+ has_many :scores, :class_name => 'Like', :foreign_key => "quote_id"
4
5
  end
@@ -19,4 +19,10 @@ ActiveRecord::Schema.define do
19
19
  t.integer "book_id"
20
20
  end
21
21
 
22
+ create_table "likes", :force => true do |t|
23
+ t.integer "score"
24
+ t.integer "quote_id"
25
+ end
26
+
27
+
22
28
  end
data/test/test_dase.rb CHANGED
@@ -57,13 +57,42 @@ class TestBase < Test::Unit::TestCase
57
57
  compare_counts(traditional_counts, dase_counts, true_counts)
58
58
  end
59
59
 
60
- # Not yet implemented
61
- #should "count quotations" do
62
- # traditional_counts = Author.order(:name).map { |a| a.quotes.count }
63
- # dase_counts = Author.order(:name).includes_count_of(:quotes).map { |a| a.quotes_count }
60
+ should "count books for year 2012 using :only option" do
61
+ dase_counts = Author.includes_count_of(:books, :only => Book.year2012).order(:name).map { |a| a.books_count }
62
+ # the order is: Bobby, Joe, Teddy - due to order(:name)
63
+ true_counts = [0, 1, 0] # see books.yml
64
+ assert_equal true_counts, dase_counts, "results mismatch"
65
+ end
66
+
67
+ #should "count using block conditions (arity: 0)" do
68
+ # dase_counts = Author.includes_count_of(:books){where(:year => 2012)}.order(:name).map { |a| a.books_count }
64
69
  # # the order is: Bobby, Joe, Teddy - due to order(:name)
65
- # true_counts = [2, 1, 0] # see quotes.yml
66
- # compare_counts(traditional_counts, dase_counts, true_counts)
70
+ # true_counts = [0, 1, 0] # see books.yml
71
+ # assert_equal true_counts, dase_counts, "results mismatch"
67
72
  #end
73
+ #
74
+ #should "count using block conditions (arity: 1)" do
75
+ # @y = 2012
76
+ # dase_counts = Author.includes_count_of(:books){ |books| books.where(:year => @y)}.order(:name).map { |a| a.books_count }
77
+ # # the order is: Bobby, Joe, Teddy - due to order(:name)
78
+ # true_counts = [0, 1, 0] # see books.yml
79
+ # assert_equal true_counts, dase_counts, "results mismatch"
80
+ #end
81
+
82
+ should "count likes" do
83
+ dase_counts = Author.order(:name).includes_count_of(:scores).map { |a| a.scores_count }
84
+ # the order is: Bobby, Joe, Teddy - due to order(:name)
85
+ true_counts = [0, 2, 0] # see likes.yml
86
+ assert_equal true_counts, dase_counts, "results mismatch"
87
+ end
88
+
89
+ should "count quotations" do
90
+ traditional_counts = Author.order(:name).map { |a| a.quotes.count }
91
+ dase_counts = Author.order(:name).includes_count_of(:quotes).map { |a| a.quotes_count }
92
+ # the order is: Bobby, Joe, Teddy - due to order(:name)
93
+ true_counts = [2, 1, 0] # see quotes.yml
94
+ compare_counts(traditional_counts, dase_counts, true_counts)
95
+ end
96
+
68
97
  end
69
98
  end
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.2
4
+ version: 3.2.4
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-23 00:00:00.000000000 Z
12
+ date: 2012-09-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
16
- requirement: &70318116217480 !ruby/object:Gem::Requirement
16
+ requirement: &70360982006260 !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: *70318116217480
24
+ version_requirements: *70360982006260
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: activesupport
27
- requirement: &70318116214540 !ruby/object:Gem::Requirement
27
+ requirement: &70360982005420 !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: *70318116214540
35
+ version_requirements: *70360982005420
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: shoulda
38
- requirement: &70318116213300 !ruby/object:Gem::Requirement
38
+ requirement: &70360982004720 !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: *70318116213300
46
+ version_requirements: *70360982004720
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: sqlite3
49
- requirement: &70318116211980 !ruby/object:Gem::Requirement
49
+ requirement: &70360982003420 !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: *70318116211980
57
+ version_requirements: *70360982003420
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: debugger
60
- requirement: &70318116211100 !ruby/object:Gem::Requirement
60
+ requirement: &70360982001620 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,7 +65,7 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70318116211100
68
+ version_requirements: *70360982001620
69
69
  description: ! "Dase gem creates includes_count_of method in ActiveRecord::Relation\n
70
70
  \ to count associated records efficiently. See examples at
71
71
  https://github.com/vovayartsev/dase\n "
@@ -91,6 +91,8 @@ files:
91
91
  - test/fixtures/authors.yml
92
92
  - test/fixtures/book.rb
93
93
  - test/fixtures/books.yml
94
+ - test/fixtures/like.rb
95
+ - test/fixtures/likes.yml
94
96
  - test/fixtures/quote.rb
95
97
  - test/fixtures/quotes.yml
96
98
  - test/fixtures/schema.rb
@@ -126,6 +128,8 @@ test_files:
126
128
  - test/fixtures/authors.yml
127
129
  - test/fixtures/book.rb
128
130
  - test/fixtures/books.yml
131
+ - test/fixtures/like.rb
132
+ - test/fixtures/likes.yml
129
133
  - test/fixtures/quote.rb
130
134
  - test/fixtures/quotes.yml
131
135
  - test/fixtures/schema.rb