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.
Files changed (41) hide show
  1. checksums.yaml +8 -8
  2. data/Appraisals +11 -0
  3. data/CHANGELOG +34 -0
  4. data/LICENSE +19 -0
  5. data/audited-activerecord.gemspec +21 -0
  6. data/audited-mongo_mapper.gemspec +21 -0
  7. data/audited.gemspec +26 -0
  8. data/gemfiles/rails30.gemfile +7 -0
  9. data/gemfiles/rails31.gemfile +7 -0
  10. data/gemfiles/rails32.gemfile +7 -0
  11. data/lib/audited.rb +15 -0
  12. data/lib/audited/audit.rb +102 -0
  13. data/lib/audited/auditor.rb +270 -0
  14. data/lib/audited/rspec_matchers.rb +173 -0
  15. data/lib/audited/sweeper.rb +51 -0
  16. data/notifiably_audited.gemspec +11 -18
  17. data/spec/audited_spec_helpers.rb +31 -0
  18. data/spec/rails_app/config/application.rb +5 -0
  19. data/spec/rails_app/config/database.yml +24 -0
  20. data/spec/rails_app/config/environment.rb +5 -0
  21. data/spec/rails_app/config/environments/development.rb +19 -0
  22. data/spec/rails_app/config/environments/production.rb +33 -0
  23. data/spec/rails_app/config/environments/test.rb +33 -0
  24. data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  25. data/spec/rails_app/config/initializers/inflections.rb +2 -0
  26. data/spec/rails_app/config/initializers/secret_token.rb +2 -0
  27. data/spec/rails_app/config/routes.rb +6 -0
  28. data/spec/spec_helper.rb +23 -0
  29. data/spec/support/active_record/models.rb +84 -0
  30. data/spec/support/active_record/schema.rb +54 -0
  31. data/spec/support/mongo_mapper/connection.rb +4 -0
  32. data/spec/support/mongo_mapper/models.rb +210 -0
  33. data/test/db/version_1.rb +17 -0
  34. data/test/db/version_2.rb +18 -0
  35. data/test/db/version_3.rb +19 -0
  36. data/test/db/version_4.rb +20 -0
  37. data/test/db/version_5.rb +18 -0
  38. data/test/install_generator_test.rb +17 -0
  39. data/test/test_helper.rb +19 -0
  40. data/test/upgrade_generator_test.rb +65 -0
  41. 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
@@ -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
- #require 'notifiably_audited/version'
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 = ["senthil kumar"]
10
- spec.email = ["senthilkumar.hce@gmail.com"]
11
- spec.summary = %q{It audits and notifies}
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
- gem.add_development_dependency 'activerecord', '~> 3.0'
17
- gem.add_development_dependency 'appraisal', '~> 0.4'
18
- gem.add_development_dependency 'bson_ext', '~> 1.6'
19
- gem.add_development_dependency 'mongo_mapper', '~> 0.11'
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
- gem.files = `git ls-files`.split($\).reject{|f| f =~ /(lib\/audited\-|adapters|generators)/ }
25
- gem.test_files = gem.files.grep(/^spec\//)
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,5 @@
1
+ module RailsApp
2
+ class Application < Rails::Application
3
+ config.root = File.expand_path('../../', __FILE__)
4
+ end
5
+ 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,5 @@
1
+ # Load the rails application
2
+ require File.expand_path('../application', __FILE__)
3
+
4
+ # Initialize the rails application
5
+ RailsApp::Application.initialize!
@@ -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!
@@ -0,0 +1,2 @@
1
+ ActiveSupport::Inflector.inflections do |inflect|
2
+ end
@@ -0,0 +1,2 @@
1
+ Rails.application.config.secret_token = 'ea942c41850d502f2c8283e26bdc57829f471bb18224ddff0a192c4f32cdf6cb5aa0d82b3a7a7adbeb640c4b06f3aa1cd5f098162d8240f669b39d6b49680571'
2
+ Rails.application.config.session_store :cookie_store, :key => "_my_app"