cia 0.5.7 → 0.5.8

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.
@@ -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