notifiably_audited 0.0.1 → 0.0.2
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.
- checksums.yaml +8 -8
- data/Appraisals +11 -0
- data/CHANGELOG +34 -0
- data/LICENSE +19 -0
- data/audited-activerecord.gemspec +21 -0
- data/audited-mongo_mapper.gemspec +21 -0
- data/audited.gemspec +26 -0
- data/gemfiles/rails30.gemfile +7 -0
- data/gemfiles/rails31.gemfile +7 -0
- data/gemfiles/rails32.gemfile +7 -0
- data/lib/audited.rb +15 -0
- data/lib/audited/audit.rb +102 -0
- data/lib/audited/auditor.rb +270 -0
- data/lib/audited/rspec_matchers.rb +173 -0
- data/lib/audited/sweeper.rb +51 -0
- data/notifiably_audited.gemspec +11 -18
- data/spec/audited_spec_helpers.rb +31 -0
- data/spec/rails_app/config/application.rb +5 -0
- data/spec/rails_app/config/database.yml +24 -0
- data/spec/rails_app/config/environment.rb +5 -0
- data/spec/rails_app/config/environments/development.rb +19 -0
- data/spec/rails_app/config/environments/production.rb +33 -0
- data/spec/rails_app/config/environments/test.rb +33 -0
- data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails_app/config/initializers/inflections.rb +2 -0
- data/spec/rails_app/config/initializers/secret_token.rb +2 -0
- data/spec/rails_app/config/routes.rb +6 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/active_record/models.rb +84 -0
- data/spec/support/active_record/schema.rb +54 -0
- data/spec/support/mongo_mapper/connection.rb +4 -0
- data/spec/support/mongo_mapper/models.rb +210 -0
- data/test/db/version_1.rb +17 -0
- data/test/db/version_2.rb +18 -0
- data/test/db/version_3.rb +19 -0
- data/test/db/version_4.rb +20 -0
- data/test/db/version_5.rb +18 -0
- data/test/install_generator_test.rb +17 -0
- data/test/test_helper.rb +19 -0
- data/test/upgrade_generator_test.rb +65 -0
- metadata +56 -2
@@ -0,0 +1,173 @@
|
|
1
|
+
module Audited
|
2
|
+
module RspecMatchers
|
3
|
+
# Ensure that the model is audited.
|
4
|
+
#
|
5
|
+
# Options:
|
6
|
+
# * <tt>associated_with</tt> - tests that the audit makes use of the associated_with option
|
7
|
+
# * <tt>only</tt> - tests that the audit makes use of the only option *Overrides <tt>except</tt> option*
|
8
|
+
# * <tt>except</tt> - tests that the audit makes use of the except option
|
9
|
+
# * <tt>requires_comment</tt> - if specified, then the audit must require comments through the <tt>audit_comment</tt> attribute
|
10
|
+
# * <tt>on</tt> - tests that the audit makes use of the on option with specified parameters
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
# it { should be_audited }
|
14
|
+
# it { should be_audited.associated_with(:user) }
|
15
|
+
# it { should be_audited.only(:field_name) }
|
16
|
+
# it { should be_audited.except(:password) }
|
17
|
+
# it { should be_audited.requires_comment }
|
18
|
+
# it { should be_audited.on(:create).associated_with(:user).except(:password) }
|
19
|
+
#
|
20
|
+
def be_audited
|
21
|
+
AuditMatcher.new
|
22
|
+
end
|
23
|
+
|
24
|
+
# Ensure that the model has associated audits
|
25
|
+
#
|
26
|
+
# Example:
|
27
|
+
# it { should have_associated_audits }
|
28
|
+
#
|
29
|
+
def have_associated_audits
|
30
|
+
AssociatedAuditMatcher.new
|
31
|
+
end
|
32
|
+
|
33
|
+
class AuditMatcher # :nodoc:
|
34
|
+
def initialize
|
35
|
+
@options = {}
|
36
|
+
end
|
37
|
+
|
38
|
+
def associated_with(model)
|
39
|
+
@options[:associated_with] = model
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
def only(*fields)
|
44
|
+
@options[:only] = fields.flatten
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def except(*fields)
|
49
|
+
@options[:except] = fields.flatten
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
def requires_comment
|
54
|
+
@options[:comment_required] = true
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
def on(*actions)
|
59
|
+
@options[:on] = actions.flatten
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
def matches?(subject)
|
64
|
+
@subject = subject
|
65
|
+
auditing_enabled? &&
|
66
|
+
associated_with_model? &&
|
67
|
+
records_changes_to_specified_fields? &&
|
68
|
+
comment_required_valid?
|
69
|
+
end
|
70
|
+
|
71
|
+
def failure_message
|
72
|
+
"Expected #{@expectation}"
|
73
|
+
end
|
74
|
+
|
75
|
+
def negative_failure_message
|
76
|
+
"Did not expect #{@expectation}"
|
77
|
+
end
|
78
|
+
|
79
|
+
def description
|
80
|
+
description = "audited"
|
81
|
+
description += " associated with #{@options[:associated_with]}" if @options.key?(:associated_with)
|
82
|
+
description += " only => #{@options[:only].join ', '}" if @options.key?(:only)
|
83
|
+
description += " except => #{@options[:except].join(', ')}" if @options.key?(:except)
|
84
|
+
description += " requires audit_comment" if @options.key?(:comment_required)
|
85
|
+
|
86
|
+
description
|
87
|
+
end
|
88
|
+
|
89
|
+
protected
|
90
|
+
|
91
|
+
def expects(message)
|
92
|
+
@expectation = message
|
93
|
+
end
|
94
|
+
|
95
|
+
def auditing_enabled?
|
96
|
+
expects "#{model_class} to be audited"
|
97
|
+
model_class.respond_to?(:auditing_enabled) && model_class.auditing_enabled
|
98
|
+
end
|
99
|
+
|
100
|
+
def model_class
|
101
|
+
@subject.class
|
102
|
+
end
|
103
|
+
|
104
|
+
def associated_with_model?
|
105
|
+
expects "#{model_class} to record audits to associated model #{@options[:associated_with]}"
|
106
|
+
model_class.audit_associated_with == @options[:associated_with]
|
107
|
+
end
|
108
|
+
|
109
|
+
def records_changes_to_specified_fields?
|
110
|
+
if @options[:only] || @options[:except]
|
111
|
+
if @options[:only]
|
112
|
+
except = model_class.column_names - @options[:only].map(&:to_s)
|
113
|
+
else
|
114
|
+
except = model_class.default_ignored_attributes + Audited.ignored_attributes
|
115
|
+
except |= @options[:except].collect(&:to_s) if @options[:except]
|
116
|
+
end
|
117
|
+
|
118
|
+
expects "non audited columns (#{model_class.non_audited_columns.inspect}) to match (#{expect})"
|
119
|
+
model_class.non_audited_columns =~ except
|
120
|
+
else
|
121
|
+
true
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def comment_required_valid?
|
126
|
+
if @options[:comment_required]
|
127
|
+
@subject.audit_comment = nil
|
128
|
+
|
129
|
+
expects "to be invalid when audit_comment is not specified"
|
130
|
+
@subject.valid? == false && @subject.errors.key?(:audit_comment)
|
131
|
+
else
|
132
|
+
true
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
class AssociatedAuditMatcher # :nodoc:
|
138
|
+
def matches?(subject)
|
139
|
+
@subject = subject
|
140
|
+
|
141
|
+
association_exists?
|
142
|
+
end
|
143
|
+
|
144
|
+
def failure_message
|
145
|
+
"Expected #{model_class} to have associated audits"
|
146
|
+
end
|
147
|
+
|
148
|
+
def negative_failure_message
|
149
|
+
"Expected #{model_class} to not have associated audits"
|
150
|
+
end
|
151
|
+
|
152
|
+
def description
|
153
|
+
"has associated audits"
|
154
|
+
end
|
155
|
+
|
156
|
+
protected
|
157
|
+
|
158
|
+
def model_class
|
159
|
+
@subject.class
|
160
|
+
end
|
161
|
+
|
162
|
+
def reflection
|
163
|
+
model_class.reflect_on_association(:associated_audits)
|
164
|
+
end
|
165
|
+
|
166
|
+
def association_exists?
|
167
|
+
(!reflection.nil?) &&
|
168
|
+
reflection.macro == :has_many &&
|
169
|
+
reflection.options[:class_name] == Audited.audit_class.name
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Audited
|
2
|
+
class Sweeper < ActiveModel::Observer
|
3
|
+
observe Audited.audit_class
|
4
|
+
|
5
|
+
def before(controller)
|
6
|
+
self.controller = controller
|
7
|
+
true
|
8
|
+
end
|
9
|
+
|
10
|
+
def after(controller)
|
11
|
+
self.controller = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def before_create(audit)
|
15
|
+
audit.user ||= current_user
|
16
|
+
audit.remote_address = controller.try(:request).try(:ip)
|
17
|
+
end
|
18
|
+
|
19
|
+
def current_user
|
20
|
+
controller.send(Audited.current_user_method) if controller.respond_to?(Audited.current_user_method, true)
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_observer!(klass)
|
24
|
+
super
|
25
|
+
define_callback(klass)
|
26
|
+
end
|
27
|
+
|
28
|
+
def define_callback(klass)
|
29
|
+
observer = self
|
30
|
+
callback_meth = :"_notify_audited_sweeper"
|
31
|
+
klass.send(:define_method, callback_meth) do
|
32
|
+
observer.update(:before_create, self)
|
33
|
+
end
|
34
|
+
klass.send(:before_create, callback_meth)
|
35
|
+
end
|
36
|
+
|
37
|
+
def controller
|
38
|
+
::Audited.store[:current_controller]
|
39
|
+
end
|
40
|
+
|
41
|
+
def controller=(value)
|
42
|
+
::Audited.store[:current_controller] = value
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
if defined?(ActionController) and defined?(ActionController::Base)
|
48
|
+
ActionController::Base.class_eval do
|
49
|
+
around_filter Audited::Sweeper.instance
|
50
|
+
end
|
51
|
+
end
|
data/notifiably_audited.gemspec
CHANGED
@@ -1,30 +1,23 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
lib = File.expand_path('../lib', __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
|
4
|
+
require 'notifiably_audited/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "notifiably_audited"
|
8
8
|
spec.version = NotifiablyAudited::VERSION
|
9
|
-
spec.authors = ["
|
10
|
-
spec.email = ["
|
11
|
-
spec.summary = %q{
|
12
|
-
spec.description = %q{TODO}
|
9
|
+
spec.authors = ["TODO: Write your name"]
|
10
|
+
spec.email = ["TODO: Write your email address"]
|
11
|
+
spec.summary = %q{TODO: Write a short summary. Required.}
|
12
|
+
spec.description = %q{TODO: Write a longer description. Optional.}
|
13
13
|
spec.homepage = ""
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
gem.add_development_dependency 'rails', '~> 3.0'
|
21
|
-
gem.add_development_dependency 'rspec-rails', '~> 2.0'
|
22
|
-
gem.add_development_dependency 'sqlite3', '~> 1.0'
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
23
20
|
|
24
|
-
|
25
|
-
|
26
|
-
gem.require_paths = ['lib']
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
22
|
+
spec.add_development_dependency "rake"
|
27
23
|
end
|
28
|
-
|
29
|
-
|
30
|
-
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module AuditedSpecHelpers
|
2
|
+
|
3
|
+
def create_user(use_mongo = false, attrs = {})
|
4
|
+
klass = use_mongo ? Models::MongoMapper::User : Models::ActiveRecord::User
|
5
|
+
klass.create({:name => 'Brandon', :username => 'brandon', :password => 'password'}.merge(attrs))
|
6
|
+
end
|
7
|
+
|
8
|
+
def create_versions(n = 2, use_mongo = false)
|
9
|
+
klass = use_mongo ? Models::MongoMapper::User : Models::ActiveRecord::User
|
10
|
+
|
11
|
+
klass.create(:name => 'Foobar 1').tap do |u|
|
12
|
+
(n - 1).times do |i|
|
13
|
+
u.update_attribute :name, "Foobar #{i + 2}"
|
14
|
+
end
|
15
|
+
u.reload
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def create_active_record_user(attrs = {})
|
20
|
+
create_user(false, attrs)
|
21
|
+
end
|
22
|
+
|
23
|
+
def create_mongo_user(attrs = {})
|
24
|
+
create_user(true, attrs)
|
25
|
+
end
|
26
|
+
|
27
|
+
def create_mongo_versions(n = 2)
|
28
|
+
create_versions(n, true)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
sqlite3mem: &SQLITE3MEM
|
2
|
+
adapter: sqlite3
|
3
|
+
database: ":memory:"
|
4
|
+
|
5
|
+
sqlite3: &SQLITE
|
6
|
+
adapter: sqlite3
|
7
|
+
database: audited_test.sqlite3.db
|
8
|
+
|
9
|
+
postgresql: &POSTGRES
|
10
|
+
adapter: postgresql
|
11
|
+
username: postgres
|
12
|
+
password: postgres
|
13
|
+
database: audited_test
|
14
|
+
min_messages: ERROR
|
15
|
+
|
16
|
+
mysql: &MYSQL
|
17
|
+
adapter: mysql
|
18
|
+
host: localhost
|
19
|
+
username: root
|
20
|
+
password:
|
21
|
+
database: audited_test
|
22
|
+
|
23
|
+
test:
|
24
|
+
<<: *<%= ENV['DB'] || 'SQLITE3MEM' %>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
RailsApp::Application.configure do
|
2
|
+
# Settings specified here will take precedence over those in config/environment.rb
|
3
|
+
|
4
|
+
# In the development environment your application's code is reloaded on
|
5
|
+
# every request. This slows down response time but is perfect for development
|
6
|
+
# since you don't have to restart the webserver when you make code changes.
|
7
|
+
config.cache_classes = false
|
8
|
+
|
9
|
+
# Log error messages when you accidentally call methods on nil.
|
10
|
+
config.whiny_nils = true
|
11
|
+
|
12
|
+
# Show full error reports and disable caching
|
13
|
+
config.consider_all_requests_local = true
|
14
|
+
config.action_view.debug_rjs = true
|
15
|
+
config.action_controller.perform_caching = false
|
16
|
+
|
17
|
+
# Don't care if the mailer can't send
|
18
|
+
config.action_mailer.raise_delivery_errors = false
|
19
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
RailsApp::Application.configure do
|
2
|
+
# Settings specified here will take precedence over those in config/environment.rb
|
3
|
+
|
4
|
+
# The production environment is meant for finished, "live" apps.
|
5
|
+
# Code is not reloaded between requests
|
6
|
+
config.cache_classes = true
|
7
|
+
|
8
|
+
# Full error reports are disabled and caching is turned on
|
9
|
+
config.consider_all_requests_local = false
|
10
|
+
config.action_controller.perform_caching = true
|
11
|
+
|
12
|
+
# See everything in the log (default is :info)
|
13
|
+
# config.log_level = :debug
|
14
|
+
|
15
|
+
# Use a different logger for distributed setups
|
16
|
+
# config.logger = SyslogLogger.new
|
17
|
+
|
18
|
+
# Use a different cache store in production
|
19
|
+
# config.cache_store = :mem_cache_store
|
20
|
+
|
21
|
+
# Disable Rails's static asset server
|
22
|
+
# In production, Apache or nginx will already do this
|
23
|
+
config.serve_static_assets = false
|
24
|
+
|
25
|
+
# Enable serving of images, stylesheets, and javascripts from an asset server
|
26
|
+
# config.action_controller.asset_host = "http://assets.example.com"
|
27
|
+
|
28
|
+
# Disable delivery errors, bad email addresses will be ignored
|
29
|
+
# config.action_mailer.raise_delivery_errors = false
|
30
|
+
|
31
|
+
# Enable threaded mode
|
32
|
+
# config.threadsafe!
|
33
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
RailsApp::Application.configure do
|
2
|
+
# Settings specified here will take precedence over those in config/environment.rb
|
3
|
+
|
4
|
+
# The test environment is used exclusively to run your application's
|
5
|
+
# test suite. You never need to work with it otherwise. Remember that
|
6
|
+
# your test database is "scratch space" for the test suite and is wiped
|
7
|
+
# and recreated between test runs. Don't rely on the data there!
|
8
|
+
config.cache_classes = true
|
9
|
+
|
10
|
+
# Log error messages when you accidentally call methods on nil.
|
11
|
+
config.whiny_nils = true
|
12
|
+
|
13
|
+
# Show full error reports and disable caching
|
14
|
+
config.consider_all_requests_local = true
|
15
|
+
config.action_controller.perform_caching = false
|
16
|
+
|
17
|
+
# Disable request forgery protection in test environment
|
18
|
+
config.action_controller.allow_forgery_protection = false
|
19
|
+
|
20
|
+
# Tell Action Mailer not to deliver emails to the real world.
|
21
|
+
# The :test delivery method accumulates sent emails in the
|
22
|
+
# ActionMailer::Base.deliveries array.
|
23
|
+
config.action_mailer.delivery_method = :test
|
24
|
+
|
25
|
+
# Use SQL instead of Active Record's schema dumper when creating the test database.
|
26
|
+
# This is necessary if your schema can't be completely dumped by the schema dumper,
|
27
|
+
# like if you have constraints or database-specific column types
|
28
|
+
# config.active_record.schema_format = :sql
|
29
|
+
|
30
|
+
config.action_dispatch.show_exceptions = false
|
31
|
+
|
32
|
+
config.active_support.deprecation = :stderr
|
33
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# Be sure to restart your server when you modify this file.
|
2
|
+
|
3
|
+
# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
|
4
|
+
# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
|
5
|
+
|
6
|
+
# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
|
7
|
+
Rails.backtrace_cleaner.remove_silencers!
|