deactivatable 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Greg Fitzgerald
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/README.rdoc ADDED
@@ -0,0 +1,21 @@
1
+ = deactivatable
2
+
3
+ Deactivatable provides methods and a default_scope to allow ActiveRecord objects to be deactivated instead of deleted.
4
+ This is useful if an object needs to be removed from general use, but it's data needs to be retained.
5
+ Additionally, Deactivatable provides the ability to specify dependencies which also need to be deactivated.
6
+ Deactivation is determined by populating a deactivated_at field with the date time at which deactivation happened.
7
+
8
+ == Note on Patches/Pull Requests
9
+
10
+ * Fork the project.
11
+ * Make your feature addition or bug fix.
12
+ * Add tests for it. This is important so I don't break it in a
13
+ future version unintentionally.
14
+ * Commit, do not mess with rakefile, version, or history.
15
+ (if you want to have your own version, that is fine but
16
+ bump version in a commit by itself I can ignore when I pull)
17
+ * Send me a pull request. Bonus points for topic branches.
18
+
19
+ == Copyright
20
+
21
+ Copyright (c) 2009 Greg Fitzgerald. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,62 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "deactivatable"
8
+ gem.summary = %Q{Adds methods and scopes to ActiveRecord objects to allow deactivation instead of deletion.}
9
+ gem.description = %Q{Deactivatable provides methods and a default_scope to allow ActiveRecord objects to be deactivated instead of deleted.
10
+ This is useful if an object needs to be removed from general use, but it's data needs to be retained.
11
+ Additionally, Deactivatable provides the ability to specify dependencies which also need to be deactivated.
12
+ Deactivation is determined by populating a deactivated_at field with the date time at which deactivation happened.
13
+ }
14
+ gem.email = "greg_fitz@yahoo.com"
15
+ gem.homepage = "http://github.com/gregfitz23/deactivatable"
16
+ gem.authors = ["Greg Fitzgerald"]
17
+ gem.add_dependency "activerecord", ">= 2.3"
18
+ gem.add_development_dependency "thoughtbot-shoulda"
19
+
20
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
21
+ end
22
+ rescue LoadError
23
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
24
+ end
25
+
26
+ require 'rake/testtask'
27
+ Rake::TestTask.new(:test) do |test|
28
+ test.libs << 'lib' << 'test'
29
+ test.pattern = 'test/**/*_test.rb'
30
+ test.verbose = true
31
+ end
32
+
33
+ begin
34
+ require 'rcov/rcovtask'
35
+ Rcov::RcovTask.new do |test|
36
+ test.libs << 'test'
37
+ test.pattern = 'test/**/*_test.rb'
38
+ test.verbose = true
39
+ end
40
+ rescue LoadError
41
+ task :rcov do
42
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
43
+ end
44
+ end
45
+
46
+ task :test => :check_dependencies
47
+
48
+ task :default => :test
49
+
50
+ require 'rake/rdoctask'
51
+ Rake::RDocTask.new do |rdoc|
52
+ if File.exist?('VERSION')
53
+ version = File.read('VERSION')
54
+ else
55
+ version = ""
56
+ end
57
+
58
+ rdoc.rdoc_dir = 'rdoc'
59
+ rdoc.title = "deactivatable #{version}"
60
+ rdoc.rdoc_files.include('README*')
61
+ rdoc.rdoc_files.include('lib/**/*.rb')
62
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,118 @@
1
+ module ActiveRecord
2
+ module Acts
3
+ module Deactivatable
4
+
5
+ def self.append_features(base) #:nodoc:
6
+ super
7
+ base.extend(Definition)
8
+ end
9
+
10
+ module Definition
11
+ # Define the calling class as being deactivatable.
12
+ # A call to this will set the default scope of the object to look for deactivated_at = nil.
13
+ # Options
14
+ # *:dependencies* => A list of symbols specifying any associations that are also deactivatable. (This associations must separately be defined with acts_as_deactivatable).
15
+ #
16
+ def acts_as_deactivatable(options={})
17
+ extend ActiveRecord::Acts::Deactivatable::ClassMethods
18
+ include ActiveRecord::Acts::Deactivatable::InstanceMethods
19
+
20
+ default_scope :conditions => {:deactivated_at => nil}
21
+
22
+ @deactivatable_options = options
23
+ end
24
+
25
+ end
26
+
27
+ module ClassMethods
28
+
29
+ def deactivatable_options
30
+ @deactivatable_options || {}
31
+ end
32
+
33
+ def deactivated_dependencies
34
+ @deactivated_dependencies ||= []
35
+ end
36
+
37
+ # Yields to a block, executing that block after removing the deactivated_at scope.
38
+ #
39
+ def with_deactivated_objects_scope
40
+ with_exclusive_scope do
41
+ with_scope(:find => {:conditions => "`#{self.table_name}`.`deactivated_at` IS NOT NULL"}) do
42
+ yield
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ module InstanceMethods
49
+
50
+ # Deactivate this object, and any associated objects as specified at definition time.
51
+ #
52
+ def deactivate!
53
+ with_transaction do
54
+ self.deactivated_at = Time.now
55
+ deactivate_dependencies
56
+ self.save!
57
+ end
58
+ end
59
+
60
+ # Activate this object, and any associated objects as specified at definition time.
61
+ #
62
+ def activate!
63
+ with_transaction do
64
+ self.deactivated_at = nil
65
+ activate_dependencies
66
+ self.save!
67
+ end
68
+ end
69
+
70
+ def deactivated?
71
+ deactivated_at?
72
+ end
73
+
74
+ private
75
+ # Iterate the list of associated objects that need to be deactivated, and deactivate each of them.
76
+ #
77
+ def deactivate_dependencies
78
+ traverse_dependencies(:deactivate!)
79
+ end
80
+
81
+ # Iterate the list of associated objects that need to be activated, and activate each of them.
82
+ #
83
+ def activate_dependencies
84
+ traverse_dependencies(:activate!)
85
+ end
86
+
87
+ # Traverse the list of dependencies, executing *method* on each of them.
88
+ #
89
+ def traverse_dependencies(method)
90
+ if dependencies = self.class.deactivatable_options[:dependencies]
91
+ dependencies.each { |dependency_name| execute_on_dependency(dependency_name, method) }
92
+ end
93
+ end
94
+
95
+ # Find the dependency indicated by *dependency_name* and execute *method* on it.
96
+ # Execution must be wrapped in the dependency's with_deactivated_objects_scope for activate! to work.
97
+ #
98
+ def execute_on_dependency(dependency_name, method)
99
+ self.class.reflections[dependency_name].klass.send(:with_exclusive_scope) do
100
+ dependency = self.__send__(dependency_name)
101
+ dependency.respond_to?(:map) ? dependency.map(&method) : dependency.__send__(method)
102
+ end
103
+ end
104
+
105
+ def with_transaction
106
+ self.class.transaction do
107
+ yield
108
+ end
109
+ end
110
+
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ ActiveRecord::Base.class_eval do
117
+ include ActiveRecord::Acts::Deactivatable
118
+ end
@@ -0,0 +1,5 @@
1
+ class DeactivatableDependency < ActiveRecord::Base
2
+
3
+ acts_as_deactivatable
4
+
5
+ end
@@ -0,0 +1,7 @@
1
+ class DeactivatableItem < ActiveRecord::Base
2
+
3
+ has_many :deactivatable_dependencies
4
+
5
+ acts_as_deactivatable :dependencies => [:deactivatable_dependencies]
6
+
7
+ end
@@ -0,0 +1,94 @@
1
+ require 'test_helper'
2
+
3
+ class DeactivatableTest < Test::Unit::TestCase
4
+
5
+ context "An inactive item, @item" do
6
+ setup do
7
+ @inactive_item = DeactivatableItem.new
8
+ @inactive_item.deactivated_at = Time.now
9
+ @inactive_item.save!
10
+ end
11
+
12
+ should "not be returned on find" do
13
+ assert !DeactivatableItem.exists?(@inactive_item.id)
14
+ end
15
+
16
+ should "be findable in using the deactivated_objects_scope" do
17
+ assert DeactivatableItem.with_deactivated_objects_scope { DeactivatableItem.exists?(@inactive_item.id) }
18
+ end
19
+
20
+ should "return true on deactivated?" do
21
+ assert @inactive_item.deactivated?
22
+ end
23
+
24
+ context "when activated" do
25
+ setup do
26
+ @inactive_item.activate!
27
+ end
28
+
29
+ should "be findable" do
30
+ assert DeactivatableItem.exists?(@inactive_item.id)
31
+ end
32
+ end #when reactivated
33
+ end #An inactive item, @inactive_item
34
+
35
+ context "An active item, @item" do
36
+ setup do
37
+ @item = DeactivatableItem.create!
38
+ end
39
+
40
+ should "have a null deactivated_at" do
41
+ assert_nil @item.deactivated_at
42
+ end
43
+
44
+ context "when deactivated" do
45
+ setup do
46
+ @item.deactivate!
47
+ end
48
+
49
+ should "set deactivated_at" do
50
+ assert_not_nil @item.deactivated_at
51
+ end
52
+
53
+ should "not be findable" do
54
+ assert !DeactivatableItem.exists?(@item.id)
55
+ end
56
+ end #when deactivated
57
+
58
+ context "with dependencies, @dependencies" do
59
+ setup do
60
+ @dependencies = (0..5).map { DeactivatableDependency.new }
61
+ @item.deactivatable_dependencies = @dependencies
62
+ end
63
+
64
+ context "on a call to deactivate!" do
65
+ setup do
66
+ @item.deactivate!
67
+ end
68
+
69
+ should "render dependency unfindable" do
70
+ @dependencies.each { |dependency| assert !DeactivatableDependency.exists?(dependency.id) }
71
+ end
72
+
73
+ should "set deactivate on dependency post" do
74
+ DeactivatableDependency.send(:with_exclusive_scope) do
75
+ @dependencies.map(&:reload)
76
+ @dependencies.each {|dependency| assert_not_nil(dependency.deactivated_at) }
77
+ end
78
+ end
79
+
80
+ context "when reactivated" do
81
+ setup do
82
+ @item.activate!
83
+ end
84
+
85
+ should "reactivate all dependencies" do
86
+ @dependencies.each { |dependency| assert(DeactivatableDependency.exists?(dependency.id)) }
87
+ end
88
+ end #when reactivated
89
+
90
+ end #on a call to deactivate!
91
+ end #with dependencies, @dependencies
92
+ end #An active item, @item
93
+
94
+ end
@@ -0,0 +1,28 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'activerecord'
8
+ require 'deactivatable'
9
+ require 'deactivatable_dependency'
10
+ require 'deactivatable_item'
11
+
12
+ class Test::Unit::TestCase
13
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :dbfile => ":memory:")
14
+
15
+ ActiveRecord::Schema.define(:version => 1) do
16
+ create_table :deactivatable_items do |t|
17
+ t.datetime :deactivated_at
18
+ end
19
+ end
20
+
21
+ ActiveRecord::Schema.define(:version => 1) do
22
+ create_table :deactivatable_dependencies do |t|
23
+ t.datetime :deactivated_at
24
+ t.belongs_to :deactivatable_item
25
+ end
26
+ end
27
+
28
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: deactivatable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Greg Fitzgerald
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-11-06 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activerecord
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "2.3"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: thoughtbot-shoulda
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ description: "Deactivatable provides methods and a default_scope to allow ActiveRecord objects to be deactivated instead of deleted.\n This is useful if an object needs to be removed from general use, but it's data needs to be retained.\n Additionally, Deactivatable provides the ability to specify dependencies which also need to be deactivated.\n Deactivation is determined by populating a deactivated_at field with the date time at which deactivation happened.\n "
36
+ email: greg_fitz@yahoo.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - LICENSE
43
+ - README.rdoc
44
+ files:
45
+ - .document
46
+ - .gitignore
47
+ - LICENSE
48
+ - README.rdoc
49
+ - Rakefile
50
+ - VERSION
51
+ - lib/deactivatable.rb
52
+ - test/deactivatable_dependency.rb
53
+ - test/deactivatable_item.rb
54
+ - test/deactivatable_test.rb
55
+ - test/test_helper.rb
56
+ has_rdoc: true
57
+ homepage: http://github.com/gregfitz23/deactivatable
58
+ licenses: []
59
+
60
+ post_install_message:
61
+ rdoc_options:
62
+ - --charset=UTF-8
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: "0"
70
+ version:
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: "0"
76
+ version:
77
+ requirements: []
78
+
79
+ rubyforge_project:
80
+ rubygems_version: 1.3.5
81
+ signing_key:
82
+ specification_version: 3
83
+ summary: Adds methods and scopes to ActiveRecord objects to allow deactivation instead of deletion.
84
+ test_files:
85
+ - test/deactivatable_dependency.rb
86
+ - test/deactivatable_item.rb
87
+ - test/deactivatable_test.rb
88
+ - test/test_helper.rb