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 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