track_changes 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/LICENSE +20 -20
  2. data/README.rdoc +87 -87
  3. data/TODO +4 -4
  4. data/VERSION.yml +5 -5
  5. data/lib/track_changes/audit_filter.rb +69 -69
  6. data/lib/track_changes/base.rb +9 -9
  7. data/lib/track_changes/class_methods.rb +74 -74
  8. data/lib/track_changes/configuration.rb +38 -38
  9. data/lib/track_changes/filter.rb +43 -43
  10. data/lib/track_changes/initializer.rb +18 -18
  11. data/lib/track_changes/instance_methods.rb +5 -5
  12. data/lib/track_changes/result.rb +36 -36
  13. data/lib/track_changes.rb +12 -12
  14. data/test/base_test.rb +20 -20
  15. data/test/class_methods_test.rb +29 -29
  16. data/test/filter_test.rb +73 -73
  17. data/test/functional/posts_controller_test.rb +16 -16
  18. data/test/rails_root/app/controllers/application_controller.rb +2 -2
  19. data/test/rails_root/app/controllers/posts_controller.rb +36 -36
  20. data/test/rails_root/app/models/audit.rb +4 -4
  21. data/test/rails_root/app/models/post.rb +3 -3
  22. data/test/rails_root/config/boot.rb +110 -110
  23. data/test/rails_root/config/database.yml +3 -3
  24. data/test/rails_root/config/environment.rb +7 -7
  25. data/test/rails_root/config/environments/test.rb +7 -7
  26. data/test/rails_root/config/initializers/new_rails_defaults.rb +7 -7
  27. data/test/rails_root/config/initializers/session_store.rb +4 -4
  28. data/test/rails_root/config/initializers/track_changes_setup.rb +8 -8
  29. data/test/rails_root/config/routes.rb +3 -3
  30. data/test/rails_root/db/migrate/20100115021125_create_audits.rb +16 -16
  31. data/test/rails_root/db/migrate/20100115021151_create_posts.rb +15 -15
  32. data/test/rails_root/log/test.log +377 -2494
  33. data/test/rails_root/script/console +3 -3
  34. data/test/rails_root/script/generate +3 -3
  35. data/test/result_test.rb +35 -35
  36. data/test/test_helper.rb +29 -29
  37. data/test/track_changes/audit_filter_test.rb +154 -154
  38. data/test/track_changes/configuration_test.rb +36 -36
  39. data/test/track_changes/initializer_test.rb +37 -37
  40. metadata +31 -19
data/LICENSE CHANGED
@@ -1,20 +1,20 @@
1
- Copyright (c) 2008-2010 Matt Haley
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.
1
+ Copyright (c) 2008-2010 Matt Haley
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 CHANGED
@@ -1,87 +1,87 @@
1
- = track_changes
2
-
3
- TrackChanges is a Rails plugin to facilitate tracking
4
- changes made to an ActiveRecord model in your
5
- Controller actions.
6
-
7
- == Why?
8
-
9
- I originally looked at the available auditing solutions
10
- and it appeared that most of them were not thread-safe.
11
-
12
- == Installation
13
-
14
- gem install track_changes
15
-
16
- == Configuration
17
-
18
- You need to have an Audit class that has a polymorphic
19
- <tt>belongs_to</tt> association to <tt>:audited</tt>. In
20
- addition, the Audit model must have a <tt>user</tt>
21
- attribute, and your controllers must respond to
22
- <tt>current_user</tt>.
23
-
24
- Example Audit class:
25
-
26
- class Audit < ActiveRecord::Base
27
- belongs_to :audited, :polymorphic => true
28
- serialize :change_set
29
- end
30
-
31
- Example Audited class:
32
-
33
- class Post < ActiveRecord::Base
34
- has_many :audits, :as => :audited
35
- end
36
-
37
- You can also tweak some of the defaults by creating an initializer
38
- in <tt>RAILS_ROOT/config/initializers/track_changes_configuration.rb</tt>
39
- and calling TrackChanges::Initializer.instance which yields a
40
- TrackChanges::Configuration object:
41
-
42
- # These are the defaults anyways
43
- TrackChanges::Initializer.instance do |c|
44
- c.audit_assocation = :audits # Calls <tt>create!</tt> on this association method
45
- c.current_user = :current_user # Controller is sent this method to obtain current user.
46
- c.ignore_nil = true # Don't crash if a model is nil.
47
- end
48
-
49
- == Controller Example
50
-
51
- In this example, after the `update` action is called,
52
- the `@post` will be compared to a previous version, and
53
- if there are any changes, an audit will be created.
54
-
55
- class PostController < ApplicationController
56
- include TrackChanges
57
-
58
- before_filter :get_post, :except => [:index, :new]
59
-
60
- # specify a single model
61
- track_changes :post
62
-
63
- # you can also specify multiple models
64
- #track_changes :post1, :post2, ...
65
-
66
- def update
67
- if @post.update_attributes(params[:post])
68
- flash[:notice] = "Success."
69
- else
70
- render :action => "edit"
71
- end
72
- end
73
-
74
- # Normal controller actions
75
- # ...
76
-
77
- protected
78
-
79
- def get_post
80
- @post = Post.find(params[:id])
81
- end
82
- end
83
-
84
-
85
- == COPYRIGHT
86
-
87
- Copyright (c) 2008-2010 Matt Haley. See LICENSE for details.
1
+ = track_changes
2
+
3
+ TrackChanges is a Rails plugin to facilitate tracking
4
+ changes made to an ActiveRecord model in your
5
+ Controller actions.
6
+
7
+ == Why?
8
+
9
+ I originally looked at the available auditing solutions
10
+ and it appeared that most of them were not thread-safe.
11
+
12
+ == Installation
13
+
14
+ gem install track_changes
15
+
16
+ == Configuration
17
+
18
+ You need to have an Audit class that has a polymorphic
19
+ <tt>belongs_to</tt> association to <tt>:audited</tt>. In
20
+ addition, the Audit model must have a <tt>user</tt>
21
+ attribute, and your controllers must respond to
22
+ <tt>current_user</tt>.
23
+
24
+ Example Audit class:
25
+
26
+ class Audit < ActiveRecord::Base
27
+ belongs_to :audited, :polymorphic => true
28
+ serialize :change_set
29
+ end
30
+
31
+ Example Audited class:
32
+
33
+ class Post < ActiveRecord::Base
34
+ has_many :audits, :as => :audited
35
+ end
36
+
37
+ You can also tweak some of the defaults by creating an initializer
38
+ in <tt>RAILS_ROOT/config/initializers/track_changes_configuration.rb</tt>
39
+ and calling TrackChanges::Initializer.instance which yields a
40
+ TrackChanges::Configuration object:
41
+
42
+ # These are the defaults anyways
43
+ TrackChanges::Initializer.instance do |c|
44
+ c.audit_assocation = :audits # Calls <tt>create!</tt> on this association method
45
+ c.current_user = :current_user # Controller is sent this method to obtain current user.
46
+ c.ignore_nil = true # Don't crash if a model is nil.
47
+ end
48
+
49
+ == Controller Example
50
+
51
+ In this example, after the `update` action is called,
52
+ the `@post` will be compared to a previous version, and
53
+ if there are any changes, an audit will be created.
54
+
55
+ class PostController < ApplicationController
56
+ include TrackChanges
57
+
58
+ before_filter :get_post, :except => [:index, :new]
59
+
60
+ # specify a single model
61
+ track_changes :post
62
+
63
+ # you can also specify multiple models
64
+ #track_changes :post1, :post2, ...
65
+
66
+ def update
67
+ if @post.update_attributes(params[:post])
68
+ flash[:notice] = "Success."
69
+ else
70
+ render :action => "edit"
71
+ end
72
+ end
73
+
74
+ # Normal controller actions
75
+ # ...
76
+
77
+ protected
78
+
79
+ def get_post
80
+ @post = Post.find(params[:id])
81
+ end
82
+ end
83
+
84
+
85
+ == COPYRIGHT
86
+
87
+ Copyright (c) 2008-2010 Matt Haley. See LICENSE for details.
data/TODO CHANGED
@@ -1,4 +1,4 @@
1
- Track Changes TODO List
2
- =======================
3
-
4
- * Check for ivars
1
+ Track Changes TODO List
2
+ =======================
3
+
4
+ * Check for ivars
data/VERSION.yml CHANGED
@@ -1,5 +1,5 @@
1
- ---
2
- :major: 0
3
- :minor: 5
4
- :patch: 0
5
- :build:
1
+ ---
2
+ :patch: 1
3
+ :major: 0
4
+ :build:
5
+ :minor: 5
@@ -1,69 +1,69 @@
1
- module TrackChanges
2
- # An around filter that will audit changes to your models.
3
- #
4
- # Example:
5
- # class OrdersController < ApplicationController
6
- # before_filter :find_order, :except => [:index, :new, :create]
7
- # around_filter AuditFilter.new(:order), :only => :update
8
- #
9
- # def update
10
- # ...
11
- # end
12
- #
13
- # protected
14
- #
15
- # def find_order
16
- # @order = Order.find(params[:id])
17
- # end
18
- # end
19
- class AuditFilter
20
- attr_accessor :cached_models, :models
21
-
22
- def initialize(*models)
23
- models.flatten!
24
- options = models.extract_options!
25
-
26
- self.cached_models = {}
27
- self.models = models
28
-
29
- @audit_accessor = options[:audit_accessor] || :audits
30
- @current_user = options[:current_user] || :current_user
31
- @ignore_nils = if options[:ignore_nil] == false
32
- false
33
- else
34
- true
35
- end
36
- end
37
-
38
- def before(controller)
39
- self.models.each do |model|
40
- instance_variable_symbol = "@#{model}".to_sym
41
- instance = controller.instance_variable_get(instance_variable_symbol)
42
- next if instance.nil? && @ignore_nils
43
-
44
- self.cached_models[instance_variable_symbol] = instance.clone
45
- end
46
- end
47
-
48
- def after(controller)
49
- self.cached_models.each_pair do |instance_variable_symbol, original_instance|
50
- new_instance = controller.instance_variable_get(instance_variable_symbol)
51
- next if new_instance.changed? # Dirty objects didn't save
52
-
53
- changes = changes_between(new_instance, original_instance)
54
-
55
- unless changes.empty?
56
- audit = new_instance.send(@audit_accessor).create!(:user => controller.send(@current_user), :change_set => changes)
57
- end
58
- end
59
- end
60
-
61
- private
62
-
63
- def changes_between(new, old)
64
- clone = old.clone
65
- clone.attributes = new.attributes
66
- clone.changes
67
- end
68
- end
69
- end
1
+ module TrackChanges
2
+ # An around filter that will audit changes to your models.
3
+ #
4
+ # Example:
5
+ # class OrdersController < ApplicationController
6
+ # before_filter :find_order, :except => [:index, :new, :create]
7
+ # around_filter AuditFilter.new(:order), :only => :update
8
+ #
9
+ # def update
10
+ # ...
11
+ # end
12
+ #
13
+ # protected
14
+ #
15
+ # def find_order
16
+ # @order = Order.find(params[:id])
17
+ # end
18
+ # end
19
+ class AuditFilter
20
+ attr_accessor :cached_models, :models
21
+
22
+ def initialize(*models)
23
+ models.flatten!
24
+ options = models.extract_options!
25
+
26
+ self.cached_models = {}
27
+ self.models = models
28
+
29
+ @audit_accessor = options[:audit_accessor] || :audits
30
+ @current_user = options[:current_user] || :current_user
31
+ @ignore_nils = if options[:ignore_nil] == false
32
+ false
33
+ else
34
+ true
35
+ end
36
+ end
37
+
38
+ def before(controller)
39
+ self.models.each do |model|
40
+ instance_variable_symbol = "@#{model}".to_sym
41
+ instance = controller.instance_variable_get(instance_variable_symbol)
42
+ next if instance.nil? && @ignore_nils
43
+
44
+ self.cached_models[instance_variable_symbol] = instance.clone
45
+ end
46
+ end
47
+
48
+ def after(controller)
49
+ self.cached_models.each_pair do |instance_variable_symbol, original_instance|
50
+ new_instance = controller.instance_variable_get(instance_variable_symbol)
51
+ next if new_instance.changed? # Dirty objects didn't save
52
+
53
+ changes = changes_between(new_instance, original_instance)
54
+
55
+ unless changes.empty?
56
+ audit = new_instance.send(@audit_accessor).create!(:user => controller.send(@current_user), :change_set => changes)
57
+ end
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def changes_between(new, old)
64
+ clone = old.clone
65
+ clone.attributes = new.attributes
66
+ clone.changes
67
+ end
68
+ end
69
+ end
@@ -1,9 +1,9 @@
1
- require 'track_changes/instance_methods'
2
- require 'track_changes/class_methods'
3
-
4
- module TrackChanges
5
- def self.included(controller)
6
- controller.send(:include, InstanceMethods)
7
- controller.extend(ClassMethods)
8
- end
9
- end
1
+ require 'track_changes/instance_methods'
2
+ require 'track_changes/class_methods'
3
+
4
+ module TrackChanges
5
+ def self.included(controller)
6
+ controller.send(:include, InstanceMethods)
7
+ controller.extend(ClassMethods)
8
+ end
9
+ end
@@ -1,74 +1,74 @@
1
- require 'active_support'
2
- require 'track_changes/filter'
3
-
4
- module TrackChanges
5
- module ClassMethods
6
- # Create an audit of the changes made to a module during a controller action.
7
- # Some assumptions are made, primarily that the given models will have a
8
- # polymorphic association to an Audit model. An example Audit model:
9
- #
10
- # class Audit < ActiveRecord::Base
11
- # belongs_to :audited, :polymorphic => true
12
- # belongs_to :user
13
- # serialize :change_set
14
- # end
15
- #
16
- # # Example model that would be audited:
17
- # class Order < ActiveRecord::Base
18
- # has_many :audits, :as => :audited
19
- # end
20
- #
21
- # ==== Parameters
22
- #
23
- # * A symbol or many symbols of the name(s) of the instance variable to create audits for.
24
- # * An optional hash that will be passed as filter conditions to <tt>around_filter</tt>
25
- #
26
- # ==== Examples
27
- #
28
- # # Create an audit of changes made to @order during the <tt>:update</tt> action.
29
- # track_changes :order
30
- #
31
- # # Creates an audit of changes made to @order or @customer during the <tt>:update</tt> action.
32
- # track_changes :order, :customer
33
- #
34
- # # Create an audit of changes made to @order during the <tt>:update</tt> or <tt>:process</tt> actions.
35
- # track_changes :order, :only => [:update, :process]
36
- def track_changes(*args)
37
- options = args.extract_options!
38
- models = args.flatten
39
- options = [:only => :update] if options.empty?
40
-
41
- configuration = TrackChanges::Initializer.instance.configuration
42
-
43
- audit_filter = AuditFilter.new(models,
44
- :audit_accessor => configuration.audit_association,
45
- :current_user => configuration.current_user,
46
- :ignore_nil => configuration.ignore_nil)
47
- around_filter(audit_filter, options)
48
- end
49
-
50
- # Uses a combination of <tt>before_filter</tt> and <tt>after_filter</tt>
51
- # to act on changes made to a module during a controllers <tt>update</tt>
52
- # action.
53
- #
54
- # Parameters:
55
- #
56
- # * <tt>model</tt>: A symbol of the model instance name to track changes to
57
- # * <tt>only_if_changed</tt>: If true, will only yield block if changes is not empty. Defaults to <tt>true</tt>.
58
- # * <tt>options</tt>: An options hash the will be supplied to <tt>before_filter</tt> and <tt>after_filter</tt>. Defaults to <tt>:only => :update</tt>.
59
- # * <tt>&block</tt>: The supplied block is called with an instance of Result as its parameter.
60
- def track_changes_to(models, only_if_changed = true, options = {:only => :update}, &block)
61
- warn "[DEPRECATED] track_changes_to is deprecated. Use track_changes instead"
62
- append_before_filter(options) do |controller|
63
- track_filter = Filter.new(models, only_if_changed, block)
64
- controller.track_changes_filter = track_filter
65
-
66
- track_filter.before(controller)
67
- end
68
-
69
- append_after_filter(options)do |controller|
70
- controller.track_changes_filter.after(controller)
71
- end
72
- end
73
- end
74
- end
1
+ require 'active_support'
2
+ require 'track_changes/filter'
3
+
4
+ module TrackChanges
5
+ module ClassMethods
6
+ # Create an audit of the changes made to a module during a controller action.
7
+ # Some assumptions are made, primarily that the given models will have a
8
+ # polymorphic association to an Audit model. An example Audit model:
9
+ #
10
+ # class Audit < ActiveRecord::Base
11
+ # belongs_to :audited, :polymorphic => true
12
+ # belongs_to :user
13
+ # serialize :change_set
14
+ # end
15
+ #
16
+ # # Example model that would be audited:
17
+ # class Order < ActiveRecord::Base
18
+ # has_many :audits, :as => :audited
19
+ # end
20
+ #
21
+ # ==== Parameters
22
+ #
23
+ # * A symbol or many symbols of the name(s) of the instance variable to create audits for.
24
+ # * An optional hash that will be passed as filter conditions to <tt>around_filter</tt>
25
+ #
26
+ # ==== Examples
27
+ #
28
+ # # Create an audit of changes made to @order during the <tt>:update</tt> action.
29
+ # track_changes :order
30
+ #
31
+ # # Creates an audit of changes made to @order or @customer during the <tt>:update</tt> action.
32
+ # track_changes :order, :customer
33
+ #
34
+ # # Create an audit of changes made to @order during the <tt>:update</tt> or <tt>:process</tt> actions.
35
+ # track_changes :order, :only => [:update, :process]
36
+ def track_changes(*args)
37
+ options = args.extract_options!
38
+ models = args.flatten
39
+ options = {:only => :update} if options.empty?
40
+
41
+ configuration = TrackChanges::Initializer.instance.configuration
42
+
43
+ audit_filter = AuditFilter.new(models,
44
+ :audit_accessor => configuration.audit_association,
45
+ :current_user => configuration.current_user,
46
+ :ignore_nil => configuration.ignore_nil)
47
+ around_filter(audit_filter, options)
48
+ end
49
+
50
+ # Uses a combination of <tt>before_filter</tt> and <tt>after_filter</tt>
51
+ # to act on changes made to a module during a controllers <tt>update</tt>
52
+ # action.
53
+ #
54
+ # Parameters:
55
+ #
56
+ # * <tt>model</tt>: A symbol of the model instance name to track changes to
57
+ # * <tt>only_if_changed</tt>: If true, will only yield block if changes is not empty. Defaults to <tt>true</tt>.
58
+ # * <tt>options</tt>: An options hash the will be supplied to <tt>before_filter</tt> and <tt>after_filter</tt>. Defaults to <tt>:only => :update</tt>.
59
+ # * <tt>&block</tt>: The supplied block is called with an instance of Result as its parameter.
60
+ def track_changes_to(models, only_if_changed = true, options = {:only => :update}, &block)
61
+ warn "[DEPRECATED] track_changes_to is deprecated. Use track_changes instead"
62
+ append_before_filter(options) do |controller|
63
+ track_filter = Filter.new(models, only_if_changed, block)
64
+ controller.track_changes_filter = track_filter
65
+
66
+ track_filter.before(controller)
67
+ end
68
+
69
+ append_after_filter(options)do |controller|
70
+ controller.track_changes_filter.after(controller)
71
+ end
72
+ end
73
+ end
74
+ end
@@ -1,38 +1,38 @@
1
- module TrackChanges
2
- class Configuration
3
- # The association used to create an audit. Defaults to
4
- # <tt>:audits</tt>
5
- #
6
- # Example:
7
- # :audits # => Call model.audits.create
8
- # :changes # => Call model.changes.create
9
- attr_accessor :audit_association
10
-
11
- # The controller method called to obtain the current user.
12
- # Defaults to <tt>:current_user</tt>
13
- attr_accessor :current_user
14
-
15
- # Ignore if the audited item is nil. Defaults to <tt>true</tt>.
16
- attr_accessor :ignore_nil
17
-
18
- def initialize
19
- self.audit_association = default_audit_association
20
- self.current_user = default_current_user
21
- self.ignore_nil = default_ignore_nil
22
- end
23
-
24
- private
25
-
26
- def default_audit_association
27
- :audits
28
- end
29
-
30
- def default_current_user
31
- :current_user
32
- end
33
-
34
- def default_ignore_nil
35
- true
36
- end
37
- end
38
- end
1
+ module TrackChanges
2
+ class Configuration
3
+ # The association used to create an audit. Defaults to
4
+ # <tt>:audits</tt>
5
+ #
6
+ # Example:
7
+ # :audits # => Call model.audits.create
8
+ # :changes # => Call model.changes.create
9
+ attr_accessor :audit_association
10
+
11
+ # The controller method called to obtain the current user.
12
+ # Defaults to <tt>:current_user</tt>
13
+ attr_accessor :current_user
14
+
15
+ # Ignore if the audited item is nil. Defaults to <tt>true</tt>.
16
+ attr_accessor :ignore_nil
17
+
18
+ def initialize
19
+ self.audit_association = default_audit_association
20
+ self.current_user = default_current_user
21
+ self.ignore_nil = default_ignore_nil
22
+ end
23
+
24
+ private
25
+
26
+ def default_audit_association
27
+ :audits
28
+ end
29
+
30
+ def default_current_user
31
+ :current_user
32
+ end
33
+
34
+ def default_ignore_nil
35
+ true
36
+ end
37
+ end
38
+ end