Skip to content

Commit 22e5821

Browse files
author
Saimon Moore
committed
Initial commit
0 parents  commit 22e5821

13 files changed

+287
-0
lines changed

History.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
=== 0.9.8 / 2008-12-07
2+
3+
* Initial commit

LICENSE

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Copyright (c) 2008 Saimon Moore
2+
3+
Permission is hereby granted, free of charge, to any person obtaining
4+
a copy of this software and associated documentation files (the
5+
"Software"), to deal in the Software without restriction, including
6+
without limitation the rights to use, copy, modify, merge, publish,
7+
distribute, sublicense, and/or sell copies of the Software, and to
8+
permit persons to whom the Software is furnished to do so, subject to
9+
the following conditions:
10+
11+
The above copyright notice and this permission notice shall be
12+
included in all copies or substantial portions of the Software.
13+
14+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Manifest.txt

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
History.txt
2+
LICENSE
3+
Manifest.txt
4+
README.txt
5+
Rakefile
6+
TODO
7+
lib/dm-counter-cache.rb
8+
lib/dm-counter-cache/version.rb
9+
spec/integration/dm-counter-cache_spec.rb
10+
spec/spec.opts
11+
spec/spec_helper.rb
12+
tasks/install.rb
13+
tasks/spec.rb

README.txt

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
== README
2+
3+
DataMapper::CounterCacheable automates the dec/incrementing of association counter fields. This is
4+
similar to counter caches in ActiveRecord.
5+
6+
Example:
7+
8+
class Post
9+
include DataMapper::Resource
10+
11+
has n :comments
12+
13+
property :id, Integer, :serial => true
14+
property :comments_count, Integer, :default => 0
15+
end
16+
17+
class Comment
18+
include DataMapper::Resource
19+
include DataMapper::CounterCacheable
20+
21+
belongs_to :post, :counter_cache => true
22+
23+
end

Rakefile

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
require 'pathname'
2+
require 'rubygems'
3+
4+
ROOT = Pathname(__FILE__).dirname.expand_path
5+
JRUBY = RUBY_PLATFORM =~ /java/
6+
WINDOWS = Gem.win_platform?
7+
SUDO = (WINDOWS || JRUBY) ? '' : ('sudo' unless ENV['SUDOLESS'])
8+
9+
require ROOT + 'lib/dm-counter-cache/version'
10+
11+
AUTHOR = 'Saimon Moore'
12+
EMAIL = 'saimonmoore [a] gmail [d] com'
13+
GEM_NAME = 'dm-counter-cache'
14+
GEM_VERSION = DataMapper::CounterCacheable::VERSION
15+
GEM_DEPENDENCIES = [['dm-core', "~>#{GEM_VERSION}"]]
16+
GEM_CLEAN = %w[ log pkg coverage ]
17+
GEM_EXTRAS = { :has_rdoc => true, :extra_rdoc_files => %w[ README.txt LICENSE TODO History.txt ] }
18+
19+
PROJECT_NAME = 'datamapper'
20+
PROJECT_URL = "http://github.com/saimonmoore/dm-counter-cache/tree/master/#{GEM_NAME}"
21+
PROJECT_DESCRIPTION = PROJECT_SUMMARY = 'DataMapper plugin for for counter caches ala ActiveRecord'
22+
23+
[ ROOT, ROOT.parent ].each do |dir|
24+
Pathname.glob(dir.join('tasks/**/*.rb').to_s).each { |f| require f }
25+
end

TODO

Whitespace-only changes.

lib/dm-counter-cache.rb

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
module DataMapper
2+
# CounterCacheable allows you to transparently maintain counts on collection association of this model on the parent model.
3+
# You can also specify a custom counter cache column by providing a column name instead of a true/false value
4+
# to this option (e.g., :counter_cache => :my_custom_counter.)
5+
module CounterCacheable
6+
7+
def self.included(klass)
8+
DataMapper::Associations::ManyToOne.module_eval {
9+
extend DataMapper::CounterCacheable::ClassMethods
10+
(class << self; self; end).class_eval do
11+
alias_method :setup_without_counter_caching, :setup
12+
alias_method :setup, :setup_with_counter_caching
13+
end
14+
}
15+
end
16+
17+
module ClassMethods
18+
19+
def setup_with_counter_caching(name, model, options = {})
20+
counter_cache_attribute = options.delete(:counter_cache)
21+
relationship = setup_without_counter_caching(name, model, options)
22+
23+
if counter_cache_attribute
24+
case counter_cache_attribute
25+
when String, Symbol
26+
counter_cache_attribute = counter_cache_attribute.intern
27+
else
28+
counter_cache_attribute = "#{model.storage_name}_count".intern
29+
end
30+
31+
relationship.parent_model.class_eval <<-EOS, __FILE__, __LINE__
32+
property :#{counter_cache_attribute}, Integer, :default => 0, :lazy => false
33+
EOS
34+
35+
parent_name = relationship.parent_model.name.downcase
36+
37+
model.class_eval <<-EOS, __FILE__, __LINE__
38+
after :create, :increment_counter_cache_for_#{parent_name}
39+
after :destroy, :decrement_counter_cache_for_#{parent_name}
40+
41+
def increment_counter_cache_for_#{parent_name}
42+
self.#{parent_name}.update_attributes(:#{counter_cache_attribute} => self.#{parent_name}.#{counter_cache_attribute}.succ)
43+
end
44+
45+
def decrement_counter_cache_for_#{parent_name}
46+
self.#{parent_name}.update_attributes(:#{counter_cache_attribute} => self.#{parent_name}.#{counter_cache_attribute} - 1)
47+
end
48+
49+
EOS
50+
end
51+
52+
relationship
53+
end
54+
55+
end # ClassMethods
56+
57+
end # CounterCacheable
58+
end # DataMapper
59+
60+
if $0 == __FILE__
61+
require 'rubygems'
62+
63+
gem 'dm-core', '~>0.9.8'
64+
require 'dm-core'
65+
66+
FileUtils.touch(File.join(Dir.pwd, "migration_test.db"))
67+
DataMapper.setup(:default, "sqlite3://#{Dir.pwd}/migration_test.db")
68+
69+
class Post
70+
include DataMapper::Resource
71+
72+
property :id, Integer, :serial => true
73+
has n, :comments
74+
end
75+
Post.auto_migrate!
76+
77+
class Comment
78+
include DataMapper::Resource
79+
include DataMapper::CounterCacheable
80+
81+
belongs_to :post, :counter_cache => true
82+
end
83+
Comments.auto_migrate!
84+
85+
Post.create.comments.create
86+
end

lib/dm-counter-cache/version.rb

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module DataMapper
2+
module CounterCacheable
3+
VERSION = '0.9.8'
4+
end
5+
end
+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
require 'pathname'
2+
require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
3+
4+
describe DataMapper::CounterCacheable do
5+
before :all do
6+
class Post
7+
include DataMapper::Resource
8+
9+
has n, :comments
10+
11+
property :id, Integer, :serial => true
12+
property :comments_count, Integer, :default => 0
13+
end
14+
15+
class Comment
16+
include DataMapper::Resource
17+
include DataMapper::CounterCacheable
18+
19+
property :id, Integer, :serial => true
20+
belongs_to :post, :counter_cache => true
21+
22+
end
23+
24+
Comment.auto_migrate!
25+
Post.auto_migrate!
26+
end
27+
28+
before(:each) do
29+
@post = Post.create
30+
end
31+
32+
it "should increment comments_count" do
33+
@post.comments.create
34+
@post.reload.comments_count.should == 1
35+
end
36+
37+
it "should decrement comments_count" do
38+
comment1 = @post.comments.create
39+
comment2 = @post.comments.create
40+
comment2.destroy
41+
@post.reload.comments_count.should == 1
42+
end
43+
44+
end

spec/spec.opts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--colour

spec/spec_helper.rb

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
require 'pathname'
2+
require 'rubygems'
3+
4+
gem 'rspec', '~>1.1.11'
5+
require 'spec'
6+
7+
gem 'dm-core', '~>0.9.8'
8+
require 'dm-core'
9+
10+
require Pathname(__FILE__).dirname.parent.expand_path + 'lib/dm-counter-cache'
11+
12+
def load_driver(name, default_uri)
13+
return false if ENV['ADAPTER'] != name.to_s
14+
15+
begin
16+
DataMapper.setup(name, ENV["#{name.to_s.upcase}_SPEC_URI"] || default_uri)
17+
DataMapper::Repository.adapters[:default] = DataMapper::Repository.adapters[name]
18+
true
19+
rescue LoadError => e
20+
warn "Could not load do_#{name}: #{e}"
21+
false
22+
end
23+
end
24+
25+
ENV['ADAPTER'] ||= 'sqlite3'
26+
27+
HAS_SQLITE3 = load_driver(:sqlite3, 'sqlite3::memory:')
28+
HAS_MYSQL = load_driver(:mysql, 'mysql://localhost/dm_core_test')
29+
HAS_POSTGRES = load_driver(:postgres, 'postgres://postgres@localhost/dm_core_test')

tasks/install.rb

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
def sudo_gem(cmd)
2+
sh "#{SUDO} #{RUBY} -S gem #{cmd}", :verbose => false
3+
end
4+
5+
desc "Install #{GEM_NAME} #{GEM_VERSION}"
6+
task :install => [ :package ] do
7+
sudo_gem "install --local pkg/#{GEM_NAME}-#{GEM_VERSION} --no-update-sources"
8+
end
9+
10+
desc "Uninstall #{GEM_NAME} #{GEM_VERSION}"
11+
task :uninstall => [ :clobber ] do
12+
sudo_gem "uninstall #{GEM_NAME} -v#{GEM_VERSION} -Ix"
13+
end

tasks/spec.rb

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
begin
2+
gem 'rspec', '~>1.1.11'
3+
require 'spec'
4+
require 'spec/rake/spectask'
5+
6+
task :default => [ :spec ]
7+
8+
desc 'Run specifications'
9+
Spec::Rake::SpecTask.new(:spec) do |t|
10+
t.spec_opts << '--options' << 'spec/spec.opts' if File.exists?('spec/spec.opts')
11+
t.spec_files = Pathname.glob((ROOT + 'spec/**/*_spec.rb').to_s)
12+
13+
begin
14+
gem 'rcov', '~>0.8'
15+
t.rcov = JRUBY ? false : (ENV.has_key?('NO_RCOV') ? ENV['NO_RCOV'] != 'true' : true)
16+
t.rcov_opts << '--exclude' << 'spec'
17+
t.rcov_opts << '--text-summary'
18+
t.rcov_opts << '--sort' << 'coverage' << '--sort-reverse'
19+
rescue LoadError
20+
# rcov not installed
21+
end
22+
end
23+
rescue LoadError
24+
# rspec not installed
25+
end

0 commit comments

Comments
 (0)