action_auditor 0.1.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.
@@ -0,0 +1,3 @@
1
+ spec/debug.log
2
+ doc
3
+ coverage
@@ -0,0 +1,5 @@
1
+ = ActionAuditor
2
+
3
+ Allows you to write simple logs of your Rails actions.
4
+
5
+ Copyright (c) 2009 Matt Powell, released under the MIT license
@@ -0,0 +1 @@
1
+ Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,4 @@
1
+ Description:
2
+ Generates a migration to create a database table to store
3
+ your application's audit trail.
4
+
@@ -0,0 +1,15 @@
1
+ class ActionAuditorMigrationGenerator < Rails::Generator::Base
2
+ def initialize(runtime_args, runtime_options = {})
3
+ super
4
+ end
5
+
6
+ def manifest
7
+ record do |m|
8
+ m.migration_template 'migration.rb', "db/migrate"
9
+ end
10
+ end
11
+
12
+ def file_name
13
+ "create_action_auditor_table"
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ class CreateActionAuditorTable < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :logged_actions do |t|
4
+ t.belongs_to :scope, :polymorphic => true
5
+ t.text :message
6
+ t.text :parameters
7
+ t.timestamp :created_at
8
+ end
9
+
10
+ add_index :logged_actions, [ :scope_type, :scope_id, :created_at ]
11
+ end
12
+
13
+ def self.down
14
+ drop_table :logged_actions
15
+ end
16
+ end
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,13 @@
1
+ module ActionAuditor
2
+ def self.auditors
3
+ @@auditors ||= []
4
+ end
5
+
6
+ def self.auditors=(auditors)
7
+ @@auditors = Array(auditors)
8
+ end
9
+
10
+ def self.log(message, parameters = {})
11
+ auditors.each { |auditor| auditor.log(message, parameters) }
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ module ActionAuditor
2
+ module Auditor
3
+ class ActiveRecord < Base
4
+ class LoggedAction < ::ActiveRecord::Base
5
+ set_table_name "logged_actions"
6
+
7
+ serialize :parameters
8
+ end
9
+
10
+ def clear!
11
+ LoggedAction.destroy_all
12
+ end
13
+
14
+ def log(message, parameters = {})
15
+ LoggedAction.create :message => message, :parameters => parameters
16
+ end
17
+
18
+ def size
19
+ LoggedAction.count
20
+ end
21
+
22
+ def last
23
+ LoggedAction.last
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,7 @@
1
+ module ActionAuditor
2
+ module Auditor
3
+ class Base
4
+
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,33 @@
1
+ module ActionAuditor
2
+ module Auditor
3
+ # The simplest possible implementation of an auditor.
4
+ # Simply maintains a list of [message, hash] pairs,
5
+ # with no persistence.
6
+ # This is mainly of use for testing.
7
+ class Simple < Base
8
+ class LoggedAction < Struct.new(:message, :parameters)
9
+
10
+ end
11
+
12
+ def initialize
13
+ @messages = []
14
+ end
15
+
16
+ def clear!
17
+ @messages = []
18
+ end
19
+
20
+ def log(message, parameters = {})
21
+ @messages << LoggedAction.new(message, parameters)
22
+ end
23
+
24
+ def size
25
+ @messages.size
26
+ end
27
+
28
+ def last
29
+ @messages.last
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,107 @@
1
+ module ActionAuditor
2
+ module Extensions
3
+ module ActionController
4
+ module ClassMethods
5
+ # Set up an audit trail for the next action defined.
6
+ # This is similar to +desc+ in a Rake task: you call it
7
+ # immediately before the action.
8
+ #
9
+ # == Simple messages
10
+ # This will log "hello, world" each time someone
11
+ # visits the +index+ page.
12
+ # class PostsController < ApplicationController
13
+ # audit "hello, world"
14
+ # def index
15
+ # end
16
+ # end
17
+ #
18
+ # == Blocks
19
+ # If you need to interpolate any information at runtime,
20
+ # you'll need to use a block:
21
+ # class PostsController < ApplicationController
22
+ # audit { "#{current_user} stopped by" }
23
+ # def index
24
+ # end
25
+ # end
26
+ # ...otherwise the string will get evaluated when the
27
+ # controller class is defined.
28
+ #
29
+ # == Parameters
30
+ # If you need to save any information besides a message,
31
+ # you can write a block that returns [+message+, +params+],
32
+ # where +params+ is a hash of objects:
33
+ # class PostsController < ApplicationController
34
+ # audit {[
35
+ # "{{user}} created {{post}}",
36
+ # { :user => current_user, :post => @post }
37
+ # ]}
38
+ # def create
39
+ # @post = Post.create(params[:post])
40
+ # end
41
+ # end
42
+ # Notice that you can interpolate these parameters
43
+ # within your log message.
44
+ #
45
+ # It's up to the individual auditors how your parameters
46
+ # are saved — and, indeed, whether they're saved at all.
47
+ # An ActiveRecord implementation might serialize the
48
+ # objects, or just their IDs, while a text-only auditor
49
+ # might discard them completely. You should not rely on
50
+ # having access to these parameters later on, unless
51
+ # you know how and where they are saved.
52
+ def audit(*args, &block)
53
+ options = args.last.is_a?(Hash) ? args.pop : {}
54
+ block_or_message = block_given? ? block : args.shift
55
+ @pending_auditor = [ args, options, block_or_message ]
56
+ end
57
+
58
+ def method_added_with_auditing(name) #:nodoc:
59
+ method_added_without_auditing(name)
60
+ if @pending_auditor
61
+ auditors[name.to_sym] = @pending_auditor
62
+ @pending_auditor = nil
63
+ end
64
+ end
65
+ end
66
+
67
+ # Filter method called after each action.
68
+ # Checks if there's any auditing set up for this
69
+ # action, then logs relevant information using
70
+ # any active auditors.
71
+ def audit_last_action
72
+ if auditor = self.class.auditors[action_name.to_sym]
73
+ args, options, block_or_message = auditor
74
+ parameters = {}
75
+
76
+ log_message, parameters = case block_or_message
77
+ when String then [ block_or_message, {} ]
78
+ when Proc
79
+ values = Array(instance_eval(&block_or_message))
80
+ values << {} if values.size < 2
81
+ values[0,2]
82
+ end
83
+
84
+ log_message.gsub!(/\{\{(\w+)\}\}/) { parameters[$1.to_sym].to_s }
85
+
86
+ ActionAuditor.log(log_message, parameters)
87
+ end
88
+ end
89
+ protected :audit_last_action
90
+
91
+ def self.included(receiver) #:nodoc:
92
+ receiver.extend ClassMethods
93
+
94
+ receiver.class_eval do
95
+ class_inheritable_accessor :auditors
96
+ self.auditors = {}
97
+
98
+ class << receiver
99
+ alias_method_chain :method_added, :auditing
100
+ end
101
+
102
+ after_filter :audit_last_action
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,3 @@
1
+ # require "action_auditor"
2
+
3
+ ActionController::Base.send :include, ActionAuditor::Extensions::ActionController
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
@@ -0,0 +1,116 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ class Thing
4
+ attr_accessor :attributes
5
+
6
+ def initialize(attributes = {})
7
+ self.attributes = attributes
8
+ end
9
+
10
+ def to_s
11
+ "Thing(#{attributes.collect { |k, v| "#{k}:#{v}" }.join(", ")})"
12
+ end
13
+
14
+ def self.clear!
15
+ @things = []
16
+ end
17
+
18
+ def self.create(params = {})
19
+ returning new(params) do |thing|
20
+ @things << thing
21
+ end
22
+ end
23
+
24
+ def self.last
25
+ @things.last
26
+ end
27
+ end
28
+
29
+ class TestController < ActionController::Base
30
+ audit "Someone looked at something"
31
+ def simple
32
+
33
+ end
34
+
35
+ def unlogged
36
+
37
+ end
38
+
39
+ audit { "Message from #{current_user}" }
40
+ def substituted
41
+
42
+ end
43
+
44
+ audit { [ "{{user}} created {{thing}}", { :user => current_user, :thing => @thing } ] }
45
+ def complex
46
+ @thing = Thing.create :colour => :red
47
+ end
48
+
49
+ protected
50
+ def current_user
51
+ "Matt"
52
+ end
53
+ end
54
+
55
+ describe TestController do
56
+ [
57
+ ActionAuditor::Auditor::Simple.new,
58
+ ActionAuditor::Auditor::ActiveRecord.new
59
+ ].each do |auditor|
60
+ describe "with #{auditor.class.name.demodulize} auditing" do
61
+ before :each do
62
+ ActionAuditor.auditors = auditor
63
+ Thing.clear!
64
+ auditor.clear!
65
+ end
66
+
67
+ it "should allow auditing" do
68
+ controller.class.should respond_to(:audit)
69
+ end
70
+
71
+ it "should log visits to '/index" do
72
+ lambda {
73
+ get :simple
74
+ }.should change(auditor, :size).by(1)
75
+ end
76
+
77
+ it "should provide a message on visits to /simple" do
78
+ get :simple
79
+ auditor.last.message.should == "Someone looked at something"
80
+ end
81
+
82
+ it "should not log visits to /unlogged" do
83
+ lambda {
84
+ get :unlogged
85
+ }.should_not change(auditor, :size)
86
+ end
87
+
88
+ it "should log visits to /substituted" do
89
+ lambda {
90
+ get :substituted
91
+ }.should change(auditor, :size).by(1)
92
+ end
93
+
94
+ it "should keep a correct log of visits to /substituted" do
95
+ get :substituted
96
+ auditor.last.message.should == "Message from Matt"
97
+ auditor.last.parameters.should be_empty
98
+ end
99
+
100
+ it "should log visits to /complex" do
101
+ lambda {
102
+ get :complex
103
+ }.should change(auditor, :size).by(1)
104
+ end
105
+
106
+ it "should keep a correct log of visits to /complex" do
107
+ get :complex
108
+ log = auditor.last
109
+ log.message.should == "Matt created Thing(colour:red)"
110
+ log.parameters[:user].should == "Matt"
111
+ log.parameters[:thing].should be_a(Thing)
112
+ end
113
+ end
114
+ end
115
+ end
116
+
@@ -0,0 +1,10 @@
1
+ begin
2
+ require File.dirname(__FILE__) + '/../../../../spec/spec_helper'
3
+ rescue LoadError
4
+ puts "You need to install rspec in your base app"
5
+ exit
6
+ end
7
+
8
+ plugin_spec_dir = File.dirname(__FILE__)
9
+ ActiveRecord::Base.logger = Logger.new(plugin_spec_dir + "/debug.log")
10
+
@@ -0,0 +1,17 @@
1
+ begin
2
+ require 'hanna/rdoctask'
3
+ rescue
4
+ require 'rake/rdoctask'
5
+ end
6
+
7
+ desc 'Generate RDoc documentation.'
8
+ Rake::RDocTask.new(:rdoc) do |rdoc|
9
+ rdoc.rdoc_files.include('README.rdoc').
10
+ include('lib/**/*.rb')
11
+
12
+ rdoc.main = "README.rdoc" # page to start on
13
+ rdoc.title = "ActionAuditor documentation"
14
+
15
+ rdoc.rdoc_dir = 'doc' # rdoc output folder
16
+ rdoc.options << '--webcvs=http://github.com/fauxparse/action_auditor/'
17
+ end
@@ -0,0 +1,45 @@
1
+ # Don't load rspec if running "rake gems:*"
2
+ unless ARGV.any? {|a| a =~ /^gems/}
3
+
4
+ begin
5
+ require 'spec/rake/spectask'
6
+ rescue MissingSourceFile
7
+ module Spec
8
+ module Rake
9
+ class SpecTask
10
+ def initialize(name)
11
+ task name do
12
+ raise <<-MSG
13
+
14
+ #{"*" * 80}
15
+ * You are trying to run an rspec rake task defined in
16
+ * #{__FILE__},
17
+ * but rspec cannot be found.
18
+ #{"*" * 80}
19
+ MSG
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ task :default => :spec
28
+
29
+ desc "Run all specs in spec directory (excluding plugin specs)"
30
+ Spec::Rake::SpecTask.new(:spec) do |t|
31
+ t.spec_opts = ['--colour --format progress --loadby mtime --reverse']
32
+ t.spec_files = FileList['spec/**/*_spec.rb']
33
+ end
34
+
35
+ namespace :spec do
36
+ desc "Run all specs in spec directory with RCov"
37
+ Spec::Rake::SpecTask.new(:rcov) do |t|
38
+ t.spec_opts = ['--colour --format progress --loadby mtime --reverse']
39
+ t.spec_files = FileList['spec/**/*_spec.rb']
40
+ t.rcov = true
41
+ t.rcov_opts = ['--text-report --rails --exclude "spec/*,application_controller.rb,application_helper.rb"']
42
+ end
43
+ end
44
+
45
+ end
@@ -0,0 +1 @@
1
+ # Uninstall hook code here
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: action_auditor
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Matt Powell
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-11-27 00:00:00 +13:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Keep an audit trail of actions in your application
17
+ email: fauxparse@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ files:
25
+ - .gitignore
26
+ - README.rdoc
27
+ - Rakefile
28
+ - VERSION
29
+ - generators/action_auditor_migration/USAGE
30
+ - generators/action_auditor_migration/action_auditor_migration_generator.rb
31
+ - generators/action_auditor_migration/templates/migration.rb
32
+ - install.rb
33
+ - lib/action_auditor.rb
34
+ - lib/action_auditor/auditor/active_record.rb
35
+ - lib/action_auditor/auditor/base.rb
36
+ - lib/action_auditor/auditor/simple.rb
37
+ - lib/action_auditor/extensions/action_controller.rb
38
+ - rails/init.rb
39
+ - spec/action_auditor_spec.rb
40
+ - spec/controllers/controller_integration_spec.rb
41
+ - spec/spec_helper.rb
42
+ - tasks/rdoc.rake
43
+ - tasks/spec.rake
44
+ - uninstall.rb
45
+ has_rdoc: true
46
+ homepage: http://github.com/fauxparse/action_auditor
47
+ licenses: []
48
+
49
+ post_install_message:
50
+ rdoc_options:
51
+ - --charset=UTF-8
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ version:
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ version:
66
+ requirements: []
67
+
68
+ rubyforge_project:
69
+ rubygems_version: 1.3.5
70
+ signing_key:
71
+ specification_version: 3
72
+ summary: Keep an audit trail of actions in your application
73
+ test_files:
74
+ - spec/action_auditor_spec.rb
75
+ - spec/controllers/controller_integration_spec.rb
76
+ - spec/spec_helper.rb