cia 0.5.7 → 0.5.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -11,7 +11,7 @@ module CIA
11
11
  validates_presence_of :event, :attribute_name
12
12
 
13
13
  if ActiveRecord::VERSION::MAJOR > 2
14
- scope :previous, :order => "id desc"
14
+ scope :previous, lambda { order("id desc") }
15
15
  else
16
16
  named_scope :previous, :order => "id desc"
17
17
  end
@@ -19,7 +19,8 @@ module CIA
19
19
  delegate :created_at, :to => :event
20
20
 
21
21
  def self.on_attribute(attribute)
22
- scoped(:conditions => {:attribute_name => attribute})
22
+ conditions = {:attribute_name => attribute}
23
+ ActiveRecord::VERSION::MAJOR == 2 ? scoped(:conditions => conditions) : where(conditions)
23
24
  end
24
25
 
25
26
  def self.max_value_size
@@ -10,7 +10,8 @@ module CIA
10
10
  validates_presence_of :action
11
11
 
12
12
  def self.previous
13
- scoped(:order => "created_at desc")
13
+ order = "created_at desc"
14
+ ActiveRecord::VERSION::MAJOR == 2 ? scoped(:order => order) : order(order)
14
15
  end
15
16
 
16
17
  def attribute_change_hash
@@ -1,3 +1,3 @@
1
1
  module CIA
2
- VERSION = '0.5.7'
2
+ VERSION = '0.5.8'
3
3
  end
metadata CHANGED
@@ -1,27 +1,126 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cia
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.7
4
+ version: 0.5.8
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Michael Grosser
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2013-12-05 00:00:00.000000000 Z
12
+ date: 2014-09-03 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: json
15
16
  requirement: !ruby/object:Gem::Requirement
17
+ none: false
16
18
  requirements:
17
- - - '>='
19
+ - - ! '>='
18
20
  - !ruby/object:Gem::Version
19
21
  version: '0'
20
22
  type: :runtime
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
23
26
  requirements:
24
- - - '>='
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: bump
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '2'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '2'
78
+ - !ruby/object:Gem::Dependency
79
+ name: wwtd
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: activerecord
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: sqlite3
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
25
124
  - !ruby/object:Gem::Version
26
125
  version: '0'
27
126
  description:
@@ -30,51 +129,42 @@ executables: []
30
129
  extensions: []
31
130
  extra_rdoc_files: []
32
131
  files:
33
- - .travis.yml
34
- - Appraisals
35
- - Gemfile
36
- - Gemfile.lock
37
- - MIGRATION.rb
38
- - Rakefile
39
- - Readme.md
40
- - cia.gemspec
41
- - gemfiles/rails2.gemfile
42
- - gemfiles/rails2.gemfile.lock
43
- - gemfiles/rails3.gemfile
44
- - gemfiles/rails3.gemfile.lock
45
132
  - lib/cia.rb
46
133
  - lib/cia/attribute_change.rb
47
134
  - lib/cia/auditable.rb
48
135
  - lib/cia/event.rb
49
136
  - lib/cia/source_validation.rb
50
137
  - lib/cia/version.rb
51
- - spec/cia/attribute_change_spec.rb
52
- - spec/cia/event_spec.rb
53
- - spec/cia_spec.rb
54
- - spec/spec_helper.rb
55
- homepage: http://github.com/grosser/cia
138
+ homepage: https://github.com/grosser/cia
56
139
  licenses:
57
140
  - MIT
58
- metadata: {}
59
141
  post_install_message:
60
142
  rdoc_options: []
61
143
  require_paths:
62
144
  - lib
63
145
  required_ruby_version: !ruby/object:Gem::Requirement
146
+ none: false
64
147
  requirements:
65
- - - '>='
148
+ - - ! '>='
66
149
  - !ruby/object:Gem::Version
67
150
  version: '0'
151
+ segments:
152
+ - 0
153
+ hash: 3646470647389073675
68
154
  required_rubygems_version: !ruby/object:Gem::Requirement
155
+ none: false
69
156
  requirements:
70
- - - '>='
157
+ - - ! '>='
71
158
  - !ruby/object:Gem::Version
72
159
  version: '0'
160
+ segments:
161
+ - 0
162
+ hash: 3646470647389073675
73
163
  requirements: []
74
164
  rubyforge_project:
75
- rubygems_version: 2.0.3
165
+ rubygems_version: 1.8.23
76
166
  signing_key:
77
- specification_version: 4
167
+ specification_version: 3
78
168
  summary: Audit model events like update/create/delete + attribute changes + group
79
169
  them by transaction, in normalized table layout for easy query access.
80
170
  test_files: []
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: 095757bdffb2cf6dd9966a37562bd4a61958fd20
4
- data.tar.gz: d01a2b7a29c0791224934dcf4a012a20cc60c512
5
- SHA512:
6
- metadata.gz: 377d5103d09854f5dac67ae4fcc9e84ee3bc94ec51519a19b5dfa97b3482bb15198dda28a543121eb5a44ff6a765b947c95f045982e080b90aa978c46a6d6a55
7
- data.tar.gz: 4d940a36f27b7108e609509ae6b76d8eba745b33f59cb634ac5b1ec0530a85d056ab4857a63e133b1fb5f0f4d884c096705a4c5b255844c1c5b6a743785b64d6
@@ -1,12 +0,0 @@
1
- bundler_args: ""
2
- rvm:
3
- - 1.9.3
4
- - 2.0.0
5
- gemfile:
6
- - gemfiles/rails2.gemfile
7
- - gemfiles/rails3.gemfile
8
- matrix:
9
- exclude:
10
- - rvm: 2.0.0
11
- gemfile: gemfiles/rails2.gemfile
12
- script: bundle exec rake spec
data/Appraisals DELETED
@@ -1,8 +0,0 @@
1
- appraise "rails2" do
2
- gem 'activerecord', "2.3.14"
3
- gem 'after_commit'
4
- end
5
-
6
- appraise "rails3" do
7
- gem 'activerecord', "3.2.3"
8
- end
data/Gemfile DELETED
@@ -1,10 +0,0 @@
1
- source "https://rubygems.org"
2
- gemspec
3
-
4
- gem 'bump'
5
- gem 'rake'
6
- gem 'rspec', '~>2'
7
- gem 'appraisal'
8
- gem 'wwtd'
9
- gem 'activerecord'
10
- gem 'sqlite3'
@@ -1,55 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- cia (0.5.7)
5
- json
6
-
7
- GEM
8
- remote: https://rubygems.org/
9
- specs:
10
- activemodel (3.2.11)
11
- activesupport (= 3.2.11)
12
- builder (~> 3.0.0)
13
- activerecord (3.2.11)
14
- activemodel (= 3.2.11)
15
- activesupport (= 3.2.11)
16
- arel (~> 3.0.2)
17
- tzinfo (~> 0.3.29)
18
- activesupport (3.2.11)
19
- i18n (~> 0.6)
20
- multi_json (~> 1.0)
21
- appraisal (0.5.1)
22
- bundler
23
- rake
24
- arel (3.0.2)
25
- builder (3.0.4)
26
- bump (0.3.9)
27
- diff-lcs (1.1.3)
28
- i18n (0.6.1)
29
- json (1.8.1)
30
- multi_json (1.5.0)
31
- rake (10.0.3)
32
- rspec (2.12.0)
33
- rspec-core (~> 2.12.0)
34
- rspec-expectations (~> 2.12.0)
35
- rspec-mocks (~> 2.12.0)
36
- rspec-core (2.12.2)
37
- rspec-expectations (2.12.1)
38
- diff-lcs (~> 1.1.3)
39
- rspec-mocks (2.12.1)
40
- sqlite3 (1.3.6)
41
- tzinfo (0.3.35)
42
- wwtd (0.4.4)
43
-
44
- PLATFORMS
45
- ruby
46
-
47
- DEPENDENCIES
48
- activerecord
49
- appraisal
50
- bump
51
- cia!
52
- rake
53
- rspec (~> 2)
54
- sqlite3
55
- wwtd
@@ -1,20 +0,0 @@
1
- create_table :cia_events do |t|
2
- t.integer :actor_id
3
- t.string :actor_type
4
- t.string :source_type, :action, :null => false
5
- t.integer :source_id, :null => false
6
- t.string :ip_address
7
- t.string :message
8
- t.string :source_display_name
9
- t.timestamp :created_at#, :null => false
10
- end
11
-
12
- create_table :cia_attribute_changes do |t|
13
- t.integer :cia_event_id, :source_id, :null => false
14
- t.string :attribute_name, :source_type, :null => false
15
- t.string :old_value, :new_value
16
- end
17
-
18
- # DOWN
19
- # drop_table :cia_events
20
- # drop_table :cia_attribute_changes
data/Rakefile DELETED
@@ -1,11 +0,0 @@
1
- require 'bundler/setup'
2
- require 'appraisal'
3
- require 'bundler/gem_tasks'
4
- require 'bump/tasks'
5
- require 'wwtd/tasks'
6
-
7
- task :default => ["appraisal:gemfiles", :wwtd]
8
-
9
- task :spec do
10
- sh "rspec spec/"
11
- end
data/Readme.md DELETED
@@ -1,106 +0,0 @@
1
- Central Internal Auditing
2
- ============================
3
-
4
- Audit model actions like update/create/destroy/<custom> + attribute changes.
5
-
6
- - normalized and queryable through table layout
7
- - actors and subjects are polymorphic
8
- - works on ActiveRecord 2 and 3
9
-
10
- Table layout:
11
-
12
- Event (actor/ip/time/updated subject + message)
13
- -> has many attribute changes (changed password from foo to bar on subject)
14
-
15
-
16
- Install
17
- =======
18
- gem install cia
19
- Or
20
-
21
- rails plugin install git://github.com/grosser/cia.git
22
-
23
- `rails g migration add_cia` + paste [Migration](https://raw.github.com/grosser/cia/master/MIGRATION.rb)
24
-
25
-
26
- Usage
27
- =====
28
-
29
- ```Ruby
30
- class User < ActiveRecord::Base
31
- include CIA::Auditable
32
- audited_attributes :email, :crypted_password
33
- end
34
-
35
- class ApplicationController < ActionController::Base
36
- around_filter :scope_auditing
37
-
38
- def scope_auditing
39
- CIA.audit :actor => current_user, :ip_address => request.remote_ip do
40
- yield
41
- end
42
- end
43
- end
44
-
45
- # quick access
46
- User.last.cia_events
47
- changes = User.last.cia_attribute_changes
48
- last_passwords = changes.where(:attribute_name => "crypted_password").map(&:new_value)
49
-
50
- # exceptions (raised by default)
51
- CIA.exception_handler = lambda{|e| raise e unless Rails.env.production? }
52
-
53
- # conditional auditing
54
- class User < ActiveRecord::Base
55
- audited_attributes :email, :if => :interesting?
56
-
57
- def interesting?
58
- ...
59
- end
60
- end
61
-
62
- # adding an actor e.g. for user creation
63
- CIA.current_actor = @user
64
-
65
- # custom changes
66
- class User < ActiveRecord::Base
67
- def cia_changes
68
- changes.merge("this" => ["always", "changes"])
69
- end
70
- end
71
-
72
- # using after_commit, useful if the CIA::Event is stored in a different database then the audited class
73
- class User < ActiveRecord::Base
74
- include CIA::Auditable
75
- audited_attributes :email, :crypted_password, :callback => :after_commit
76
- end
77
-
78
- # passing arbitrary attributes into the .audit method
79
- CIA.non_recordable_attributes = [:my_pretty_audit_property]
80
- CIA.audit(:actor => current_user, :my_pretty_audit_property => "12345") do
81
- ...
82
- end
83
-
84
- # storing complex objects in old/new and reducing it's size if it's to big (serialized via json)
85
- value = CIA::AttributeChange.serialize_for_storage(["some", "complex"*1000, "object"]){|too_big| too_big.delete_at(1); too_big }
86
- CIA::AttributeChange.create!(:old_value => value)
87
-
88
- # add something to current transaction or start a new audit
89
- CIA.audit :bar => :baz, :foo => :bang do
90
- CIA.amend_audit :foo => :bar do
91
- puts CIA.current_transaction
92
- end
93
- end
94
- -> {:foo => :bar, :bar => :baz}
95
- ```
96
-
97
-
98
- # TODO
99
- - reuse AR3+ previous_changes in a nice way
100
-
101
- Author
102
- ======
103
- [Michael Grosser](http://grosser.it)<br/>
104
- michael@grosser.it<br/>
105
- License: MIT<br/>
106
- [![Build Status](https://secure.travis-ci.org/grosser/cia.png)](http://travis-ci.org/grosser/cia)
@@ -1,13 +0,0 @@
1
- $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
2
- name = "cia"
3
- require "#{name}/version"
4
-
5
- Gem::Specification.new name, CIA::VERSION do |s|
6
- s.summary = "Audit model events like update/create/delete + attribute changes + group them by transaction, in normalized table layout for easy query access."
7
- s.authors = ["Michael Grosser"]
8
- s.email = "michael@grosser.it"
9
- s.homepage = "http://github.com/grosser/#{name}"
10
- s.files = `git ls-files`.split("\n")
11
- s.license = 'MIT'
12
- s.add_runtime_dependency "json"
13
- end
@@ -1,14 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "bump"
6
- gem "rake"
7
- gem "rspec", "~>2"
8
- gem "appraisal"
9
- gem "wwtd"
10
- gem "sqlite3"
11
- gem "activerecord", "2.3.14"
12
- gem "after_commit"
13
-
14
- gemspec :path=>"../"
@@ -1,45 +0,0 @@
1
- PATH
2
- remote: ../
3
- specs:
4
- cia (0.5.6)
5
- json
6
-
7
- GEM
8
- remote: https://rubygems.org/
9
- specs:
10
- activerecord (2.3.14)
11
- activesupport (= 2.3.14)
12
- activesupport (2.3.14)
13
- after_commit (1.0.10)
14
- activerecord (>= 1.15.6, < 3.0.0)
15
- appraisal (0.4.1)
16
- bundler
17
- rake
18
- bump (0.3.9)
19
- diff-lcs (1.1.3)
20
- json (1.8.1)
21
- rake (0.9.2.2)
22
- rspec (2.10.0)
23
- rspec-core (~> 2.10.0)
24
- rspec-expectations (~> 2.10.0)
25
- rspec-mocks (~> 2.10.0)
26
- rspec-core (2.10.1)
27
- rspec-expectations (2.10.0)
28
- diff-lcs (~> 1.1.3)
29
- rspec-mocks (2.10.1)
30
- sqlite3 (1.3.6)
31
- wwtd (0.4.4)
32
-
33
- PLATFORMS
34
- ruby
35
-
36
- DEPENDENCIES
37
- activerecord (= 2.3.14)
38
- after_commit
39
- appraisal
40
- bump
41
- cia!
42
- rake
43
- rspec (~> 2)
44
- sqlite3
45
- wwtd
@@ -1,13 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "bump"
6
- gem "rake"
7
- gem "rspec", "~>2"
8
- gem "appraisal"
9
- gem "wwtd"
10
- gem "sqlite3"
11
- gem "activerecord", "3.2.3"
12
-
13
- gemspec :path=>"../"
@@ -1,55 +0,0 @@
1
- PATH
2
- remote: ../
3
- specs:
4
- cia (0.5.6)
5
- json
6
-
7
- GEM
8
- remote: https://rubygems.org/
9
- specs:
10
- activemodel (3.2.3)
11
- activesupport (= 3.2.3)
12
- builder (~> 3.0.0)
13
- activerecord (3.2.3)
14
- activemodel (= 3.2.3)
15
- activesupport (= 3.2.3)
16
- arel (~> 3.0.2)
17
- tzinfo (~> 0.3.29)
18
- activesupport (3.2.3)
19
- i18n (~> 0.6)
20
- multi_json (~> 1.0)
21
- appraisal (0.5.1)
22
- bundler
23
- rake
24
- arel (3.0.2)
25
- builder (3.0.4)
26
- bump (0.3.9)
27
- diff-lcs (1.1.3)
28
- i18n (0.6.1)
29
- json (1.8.1)
30
- multi_json (1.5.0)
31
- rake (10.0.3)
32
- rspec (2.12.0)
33
- rspec-core (~> 2.12.0)
34
- rspec-expectations (~> 2.12.0)
35
- rspec-mocks (~> 2.12.0)
36
- rspec-core (2.12.2)
37
- rspec-expectations (2.12.1)
38
- diff-lcs (~> 1.1.3)
39
- rspec-mocks (2.12.1)
40
- sqlite3 (1.3.6)
41
- tzinfo (0.3.35)
42
- wwtd (0.4.4)
43
-
44
- PLATFORMS
45
- ruby
46
-
47
- DEPENDENCIES
48
- activerecord (= 3.2.3)
49
- appraisal
50
- bump
51
- cia!
52
- rake
53
- rspec (~> 2)
54
- sqlite3
55
- wwtd
@@ -1,100 +0,0 @@
1
- # encoding: utf-8
2
- require 'spec_helper'
3
-
4
- describe CIA::AttributeChange do
5
- it "stores times as db format" do
6
- t = Time.now
7
- create_change(:old_value => t).reload.old_value.sub(/\.\d+$/,'').should == t.to_s(:db)
8
- end
9
-
10
- it "stores dates as db format" do
11
- create_change(:old_value => Date.new(2012)).reload.old_value.should == "2012-01-01"
12
- end
13
-
14
- it "stores booleans as db format" do
15
- create_change(:old_value => false).reload.old_value.should == "f"
16
- create_change(:old_value => true).reload.old_value.should == "t"
17
- end
18
-
19
- it "stores nil as nil" do
20
- create_change(:old_value => nil).reload.old_value.should == nil
21
- end
22
-
23
- it "delegates create_at to event" do
24
- t = Time.now
25
- event = CIA::Event.new(:created_at => t)
26
- change = CIA::AttributeChange.new(:event => event)
27
- change.created_at.should == event.created_at
28
- end
29
-
30
- describe ".previous" do
31
- it "finds by id desc" do
32
- CIA::AttributeChange.delete_all
33
- a = create_change
34
- b = create_change
35
- CIA::AttributeChange.previous.should == [b,a]
36
- end
37
- end
38
-
39
- describe ".on_attribute" do
40
- it "finds with attribute" do
41
- a = create_change :attribute_name => :xxx
42
- b = create_change :attribute_name => :yyy
43
- CIA::AttributeChange.on_attribute(:xxx).all.should == [a]
44
- end
45
- end
46
-
47
- describe "enforcing presence of source" do
48
- it "requires a source when associated event requires a source" do
49
- event = CIA::Event.new { |event| event.id = 1 }
50
- event.stub(:source_must_be_present? => true)
51
- change = CIA::AttributeChange.new(:event => event, :attribute_name => 'awesomeness')
52
-
53
- change.valid?.should be_false
54
- change.errors.full_messages.should =~ ["Source can't be blank"]
55
- end
56
-
57
- it "does not require a source when associated event does not" do
58
- event = CIA::Event.new { |event| event.id = 1 }
59
- event.stub(:source_must_be_present? => false)
60
- change = CIA::AttributeChange.new(:event => event, :attribute_name => 'awesomeness',
61
- :source_type => 'ObscureType', :source_id => 101)
62
-
63
- change.valid?.should be_true
64
- end
65
- end
66
-
67
- describe ".max_value_size" do
68
- it "is the width of the old/new column" do
69
- CIA::AttributeChange.max_value_size.should == 255
70
- end
71
- end
72
-
73
- describe ".serialize_for_storage" do
74
- it "stores as json" do
75
- CIA::AttributeChange.serialize_for_storage([["xxx"]]){}.should == '[["xxx"]]'
76
- end
77
-
78
- it "calls the block to remove an item" do
79
- CIA::AttributeChange.serialize_for_storage([["xxx"], ["x"*300], ["yyy"]]){ |array| array.delete_at(1); array }.should == '[["xxx"],["yyy"]]'
80
- end
81
-
82
- it "blows up if block fails to reduce size to prevent loops" do
83
- expect{
84
- CIA::AttributeChange.serialize_for_storage([["xxx"], ["x"*300], ["yyy"]]){ |array| array }
85
- }.to raise_error
86
- end
87
-
88
- it "takes multibyte into account" do
89
- called = false
90
- CIA::AttributeChange.serialize_for_storage(["å" * 200]){ |array| called = true; "x" }
91
- called.should == true
92
- end
93
-
94
- it "does not go crazy on multibytes" do
95
- called = false
96
- CIA::AttributeChange.serialize_for_storage(["å" * 100]){ |array| called = true; "x" }
97
- called.should == false
98
- end
99
- end
100
- end
@@ -1,57 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe CIA::Event do
4
- it "has many attribute_changes" do
5
- change = create_change
6
- change.event.attribute_changes.should == [change]
7
- change.event.destroy
8
- expect{ change.reload }.to raise_error(ActiveRecord::RecordNotFound)
9
- end
10
-
11
- context "attribute_change_hash" do
12
- it "is empty for empty changes" do
13
- create_event.attribute_change_hash.should == {}
14
- end
15
-
16
- it "contains all changes" do
17
- change = create_change(:old_value => "a", :new_value => "b")
18
- change = create_change(:attribute_name => "foo", :old_value => "b", :new_value => nil, :event => change.event)
19
- change.event.attribute_change_hash.should == {"bar" => ["a", "b"], "foo" => ["b", nil]}
20
- end
21
- end
22
-
23
- context ".previous" do
24
- it "is sorted id desc" do
25
- events = [create_event(:created_at => 3.days.ago), create_event(:created_at => 2.days.ago), create_event(:created_at => 1.day.ago)].map(&:id)
26
- CIA::Event.previous.map(&:id).should == events.reverse
27
- end
28
- end
29
-
30
- context "validations" do
31
- let(:source_attributes){ {:source => nil, :source_id => 99999, :source_type => "Car"} }
32
-
33
- it "validates source" do
34
- expect{
35
- create_event(source_attributes)
36
- }.to raise_error
37
- end
38
-
39
- it "does not validates source when action is destroy" do
40
- create_event(source_attributes.merge(:action => "destroy"))
41
- end
42
-
43
- it "does not validates source when updating" do
44
- create_event.update_attributes!(:source_id => 9999)
45
- end
46
-
47
- it "does not validates source when source_display_name is present" do
48
- create_event(:source => nil, :source_id => -111, :source_type => 'FakeTypeHere', :source_display_name => 'abc')
49
- end
50
-
51
- it "validates source when source_display_name is blank" do
52
- expect{
53
- create_event(:source => nil, :source_id => -111, :source_type => 'FakeTypeHere', :source_display_name => '')
54
- }.to raise_error
55
- end
56
- end
57
- end
@@ -1,439 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe CIA do
4
- it "has a VERSION" do
5
- CIA::VERSION.should =~ /^[\.\da-z]+$/
6
- end
7
-
8
- describe ".audit" do
9
- it "has no transaction when it starts" do
10
- CIA.current_transaction.should == nil
11
- end
12
-
13
- it "starts a new transaction" do
14
- result = 1
15
- CIA.audit({:a => 1}) do
16
- result = CIA.current_transaction
17
- end
18
- result.should == {:a => 1}
19
- end
20
-
21
- it "stops the transaction after the block" do
22
- CIA.audit({}){}
23
- CIA.current_transaction.should == nil
24
- end
25
-
26
- it "returns the block content" do
27
- CIA.audit({}){ 1 }.should == 1
28
- end
29
-
30
- it "is threadsafe" do
31
- Thread.new do
32
- CIA.audit({}) do
33
- sleep 0.04
34
- end
35
- end
36
- sleep 0.01
37
- CIA.current_transaction.should == nil
38
- sleep 0.04 # so next tests dont fail
39
- end
40
-
41
- it "can stack" do
42
- states = []
43
- CIA.audit(:a => 1) do
44
- states << CIA.current_transaction
45
- CIA.audit(:b => 1) do
46
- states << CIA.current_transaction
47
- end
48
- states << CIA.current_transaction
49
- end
50
- states << CIA.current_transaction
51
- states.should == [{:a => 1}, {:b => 1}, {:a => 1}, nil]
52
- end
53
- end
54
-
55
- describe ".amend_audit" do
56
- it "opens a new transaction when none exists" do
57
- t = nil
58
- CIA.amend_audit(:actor => 111){ t = CIA.current_transaction }
59
- t.should == {:actor => 111}
60
- end
61
-
62
- it "amends a running transaction" do
63
- t = nil
64
- CIA.amend_audit(:actor => 222, :ip_address => 123) do
65
- CIA.amend_audit(:actor => 111) { t = CIA.current_transaction }
66
- end
67
- t.should == {:actor => 111, :ip_address => 123}
68
- end
69
-
70
- it "returns to old state after transaction" do
71
- CIA.amend_audit(:actor => 222, :ip_address => 123) do
72
- CIA.amend_audit(:actor => 111) { }
73
- end
74
- CIA.current_transaction.should == nil
75
-
76
- CIA.amend_audit(:actor => 111) { }
77
- CIA.current_transaction.should == nil
78
- end
79
- end
80
-
81
- describe ".record" do
82
- let(:object) { Car.new }
83
-
84
- around do |example|
85
- CIA.audit :actor => User.create! do
86
- example.call
87
- end
88
- end
89
-
90
- it "tracks create" do
91
- expect{
92
- object.save!
93
- }.to change{ CIA::Event.count }.by(+1)
94
- CIA::Event.last.action.should == "create"
95
- end
96
-
97
- it "tracks delete" do
98
- object.save!
99
- expect{
100
- object.destroy
101
- }.to change{ CIA::Event.count }.by(+1)
102
- CIA::Event.last.action.should == "destroy"
103
- end
104
-
105
- it "tracks update" do
106
- object.save!
107
- expect{
108
- object.update_attributes(:wheels => 3)
109
- }.to change{ CIA::Event.count }.by(+1)
110
- CIA::Event.last.action.should == "update"
111
- end
112
-
113
- it "does not track failed changes" do
114
- car = Car.create!(:wheels => 1).id
115
- expect{
116
- expect{ FailCar.new(:wheels => 4).save }.to raise_error(FailCar::Oops)
117
- car = FailCar.find(car)
118
- expect{ car.update_attributes(:wheels => 2) }.to raise_error(FailCar::Oops)
119
- expect{ car.destroy }.to raise_error(FailCar::Oops)
120
- }.to_not change{ CIA::Event.count }
121
- end
122
-
123
- it "is rolled back if auditing fails" do
124
- CIA.should_receive(:record).and_raise("XXX")
125
- expect{
126
- expect{
127
- CIA.audit{ object.save! }
128
- }.to raise_error("XXX")
129
- }.to_not change{ object.class.count }
130
- end
131
-
132
- it "is ok with non-attribute methods passed into .audit if they are set as non-recordable" do
133
- CIA.non_recordable_attributes = [:foo]
134
- expect {
135
- CIA.audit(:actor => User.create!, :foo => 'bar') {
136
- object.save!
137
- }
138
- }.to change{ CIA::Event.count }.by(+1)
139
- end
140
-
141
- context "nested classes with multiple audited_attributes" do
142
- let(:object){ NestedCar.new }
143
-
144
- it "has the exclusive sub-classes attributes of the nested class" do
145
- object.class.audited_attributes.should == ["drivers"]
146
- end
147
-
148
- it "does not record twice for nested classes" do
149
- expect{
150
- CIA.audit{ object.save! }
151
- }.to change{ CIA::Event.count }.by(+1)
152
- end
153
-
154
- it "does not record twice for super classes" do
155
- expect{
156
- CIA.audit{ Car.new.save! }
157
- }.to change{ CIA::Event.count }.by(+1)
158
- end
159
- end
160
-
161
- context "nested classes with 1 audited_attributes" do
162
- let(:object){ InheritedCar.new }
163
-
164
- it "has the super-classes attributes" do
165
- object.class.audited_attributes.should == ["wheels"]
166
- end
167
-
168
- it "does not record twice for nested classes" do
169
- expect{
170
- CIA.audit{ object.save! }
171
- }.to change{ CIA::Event.count }.by(+1)
172
- end
173
-
174
- it "does not record twice for super classes" do
175
- expect{
176
- CIA.audit{ Car.new.save! }
177
- }.to change{ CIA::Event.count }.by(+1)
178
- end
179
- end
180
-
181
- context "custom changes" do
182
- let(:object) { CarWithCustomChanges.new }
183
-
184
- it "tracks custom changes" do
185
- object.save!
186
- expect{
187
- object.update_attributes(:wheels => 3)
188
- }.to change{ CIA::Event.count }.by(+1)
189
- CIA::Event.last.action.should == "update"
190
- CIA::Event.last.attribute_change_hash.should == {
191
- "wheels" => [nil, "3"],
192
- "foo" => ["bar", "baz"]
193
- }
194
- end
195
- end
196
-
197
- context ":if" do
198
- let(:object) { CarWithIf.new }
199
-
200
- it "tracks if :if is true" do
201
- expect{
202
- object.tested = true
203
- object.save!
204
- }.to change{ CIA::Event.count }.by(+1)
205
- CIA::Event.last.action.should == "create"
206
- end
207
-
208
- it "does not track if :if is false" do
209
- expect{
210
- object.save!
211
- }.to_not change{ CIA::Event.count }
212
- CIA::Event.last.should == nil
213
- end
214
- end
215
-
216
- context ":unless" do
217
- let(:object) { CarWithUnless.new }
218
-
219
- it "tracks if :unless is false" do
220
- expect{
221
- object.save!
222
- }.to change{ CIA::Event.count }.by(+1)
223
- CIA::Event.last.action.should == "create"
224
- end
225
-
226
- it "does not track if :unless is true" do
227
- expect{
228
- object.tested = true
229
- object.save!
230
- }.to_not change{ CIA::Event.count }
231
- CIA::Event.last.should == nil
232
- end
233
- end
234
-
235
- context "events" do
236
- def parse_event_changes(event)
237
- event.attribute_changes.map { |c| [c.attribute_name, c.old_value, c.new_value] }
238
- end
239
-
240
- def no_audit_created!
241
- event = nil
242
- expect{
243
- event = yield
244
- }.to_not change{ CIA::Event.count }
245
-
246
- event.should == nil
247
- end
248
-
249
- it "records attributes in transaction" do
250
- event = nil
251
- CIA.audit :actor => User.create!, :ip_address => "1.2.3.4" do
252
- event = CIA.record(:destroy, Car.create!)
253
- end
254
- event.ip_address.should == "1.2.3.4"
255
- end
256
-
257
- it "records attribute creations" do
258
- source = Car.create!
259
- source.wheels = 4
260
- event = CIA.record(:update, source).reload
261
-
262
- parse_event_changes(event).should == [["wheels", nil, "4"]]
263
- end
264
-
265
- it "can act on attributes in before_save" do
266
- x = nil
267
- CIA.current_transaction[:hacked_before_save_action] = lambda{|event| x = event.attribute_changes.size }
268
- source = Car.create!
269
- source.wheels = 4
270
- CIA.record(:update, source)
271
- x.should == 1
272
- end
273
-
274
- it "records multiple attributes" do
275
- source = CarWith3Attributes.create!
276
- source.wheels = 4
277
- source.drivers = 2
278
- source.color = "red"
279
- event = CIA.record(:update, source).reload
280
- parse_event_changes(event).should =~ [["wheels", nil, "4"], ["drivers", nil, "2"], ["color", nil, "red"]]
281
- end
282
-
283
- it "records attribute changes" do
284
- source = Car.create!(:wheels => 2)
285
- source.wheels = 4
286
- event = CIA.record(:update, source).reload
287
- parse_event_changes(event).should == [["wheels", "2", "4"]]
288
- end
289
-
290
- it "records attribute deletions" do
291
- source = Car.create!(:wheels => 2)
292
- source.wheels = nil
293
- event = CIA.record(:update, source).reload
294
- parse_event_changes(event).should == [["wheels", "2", nil]]
295
- end
296
-
297
- it "does not record unaudited attribute changes" do
298
- source = Car.create!
299
- source.drivers = 2
300
- no_audit_created!{ CIA.record(:update, source) }
301
- end
302
-
303
- it "records audit_message as message even if there are no changes" do
304
- source = CarWithAMessage.create!
305
- source.audit_message = "Foo"
306
- event = CIA.record(:update, source)
307
-
308
- event.message.should == "Foo"
309
- parse_event_changes(event).should == []
310
- end
311
-
312
- it "does not record after saving with an audit_message" do
313
- source = CarWithAMessage.create!
314
- source.audit_message = "Foo"
315
- CIA.record(:update, source)
316
-
317
- no_audit_created!{ CIA.record(:update, source) }
318
- end
319
-
320
- it "does not record if it's empty and there are no changes" do
321
- source = CarWithAMessage.create!
322
- source.audit_message = " "
323
- no_audit_created!{ CIA.record(:update, source) }
324
- end
325
-
326
- it "record non-updates even without changes" do
327
- source = Car.create!
328
- event = CIA.record(:create, source)
329
- parse_event_changes(event).should == []
330
- end
331
- end
332
-
333
- context "exception_handler" do
334
- before do
335
- $stderr.stub(:puts)
336
- CIA.stub(:current_transaction).and_raise(StandardError.new("foo"))
337
- end
338
-
339
- def capture_exception
340
- begin
341
- old = CIA.exception_handler
342
- ex = nil
343
- CIA.exception_handler = lambda{|e| ex = e }
344
- yield
345
- ex
346
- ensure
347
- CIA.exception_handler = old
348
- end
349
- end
350
-
351
- it "raises exceptions by the transaction" do
352
- ex = nil
353
- begin
354
- object.save!
355
- rescue Object => e
356
- ex = e
357
- end
358
- ex.inspect.should == '#<StandardError: foo>'
359
- end
360
-
361
- it "can capture exception via handler" do
362
- ex = capture_exception do
363
- object.save!
364
- end
365
- ex.inspect.should == '#<StandardError: foo>'
366
- end
367
- end
368
-
369
- context "with after_commit" do
370
- let(:object){ CarWithTransactions.new(:wheels => 1) }
371
-
372
- it "still tracks" do
373
- expect{
374
- CIA.audit{ object.save! }
375
- }.to change{ CIA::Event.count }.by(+1)
376
- CIA::Event.last.attribute_change_hash.should == {"wheels" => [nil, "1"]}
377
- end
378
-
379
- it "unsets temp-changes after the save" do
380
- object.save!
381
-
382
- # does not re-track old changes
383
- expect{
384
- CIA.audit{ object.update_attributes(:drivers => 2) }
385
- }.to change{ CIA::Event.count }.by(+1)
386
- CIA::Event.last.attribute_change_hash.should == {"drivers" => [nil, "2"]}
387
-
388
- # empty changes
389
- expect{
390
- CIA.audit{ object.update_attributes(:drivers => 2) }
391
- }.to_not change{ CIA::Event.count }
392
- end
393
-
394
- it "is not rolled back if auditing fails" do
395
- CIA.should_receive(:record).and_raise("XXX")
396
- begin
397
- expect{
398
- CIA.audit{ object.save! }
399
- }.to change{ object.class.count }.by(+1)
400
- rescue RuntimeError => e
401
- # errors from after_commit are never raised in rails 3+
402
- raise e if ActiveRecord::VERSION::MAJOR != 2 || e.message != "XXX"
403
- end
404
- end
405
- end
406
- end
407
-
408
- context ".current_actor" do
409
- it "is nil when nothing is set" do
410
- CIA.current_actor.should == nil
411
- end
412
-
413
- it "is nil when no actor is set" do
414
- CIA.audit do
415
- CIA.current_actor.should == nil
416
- end
417
- end
418
-
419
- it "is the current :actor" do
420
- CIA.audit :actor => 111 do
421
- CIA.current_actor.should == 111
422
- end
423
- end
424
- end
425
-
426
- context ".current_actor=" do
427
- it "does nothing if no transaction is running" do
428
- CIA.current_actor = 111
429
- CIA.current_transaction.should == nil
430
- end
431
-
432
- it "sets when transaction is started" do
433
- CIA.audit :actor => 222 do
434
- CIA.current_actor = 111
435
- CIA.current_transaction.should == {:actor => 111}
436
- end
437
- end
438
- end
439
- end
@@ -1,120 +0,0 @@
1
- require 'cia'
2
- require 'after_commit' if ActiveRecord::VERSION::MAJOR == 2
3
-
4
- RSpec.configure do |config|
5
- config.before do
6
- CIA::Event.delete_all
7
- CIA::AttributeChange.delete_all
8
- CIA.non_recordable_attributes = nil
9
- end
10
- end
11
-
12
- ActiveRecord::Base.establish_connection(
13
- :adapter => "sqlite3",
14
- :database => ":memory:"
15
- )
16
-
17
- ActiveRecord::Schema.verbose = false
18
- ActiveRecord::Schema.define(:version => 1) do
19
- eval(File.read(File.expand_path('../../MIGRATION.rb', __FILE__)))
20
-
21
- create_table :cars do |t|
22
- t.integer :wheels
23
- t.integer :drivers
24
- t.string :color
25
- end
26
-
27
- create_table :users do |t|
28
- t.string :email
29
- end
30
- end
31
-
32
- class User < ActiveRecord::Base
33
- end
34
-
35
- class Car < ActiveRecord::Base
36
- include CIA::Auditable
37
- audit_attribute :wheels
38
- end
39
-
40
- class CarWithAMessage < ActiveRecord::Base
41
- self.table_name = "cars"
42
- include CIA::Auditable
43
- audit_attribute :wheels
44
- attr_accessor :audit_message
45
- end
46
-
47
- class CarWith3Attributes < ActiveRecord::Base
48
- self.table_name = "cars"
49
- include CIA::Auditable
50
- audit_attribute :wheels, :color, :drivers
51
- end
52
-
53
- class CarWithIf < ActiveRecord::Base
54
- self.table_name = "cars"
55
- include CIA::Auditable
56
- audit_attribute :wheels, :if => :tested
57
- attr_accessor :tested
58
- end
59
-
60
- class CarWithUnless < ActiveRecord::Base
61
- self.table_name = "cars"
62
- include CIA::Auditable
63
- audit_attribute :wheels, :unless => :tested
64
- attr_accessor :tested
65
- end
66
-
67
- class CarWithCustomChanges < ActiveRecord::Base
68
- self.table_name = "cars"
69
- include CIA::Auditable
70
- audit_attribute :wheels, :foo
71
-
72
- def cia_changes
73
- changes.merge("foo" => ["bar", "baz"])
74
- end
75
- end
76
-
77
- class FailCar < ActiveRecord::Base
78
- self.table_name = "cars"
79
- include CIA::Auditable
80
- audit_attribute :wheels
81
-
82
- class Oops < Exception
83
- end
84
-
85
- after_update { |x| raise Oops }
86
- after_create { |x| raise Oops }
87
- after_destroy { |x| raise Oops }
88
- end
89
-
90
- class CarWithTransactions < ActiveRecord::Base
91
- self.table_name = "cars"
92
- include CIA::Auditable
93
- audit_attribute :wheels, :drivers, :callback => :after_commit
94
- end
95
-
96
- class NestedCar < Car
97
- audit_attribute :drivers
98
- end
99
-
100
- class InheritedCar < Car
101
- end
102
-
103
- def create_event(options={})
104
- CIA::Event.create!({:source => Car.create!, :actor => User.create!, :action => "update"}.merge(options))
105
- end
106
-
107
- def create_change(options={})
108
- event = options.delete(:event) || create_event
109
- CIA::AttributeChange.create!({:event => event, :source => event.source, :attribute_name => "bar"}.merge(options))
110
- end
111
-
112
- # simulate a hacked cia event
113
- CIA::Event.class_eval do
114
- before_save :hacked_before_save
115
- attr_accessor :hacked_before_save_action
116
-
117
- def hacked_before_save
118
- hacked_before_save_action.call(self) if hacked_before_save_action
119
- end
120
- end