dzema_dm-counter-cache 0.9.12

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/History.txt ADDED
@@ -0,0 +1,7 @@
1
+ === 0.9.12 / 2009-11-15
2
+
3
+ * Version without class_eval
4
+
5
+ === 0.9.8 / 2008-12-07
6
+
7
+ * Initial commit
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008-2009 Saimon Moore, Dmitriy Dzema
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.
data/Manifest.txt ADDED
@@ -0,0 +1,14 @@
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/integration/multiple_define_spec.rb
11
+ spec/spec.opts
12
+ spec/spec_helper.rb
13
+ tasks/install.rb
14
+ tasks/spec.rb
data/README.txt ADDED
@@ -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
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ require 'pathname'
2
+ require 'rubygems'
3
+ require 'hoe'
4
+
5
+ ROOT = Pathname(__FILE__).dirname.expand_path
6
+ JRUBY = RUBY_PLATFORM =~ /java/
7
+ WINDOWS = Gem.win_platform?
8
+ SUDO = (WINDOWS || JRUBY) ? '' : ('sudo' unless ENV['SUDOLESS'])
9
+
10
+ require ROOT + 'lib/dm-counter-cache/version'
11
+
12
+ AUTHOR = ['Saimon Moore', 'Dmitriy Dzema']
13
+ EMAIL = ['daimonmoore [a] gmail [d] com', 'dima [a] dzema [d] name']
14
+ GEM_NAME = 'dzema_dm-counter-cache'
15
+ GEM_VERSION = DataMapper::CounterCacheable::VERSION
16
+ GEM_DEPENDENCIES = [['dm-core', "=#{GEM_VERSION}"]]
17
+ GEM_CLEAN = %w[ log pkg coverage ]
18
+ GEM_EXTRAS = { :has_rdoc => true, :extra_rdoc_files => %w[ README.txt LICENSE TODO History.txt ] }
19
+
20
+ PROJECT_NAME = 'datamapper'
21
+ PROJECT_URL = "http://github.com/DimaD/dm-counter-cache"
22
+ PROJECT_DESCRIPTION = PROJECT_SUMMARY = 'DataMapper plugin for counter caches ala ActiveRecord. Original idea and implementation by Saimon Moore (daimonmoore [a] gmail [d] com)'
23
+
24
+ [ ROOT, ROOT.parent ].each do |dir|
25
+ Pathname.glob(dir.join('tasks/**/*.rb').to_s).each { |f| require f }
26
+ end
27
+
28
+ require 'tasks/hoe'
data/TODO ADDED
File without changes
@@ -0,0 +1,5 @@
1
+ module DataMapper
2
+ module CounterCacheable
3
+ VERSION = '0.9.12'
4
+ end
5
+ end
@@ -0,0 +1,109 @@
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 do
9
+ extend DataMapper::CounterCacheable::ClassMethods
10
+
11
+ (class << self; self; end).class_eval do
12
+ unless method_defined?(:setup_without_counter_caching)
13
+ alias_method :setup_without_counter_caching, :setup
14
+ alias_method :setup, :setup_with_counter_caching
15
+ end
16
+ end
17
+
18
+ end
19
+ end
20
+
21
+ module ClassMethods
22
+
23
+ def setup_with_counter_caching(name, model, options = {})
24
+ perform_counter_cache = options.delete(:counter_cache)
25
+
26
+ relationship = setup_without_counter_caching(name, model, options)
27
+
28
+ if perform_counter_cache
29
+ counter_cache_attribute = case perform_counter_cache
30
+ when String, Symbol
31
+ perform_counter_cache.to_sym
32
+ else
33
+ "#{model.storage_name}_count".to_sym
34
+ end
35
+
36
+ model.extend(ModelClassMethods)
37
+ model.__send__(:include, InstanceMethods)
38
+
39
+ model.define_counter_cache_callbacks_for(relationship, counter_cache_attribute)
40
+ end
41
+
42
+ relationship
43
+ end
44
+
45
+ end # ClassMethods
46
+
47
+ module ModelClassMethods
48
+ def define_counter_cache_callbacks_for(relationship, counter_cache_attribute)
49
+ return if defined_counter_cache_callbacks_for?(counter_cache_attribute)
50
+
51
+ self.after(:create) { adjust_counter_cache_for(relationship, counter_cache_attribute, +1) }
52
+ self.after(:destroy) { adjust_counter_cache_for(relationship, counter_cache_attribute, -1) }
53
+
54
+ counter_caches[counter_cache_attribute] = true
55
+ end # register_counter_cache_callbacks_for(relationship, counter_cache_attribute)
56
+
57
+ def defined_counter_cache_callbacks_for?(attribute)
58
+ !counter_caches[attribute].nil?
59
+ end # defined_counter_cache_callbacks_for?(attribute)
60
+
61
+ def counter_caches
62
+ @_counter_caches ||= {}
63
+ end # counter_caches
64
+ end
65
+
66
+ module InstanceMethods
67
+ protected
68
+ def adjust_counter_cache_for(relationship, counter_cache_attribute, amount)
69
+ association = get_association(relationship)
70
+
71
+ return unless relationship.parent_model.properties.has_property?(counter_cache_attribute)
72
+ association.update_attributes(counter_cache_attribute => association.reload.__send__(counter_cache_attribute) + amount)
73
+ end
74
+
75
+ def get_association(relationship)
76
+ self.__send__("#{relationship.name}_association".to_sym)
77
+ end # get_association(name)
78
+ end # InstanceMethods
79
+
80
+ end # CounterCacheable
81
+ end # DataMapper
82
+
83
+ if $0 == __FILE__
84
+ require 'rubygems'
85
+
86
+ gem 'dm-core', '~>0.9.8'
87
+ require 'dm-core'
88
+
89
+ FileUtils.touch(File.join(Dir.pwd, "migration_test.db"))
90
+ DataMapper.setup(:default, "sqlite3://#{Dir.pwd}/migration_test.db")
91
+
92
+ class Post
93
+ include DataMapper::Resource
94
+
95
+ property :id, Integer, :serial => true
96
+ has n, :comments
97
+ end
98
+ Post.auto_migrate!
99
+
100
+ class Comment
101
+ include DataMapper::Resource
102
+ include DataMapper::CounterCacheable
103
+
104
+ belongs_to :post, :counter_cache => true
105
+ end
106
+ Comments.auto_migrate!
107
+
108
+ Post.create.comments.create
109
+ end
@@ -0,0 +1,87 @@
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
+ property :id, Integer, :serial => true
10
+ property :comments_count, Integer, :default => 0
11
+ has n, :comments
12
+ end
13
+
14
+ class Comment
15
+ include DataMapper::Resource
16
+ include DataMapper::CounterCacheable
17
+
18
+ property :id, Integer, :serial => true
19
+ belongs_to :post, :counter_cache => true
20
+
21
+ end
22
+
23
+ class User
24
+ include DataMapper::Resource
25
+
26
+ property :id, Integer, :serial => true
27
+ property :groups_count, Integer, :default => 0
28
+
29
+ has n, :group_memberships
30
+ has n, :groups, :through => :group_memberships, :class_name => "Group", :remote_name => :group, :parent_key => [:id], :child_key => [:user_id]
31
+ end
32
+
33
+ class Group
34
+ include DataMapper::Resource
35
+
36
+ property :id, Integer, :serial => true
37
+ property :members_count, Integer, :default => 0
38
+ has n, :group_memberships
39
+ has n, :members, :through => :group_memberships, :class_name => "User", :remote_name => :user, :parent_key => [:id], :child_key => [:group_id]
40
+ end
41
+
42
+ class GroupMembership
43
+ include DataMapper::Resource
44
+ include DataMapper::CounterCacheable
45
+
46
+ property :id, Serial
47
+
48
+ belongs_to :group, :counter_cache => :members_count
49
+ belongs_to :member, :class_name => "User", :child_key => [:user_id], :counter_cache => :groups_count
50
+ end
51
+
52
+ GroupMembership.auto_migrate!
53
+ User.auto_migrate!
54
+ Group.auto_migrate!
55
+ Comment.auto_migrate!
56
+ Post.auto_migrate!
57
+ end
58
+
59
+ before(:each) do
60
+ @post = Post.create
61
+ @user = User.create
62
+ @group = Group.create
63
+ end
64
+
65
+ it "should increment comments_count" do
66
+ @post.comments.create
67
+ @post.reload.comments_count.should == 1
68
+
69
+ @user.group_memberships.create(:group => @group)
70
+ @user.reload.groups_count.should == 1
71
+ @group.reload.members_count.should == 1
72
+ end
73
+
74
+ it "should decrement comments_count" do
75
+ comment1 = @post.comments.create
76
+ comment2 = @post.comments.create
77
+ comment2.destroy
78
+ @post.reload.comments_count.should == 1
79
+
80
+ gm1 = @user.group_memberships.create(:group => @group)
81
+ gm2 = @user.group_memberships.create(:group => @group)
82
+ gm2.destroy
83
+ @user.reload.groups_count.should == 1
84
+ @group.reload.members_count.should == 1
85
+ end
86
+
87
+ end
@@ -0,0 +1,50 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ describe DataMapper::CounterCacheable do
4
+ context "when attribute with counter_cache declared twice" do
5
+ module Multiple
6
+ class Post
7
+ include DataMapper::Resource
8
+
9
+ property :id, Integer, :serial => true
10
+ property :comments_count, Integer, :default => 0
11
+ has n, :comments, :class_name => 'Multiple::Comment'
12
+ end
13
+
14
+ class Comment
15
+ include DataMapper::Resource
16
+ include DataMapper::CounterCacheable
17
+
18
+ property :id, Integer, :serial => true
19
+ belongs_to :post, :class_name => 'Multiple::Post', :counter_cache => :comments_count
20
+ belongs_to :post, :class_name => 'Multiple::Post', :counter_cache => :comments_count
21
+ end
22
+
23
+ Comment.auto_migrate!
24
+ Post.auto_migrate!
25
+ end
26
+
27
+ before :each do
28
+ @post = Multiple::Post.create
29
+ end
30
+
31
+ it "should increment counter only once" do
32
+ lambda do
33
+ @post.comments.create
34
+ @post.reload
35
+ end.should change(@post, :comments_count).by(+1)
36
+ end
37
+
38
+ it "should decrement counter only once" do
39
+ comment1 = @post.comments.create
40
+ comment2 = @post.comments.create
41
+ @post.reload
42
+
43
+ lambda do
44
+ comment1.destroy
45
+ @post.reload
46
+ end.should change(@post, :comments_count).by(-1)
47
+ end
48
+
49
+ end
50
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --colour
@@ -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.12'
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')
data/tasks/install.rb ADDED
@@ -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
data/tasks/spec.rb ADDED
@@ -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
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dzema_dm-counter-cache
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.12
5
+ platform: ruby
6
+ authors:
7
+ - Saimon Moore
8
+ - Dmitriy Dzema
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2009-11-15 00:00:00 +10:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: dm-core
18
+ type: :runtime
19
+ version_requirement:
20
+ version_requirements: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - "="
23
+ - !ruby/object:Gem::Version
24
+ version: 0.9.12
25
+ version:
26
+ - !ruby/object:Gem::Dependency
27
+ name: hoe
28
+ type: :development
29
+ version_requirement:
30
+ version_requirements: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: 2.3.3
35
+ version:
36
+ description: DataMapper plugin for counter caches ala ActiveRecord. Original idea and implementation by Saimon Moore (daimonmoore [a] gmail [d] com)
37
+ email:
38
+ - daimonmoore [a] gmail [d] com
39
+ - dima [a] dzema [d] name
40
+ executables: []
41
+
42
+ extensions: []
43
+
44
+ extra_rdoc_files:
45
+ - README.txt
46
+ - LICENSE
47
+ - TODO
48
+ - History.txt
49
+ files:
50
+ - History.txt
51
+ - LICENSE
52
+ - Manifest.txt
53
+ - README.txt
54
+ - Rakefile
55
+ - TODO
56
+ - lib/dm-counter-cache.rb
57
+ - lib/dm-counter-cache/version.rb
58
+ - spec/integration/dm-counter-cache_spec.rb
59
+ - spec/integration/multiple_define_spec.rb
60
+ - spec/spec.opts
61
+ - spec/spec_helper.rb
62
+ - tasks/install.rb
63
+ - tasks/spec.rb
64
+ has_rdoc: true
65
+ homepage: http://github.com/DimaD/dm-counter-cache
66
+ licenses: []
67
+
68
+ post_install_message:
69
+ rdoc_options:
70
+ - --main
71
+ - README.txt
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: "0"
79
+ version:
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: "0"
85
+ version:
86
+ requirements: []
87
+
88
+ rubyforge_project: datamapper
89
+ rubygems_version: 1.3.4
90
+ signing_key:
91
+ specification_version: 3
92
+ summary: DataMapper plugin for counter caches ala ActiveRecord. Original idea and implementation by Saimon Moore (daimonmoore [a] gmail [d] com)
93
+ test_files: []
94
+