action_auditor 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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