simple_audit 0.0.3 → 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,12 @@
1
+ # Version 0.1.0 (Sept 29, 2010)
2
+ * Rails 3 compatibility
3
+ * minor API changes break backwards compatibility
4
+ * documented code
5
+
6
+ # Version 0.0.3 (June 6, 2010)
7
+ * unit tests
8
+
9
+ # Version 0.0.2 (June 3, 2010)
10
+ * added migration generator
11
+
12
+ # Version 0.0.1 (June 1, 2010)
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2010 [Gabriel Târnovan]
1
+ Copyright (c) 2010 [Gabriel-Ștefan Târnovan, Mihai-Longin Târnovan]
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/Rakefile CHANGED
@@ -27,24 +27,27 @@ end
27
27
  #
28
28
  begin
29
29
  require 'jeweler'
30
-
30
+ test_files = FileList['test/**/*'].to_a
31
31
  gem_files = FileList[
32
32
  '[a-zA-Z]*',
33
33
  'lib/**/*',
34
+ 'generators/**/*',
34
35
  'rails/**/*',
35
- 'tasks/**/*',
36
- 'test/**/*'
37
- ]
36
+ ].to_a + test_files
38
37
 
39
38
  Jeweler::Tasks.new do |gemspec|
40
39
  gemspec.name = "simple_audit"
41
40
  gemspec.summary = "Simple auditing solution for ActiveRecord models"
42
- #gemspec.description = ""
43
- gemspec.email = "gabriel.tarnovan@cubus.ro"
41
+ gemspec.description = <<-EOD
42
+ Provides a straightforward way for auditing changes on active record models, especially for composite entities.
43
+ Also provides helper methods for easily rendering an audit trail in Ruby on Rails views.
44
+ EOD
45
+ gemspec.email = ["gabriel.tarnovan@cubus.ro", "mihai.tarnovan@cubus.ro"]
44
46
  gemspec.homepage = "http://github.com/gtarnovan/simple_audit"
45
- gemspec.authors = ["Gabriel Tarnovan"]
46
- gemspec.version = "0.0.3"
47
- gemspec.files = gem_files.to_a
47
+ gemspec.authors = ["Gabriel Tarnovan", "Mihai Tarnovan"]
48
+ gemspec.version = "0.1.0"
49
+ gemspec.files = gem_files
50
+ gemspec.test_files = test_files
48
51
 
49
52
  gemspec.rubyforge_project = 'simple_audit'
50
53
  end
data/TODO CHANGED
@@ -1,3 +1,12 @@
1
- * migration generator
2
- * customizable method for getting the current user
3
- * tests
1
+ # Architecture
2
+ * support for ActiveModel
3
+ * ORM agnosticism
4
+
5
+ # Funtionality
6
+ * provide some sort of UUID for audited models that identify these independently from the database provided ID.
7
+ in case of deletion and creation of a new entity with the same UUID, the audit trails of both entities could be bound.
8
+ e.g. for an invoice the UUID could be its number/series combination. when someone deletes an invoice and creates a new one
9
+ with the same UUID, the audit trail of both instances should be bound, such that the deletion event is visible during the audit (i.e. in the view helper)
10
+
11
+ # Minor
12
+ * provide a sample css file for audit trail rendering
@@ -0,0 +1,2 @@
1
+ Description:
2
+ Generates migration for Audit model
@@ -0,0 +1,9 @@
1
+ class SimpleAuditMigrationGenerator < Rails::Generator::Base
2
+
3
+ def manifest
4
+ record do |m|
5
+ m.migration_template 'migration.rb', "db/migrate", :migration_file_name => 'simple_audit_migration'
6
+ end
7
+ end
8
+
9
+ end
@@ -0,0 +1,22 @@
1
+ class SimpleAuditMigration < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :audits do |t|
4
+ t.belongs_to :auditable, :polymorphic => true
5
+ t.belongs_to :user, :polymorphic => true
6
+
7
+ t.string :username
8
+ t.string :action
9
+ t.text :change_log
10
+ t.timestamps
11
+
12
+ end
13
+
14
+ add_index :audits, [:auditable_id, :auditable_type], :name => 'auditable_index'
15
+ add_index :audits, [:user_id, :user_type], :name => 'user_index'
16
+ add_index :audits, :created_at
17
+ end
18
+
19
+ def self.down
20
+ drop_table :audits
21
+ end
22
+ end
@@ -0,0 +1,31 @@
1
+ require 'rails/generators/migration'
2
+
3
+ module SimpleAudit
4
+ class MigrationGenerator < Rails::Generators::Base
5
+ include Rails::Generators::Migration
6
+
7
+ desc "Generates migration for Audit model"
8
+
9
+ def self.orm
10
+ Rails::Generators.options[:rails][:orm]
11
+ end
12
+
13
+ def self.source_root
14
+ File.join(File.dirname(__FILE__), 'templates', (orm.to_s unless orm.class.eql?(String)) )
15
+ end
16
+
17
+ def self.orm_has_migration?
18
+ [:active_record].include? orm
19
+ end
20
+
21
+ def self.next_migration_number(path)
22
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
23
+ end
24
+
25
+ def create_migration_file
26
+ if self.class.orm_has_migration?
27
+ migration_template 'migration.rb', 'db/migrate/simple_audit_migration'
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,22 @@
1
+ class SimpleAuditMigration < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :audits do |t|
4
+ t.belongs_to :auditable, :polymorphic => true
5
+ t.belongs_to :user, :polymorphic => true
6
+
7
+ t.string :username
8
+ t.string :action
9
+ t.text :change_log
10
+ t.timestamps
11
+
12
+ end
13
+
14
+ add_index :audits, [:auditable_id, :auditable_type], :name => 'auditable_index'
15
+ add_index :audits, [:user_id, :user_type], :name => 'user_index'
16
+ add_index :audits, :created_at
17
+ end
18
+
19
+ def self.down
20
+ drop_table :audits
21
+ end
22
+ end
@@ -1,48 +1,14 @@
1
- module Cubus
2
- module SimpleAudit
3
-
4
- def self.included(base)
5
- base.send :extend, ClassMethods
6
- end
7
-
8
- module ClassMethods
9
-
10
- # create a new Audit record
11
- # if no user is specified, try to use sentient_user's User.current
1
+ require 'active_record'
2
+ require 'action_view'
12
3
 
13
- def simple_audit(options = {})
14
- class_eval do
15
-
16
- cattr_accessor :username_method
17
- self.username_method = (options[:username_method] || :name).to_s
18
-
19
- has_many :audits, :as => :auditable
20
- after_create {|record| audit(record, :create)}
21
- after_update {|record| audit(record, :update)}
22
-
23
-
24
- end
25
- send :include, InstanceMethods
26
- end
27
-
28
- def audit record, action = :update, user = nil
29
- user ||= User.current if User.respond_to?(:current)
30
- record.audits.create(:user => user,
31
- :username => user.try(self.username_method),
32
- :action => action.to_s,
33
- :changes => record.audit_changes
34
- )
35
- end
36
-
37
- end
38
-
39
- module InstanceMethods
40
- def audit_changes
41
- self.attributes
42
- end
43
- end
44
-
45
- end
4
+ require 'simple_audit/simple_audit'
5
+ require 'simple_audit/audit'
6
+ require 'simple_audit/helper'
46
7
 
8
+ if defined?(ActiveRecord::Base)
9
+ ActiveRecord::Base.send :include, SimpleAudit
47
10
  end
48
11
 
12
+ if defined?(ActionView::Base)
13
+ ActionView::Base.send :include, SimpleAudit::Helper
14
+ end
@@ -0,0 +1,44 @@
1
+ module SimpleAudit #:nodoc:
2
+
3
+ # Changes of the audited models will be stored here.
4
+ class Audit < ActiveRecord::Base
5
+ belongs_to :auditable, :polymorphic => true
6
+ belongs_to :user, :polymorphic => true
7
+ serialize :change_log
8
+
9
+ # Computes the differences of the change logs between two audits.
10
+ #
11
+ # Returns a hash containing arrays of the form
12
+ # {
13
+ # :key_1 => [<value_in_other_audit>, <value_in_this_audit>],
14
+ # :key_2 => [<value_in_other_audit>, <value_in_this_audit>],
15
+ # }
16
+ def delta(other_audit)
17
+
18
+ return self.change_log if other_audit.nil?
19
+
20
+ returning({}) do |d|
21
+
22
+ # first for keys present only in this audit
23
+ (self.change_log.keys - other_audit.change_log.keys).each do |k|
24
+ d[k] = [nil, self.change_log[k]]
25
+ end
26
+
27
+ # .. then for keys present only in other audit
28
+ (other_audit.change_log.keys - self.change_log.keys).each do |k|
29
+ d[k] = [other_audit.change_log[k], nil]
30
+ end
31
+
32
+ # .. finally for keys present in both, but with different values
33
+ self.change_log.keys.each do |k|
34
+ if self.change_log[k] != other_audit.change_log[k]
35
+ d[k] = [other_audit.change_log[k], self.change_log[k]]
36
+ end
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+ end
43
+
44
+ end
@@ -0,0 +1,35 @@
1
+ module SimpleAudit #:nodoc:
2
+ module Helper
3
+
4
+ # Render the change log for the given audited model
5
+
6
+ def render_audits(audited_model)
7
+ return '' unless audited_model.respond_to?(:audits)
8
+ audits = (audited_model.audits || []).dup.sort{|a,b| b.created_at <=> a.created_at}
9
+ res = ''
10
+ audits.each_with_index do |audit, index|
11
+ older_audit = audits[index + 1]
12
+ res += content_tag(:div, :class => 'audit') do
13
+ content_tag(:div, audit.action, :class => "action #{audit.action}") +
14
+ content_tag(:div, audit.username, :class => "user") +
15
+ content_tag(:div, l(audit.created_at), :class => "timestamp") +
16
+ content_tag(:div, :class => 'changes') do
17
+ if older_audit.present?
18
+ audit.delta(older_audit).collect do |k, v|
19
+ "\n" +
20
+ audited_model.class.human_attribute_name(k) +
21
+ ":" +
22
+ content_tag(:span, v.last, :class => 'current') +
23
+ content_tag(:span, v.first, :class => 'previous')
24
+ end
25
+ else
26
+ audit.change_log.reject{|k, v| v.blank?}.collect {|k, v| "\n#{audited_model.class.human_attribute_name(k)}: #{v}"}
27
+ end
28
+ end
29
+ end
30
+ end
31
+ res
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,73 @@
1
+ # Use this macro if you want changes in your model to be saved in an audit table.
2
+ # The audits table must exist.
3
+ #
4
+ # class Booking
5
+ # simple_audit
6
+ # end
7
+ #
8
+ # See SimpleAudit::ClassMethods#simple_audit for configuration options
9
+
10
+ module SimpleAudit
11
+ def self.included(base) #:nodoc:
12
+ base.send :extend, ClassMethods
13
+ end
14
+
15
+ module ClassMethods
16
+
17
+ # == Configuration options
18
+ #
19
+ # * <tt>username_method => symbol</tt> - Call this method on the current user to get the name
20
+ #
21
+ # With no block, all the attributes of the audited model will be logged.
22
+ #
23
+ # class Booking
24
+ # # this is equivalent to passing no block
25
+ # simple_audit do |audited_record|
26
+ # audited_record.attributes
27
+ # end
28
+ # end
29
+ #
30
+ # If a block is given, the data returned by the block will be saved in the audit's change log.
31
+ #
32
+ # class Booking
33
+ # has_many :housing_units
34
+ # simple_audit do |audited_record|
35
+ # {
36
+ # :some_relevant_attribute => audited_record.some_relevant_attribute,
37
+ # :human_readable_serialization_of_aggregated_models => audited_record.housing_units.collect(&:to_s),
38
+ # ...
39
+ # }
40
+ # end
41
+ # end
42
+ #
43
+
44
+ def simple_audit(options = {}, &block)
45
+ class_eval do
46
+
47
+ write_inheritable_attribute :username_method, (options[:username_method] || :name).to_sym
48
+ class_inheritable_reader :username_method
49
+
50
+ audit_changes_proc = block_given? ? block.to_proc : Proc.new {|record| record.attributes}
51
+ write_inheritable_attribute :audit_changes, audit_changes_proc
52
+ class_inheritable_reader :audit_changes
53
+
54
+ has_many :audits, :as => :auditable, :class_name => '::SimpleAudit::Audit'
55
+
56
+ after_create {|record| record.class.audit(record, :create)}
57
+ after_update {|record| record.class.audit(record, :update)}
58
+
59
+ end
60
+ end
61
+
62
+ def audit(record, action = :update, user = nil) #:nodoc:
63
+ user ||= User.current if User.respond_to?(:current)
64
+ record.audits.create(:user => user,
65
+ :username => user.try(self.username_method),
66
+ :action => action.to_s,
67
+ :change_log => self.audit_changes.call(record)
68
+ )
69
+ end
70
+
71
+ end
72
+
73
+ end
@@ -1,11 +1,2 @@
1
+ # compatibility with Rails 2
1
2
  require 'simple_audit'
2
-
3
- %w{ models helpers }.each do |dir|
4
- path = File.join(File.dirname(__FILE__), '..', 'lib', 'app', dir)
5
- $LOAD_PATH << path
6
- ActiveSupport::Dependencies.load_paths << path
7
- ActiveSupport::Dependencies.load_once_paths.delete(path)
8
- end
9
-
10
- ActiveRecord::Base.send :include, Cubus::SimpleAudit
11
- ActionController::Base.send :helper, :simple_audit
Binary file
@@ -5,41 +5,54 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{simple_audit}
8
- s.version = "0.0.3"
8
+ s.version = "0.1.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Gabriel Tarnovan"]
12
- s.date = %q{2010-06-07}
13
- s.email = %q{gabriel.tarnovan@cubus.ro}
11
+ s.authors = ["Gabriel Tarnovan", "Mihai Tarnovan"]
12
+ s.date = %q{2010-09-29}
13
+ s.description = %q{ Provides a straightforward way for auditing changes on active record models, especially for composite entities.
14
+ Also provides helper methods for easily rendering an audit trail in Ruby on Rails views.
15
+ }
16
+ s.email = ["gabriel.tarnovan@cubus.ro", "mihai.tarnovan@cubus.ro"]
14
17
  s.extra_rdoc_files = [
15
18
  "LICENSE",
16
- "README.markdown",
17
19
  "TODO"
18
20
  ]
19
21
  s.files = [
20
- "LICENSE",
21
- "README.markdown",
22
+ "CHANGELOG",
23
+ "LICENSE",
22
24
  "Rakefile",
23
25
  "TODO",
24
- "lib/app/helpers/simple_audit_helper.rb",
25
- "lib/app/models/audit.rb",
26
+ "generators/simple_audit_migration/USAGE",
27
+ "generators/simple_audit_migration/simple_audit_migration_generator.rb",
28
+ "generators/simple_audit_migration/templates/migration.rb",
29
+ "lib/generators/simple_audit/migration/migration_generator.rb",
30
+ "lib/generators/simple_audit/migration/templates/active_record/migration.rb",
26
31
  "lib/simple_audit.rb",
32
+ "lib/simple_audit/audit.rb",
33
+ "lib/simple_audit/helper.rb",
34
+ "lib/simple_audit/simple_audit.rb",
27
35
  "rails/init.rb",
28
36
  "screenshot.png",
29
37
  "simple_audit.gemspec",
30
- "tasks/simple_audit_tasks.rake",
31
- "test/simple_audit_test.rb",
32
- "test/test_helper.rb"
38
+ "test/fixtures/addresses.yml",
39
+ "test/fixtures/people.yml",
40
+ "test/test_helper.rb",
41
+ "test/unit/simple_audit_test.rb"
33
42
  ]
34
43
  s.homepage = %q{http://github.com/gtarnovan/simple_audit}
35
44
  s.rdoc_options = ["--charset=UTF-8"]
36
45
  s.require_paths = ["lib"]
37
46
  s.rubyforge_project = %q{simple_audit}
38
- s.rubygems_version = %q{1.3.5}
47
+ s.rubygems_version = %q{1.3.6}
39
48
  s.summary = %q{Simple auditing solution for ActiveRecord models}
40
49
  s.test_files = [
41
- "test/test_helper.rb",
42
- "test/simple_audit_test.rb"
50
+ "test/fixtures",
51
+ "test/fixtures/addresses.yml",
52
+ "test/fixtures/people.yml",
53
+ "test/test_helper.rb",
54
+ "test/unit",
55
+ "test/unit/simple_audit_test.rb"
43
56
  ]
44
57
 
45
58
  if s.respond_to? :specification_version then
@@ -0,0 +1,6 @@
1
+ address_1:
2
+ line_1: M. Viteazu nr. 11 sc. C ap.32
3
+ person_id: 1
4
+ address_2:
5
+ line_1: Calea Lunga nr. 104 Sibiu 123500
6
+ person_id: 2
@@ -0,0 +1,8 @@
1
+ 1:
2
+ name: Mihai Tarnovan
3
+ email: mihai.tarnovan@cubus.ro
4
+ id: 1
5
+ 2:
6
+ name: Gabriel Tarnovan
7
+ email: gabriel.tarnovan@cubus.ro
8
+ id: 2
@@ -1,3 +1,76 @@
1
1
  require 'rubygems'
2
+ require 'test/unit'
2
3
  require 'active_support'
3
- require 'active_support/test_case'
4
+ require 'active_support/test_case'
5
+ require 'active_record'
6
+ require 'active_record/fixtures'
7
+ require 'action_controller'
8
+ require 'ruby-debug'
9
+ require 'ostruct'
10
+
11
+ require File.join(File.dirname(__FILE__), "..", "generators", "simple_audit_migration", "templates", "migration.rb")
12
+ require File.join(File.dirname(__FILE__), "..", "lib", "simple_audit")
13
+
14
+ ActiveRecord::Base.establish_connection({
15
+ :adapter => 'sqlite3',
16
+ :database => ':memory:'
17
+ })
18
+
19
+ ActiveRecord::Migration.suppress_messages {
20
+ ActiveRecord::Schema.define do
21
+ suppress_messages do
22
+ create_table "people", :force => true do |t|
23
+ t.column "name", :text
24
+ t.column "email", :text
25
+ end
26
+ create_table "addresses", :force => true do |t|
27
+ t.column "line_1", :text
28
+ t.column "zip", :text
29
+ t.column "type", :text
30
+ t.references :person
31
+ end
32
+ create_table "users", :force => true do |t|
33
+ t.column "name", :text
34
+ end
35
+ end
36
+ end
37
+ SimpleAuditMigration.migrate(:up)
38
+ }
39
+
40
+ class Person < ActiveRecord::Base
41
+ has_one :address
42
+ simple_audit do |record|
43
+ {
44
+ :name => record.name,
45
+ :address => { :line_1 => record.address.line_1, :zip => record.address.zip }
46
+ }
47
+ end
48
+ end
49
+
50
+ class Address < ActiveRecord::Base
51
+ belongs_to :person
52
+ simple_audit :username_method => :full_name
53
+ end
54
+
55
+ class HomeAddress < Address
56
+ simple_audit :username_method => :short_name
57
+ end
58
+
59
+ class User < ActiveRecord::Base
60
+ def self.current; User.first ; end
61
+
62
+ def name
63
+ "name"
64
+ end
65
+
66
+ def full_name
67
+ "full_name"
68
+ end
69
+
70
+ def short_name
71
+ "short_name"
72
+ end
73
+
74
+ end
75
+
76
+ User.create(:name => "some user")
@@ -0,0 +1,66 @@
1
+ require 'test_helper'
2
+
3
+ class SimpleAuditTest < ActiveSupport::TestCase
4
+
5
+ include SimpleAudit
6
+
7
+ test "should audit entity creation" do
8
+ assert_difference 'Audit.count', 4 do
9
+ assert_difference 'Person.count', 2 do
10
+ Person.create(:name => "Mihai Tarnovan", :email => "mihai.tarnovan@cubus.ro", :address => Address.new(:line_1 => "M. Viteazu nr. 11 sc. C ap.32"))
11
+ Person.create(:name => "Gabriel Tarnovan", :email => "gabriel.tarnovan@cubus.ro", :address => Address.new(:line_1 => "Calea Lunga nr. 104 Sibiu 123500"))
12
+ end
13
+ end
14
+ end
15
+
16
+ test "should set correct action" do
17
+ person = Person.create(:name => "Mihai Tarnovan", :email => "mihai.tarnovan@cubus.ro", :address => Address.new(:line_1 => "M. Viteazu nr. 11 sc. C ap.32"))
18
+ assert_equal Audit.last.action, "create"
19
+ person.name = "Mihai T."
20
+ person.save
21
+ assert_equal person.audits.last.action, "update"
22
+ end
23
+
24
+ test "should audit only given fields" do
25
+ person = Person.create(:name => "Mihai Tarnovan", :email => "mihai.tarnovan@cubus.ro", :address => Address.new(:line_1 => "M. Viteazu nr. 11 sc. C ap.32"))
26
+ create_audit = person.audits.last
27
+ assert_difference 'Audit.count', 1 do
28
+ person.email = "mihai.tarnovan@gmail.com"
29
+ person.save
30
+ end
31
+ update_audit = person.audits.last
32
+ assert_equal update_audit.delta(create_audit), {}
33
+ end
34
+
35
+ test "should audit associated entity changes" do
36
+ person = Person.create(:name => "Mihai Tarnovan", :email => "mihai.tarnovan@cubus.ro", :address => Address.new(:line_1 => "M. Viteazu nr. 11 sc. C ap.32", :zip => "550350"))
37
+ create_audit = person.audits.last
38
+ assert_difference 'person.audits.count', 1 do
39
+ person.address = Address.new(:line_1 => "Bdul. Victoriei nr. 51", :zip => "550150")
40
+ person.name = "Gigi Kent"
41
+ person.email = "gigi@kent.ro"
42
+ person.save
43
+ end
44
+ update_audit = person.audits.last
45
+ assert_equal update_audit.delta(create_audit), {
46
+ :name => ["Mihai Tarnovan", "Gigi Kent"],
47
+ :address => [
48
+ { :line_1 => "M. Viteazu nr. 11 sc. C ap.32", :zip => "550350" },
49
+ { :line_1 => "Bdul. Victoriei nr. 51", :zip => "550150" }
50
+ ]
51
+ }
52
+ end
53
+
54
+ test "should audit all attributes by default" do
55
+ address = Address.create
56
+ assert_equal Audit.last.change_log.keys.sort, Address.column_names.sort
57
+ end
58
+
59
+ test "should use proper username method" do
60
+ address = HomeAddress.create
61
+ assert_equal User.new.short_name, address.audits.last.username
62
+ address = Address.create
63
+ assert_equal User.new.full_name, address.audits.last.username
64
+ end
65
+
66
+ end
metadata CHANGED
@@ -1,42 +1,56 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_audit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
5
10
  platform: ruby
6
11
  authors:
7
12
  - Gabriel Tarnovan
13
+ - Mihai Tarnovan
8
14
  autorequire:
9
15
  bindir: bin
10
16
  cert_chain: []
11
17
 
12
- date: 2010-06-07 00:00:00 +03:00
18
+ date: 2010-09-29 00:00:00 +03:00
13
19
  default_executable:
14
20
  dependencies: []
15
21
 
16
- description:
17
- email: gabriel.tarnovan@cubus.ro
22
+ description: " Provides a straightforward way for auditing changes on active record models, especially for composite entities. \n Also provides helper methods for easily rendering an audit trail in Ruby on Rails views.\n"
23
+ email:
24
+ - gabriel.tarnovan@cubus.ro
25
+ - mihai.tarnovan@cubus.ro
18
26
  executables: []
19
27
 
20
28
  extensions: []
21
29
 
22
30
  extra_rdoc_files:
23
31
  - LICENSE
24
- - README.markdown
25
32
  - TODO
26
33
  files:
34
+ - CHANGELOG
27
35
  - LICENSE
28
- - README.markdown
29
36
  - Rakefile
30
37
  - TODO
31
- - lib/app/helpers/simple_audit_helper.rb
32
- - lib/app/models/audit.rb
38
+ - generators/simple_audit_migration/USAGE
39
+ - generators/simple_audit_migration/simple_audit_migration_generator.rb
40
+ - generators/simple_audit_migration/templates/migration.rb
41
+ - lib/generators/simple_audit/migration/migration_generator.rb
42
+ - lib/generators/simple_audit/migration/templates/active_record/migration.rb
33
43
  - lib/simple_audit.rb
44
+ - lib/simple_audit/audit.rb
45
+ - lib/simple_audit/helper.rb
46
+ - lib/simple_audit/simple_audit.rb
34
47
  - rails/init.rb
35
48
  - screenshot.png
36
49
  - simple_audit.gemspec
37
- - tasks/simple_audit_tasks.rake
38
- - test/simple_audit_test.rb
50
+ - test/fixtures/addresses.yml
51
+ - test/fixtures/people.yml
39
52
  - test/test_helper.rb
53
+ - test/unit/simple_audit_test.rb
40
54
  has_rdoc: true
41
55
  homepage: http://github.com/gtarnovan/simple_audit
42
56
  licenses: []
@@ -50,21 +64,25 @@ required_ruby_version: !ruby/object:Gem::Requirement
50
64
  requirements:
51
65
  - - ">="
52
66
  - !ruby/object:Gem::Version
67
+ segments:
68
+ - 0
53
69
  version: "0"
54
- version:
55
70
  required_rubygems_version: !ruby/object:Gem::Requirement
56
71
  requirements:
57
72
  - - ">="
58
73
  - !ruby/object:Gem::Version
74
+ segments:
75
+ - 0
59
76
  version: "0"
60
- version:
61
77
  requirements: []
62
78
 
63
79
  rubyforge_project: simple_audit
64
- rubygems_version: 1.3.5
80
+ rubygems_version: 1.3.6
65
81
  signing_key:
66
82
  specification_version: 3
67
83
  summary: Simple auditing solution for ActiveRecord models
68
84
  test_files:
85
+ - test/fixtures/addresses.yml
86
+ - test/fixtures/people.yml
69
87
  - test/test_helper.rb
70
- - test/simple_audit_test.rb
88
+ - test/unit/simple_audit_test.rb
@@ -1,122 +0,0 @@
1
- # simple_audit
2
-
3
- Simple auditing solution for ActiveRecord models. Provides an easy way of creating audit logs for complex model associations.
4
- Instead of storing audits for all data aggregated by the audited model, you can specify a serializable representation of the model.
5
-
6
- * a helper method is provided to easily display the audit log
7
- * the Audit object provides a #delta method which computs the differences between two audits
8
-
9
- In a nutshell:
10
-
11
- class Booking < ActiveRecord::Base
12
- simple_audit
13
-
14
- # data to be audited
15
- def audit_changes
16
- {
17
- :price => self.price,
18
- :period => self.period,
19
- ...
20
- }
21
- end
22
- end
23
-
24
- # in your view
25
- <%= render_audits(@booking) %>
26
-
27
- # Why ?
28
-
29
- simple_audit is intended as a straightforward auditing solution for complex model associations.
30
- In a normalized data structure (which is usually the case when using SQL), a core business model will aggregate data from several associated entities.
31
- More often than not in such cases, only the core model is of interest from an auditing point of view.
32
-
33
- So instead of auditing every entity sepparately, with simple_audit you decide for each audited model what data needs to be audited.
34
- This data will be serialized on every model update and the available helper methods will render a clear audit trail.
35
-
36
- ![Screenshot of helper result](http://github.com/gtarnovan/simple_audit/raw/master/screenshot.png)s
37
-
38
- # Installation & Configuration
39
-
40
- ## As a gem
41
-
42
- gem install simple_audit
43
-
44
- and require it
45
-
46
- config.gem 'simple_audit'
47
-
48
- ## As a plugin
49
-
50
- ./script/plugin install http://github.com/gtarnovan/simple_audit
51
-
52
- ## Database
53
-
54
- Generate and run migration
55
-
56
- ./script/generate simple_audit_migration create_audits
57
- rake db:migrate
58
-
59
- # Usage
60
-
61
- Audit ActiveRecord models. Somewhere in your (backend) views show the audit logs.
62
-
63
- # in your model
64
- # app/models/booking.rb
65
-
66
- class Booking < ActiveRecord::Base
67
- simple_audit
68
- ...
69
- end
70
-
71
- # in your view
72
- # app/views/bookings/booking.html.erb
73
-
74
- ...
75
- <%= render_audits(@booking) %>
76
- ...
77
-
78
- # Assumptions and limitations
79
-
80
- * Your user model is called User and the current user User.current
81
- See [sentient_user](http://github.com/bokmann/sentient_user) for more information.
82
-
83
- * You have to write your own tests (fow now)
84
-
85
-
86
-
87
- ## Customize auditing
88
-
89
- By default after each save, all model's attributes are saved in the audits table.
90
- You can customize the data which is saved by overriding the audit_changes method. All relevant data for the audited model should be included here.
91
-
92
- # app/models/booking.rb
93
-
94
- class Booking < ActiveRecord::Base
95
- simple_audit
96
-
97
- def audit_changes
98
- {
99
- :state => self.state,
100
- :price => self.price.format,
101
- :period => self.period.to_s,
102
- :housing_units => housing_units.collect(&:name).join('; '),
103
- ...
104
- }
105
- end
106
- ...
107
- end
108
-
109
- You can also customize the attribute of the User model which will be stored in the audit.
110
-
111
- # default is :name
112
- simple_audit :username_method => :email
113
-
114
- ## Rendering audit
115
-
116
- A helper method for displaying a list of audits is provided. It will render a decorated list of the provided audits;
117
- only the differences between revisions will be shown, thus making the audit information easily readable.
118
-
119
- ![Screenshot of helper result](http://github.com/gtarnovan/simple_audit/raw/master/screenshot.png)
120
-
121
-
122
- Copyright (c) 2010 [Gabriel Târnovan, Cubus Arts](http://cubus.ro "Cubus Arts"), released under the MIT license
@@ -1,33 +0,0 @@
1
- module SimpleAuditHelper
2
-
3
- def render_audits(audited_model)
4
- return '' unless audited_model.respond_to?(:audits)
5
- audits = (audited_model.audits || []).dup.sort{|a,b| b.created_at <=> a.created_at}
6
- res = ''
7
- audits.each_with_index do |audit, index|
8
- older_audit = audits[index + 1]
9
- res += content_tag(:div, :class => 'audit') do
10
- content_tag(:div, audit.action, :class => "action #{audit.action}") +
11
- content_tag(:div, audit.username, :class => "user") +
12
- content_tag(:div, l(audit.created_at), :class => "timestamp") +
13
- content_tag(:div, :class => 'changes') do
14
- if older_audit.present?
15
- audit.delta(older_audit).collect do |k, v|
16
- "\n" +
17
- audited_model.class.human_attribute_name(k) +
18
- ":" +
19
- content_tag(:span, v.last, :class => 'current') +
20
- content_tag(:span, v.first, :class => 'previous')
21
- end
22
- else
23
- audit.changes.reject{|k, v| v.blank?}.collect {|k, v| "\n#{audited_model.class.human_attribute_name(k)}: #{v}"}
24
- end
25
- end
26
- end
27
- end
28
- res
29
- end
30
-
31
- end
32
-
33
-
@@ -1,39 +0,0 @@
1
- class Audit < ActiveRecord::Base
2
- belongs_to :auditable, :polymorphic => true
3
- belongs_to :user, :polymorphic => true
4
- serialize :changes
5
-
6
- #
7
- # returns the differences of the to audits
8
- # result is a hash containing arrays of the form [<value_in_other_audit>, <value_in_this_audit>]
9
- # the values in this audit take precedence
10
- #
11
- def delta(other)
12
-
13
- return self.changes if other.nil?
14
-
15
- d = {}
16
-
17
- (self.changes.keys - other.changes.keys).each do |k|
18
- d[k] = [nil, self.changes[k]]
19
- end
20
-
21
- (other.changes.keys - self.changes.keys).each do |k|
22
- d[k] = [other.changes[k], nil]
23
- end
24
-
25
- self.changes.keys.each do |k|
26
- if self.changes[k] != other.changes[k]
27
- d[k] = [other.changes[k], self.changes[k]]
28
- end
29
- end
30
-
31
- d
32
-
33
- end
34
-
35
- def changes
36
- self[:changes]
37
- end
38
-
39
- end
@@ -1,4 +0,0 @@
1
- # desc "Explaining what the task does"
2
- # task :simple_audit do
3
- # # Task goes here
4
- # end
@@ -1,8 +0,0 @@
1
- require 'test_helper'
2
-
3
- class SimpleAuditTest < ActiveSupport::TestCase
4
- # Replace this with your real tests.
5
- test "the truth" do
6
- assert true
7
- end
8
- end