track_changes 0.3.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2008 Matt Haley
1
+ Copyright (c) 2008-2010 Matt Haley
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
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#update actions. By default, the block is
6
- only called if the changes hash is not empty.
5
+ Controller actions.
7
6
 
8
- A TrackChanges::Filter is instantiated on each run of the <tt>before_filter</tt>
9
- so it should be thread-safe.
7
+ == Why?
10
8
 
11
- Consult the generated rdoc for more information.
9
+ I originally looked at the available auditing solutions
10
+ and it appeared that most of them were not thread-safe.
12
11
 
13
- == Example
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
- track_changes_to :post do |r|
21
- # r.changes => Model#changes made during update action
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
@@ -1,5 +1,5 @@
1
1
  ---
2
2
  :major: 0
3
- :minor: 3
4
- :patch: 2
3
+ :minor: 4
4
+ :patch: 0
5
5
  :build:
@@ -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
@@ -1,4 +1,5 @@
1
1
  require 'track_changes/result'
2
+ require 'track_changes/audit_filter'
2
3
  require 'track_changes/filter'
3
4
  require 'track_changes/instance_methods'
4
5
  require 'track_changes/class_methods'
@@ -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,2 @@
1
+ class ApplicationController < ActionController::Base
2
+ 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,4 @@
1
+ class Audit < ActiveRecord::Base
2
+ belongs_to :audited, :polymorphic => true
3
+ serialize :change_set
4
+ end
@@ -0,0 +1,3 @@
1
+ class Post < ActiveRecord::Base
2
+ has_many :audits, :as => :audited
3
+ 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,3 @@
1
+ test:
2
+ :adapter: sqlite3
3
+ :database: ":memory:"
@@ -0,0 +1,7 @@
1
+ RAILS_GEM_VERSION = '2.3.5' unless defined? RAILS_GEM_VERSION
2
+
3
+ require File.join(File.dirname(__FILE__), 'boot')
4
+
5
+ Rails::Initializer.run do |config|
6
+ config.time_zone = 'UTC'
7
+ end
@@ -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,4 @@
1
+ ActionController::Base.session = {
2
+ :key => '_rails_root_session',
3
+ :secret => '54bf4d986f9e5cf80f28c31e03278d3689cb30e84aceb32893bfa617ad7a0fe0bfc4e7012f6b6e1bb9787f2c02cdff1fef4bc928f3ad060d5d6687dd2f813a54'
4
+ }
@@ -0,0 +1,3 @@
1
+ ActionController::Routing::Routes.draw do |map|
2
+ map.resources :posts
3
+ end
@@ -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,15 @@
1
+ class CreatePosts < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :posts do |t|
4
+ t.string :title
5
+ t.text :body
6
+ t.string :author
7
+
8
+ t.timestamps
9
+ end
10
+ end
11
+
12
+ def self.down
13
+ drop_table :posts
14
+ end
15
+ end
@@ -0,0 +1,117 @@
1
+ # Logfile created on 2010-01-14 20:01:04 -0700 SQL (0.2ms)  SELECT name
2
+ FROM sqlite_master
3
+ WHERE type = 'table' AND NOT name = 'sqlite_sequence'
4
+ 
5
+ SQL (0.1ms) select sqlite_version(*)
6
+ SQL (0.2ms) CREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL) 
7
+ SQL (0.1ms) CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version")
8
+ SQL (0.1ms)  SELECT name
9
+ FROM sqlite_master
10
+ WHERE type = 'table' AND NOT name = 'sqlite_sequence'
11
+ 
12
+ SQL (0.0ms) SELECT version FROM schema_migrations
13
+ Migrating to CreateAudits (20100115021125)
14
+ SQL (0.1ms) CREATE 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) 
15
+ SQL (0.0ms) INSERT INTO schema_migrations (version) VALUES ('20100115021125')
16
+ Migrating to CreatePosts (20100115021151)
17
+ SQL (0.1ms) CREATE TABLE "posts" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "title" varchar(255), "body" text, "author" varchar(255), "created_at" datetime, "updated_at" datetime) 
18
+ SQL (0.0ms) INSERT INTO schema_migrations (version) VALUES ('20100115021151')
19
+ Post Create (0.1ms) INSERT 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')
20
+ SQL (0.1ms) SELECT count(*) AS count_all FROM "audits" 
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
+ Post Load (0.1ms) SELECT * FROM "posts" WHERE ("posts"."id" = 1) 
26
+ Post Update (0.0ms) UPDATE "posts" SET "title" = 'First post!', "updated_at" = '2010-01-15 03:01:04' WHERE "id" = 1
27
+ Redirected to http://test.host/posts/1
28
+ WARNING: Can't mass-assign these protected attributes: id
29
+ Audit Create (0.1ms) INSERT 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')
37
+ Completed in 47ms (DB: 1) | 302 Found [http://test.host/posts/1?post%5Btitle%5D=First+post%21]
38
+ Audit Load (0.1ms) SELECT * FROM "audits" WHERE ("audits".audited_id = 1 AND "audits".audited_type = 'Post') LIMIT 1
39
+ SQL (0.1ms) SELECT count(*) AS count_all FROM "audits" 
40
+ SQL (0.2ms)  SELECT name
41
+ FROM sqlite_master
42
+ WHERE type = 'table' AND NOT name = 'sqlite_sequence'
43
+ 
44
+ SQL (0.1ms) select sqlite_version(*)
45
+ SQL (0.2ms) CREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL) 
46
+ SQL (0.1ms) CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version")
47
+ SQL (0.1ms)  SELECT name
48
+ FROM sqlite_master
49
+ WHERE type = 'table' AND NOT name = 'sqlite_sequence'
50
+ 
51
+ SQL (0.0ms) SELECT version FROM schema_migrations
52
+ Migrating to CreateAudits (20100115021125)
53
+ SQL (0.1ms) CREATE 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) 
54
+ SQL (0.0ms) INSERT INTO schema_migrations (version) VALUES ('20100115021125')
55
+ Migrating to CreatePosts (20100115021151)
56
+ SQL (0.1ms) CREATE TABLE "posts" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "title" varchar(255), "body" text, "author" varchar(255), "created_at" datetime, "updated_at" datetime) 
57
+ SQL (0.0ms) INSERT INTO schema_migrations (version) VALUES ('20100115021151')
58
+ Post Create (0.1ms) INSERT 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')
59
+ SQL (0.1ms) SELECT count(*) AS count_all FROM "audits" 
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
+ Post Load (0.1ms) SELECT * FROM "posts" WHERE ("posts"."id" = 1) 
65
+ Post Update (0.0ms) UPDATE "posts" SET "title" = 'First post!', "updated_at" = '2010-01-15 03:02:19' WHERE "id" = 1
66
+ Redirected to http://test.host/posts/1
67
+ WARNING: Can't mass-assign these protected attributes: id
68
+ Audit Create (0.1ms) INSERT 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')
76
+ Completed in 47ms (DB: 1) | 302 Found [http://test.host/posts/1?post%5Btitle%5D=First+post%21]
77
+ Audit Load (0.1ms) SELECT * FROM "audits" WHERE ("audits".audited_id = 1 AND "audits".audited_type = 'Post') LIMIT 1
78
+ SQL (0.1ms) SELECT count(*) AS count_all FROM "audits" 
79
+ SQL (0.2ms)  SELECT name
80
+ FROM sqlite_master
81
+ WHERE type = 'table' AND NOT name = 'sqlite_sequence'
82
+ 
83
+ SQL (0.1ms) select sqlite_version(*)
84
+ SQL (0.2ms) CREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL) 
85
+ SQL (0.1ms) CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version")
86
+ SQL (0.1ms)  SELECT name
87
+ FROM sqlite_master
88
+ WHERE type = 'table' AND NOT name = 'sqlite_sequence'
89
+ 
90
+ SQL (0.0ms) SELECT version FROM schema_migrations
91
+ Migrating to CreateAudits (20100115021125)
92
+ SQL (0.1ms) CREATE 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) 
93
+ SQL (0.0ms) INSERT INTO schema_migrations (version) VALUES ('20100115021125')
94
+ Migrating to CreatePosts (20100115021151)
95
+ SQL (0.1ms) CREATE TABLE "posts" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "title" varchar(255), "body" text, "author" varchar(255), "created_at" datetime, "updated_at" datetime) 
96
+ SQL (0.0ms) INSERT INTO schema_migrations (version) VALUES ('20100115021151')
97
+ Post Create (0.1ms) INSERT 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')
98
+ SQL (0.1ms) SELECT count(*) AS count_all FROM "audits" 
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
+ Post Load (0.1ms) SELECT * FROM "posts" WHERE ("posts"."id" = 1) 
104
+ Post Update (0.0ms) UPDATE "posts" SET "title" = 'First post!', "updated_at" = '2010-01-15 03:02:28' WHERE "id" = 1
105
+ Redirected to http://test.host/posts/1
106
+ WARNING: Can't mass-assign these protected attributes: id
107
+ Audit Create (0.1ms) INSERT 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')
115
+ Completed in 47ms (DB: 1) | 302 Found [http://test.host/posts/1?post%5Btitle%5D=First+post%21]
116
+ Audit Load (0.1ms) SELECT * FROM "audits" WHERE ("audits".audited_id = 1 AND "audits".audited_type = 'Post') LIMIT 1
117
+ SQL (0.1ms) SELECT count(*) AS count_all FROM "audits" 
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.expand_path('../../config/boot', __FILE__)
3
+ require 'commands/console'
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.expand_path('../../config/boot', __FILE__)
3
+ require 'commands/generate'
data/test/test_helper.rb CHANGED
@@ -1,12 +1,29 @@
1
- require 'rubygems'
2
- begin
3
- gem 'test-unit'
4
- rescue Gem::LoadError
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.3.2
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: 2009-12-15 00:00:00 -07:00
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/base_test.rb
72
- - test/test_helper.rb
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