cia 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cia (0.2.3)
4
+ cia (0.3.0)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
@@ -1,15 +1,11 @@
1
- create_table :cia_transactions do |t|
1
+ create_table :cia_events do |t|
2
2
  t.integer :actor_id
3
3
  t.string :actor_type
4
+ t.string :source_type, :action, :null => false
5
+ t.integer :source_id, :null => false
4
6
  t.string :ip_address
5
- t.timestamp :created_at
6
- end
7
-
8
- create_table :cia_events do |t|
9
- t.string :type, :source_type, :null => false
10
- t.integer :cia_transaction_id, :source_id, :null => false
11
7
  t.string :message
12
- t.timestamp :created_at
8
+ t.timestamp :created_at#, :null => false
13
9
  end
14
10
 
15
11
  create_table :cia_attribute_changes do |t|
@@ -19,6 +15,5 @@ create_table :cia_attribute_changes do |t|
19
15
  end
20
16
 
21
17
  # DOWN
22
- # drop_table :cia_transactions
23
18
  # drop_table :cia_events
24
19
  # drop_table :cia_attribute_changes
data/Readme.md CHANGED
@@ -1,19 +1,16 @@
1
1
  Central Internal Auditing
2
2
  ============================
3
3
 
4
- Audit model events like update/create/delete + attribute changes.
4
+ Audit model actions like update/create/destroy/<custom> + attribute changes.
5
5
 
6
- - very normalized and queryable through table layout
6
+ - normalized and queryable through table layout
7
7
  - actors and subjects are polymorphic
8
- - events come in different types like `CIA::UpdateEvent`
9
- - transactions wrap multiple events, a nice place to add debugging info like source/action/ip
10
8
  - works on ActiveRecord 2 and 3
11
9
 
12
10
  Table layout:
13
11
 
14
- 1 Transaction (actor/ip/time/...)
15
- -> has many events (updated subject + message)
16
- -> has many attribute changes (changed password from foo to bar on subject)
12
+ Event (actor/ip/time/updated subject + message)
13
+ -> has many attribute changes (changed password from foo to bar on subject)
17
14
 
18
15
 
19
16
  Install
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /Users/mgrosser/code/tools/cia
3
3
  specs:
4
- cia (0.2.2)
4
+ cia (0.2.3)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /Users/mgrosser/code/tools/cia
3
3
  specs:
4
- cia (0.2.2)
4
+ cia (0.2.3)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
data/lib/cia.rb CHANGED
@@ -1,12 +1,7 @@
1
1
  require 'active_record'
2
2
  require 'cia/version'
3
3
  require 'cia/auditable'
4
- require 'cia/null_transaction'
5
- require 'cia/transaction'
6
4
  require 'cia/event'
7
- require 'cia/create_event'
8
- require 'cia/update_event'
9
- require 'cia/delete_event'
10
5
  require 'cia/attribute_change'
11
6
 
12
7
  module CIA
@@ -15,18 +10,31 @@ module CIA
15
10
  end
16
11
 
17
12
  def self.audit(options = {})
18
- Thread.current[:cia_transaction] = Transaction.new(options)
13
+ Thread.current[:cia_transaction] = options
19
14
  yield
20
15
  ensure
21
16
  Thread.current[:cia_transaction] = nil
22
17
  end
23
18
 
24
19
  def self.current_transaction
25
- Thread.current[:cia_transaction] || NullTransaction
20
+ Thread.current[:cia_transaction]
26
21
  end
27
22
 
28
- def self.record_audit(event_type, object)
29
- CIA.current_transaction.record(event_type, object)
23
+ def self.record(action, source)
24
+ return unless current_transaction
25
+
26
+ changes = source.changes.slice(*source.class.audited_attributes)
27
+ message = source.audit_message if source.respond_to?(:audit_message)
28
+
29
+ return if not message and changes.empty? and action.to_s == "update"
30
+
31
+ event = CIA::Event.create!(
32
+ :action => action.to_s,
33
+ :source => source,
34
+ :message => message
35
+ )
36
+ event.record_attribute_changes!(changes)
37
+ event
30
38
  rescue Object => e
31
39
  if exception_handler
32
40
  exception_handler.call e
@@ -3,9 +3,9 @@ module CIA
3
3
  def self.included(base)
4
4
  base.class_attribute :audited_attributes
5
5
  base.send :extend, ClassMethods
6
- base.after_create {|record| CIA.record_audit(CIA::CreateEvent, record) }
7
- base.after_update {|record| CIA.record_audit(CIA::UpdateEvent, record) }
8
- base.after_destroy {|record| CIA.record_audit(CIA::DeleteEvent, record) }
6
+ base.after_create {|record| CIA.record(:create, record) }
7
+ base.after_update {|record| CIA.record(:update, record) }
8
+ base.after_destroy {|record| CIA.record(:destroy, record) }
9
9
  end
10
10
 
11
11
  module ClassMethods
@@ -1,13 +1,12 @@
1
1
  module CIA
2
2
  class Event < ActiveRecord::Base
3
- abstract_class
4
3
  self.table_name = "cia_events"
5
4
 
5
+ belongs_to :actor, :polymorphic => true
6
6
  belongs_to :source, :polymorphic => true
7
- belongs_to :transaction, :foreign_key => :cia_transaction_id
8
7
  has_many :attribute_changes, :foreign_key => :cia_event_id
9
8
 
10
- validates_presence_of :transaction, :source, :type
9
+ validates_presence_of :source, :action
11
10
 
12
11
  def self.previous
13
12
  scoped(:order => "id desc")
@@ -1,3 +1,3 @@
1
1
  module CIA
2
- VERSION = '0.2.3'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -7,20 +7,20 @@ describe CIA do
7
7
 
8
8
  describe ".audit" do
9
9
  it "has no transaction when it starts" do
10
- CIA.current_transaction.should == CIA::NullTransaction
10
+ CIA.current_transaction.should == nil
11
11
  end
12
12
 
13
13
  it "starts a new transaction" do
14
14
  result = 1
15
- CIA.audit({}) do
15
+ CIA.audit({:a => 1}) do
16
16
  result = CIA.current_transaction
17
17
  end
18
- result.class.should == CIA::Transaction
18
+ result.should == {:a => 1}
19
19
  end
20
20
 
21
21
  it "stops the transaction after the block" do
22
22
  CIA.audit({}){}
23
- CIA.current_transaction.should == CIA::NullTransaction
23
+ CIA.current_transaction.should == nil
24
24
  end
25
25
 
26
26
  it "returns the block content" do
@@ -34,14 +34,13 @@ describe CIA do
34
34
  end
35
35
  end
36
36
  sleep 0.01
37
- CIA.current_transaction.should == CIA::NullTransaction
37
+ CIA.current_transaction.should == nil
38
38
  sleep 0.04 # so next tests dont fail
39
39
  end
40
40
  end
41
41
 
42
- describe ".record_audit" do
42
+ describe ".record" do
43
43
  let(:object) { Car.new }
44
- let(:transaction) { CIA.current_transaction }
45
44
 
46
45
  around do |example|
47
46
  CIA.audit :actor => User.create! do
@@ -53,7 +52,7 @@ describe CIA do
53
52
  expect{
54
53
  object.save!
55
54
  }.to change{ CIA::Event.count }.by(+1)
56
- CIA::Event.last.class.should == CIA::CreateEvent
55
+ CIA::Event.last.action.should == "create"
57
56
  end
58
57
 
59
58
  it "tracks delete" do
@@ -61,7 +60,7 @@ describe CIA do
61
60
  expect{
62
61
  object.destroy
63
62
  }.to change{ CIA::Event.count }.by(+1)
64
- CIA::Event.last.class.should == CIA::DeleteEvent
63
+ CIA::Event.last.action.should == "destroy"
65
64
  end
66
65
 
67
66
  it "tracks update" do
@@ -69,13 +68,78 @@ describe CIA do
69
68
  expect{
70
69
  object.update_attributes(:wheels => 3)
71
70
  }.to change{ CIA::Event.count }.by(+1)
72
- CIA::Event.last.class.should == CIA::UpdateEvent
71
+ CIA::Event.last.action.should == "update"
72
+ end
73
+
74
+ context "events" do
75
+ def parse_event_changes(event)
76
+ event.attribute_changes.map { |c| [c.attribute_name, c.old_value, c.new_value] }
77
+ end
78
+
79
+ it "records attribute creations" do
80
+ source = Car.create!
81
+ source.wheels = 4
82
+ event = CIA.record(:update, source)
83
+
84
+ parse_event_changes(event).should == [["wheels", nil, "4"]]
85
+ end
86
+
87
+ it "records multiple attributes" do
88
+ source = CarWith3Attributes.create!
89
+ source.wheels = 4
90
+ source.drivers = 2
91
+ source.color = "red"
92
+ event = CIA.record(:update, source)
93
+
94
+ parse_event_changes(event).should =~ [["wheels", nil, "4"], ["drivers", nil, "2"], ["color", nil, "red"]]
95
+ end
96
+
97
+ it "records attribute changes" do
98
+ source = Car.create!(:wheels => 2)
99
+ source.wheels = 4
100
+ event = CIA.record(:update, source)
101
+ parse_event_changes(event).should == [["wheels", "2", "4"]]
102
+ end
103
+
104
+ it "records attribute deletions" do
105
+ source = Car.create!(:wheels => 2)
106
+ source.wheels = nil
107
+ event = CIA.record(:update, source)
108
+ parse_event_changes(event).should == [["wheels", "2", nil]]
109
+ end
110
+
111
+ it "does not record unaudited attribute changes" do
112
+ source = Car.create!
113
+ source.drivers = 2
114
+ event = nil
115
+ expect{
116
+ event = CIA.record(:update, source)
117
+ }.to_not change{ CIA::Event.count }
118
+
119
+ event.should == nil
120
+ end
121
+
122
+ it "records audit_message as message even if there are no changes" do
123
+ source = CarWithAMessage.create!
124
+ source.audit_message = "Foo"
125
+ event = CIA.record(:update, source)
126
+
127
+ event.message.should == "Foo"
128
+ parse_event_changes(event).should == []
129
+ end
130
+
131
+ it "record non-updates even without changes" do
132
+ source = Car.create!
133
+ event = CIA.record(:create, source)
134
+
135
+ parse_event_changes(event).should == []
136
+ end
73
137
  end
74
138
 
75
139
  context "exception_handler" do
76
140
  before do
77
141
  $stderr.stub(:puts)
78
- transaction.stub(:record).and_raise(StandardError.new("foo"))
142
+ CIA.stub(:current_transaction).and_raise(StandardError.new("foo"))
79
143
  end
80
144
 
81
145
  def capture_exception
@@ -3,7 +3,6 @@ require 'cia'
3
3
 
4
4
  RSpec.configure do |config|
5
5
  config.before do
6
- CIA::Transaction.delete_all
7
6
  CIA::Event.delete_all
8
7
  CIA::AttributeChange.delete_all
9
8
  end
@@ -50,17 +49,8 @@ class CarWith3Attributes < ActiveRecord::Base
50
49
  audit_attribute :drivers
51
50
  end
52
51
 
53
- class CarWithoutObservers < ActiveRecord::Base
54
- self.table_name = "cars"
55
- end
56
-
57
- class CarWithoutObservers2 < ActiveRecord::Base
58
- self.table_name = "cars"
59
- end
60
-
61
52
  def create_event
62
- transaction = CIA::Transaction.create!(:actor => User.create!)
63
- CIA::UpdateEvent.create!(:source => Car.create!, :transaction => transaction)
53
+ CIA::Event.create!(:source => Car.create!, :actor => User.create!, :action => "update")
64
54
  end
65
55
 
66
56
  def create_change(options={})
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cia
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-22 00:00:00.000000000 Z
12
+ date: 2012-06-25 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description:
15
15
  email: michael@grosser.it
@@ -32,16 +32,10 @@ files:
32
32
  - lib/cia.rb
33
33
  - lib/cia/attribute_change.rb
34
34
  - lib/cia/auditable.rb
35
- - lib/cia/create_event.rb
36
- - lib/cia/delete_event.rb
37
35
  - lib/cia/event.rb
38
- - lib/cia/null_transaction.rb
39
- - lib/cia/transaction.rb
40
- - lib/cia/update_event.rb
41
36
  - lib/cia/version.rb
42
37
  - spec/cia/attribute_change_spec.rb
43
38
  - spec/cia/event_spec.rb
44
- - spec/cia/transaction_spec.rb
45
39
  - spec/cia_spec.rb
46
40
  - spec/spec_helper.rb
47
41
  homepage: http://github.com/grosser/cia
@@ -59,7 +53,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
59
53
  version: '0'
60
54
  segments:
61
55
  - 0
62
- hash: -3661699799177729927
56
+ hash: -1846204307001126824
63
57
  required_rubygems_version: !ruby/object:Gem::Requirement
64
58
  none: false
65
59
  requirements:
@@ -68,7 +62,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
68
62
  version: '0'
69
63
  segments:
70
64
  - 0
71
- hash: -3661699799177729927
65
+ hash: -1846204307001126824
72
66
  requirements: []
73
67
  rubyforge_project:
74
68
  rubygems_version: 1.8.24
@@ -1,4 +0,0 @@
1
- module CIA
2
- class CreateEvent < Event
3
- end
4
- end
@@ -1,4 +0,0 @@
1
- module CIA
2
- class DeleteEvent < Event
3
- end
4
- end
@@ -1,6 +0,0 @@
1
- module CIA
2
- class NullTransaction
3
- def self.record(*args)
4
- end
5
- end
6
- end
@@ -1,23 +0,0 @@
1
- module CIA
2
- class Transaction < ActiveRecord::Base
3
- self.table_name = "cia_transactions"
4
-
5
- belongs_to :actor, :polymorphic => true
6
- has_many :events, :foreign_key => :cia_transaction_id
7
-
8
- def record(event_type, source)
9
- changes = source.changes.slice(*source.class.audited_attributes)
10
- message = source.audit_message if source.respond_to?(:audit_message)
11
-
12
- return if not message and changes.empty? and event_type == CIA::UpdateEvent
13
-
14
- event = event_type.create!(
15
- :source => source,
16
- :transaction => self,
17
- :message => message
18
- )
19
- event.record_attribute_changes!(changes)
20
- event
21
- end
22
- end
23
- end
@@ -1,4 +0,0 @@
1
- module CIA
2
- class UpdateEvent < Event
3
- end
4
- end
@@ -1,75 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe CIA::Transaction do
4
- it "has many events" do
5
- event = create_event
6
- event.transaction.events.should == [event]
7
- end
8
-
9
- context "#record" do
10
- def parse_event_changes(event)
11
- event.attribute_changes.map { |c| [c.attribute_name, c.old_value, c.new_value] }
12
- end
13
-
14
- let(:transaction){ CIA::Transaction.new(:actor => User.create!) }
15
-
16
- it "records attribute creations" do
17
- source = Car.create!
18
- source.wheels = 4
19
- event = transaction.record(CIA::UpdateEvent, source)
20
-
21
- parse_event_changes(event).should == [["wheels", nil, "4"]]
22
- end
23
-
24
- it "records multiple attributes" do
25
- source = CarWith3Attributes.create!
26
- source.wheels = 4
27
- source.drivers = 2
28
- source.color = "red"
29
- event = transaction.record(CIA::UpdateEvent, source)
30
-
31
- parse_event_changes(event).should =~ [["wheels", nil, "4"], ["drivers", nil, "2"], ["color", nil, "red"]]
32
- end
33
-
34
- it "records attribute changes" do
35
- source = Car.create!(:wheels => 2)
36
- source.wheels = 4
37
- event = transaction.record(CIA::UpdateEvent, source)
38
- parse_event_changes(event).should == [["wheels", "2", "4"]]
39
- end
40
-
41
- it "records attribute deletions" do
42
- source = Car.create!(:wheels => 2)
43
- source.wheels = nil
44
- event = transaction.record(CIA::UpdateEvent, source)
45
- parse_event_changes(event).should == [["wheels", "2", nil]]
46
- end
47
-
48
- it "does not record unaudited attribute changes" do
49
- source = Car.create!
50
- source.drivers = 2
51
- event = nil
52
- expect{
53
- event = transaction.record(CIA::UpdateEvent, source)
54
- }.to_not change{ CIA::Event.count }
55
-
56
- event.should == nil
57
- end
58
-
59
- it "records audit_message as message even if there are no changes" do
60
- source = CarWithAMessage.create!
61
- source.audit_message = "Foo"
62
- event = transaction.record(CIA::UpdateEvent, source)
63
-
64
- event.message.should == "Foo"
65
- parse_event_changes(event).should == []
66
- end
67
-
68
- it "record non-updates even without changes" do
69
- source = Car.create!
70
- event = transaction.record(CIA::CreateEvent, source)
71
-
72
- parse_event_changes(event).should == []
73
- end
74
- end
75
- end