track_changes 0.3.2 → 0.4.0
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/LICENSE +1 -1
- data/README.rdoc +40 -12
- data/VERSION.yml +2 -2
- data/lib/track_changes/audit_filter.rb +58 -0
- data/lib/track_changes/class_methods.rb +40 -0
- data/lib/track_changes.rb +1 -0
- data/test/functional/posts_controller_test.rb +16 -0
- data/test/rails_root/app/controllers/application_controller.rb +2 -0
- data/test/rails_root/app/controllers/posts_controller.rb +36 -0
- data/test/rails_root/app/models/audit.rb +4 -0
- data/test/rails_root/app/models/post.rb +3 -0
- data/test/rails_root/config/boot.rb +110 -0
- data/test/rails_root/config/database.yml +3 -0
- data/test/rails_root/config/environment.rb +7 -0
- data/test/rails_root/config/environments/test.rb +7 -0
- data/test/rails_root/config/initializers/new_rails_defaults.rb +7 -0
- data/test/rails_root/config/initializers/session_store.rb +4 -0
- data/test/rails_root/config/routes.rb +3 -0
- data/test/rails_root/db/migrate/20100115021125_create_audits.rb +16 -0
- data/test/rails_root/db/migrate/20100115021151_create_posts.rb +15 -0
- data/test/rails_root/log/test.log +117 -0
- data/test/rails_root/script/console +3 -0
- data/test/rails_root/script/generate +3 -0
- data/test/test_helper.rb +26 -9
- data/test/track_changes/audit_filter_test.rb +47 -0
- metadata +37 -4
data/LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -2,27 +2,55 @@
|
|
2
2
|
|
3
3
|
TrackChanges is a Rails plugin to facilitate tracking
|
4
4
|
changes made to an ActiveRecord model in your
|
5
|
-
Controller
|
6
|
-
only called if the changes hash is not empty.
|
5
|
+
Controller actions.
|
7
6
|
|
8
|
-
|
9
|
-
so it should be thread-safe.
|
7
|
+
== Why?
|
10
8
|
|
11
|
-
|
9
|
+
I originally looked at the available auditing solutions
|
10
|
+
and it appeared that most of them were not thread-safe.
|
12
11
|
|
13
|
-
==
|
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
|
+
== Controller Example
|
38
|
+
|
39
|
+
In this example, after the `update` action is called,
|
40
|
+
the `@post` will be compared to a previous version, and
|
41
|
+
if there are any changes, an audit will be created.
|
14
42
|
|
15
43
|
class PostController < ApplicationController
|
16
44
|
include TrackChanges
|
17
45
|
|
18
46
|
before_filter :get_post, :except => [:index, :new]
|
19
47
|
|
20
|
-
|
21
|
-
|
22
|
-
RAILS_DEFAULT_LOGGER.debug(r.changes.inspect)
|
23
|
-
end
|
24
|
-
|
48
|
+
# specify a single model
|
49
|
+
track_changes :post
|
25
50
|
|
51
|
+
# you can also specify multiple models
|
52
|
+
#track_changes :post1, :post2, ...
|
53
|
+
|
26
54
|
def update
|
27
55
|
if @post.update_attributes(params[:post])
|
28
56
|
flash[:notice] = "Success."
|
@@ -44,4 +72,4 @@ Consult the generated rdoc for more information.
|
|
44
72
|
|
45
73
|
== COPYRIGHT
|
46
74
|
|
47
|
-
Copyright (c) 2008 Matt Haley. See LICENSE for details.
|
75
|
+
Copyright (c) 2008-2010 Matt Haley. See LICENSE for details.
|
data/VERSION.yml
CHANGED
@@ -0,0 +1,58 @@
|
|
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
|
+
end
|
29
|
+
|
30
|
+
def before(controller)
|
31
|
+
self.models.each do |model|
|
32
|
+
instance_variable_symbol = "@#{model}".to_sym
|
33
|
+
self.cached_models[instance_variable_symbol] = controller.instance_variable_get(instance_variable_symbol).clone
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def after(controller)
|
38
|
+
self.cached_models.each_pair do |instance_variable_symbol, original_instance|
|
39
|
+
new_instance = controller.instance_variable_get(instance_variable_symbol)
|
40
|
+
next if new_instance.changed? # Dirty objects didn't save
|
41
|
+
|
42
|
+
changes = changes_between(new_instance, original_instance)
|
43
|
+
|
44
|
+
unless changes.empty?
|
45
|
+
audit = new_instance.audits.create!(:user => controller.send(:current_user), :change_set => changes)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def changes_between(new, old)
|
53
|
+
clone = old.clone
|
54
|
+
clone.attributes = new.attributes
|
55
|
+
clone.changes
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -1,7 +1,46 @@
|
|
1
|
+
require 'active_support'
|
1
2
|
require 'track_changes/filter'
|
2
3
|
|
3
4
|
module TrackChanges
|
4
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
|
+
models = args.flatten
|
38
|
+
options = args.extract_options!
|
39
|
+
options = [:only => :update] if options.empty?
|
40
|
+
audit_filter = AuditFilter.new(models)
|
41
|
+
around_filter(audit_filter, options)
|
42
|
+
end
|
43
|
+
|
5
44
|
# Uses a combination of <tt>before_filter</tt> and <tt>after_filter</tt>
|
6
45
|
# to act on changes made to a module during a controllers <tt>update</tt>
|
7
46
|
# action.
|
@@ -13,6 +52,7 @@ module TrackChanges
|
|
13
52
|
# * <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>.
|
14
53
|
# * <tt>&block</tt>: The supplied block is called with an instance of Result as its parameter.
|
15
54
|
def track_changes_to(models, only_if_changed = true, options = {:only => :update}, &block)
|
55
|
+
warn "[DEPRECATED] track_changes_to is deprecated. Use track_changes instead"
|
16
56
|
append_before_filter(options) do |controller|
|
17
57
|
track_filter = Filter.new(models, only_if_changed, block)
|
18
58
|
controller.track_changes_filter = track_filter
|
data/lib/track_changes.rb
CHANGED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class PostsControllerTest < ActionController::TestCase
|
4
|
+
test "should update post" do
|
5
|
+
post = Post.create!(:title => "Hello, World", :body => "This is a test.", :author => "Matt Haley")
|
6
|
+
assert !post.new_record?
|
7
|
+
|
8
|
+
assert_difference("Audit.count") do
|
9
|
+
put :update, :id => post.to_param, :post => { :title => "First post!" }
|
10
|
+
assert_redirected_to post_path(assigns(:post))
|
11
|
+
|
12
|
+
audit = assigns(:post).audits.first
|
13
|
+
assert_equal ["Hello, World", "First post!"], audit.change_set["title"]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'track_changes'
|
2
|
+
|
3
|
+
class PostsController < ApplicationController
|
4
|
+
include TrackChanges
|
5
|
+
before_filter :find_post
|
6
|
+
track_changes :post
|
7
|
+
|
8
|
+
def show
|
9
|
+
render :text => @post.inspect
|
10
|
+
end
|
11
|
+
|
12
|
+
# PUT /posts/1
|
13
|
+
# PUT /posts/1.xml
|
14
|
+
def update
|
15
|
+
respond_to do |format|
|
16
|
+
if @post.update_attributes(params[:post])
|
17
|
+
flash[:notice] = 'Post was successfully updated.'
|
18
|
+
format.html { redirect_to(@post) }
|
19
|
+
format.xml { head :ok }
|
20
|
+
else
|
21
|
+
format.html { render :action => "edit" }
|
22
|
+
format.xml { render :xml => @post.errors, :status => :unprocessable_entity }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
def current_user
|
30
|
+
"John Smith"
|
31
|
+
end
|
32
|
+
|
33
|
+
def find_post
|
34
|
+
@post = Post.find(params[:id])
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# Don't change this file!
|
2
|
+
# Configure your app in config/environment.rb and config/environments/*.rb
|
3
|
+
|
4
|
+
RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
|
5
|
+
|
6
|
+
module Rails
|
7
|
+
class << self
|
8
|
+
def boot!
|
9
|
+
unless booted?
|
10
|
+
preinitialize
|
11
|
+
pick_boot.run
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def booted?
|
16
|
+
defined? Rails::Initializer
|
17
|
+
end
|
18
|
+
|
19
|
+
def pick_boot
|
20
|
+
(vendor_rails? ? VendorBoot : GemBoot).new
|
21
|
+
end
|
22
|
+
|
23
|
+
def vendor_rails?
|
24
|
+
File.exist?("#{RAILS_ROOT}/vendor/rails")
|
25
|
+
end
|
26
|
+
|
27
|
+
def preinitialize
|
28
|
+
load(preinitializer_path) if File.exist?(preinitializer_path)
|
29
|
+
end
|
30
|
+
|
31
|
+
def preinitializer_path
|
32
|
+
"#{RAILS_ROOT}/config/preinitializer.rb"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Boot
|
37
|
+
def run
|
38
|
+
load_initializer
|
39
|
+
Rails::Initializer.run(:set_load_path)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class VendorBoot < Boot
|
44
|
+
def load_initializer
|
45
|
+
require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
|
46
|
+
Rails::Initializer.run(:install_gem_spec_stubs)
|
47
|
+
Rails::GemDependency.add_frozen_gem_path
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class GemBoot < Boot
|
52
|
+
def load_initializer
|
53
|
+
self.class.load_rubygems
|
54
|
+
load_rails_gem
|
55
|
+
require 'initializer'
|
56
|
+
end
|
57
|
+
|
58
|
+
def load_rails_gem
|
59
|
+
if version = self.class.gem_version
|
60
|
+
gem 'rails', version
|
61
|
+
else
|
62
|
+
gem 'rails'
|
63
|
+
end
|
64
|
+
rescue Gem::LoadError => load_error
|
65
|
+
$stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)
|
66
|
+
exit 1
|
67
|
+
end
|
68
|
+
|
69
|
+
class << self
|
70
|
+
def rubygems_version
|
71
|
+
Gem::RubyGemsVersion rescue nil
|
72
|
+
end
|
73
|
+
|
74
|
+
def gem_version
|
75
|
+
if defined? RAILS_GEM_VERSION
|
76
|
+
RAILS_GEM_VERSION
|
77
|
+
elsif ENV.include?('RAILS_GEM_VERSION')
|
78
|
+
ENV['RAILS_GEM_VERSION']
|
79
|
+
else
|
80
|
+
parse_gem_version(read_environment_rb)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def load_rubygems
|
85
|
+
min_version = '1.3.2'
|
86
|
+
require 'rubygems'
|
87
|
+
unless rubygems_version >= min_version
|
88
|
+
$stderr.puts %Q(Rails requires RubyGems >= #{min_version} (you have #{rubygems_version}). Please `gem update --system` and try again.)
|
89
|
+
exit 1
|
90
|
+
end
|
91
|
+
|
92
|
+
rescue LoadError
|
93
|
+
$stderr.puts %Q(Rails requires RubyGems >= #{min_version}. Please install RubyGems and try again: http://rubygems.rubyforge.org)
|
94
|
+
exit 1
|
95
|
+
end
|
96
|
+
|
97
|
+
def parse_gem_version(text)
|
98
|
+
$1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*["']([!~<>=]*\s*[\d.]+)["']/
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
def read_environment_rb
|
103
|
+
File.read("#{RAILS_ROOT}/config/environment.rb")
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# All that for this:
|
110
|
+
Rails.boot!
|
@@ -0,0 +1,7 @@
|
|
1
|
+
config.cache_classes = true
|
2
|
+
config.whiny_nils = true
|
3
|
+
config.action_controller.consider_all_requests_local = true
|
4
|
+
config.action_controller.perform_caching = false
|
5
|
+
config.action_view.cache_template_loading = true
|
6
|
+
config.action_controller.allow_forgery_protection = false
|
7
|
+
config.action_mailer.delivery_method = :test
|
@@ -0,0 +1,7 @@
|
|
1
|
+
if defined?(ActiveRecord)
|
2
|
+
ActiveRecord::Base.include_root_in_json = true
|
3
|
+
ActiveRecord::Base.store_full_sti_class = true
|
4
|
+
end
|
5
|
+
ActionController::Routing.generate_best_match = false
|
6
|
+
ActiveSupport.use_standard_json_time_format = true
|
7
|
+
ActiveSupport.escape_html_entities_in_json = false
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class CreateAudits < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :audits do |t|
|
4
|
+
t.string :audited_type
|
5
|
+
t.integer :audited_id
|
6
|
+
t.string :user
|
7
|
+
t.text :change_set
|
8
|
+
|
9
|
+
t.timestamps
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.down
|
14
|
+
drop_table :audits
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# Logfile created on 2010-01-14 20:01:04 -0700 [4;36;1mSQL (0.2ms)[0m [0;1m SELECT name
|
2
|
+
FROM sqlite_master
|
3
|
+
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
|
4
|
+
[0m
|
5
|
+
[4;35;1mSQL (0.1ms)[0m [0mselect sqlite_version(*)[0m
|
6
|
+
[4;36;1mSQL (0.2ms)[0m [0;1mCREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL) [0m
|
7
|
+
[4;35;1mSQL (0.1ms)[0m [0mCREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version")[0m
|
8
|
+
[4;36;1mSQL (0.1ms)[0m [0;1m SELECT name
|
9
|
+
FROM sqlite_master
|
10
|
+
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
|
11
|
+
[0m
|
12
|
+
[4;35;1mSQL (0.0ms)[0m [0mSELECT version FROM schema_migrations[0m
|
13
|
+
Migrating to CreateAudits (20100115021125)
|
14
|
+
[4;36;1mSQL (0.1ms)[0m [0;1mCREATE TABLE "audits" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "audited_type" varchar(255), "audited_id" integer, "user" varchar(255), "change_set" text, "created_at" datetime, "updated_at" datetime) [0m
|
15
|
+
[4;35;1mSQL (0.0ms)[0m [0mINSERT INTO schema_migrations (version) VALUES ('20100115021125')[0m
|
16
|
+
Migrating to CreatePosts (20100115021151)
|
17
|
+
[4;36;1mSQL (0.1ms)[0m [0;1mCREATE TABLE "posts" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "title" varchar(255), "body" text, "author" varchar(255), "created_at" datetime, "updated_at" datetime) [0m
|
18
|
+
[4;35;1mSQL (0.0ms)[0m [0mINSERT INTO schema_migrations (version) VALUES ('20100115021151')[0m
|
19
|
+
[4;36;1mPost Create (0.1ms)[0m [0;1mINSERT INTO "posts" ("title", "body", "author", "created_at", "updated_at") VALUES('Hello, World', 'This is a test.', 'Matt Haley', '2010-01-15 03:01:04', '2010-01-15 03:01:04')[0m
|
20
|
+
[4;35;1mSQL (0.1ms)[0m [0mSELECT count(*) AS count_all FROM "audits" [0m
|
21
|
+
|
22
|
+
|
23
|
+
Processing PostsController#update (for 0.0.0.0 at 2010-01-14 20:01:04) [PUT]
|
24
|
+
Parameters: {"post"=>{"title"=>"First post!"}, "id"=>"1"}
|
25
|
+
[4;36;1mPost Load (0.1ms)[0m [0;1mSELECT * FROM "posts" WHERE ("posts"."id" = 1) [0m
|
26
|
+
[4;35;1mPost Update (0.0ms)[0m [0mUPDATE "posts" SET "title" = 'First post!', "updated_at" = '2010-01-15 03:01:04' WHERE "id" = 1[0m
|
27
|
+
Redirected to http://test.host/posts/1
|
28
|
+
WARNING: Can't mass-assign these protected attributes: id
|
29
|
+
[4;36;1mAudit Create (0.1ms)[0m [0;1mINSERT INTO "audits" ("audited_type", "audited_id", "user", "change_set", "created_at", "updated_at") VALUES('Post', 1, 'John Smith', '---
|
30
|
+
title:
|
31
|
+
- Hello, World
|
32
|
+
- First post!
|
33
|
+
updated_at:
|
34
|
+
- 2010-01-15 03:01:04 Z
|
35
|
+
- 2010-01-15 03:01:04.996091 Z
|
36
|
+
', '2010-01-15 03:01:05', '2010-01-15 03:01:05')[0m
|
37
|
+
Completed in 47ms (DB: 1) | 302 Found [http://test.host/posts/1?post%5Btitle%5D=First+post%21]
|
38
|
+
[4;35;1mAudit Load (0.1ms)[0m [0mSELECT * FROM "audits" WHERE ("audits".audited_id = 1 AND "audits".audited_type = 'Post') LIMIT 1[0m
|
39
|
+
[4;36;1mSQL (0.1ms)[0m [0;1mSELECT count(*) AS count_all FROM "audits" [0m
|
40
|
+
[4;36;1mSQL (0.2ms)[0m [0;1m SELECT name
|
41
|
+
FROM sqlite_master
|
42
|
+
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
|
43
|
+
[0m
|
44
|
+
[4;35;1mSQL (0.1ms)[0m [0mselect sqlite_version(*)[0m
|
45
|
+
[4;36;1mSQL (0.2ms)[0m [0;1mCREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL) [0m
|
46
|
+
[4;35;1mSQL (0.1ms)[0m [0mCREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version")[0m
|
47
|
+
[4;36;1mSQL (0.1ms)[0m [0;1m SELECT name
|
48
|
+
FROM sqlite_master
|
49
|
+
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
|
50
|
+
[0m
|
51
|
+
[4;35;1mSQL (0.0ms)[0m [0mSELECT version FROM schema_migrations[0m
|
52
|
+
Migrating to CreateAudits (20100115021125)
|
53
|
+
[4;36;1mSQL (0.1ms)[0m [0;1mCREATE TABLE "audits" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "audited_type" varchar(255), "audited_id" integer, "user" varchar(255), "change_set" text, "created_at" datetime, "updated_at" datetime) [0m
|
54
|
+
[4;35;1mSQL (0.0ms)[0m [0mINSERT INTO schema_migrations (version) VALUES ('20100115021125')[0m
|
55
|
+
Migrating to CreatePosts (20100115021151)
|
56
|
+
[4;36;1mSQL (0.1ms)[0m [0;1mCREATE TABLE "posts" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "title" varchar(255), "body" text, "author" varchar(255), "created_at" datetime, "updated_at" datetime) [0m
|
57
|
+
[4;35;1mSQL (0.0ms)[0m [0mINSERT INTO schema_migrations (version) VALUES ('20100115021151')[0m
|
58
|
+
[4;36;1mPost Create (0.1ms)[0m [0;1mINSERT INTO "posts" ("title", "body", "author", "created_at", "updated_at") VALUES('Hello, World', 'This is a test.', 'Matt Haley', '2010-01-15 03:02:19', '2010-01-15 03:02:19')[0m
|
59
|
+
[4;35;1mSQL (0.1ms)[0m [0mSELECT count(*) AS count_all FROM "audits" [0m
|
60
|
+
|
61
|
+
|
62
|
+
Processing PostsController#update (for 0.0.0.0 at 2010-01-14 20:02:19) [PUT]
|
63
|
+
Parameters: {"post"=>{"title"=>"First post!"}, "id"=>"1"}
|
64
|
+
[4;36;1mPost Load (0.1ms)[0m [0;1mSELECT * FROM "posts" WHERE ("posts"."id" = 1) [0m
|
65
|
+
[4;35;1mPost Update (0.0ms)[0m [0mUPDATE "posts" SET "title" = 'First post!', "updated_at" = '2010-01-15 03:02:19' WHERE "id" = 1[0m
|
66
|
+
Redirected to http://test.host/posts/1
|
67
|
+
WARNING: Can't mass-assign these protected attributes: id
|
68
|
+
[4;36;1mAudit Create (0.1ms)[0m [0;1mINSERT INTO "audits" ("audited_type", "audited_id", "user", "change_set", "created_at", "updated_at") VALUES('Post', 1, 'John Smith', '---
|
69
|
+
title:
|
70
|
+
- Hello, World
|
71
|
+
- First post!
|
72
|
+
updated_at:
|
73
|
+
- 2010-01-15 03:02:19 Z
|
74
|
+
- 2010-01-15 03:02:19.156565 Z
|
75
|
+
', '2010-01-15 03:02:19', '2010-01-15 03:02:19')[0m
|
76
|
+
Completed in 47ms (DB: 1) | 302 Found [http://test.host/posts/1?post%5Btitle%5D=First+post%21]
|
77
|
+
[4;35;1mAudit Load (0.1ms)[0m [0mSELECT * FROM "audits" WHERE ("audits".audited_id = 1 AND "audits".audited_type = 'Post') LIMIT 1[0m
|
78
|
+
[4;36;1mSQL (0.1ms)[0m [0;1mSELECT count(*) AS count_all FROM "audits" [0m
|
79
|
+
[4;36;1mSQL (0.2ms)[0m [0;1m SELECT name
|
80
|
+
FROM sqlite_master
|
81
|
+
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
|
82
|
+
[0m
|
83
|
+
[4;35;1mSQL (0.1ms)[0m [0mselect sqlite_version(*)[0m
|
84
|
+
[4;36;1mSQL (0.2ms)[0m [0;1mCREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL) [0m
|
85
|
+
[4;35;1mSQL (0.1ms)[0m [0mCREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version")[0m
|
86
|
+
[4;36;1mSQL (0.1ms)[0m [0;1m SELECT name
|
87
|
+
FROM sqlite_master
|
88
|
+
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
|
89
|
+
[0m
|
90
|
+
[4;35;1mSQL (0.0ms)[0m [0mSELECT version FROM schema_migrations[0m
|
91
|
+
Migrating to CreateAudits (20100115021125)
|
92
|
+
[4;36;1mSQL (0.1ms)[0m [0;1mCREATE TABLE "audits" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "audited_type" varchar(255), "audited_id" integer, "user" varchar(255), "change_set" text, "created_at" datetime, "updated_at" datetime) [0m
|
93
|
+
[4;35;1mSQL (0.0ms)[0m [0mINSERT INTO schema_migrations (version) VALUES ('20100115021125')[0m
|
94
|
+
Migrating to CreatePosts (20100115021151)
|
95
|
+
[4;36;1mSQL (0.1ms)[0m [0;1mCREATE TABLE "posts" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "title" varchar(255), "body" text, "author" varchar(255), "created_at" datetime, "updated_at" datetime) [0m
|
96
|
+
[4;35;1mSQL (0.0ms)[0m [0mINSERT INTO schema_migrations (version) VALUES ('20100115021151')[0m
|
97
|
+
[4;36;1mPost Create (0.1ms)[0m [0;1mINSERT INTO "posts" ("title", "body", "author", "created_at", "updated_at") VALUES('Hello, World', 'This is a test.', 'Matt Haley', '2010-01-15 03:02:28', '2010-01-15 03:02:28')[0m
|
98
|
+
[4;35;1mSQL (0.1ms)[0m [0mSELECT count(*) AS count_all FROM "audits" [0m
|
99
|
+
|
100
|
+
|
101
|
+
Processing PostsController#update (for 0.0.0.0 at 2010-01-14 20:02:28) [PUT]
|
102
|
+
Parameters: {"post"=>{"title"=>"First post!"}, "id"=>"1"}
|
103
|
+
[4;36;1mPost Load (0.1ms)[0m [0;1mSELECT * FROM "posts" WHERE ("posts"."id" = 1) [0m
|
104
|
+
[4;35;1mPost Update (0.0ms)[0m [0mUPDATE "posts" SET "title" = 'First post!', "updated_at" = '2010-01-15 03:02:28' WHERE "id" = 1[0m
|
105
|
+
Redirected to http://test.host/posts/1
|
106
|
+
WARNING: Can't mass-assign these protected attributes: id
|
107
|
+
[4;36;1mAudit Create (0.1ms)[0m [0;1mINSERT INTO "audits" ("audited_type", "audited_id", "user", "change_set", "created_at", "updated_at") VALUES('Post', 1, 'John Smith', '---
|
108
|
+
title:
|
109
|
+
- Hello, World
|
110
|
+
- First post!
|
111
|
+
updated_at:
|
112
|
+
- 2010-01-15 03:02:28 Z
|
113
|
+
- 2010-01-15 03:02:28.791727 Z
|
114
|
+
', '2010-01-15 03:02:28', '2010-01-15 03:02:28')[0m
|
115
|
+
Completed in 47ms (DB: 1) | 302 Found [http://test.host/posts/1?post%5Btitle%5D=First+post%21]
|
116
|
+
[4;35;1mAudit Load (0.1ms)[0m [0mSELECT * FROM "audits" WHERE ("audits".audited_id = 1 AND "audits".audited_type = 'Post') LIMIT 1[0m
|
117
|
+
[4;36;1mSQL (0.1ms)[0m [0;1mSELECT count(*) AS count_all FROM "audits" [0m
|
data/test/test_helper.rb
CHANGED
@@ -1,12 +1,29 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
# Copied from the shoulda library
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
# Load the environment
|
5
|
+
ENV['RAILS_ENV'] = 'test'
|
6
|
+
|
7
|
+
rails_root = File.dirname(__FILE__) + '/rails_root'
|
8
|
+
|
9
|
+
require "#{rails_root}/config/environment.rb"
|
10
|
+
|
11
|
+
# Load the testing framework
|
12
|
+
require 'test_help'
|
13
|
+
silence_warnings { RAILS_ENV = ENV['RAILS_ENV'] }
|
14
|
+
|
15
|
+
# Run the migrations
|
16
|
+
ActiveRecord::Migration.verbose = false
|
17
|
+
ActiveRecord::Migrator.migrate("#{RAILS_ROOT}/db/migrate")
|
18
|
+
|
19
|
+
# Setup the fixtures path
|
20
|
+
ActiveSupport::TestCase.fixture_path =
|
21
|
+
File.join(File.dirname(__FILE__), "fixtures")
|
22
|
+
|
23
|
+
class ActiveSupport::TestCase #:nodoc:
|
24
|
+
self.use_transactional_fixtures = false
|
25
|
+
self.use_instantiated_fixtures = false
|
5
26
|
end
|
6
|
-
require 'test/unit'
|
7
|
-
require 'shoulda'
|
8
|
-
require 'mocha'
|
9
|
-
require 'ostruct'
|
10
27
|
|
11
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
12
28
|
require 'track_changes'
|
29
|
+
require 'shoulda'
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module TrackChanges
|
4
|
+
class AuditFilterTest < Test::Unit::TestCase
|
5
|
+
should "accept a single model" do
|
6
|
+
assert TrackChanges::AuditFilter.new(:model)
|
7
|
+
end
|
8
|
+
|
9
|
+
should "accept multiple models" do
|
10
|
+
assert TrackChanges::AuditFilter.new(:model_a, :model_b)
|
11
|
+
end
|
12
|
+
|
13
|
+
should "respond to #before" do
|
14
|
+
assert TrackChanges::AuditFilter.new.respond_to?(:before)
|
15
|
+
end
|
16
|
+
|
17
|
+
should "respond to #after" do
|
18
|
+
assert TrackChanges::AuditFilter.new.respond_to?(:after)
|
19
|
+
end
|
20
|
+
|
21
|
+
should "create an audit when before and after called" do
|
22
|
+
expected_change_set = {"value" => ["old", "new"]}
|
23
|
+
expected_user = "current user"
|
24
|
+
|
25
|
+
audit = mock('audit')
|
26
|
+
|
27
|
+
model = mock('model')
|
28
|
+
model.stubs(:attributes=).returns({})
|
29
|
+
model.stubs(:changes).returns(expected_change_set)
|
30
|
+
|
31
|
+
modified_model = mock('modified model')
|
32
|
+
modified_model.stubs(:changed?).returns(false)
|
33
|
+
modified_model.stubs(:attributes).returns({})
|
34
|
+
modified_model.stubs(:audits).returns(audit)
|
35
|
+
|
36
|
+
controller = mock('controller')
|
37
|
+
controller.stubs(:instance_variable_get).returns(model, modified_model)
|
38
|
+
controller.stubs(:current_user).returns(expected_user)
|
39
|
+
|
40
|
+
audit_filter = AuditFilter.new(:model)
|
41
|
+
audit_filter.before(controller)
|
42
|
+
|
43
|
+
audit.expects(:create!).with(has_entries(:user => expected_user, :change_set => expected_change_set)).returns(true)
|
44
|
+
audit_filter.after(controller)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: track_changes
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Haley
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2010-01-14 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -28,6 +28,7 @@ files:
|
|
28
28
|
- TODO
|
29
29
|
- VERSION.yml
|
30
30
|
- lib/track_changes.rb
|
31
|
+
- lib/track_changes/audit_filter.rb
|
31
32
|
- lib/track_changes/base.rb
|
32
33
|
- lib/track_changes/class_methods.rb
|
33
34
|
- lib/track_changes/filter.rb
|
@@ -36,8 +37,26 @@ files:
|
|
36
37
|
- test/base_test.rb
|
37
38
|
- test/class_methods_test.rb
|
38
39
|
- test/filter_test.rb
|
40
|
+
- test/functional/posts_controller_test.rb
|
41
|
+
- test/rails_root/app/controllers/application_controller.rb
|
42
|
+
- test/rails_root/app/controllers/posts_controller.rb
|
43
|
+
- test/rails_root/app/models/audit.rb
|
44
|
+
- test/rails_root/app/models/post.rb
|
45
|
+
- test/rails_root/config/boot.rb
|
46
|
+
- test/rails_root/config/database.yml
|
47
|
+
- test/rails_root/config/environment.rb
|
48
|
+
- test/rails_root/config/environments/test.rb
|
49
|
+
- test/rails_root/config/initializers/new_rails_defaults.rb
|
50
|
+
- test/rails_root/config/initializers/session_store.rb
|
51
|
+
- test/rails_root/config/routes.rb
|
52
|
+
- test/rails_root/db/migrate/20100115021125_create_audits.rb
|
53
|
+
- test/rails_root/db/migrate/20100115021151_create_posts.rb
|
54
|
+
- test/rails_root/log/test.log
|
55
|
+
- test/rails_root/script/console
|
56
|
+
- test/rails_root/script/generate
|
39
57
|
- test/result_test.rb
|
40
58
|
- test/test_helper.rb
|
59
|
+
- test/track_changes/audit_filter_test.rb
|
41
60
|
- README.rdoc
|
42
61
|
has_rdoc: true
|
43
62
|
homepage: http://github.com/mhaley/track_changes
|
@@ -68,8 +87,22 @@ signing_key:
|
|
68
87
|
specification_version: 3
|
69
88
|
summary: Easier auditing of Rails model changes in your controllers.
|
70
89
|
test_files:
|
71
|
-
- test/
|
72
|
-
- test/
|
90
|
+
- test/functional/posts_controller_test.rb
|
91
|
+
- test/rails_root/db/migrate/20100115021151_create_posts.rb
|
92
|
+
- test/rails_root/db/migrate/20100115021125_create_audits.rb
|
93
|
+
- test/rails_root/config/environments/test.rb
|
94
|
+
- test/rails_root/config/initializers/session_store.rb
|
95
|
+
- test/rails_root/config/initializers/new_rails_defaults.rb
|
96
|
+
- test/rails_root/config/boot.rb
|
97
|
+
- test/rails_root/config/routes.rb
|
98
|
+
- test/rails_root/config/environment.rb
|
99
|
+
- test/rails_root/app/models/audit.rb
|
100
|
+
- test/rails_root/app/models/post.rb
|
101
|
+
- test/rails_root/app/controllers/posts_controller.rb
|
102
|
+
- test/rails_root/app/controllers/application_controller.rb
|
73
103
|
- test/class_methods_test.rb
|
74
104
|
- test/result_test.rb
|
75
105
|
- test/filter_test.rb
|
106
|
+
- test/track_changes/audit_filter_test.rb
|
107
|
+
- test/base_test.rb
|
108
|
+
- test/test_helper.rb
|