Skip to content

Commit

Permalink
adapt has_count for approved_comments [state:closed]
Browse files Browse the repository at this point in the history
  • Loading branch information
Sven Fuchs committed Jul 17, 2008
1 parent 7f92c29 commit a6fe940
Show file tree
Hide file tree
Showing 9 changed files with 5,228 additions and 44 deletions.
2 changes: 0 additions & 2 deletions spec/spec_helper.rb
Expand Up @@ -2,8 +2,6 @@
# from the project root directory.
ENV["RAILS_ENV"] = "test"
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
# require 'spec'
# require 'spec/rails'

require File.dirname(__FILE__) + '/spec_helpers/spec_controller_helper'
require File.dirname(__FILE__) + '/spec_helpers/spec_model_helper'
Expand Down
10 changes: 9 additions & 1 deletion vendor/engines/adva_comments/app/models/comment.rb
Expand Up @@ -18,7 +18,7 @@ class Jail < Safemode::Jail

before_validation :set_owners
before_create :authorize_commenting
after_create :update_commentable
after_save :update_commentable
after_destroy :update_commentable

def owner
Expand All @@ -33,6 +33,14 @@ def unapproved?
!approved?
end

def just_approved?
approved? && approved_changed?
end

def just_unapproved?
!approved? && approved_changed?
end

def spam_info
read_attribute(:spam_info) || {}
end
Expand Down
Expand Up @@ -7,15 +7,17 @@ def self.included(base)
module ActMacro
def acts_as_commentable(options = {})
return if acts_as_commentable?

# TODO somehow the various accept_comments? options seem a bit chaotic
# this doesn't even seem to be used anymore
# write_inheritable_attribute :accept_comments?, options.delete(:accept_comments?) || true

options[:order] = 'comments.created_at'
options[:as] = :commentable if options.delete(:polymorphic)

has_counter :comments, :as => options[:as] || name.underscore

has_counter :approved_comments,
:as => options[:as] || name.underscore,
:class_name => 'Comment',
:after_create => false,
:after_destroy => false

with_options options do |c|
c.has_many :comments, :dependent => :delete_all do
Expand All @@ -40,22 +42,23 @@ def acts_as_commentable?
end

module InstanceMethods
def comments_count
@comments_count ||= comments.count # TODO implement as a counter
end

def approved_comments_count
@approved_comments_count ||= approved_comments.count # TODO implement as a counter
def after_comment_update(comment)
method = if comment.frozen?
:decrement!
elsif comment.just_approved?
:increment!
elsif comment.just_unapproved?
:decrement!
end
approved_comments_counter.send method if method
end

# def accept_comments?
# @accept_comments ||= begin
# case accessor = self.class.read_inheritable_attribute(:accept_comments?) || :accept_comments?
# when Symbol then send(accessor)
# when Proc then accessor.call(self)
# else accessor
# end
# end
# def comments_count
# @comments_count ||= comments.count # TODO implement as a counter
# end
#
# def approved_comments_count
# @approved_comments_count ||= approved_comments.count # TODO implement as a counter
# end
end
end
Expand Down
24 changes: 12 additions & 12 deletions vendor/plugins/has_counter/README.markdown
@@ -1,24 +1,24 @@
## Has Counter

Allows to cache the number of records for a has\_many association.
Allows to cache the number of records for a `has_many` association.

Yes, this is the same thing you can do by setting the :counter\_cache option
on the corresponding belongs\_to association.
Yes, this is the same thing you can do by setting the `:counter_cache` option
on the corresponding `belongs_to` association.

The reason for reinventing the counter\_cache wheel here is that counter_cache
is a bit inflexible. We need to "hardcode" the counter_cache column and
thereby clutter the schema. This can especially get annoying in combo with
STI, when - e.g. one subclass needs some counter\_caches that are specific to
this subclass only.
The reason for reinventing the `counter_cache` wheel here is that
`counter_cache` is a bit inflexible. We need to "hardcode" the counter_cache
column and thereby clutter the schema. This can especially get annoying in
combo with STI, when - e.g. one subclass needs some `counter_caches` that are
specific to this subclass only.

Instead, with has\_counter, counters can be separated into an external table
Instead, with `has_counter`, counters can be separated into an external table
with generic column names.

The ActiveRecord counter\_cache mechanism also requires to put the
:counter\_cache directive on the belongs\_to association of the counted class,
The ActiveRecord `counter_cache` mechanism also requires to put the
`:counter_cache` directive on the `belongs_to` association of the counted class,
which adds some tight coupleing that we might want to avoid.

Instead, with has\_counter, we can observe the counted class and update our
Instead, with `has_counter`, we can observe the counted class and update our
counters "from the outside", so the counted class does not need to know about
the fact that somebody else keeps a counter on it.

Expand Down
34 changes: 23 additions & 11 deletions vendor/plugins/has_counter/lib/active_record/has_counter.rb
Expand Up @@ -9,32 +9,44 @@ def included(base)
module ActMacro
def has_counter(*names)
options = names.extract_options!
options.reverse_merge! :after_create => :increment!,
:after_destroy => :decrement!

names.each do |name|
counter_name = :"#{name}_counter"

owner_name = options[:as] || self.name.demodulize.underscore
class_name = options[:class_name] || name

define_method :"#{name}_count" do
send(counter_name).count
end

has_one counter_name, :as => :owner,
:class_name => 'Counter',
:conditions => "name = '#{name}'",
:conditions => "name = '#{name}'",
:dependent => :delete

after_create do |forum|
counter = Counter.create! :owner => forum, :name => name.to_s
end

# Wires up the counted class so that it updates our counter
owner_name = options[:as] || self.name.underscore
name.to_s.classify.constantize.class_eval do
# Wire up the counted class so that it updates our counter
update = lambda{|record, event|
if counter = record.send(owner_name).send(counter_name)
method = options[event]
method = method.call(record) if Proc === method
counter.send method if method
end
}
class_name.to_s.classify.constantize.class_eval do
after_create do |record|
counter = record.send(owner_name).send(counter_name)
counter.increment! if counter
end
after_destroy do |record|
counter = record.send(owner_name).send(counter_name)
counter.decrement! if counter
update.call(record, :after_create)
end
after_save do |record|
update.call(record, :after_save)
end
after_destroy do |record|
update.call(record, :after_destroy)
end
end
end
Expand Down

0 comments on commit a6fe940

Please sign in to comment.