tamaudit 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +57 -0
  4. data/Rakefile +31 -0
  5. data/app/models/tamaudit/audit.rb +44 -0
  6. data/config/database.yml +24 -0
  7. data/config/routes.rb +2 -0
  8. data/db/migrate/20131029200927_create_auditable_audits.rb +14 -0
  9. data/lib/tamaudit.rb +20 -0
  10. data/lib/tamaudit/auditor.rb +7 -0
  11. data/lib/tamaudit/auditor_behavior.rb +98 -0
  12. data/lib/tamaudit/auditor_request.rb +13 -0
  13. data/lib/tamaudit/engine.rb +19 -0
  14. data/lib/tamaudit/version.rb +3 -0
  15. data/lib/tasks/tamaudit_tasks.rake +4 -0
  16. data/spec/controllers/audits_controller_spec.rb +47 -0
  17. data/spec/dummy/README.rdoc +28 -0
  18. data/spec/dummy/Rakefile +6 -0
  19. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  20. data/spec/dummy/app/assets/javascripts/general_controller.js +2 -0
  21. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  22. data/spec/dummy/app/assets/stylesheets/general_controller.css +4 -0
  23. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  24. data/spec/dummy/app/controllers/general_controller_controller.rb +2 -0
  25. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  26. data/spec/dummy/app/helpers/general_controller_helper.rb +2 -0
  27. data/spec/dummy/app/models/general_model.rb +4 -0
  28. data/spec/dummy/app/models/user.rb +2 -0
  29. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  30. data/spec/dummy/bin/bundle +3 -0
  31. data/spec/dummy/bin/rails +4 -0
  32. data/spec/dummy/bin/rake +4 -0
  33. data/spec/dummy/config.ru +4 -0
  34. data/spec/dummy/config/application.rb +23 -0
  35. data/spec/dummy/config/boot.rb +5 -0
  36. data/spec/dummy/config/database.yml +25 -0
  37. data/spec/dummy/config/environment.rb +5 -0
  38. data/spec/dummy/config/environments/development.rb +29 -0
  39. data/spec/dummy/config/environments/production.rb +80 -0
  40. data/spec/dummy/config/environments/test.rb +36 -0
  41. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  42. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  43. data/spec/dummy/config/initializers/inflections.rb +16 -0
  44. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  45. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  46. data/spec/dummy/config/initializers/session_store.rb +3 -0
  47. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  48. data/spec/dummy/config/locales/en.yml +23 -0
  49. data/spec/dummy/config/routes.rb +4 -0
  50. data/spec/dummy/db/migrate/20131029211126_create_general_models.rb +12 -0
  51. data/spec/dummy/db/migrate/20131030014901_create_users.rb +11 -0
  52. data/spec/dummy/db/schema.rb +52 -0
  53. data/spec/dummy/public/404.html +58 -0
  54. data/spec/dummy/public/422.html +58 -0
  55. data/spec/dummy/public/500.html +57 -0
  56. data/spec/dummy/public/favicon.ico +0 -0
  57. data/spec/factories/auditable_audits.rb +11 -0
  58. data/spec/factories/general_models.rb +10 -0
  59. data/spec/factories/users.rb +9 -0
  60. data/spec/models/models/general_model_spec.rb +206 -0
  61. data/spec/models/models/user_spec.rb +5 -0
  62. data/spec/models/tattletale/audit_spec.rb +8 -0
  63. data/spec/spec_helper.rb +24 -0
  64. data/spec/support/models.rb +8 -0
  65. data/spec/support/schema.rb +34 -0
  66. metadata +283 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 803deb0c93b4f7f8396d2c975b23def4389011c3
4
+ data.tar.gz: fe511e53d190731f17d7a60a844bdcd341085eca
5
+ SHA512:
6
+ metadata.gz: 76180c393d507d6b02eeed2e7bac52597d2e35d9d7e5266867d1ade0fdc77b8d39e3dfa53253ea00bf0a03843a925958dc34b975dabbc6a244b6956002c22730
7
+ data.tar.gz: 09a34a28dc8fbe14422d074cb5c97f9fac0ea9368b5f3e26ed377e30fc0b5d0c4ba6eb19c7629533bcd80f566a3e099bc3e149eb8f70f1cbb2572db74a2049de
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2013 continuum.cl
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # Tamaudit - IN PROGRESS
2
+
3
+ ## Audits activerecord models like a boss
4
+
5
+
6
+ Audit activerecord models like a boss. Tested in rails 4 and ruby 2.0.0.
7
+
8
+ This project is heavily based in Espinita gem (https://github.com/continuum/espinita).
9
+
10
+
11
+
12
+ ## Installation
13
+
14
+ In your gemfile
15
+
16
+ gem "tamaudit"
17
+
18
+ In console
19
+
20
+ $ rake tamaudit:install:migrations
21
+ $ rake db:migrate
22
+
23
+ ## Usage
24
+
25
+ class Post < ActiveRecord::Base
26
+ auditable
27
+ end
28
+
29
+ @post.create(title: "an awesome blog post" )
30
+
31
+ Tamaudit will create an audit by default on creation , edition and destroy:
32
+
33
+ @post.audits.size #=> 1
34
+
35
+ Tamaudit provides options to include or exclude columns to trigger the creation of audit.
36
+
37
+ class Post < ActiveRecord::Base
38
+ auditable only: [:title] # except: [:some_column]
39
+ end
40
+
41
+ And lets you declare the callbacks you want for audit creation:
42
+
43
+ class Post < ActiveRecord::Base
44
+ auditable on: [:create] # on: [:create, :update]
45
+ end
46
+
47
+ You can find the audits records easily:
48
+
49
+ @post.audits.first #=> #<Tamaudit::Audit id: 1, auditable_id: 1, auditable_type: "Post", user_id: 1, user_type: "User", audited_changes: {"title"=>[nil, "MyString"], "created_at"=>[nil, 2013-10-30 15:50:14 UTC], "updated_at"=>[nil, 2013-10-30 15:50:14 UTC], "id"=>[nil, 1]}
50
+
51
+ Tamaudit will save the model changes in a serialized column called audited_changes:
52
+
53
+ @post.audits.first.audited_changes #=> {"title"=>[nil, "MyString"], "created_at"=>[nil, 2013-10-30 15:50:14 UTC], "updated_at"=>[nil, 2013-10-30 15:50:14 UTC], "id"=>[nil, 1]}
54
+
55
+ Tamaudit will detect the current user when records saved from rails controllers. By default Tamaudit uses current_user method but you can change it:
56
+
57
+ Tamaudit.current_user_method = :authenticated_user
data/Rakefile ADDED
@@ -0,0 +1,31 @@
1
+ #begin
2
+ # require 'bundler/setup'
3
+ #rescue LoadError
4
+ # puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ #end
6
+
7
+
8
+ require 'rspec/core/rake_task'
9
+ require 'bundler'
10
+ Bundler::GemHelper.install_tasks
11
+
12
+ desc 'Default: run unit specs.'
13
+ task :default => :spec
14
+
15
+ desc 'Test the lazy_high_charts plugin.'
16
+ RSpec::Core::RakeTask.new('spec') do |t|
17
+ t.pattern = FileList['spec/**/*_spec.rb']
18
+ end
19
+
20
+ require 'rdoc/task'
21
+ RDoc::Task.new(:rdoc) do |rdoc|
22
+ rdoc.rdoc_dir = 'rdoc'
23
+ rdoc.title = 'Tamaudit'
24
+ rdoc.options << '--line-numbers'
25
+ rdoc.rdoc_files.include('README.rdoc')
26
+ rdoc.rdoc_files.include('lib/**/*.rb')
27
+ end
28
+
29
+ #APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
30
+ #load 'rails/tasks/engine.rake'
31
+
@@ -0,0 +1,44 @@
1
+ module Tamaudit
2
+ class Audit < ActiveRecord::Base
3
+
4
+ self.table_name = 'audits'
5
+
6
+ belongs_to :auditable, polymorphic: true
7
+ belongs_to :user, polymorphic: true
8
+
9
+
10
+ scope :descending, ->{ reorder("version DESC")}
11
+ scope :creates, ->{ where({:action => 'create'})}
12
+ scope :updates, ->{ where({:action => 'update'})}
13
+ scope :destroys, ->{ where({:action => 'destroy'})}
14
+
15
+ scope :up_until, ->(date_or_time){where("created_at <= ?", date_or_time) }
16
+ scope :from_version, ->(version){where(['version >= ?', version]) }
17
+ scope :to_version, ->(version){where(['version <= ?', version]) }
18
+ scope :auditable_finder, ->(auditable_id, auditable_type){where(auditable_id: auditable_id, auditable_type: auditable_type)}
19
+
20
+ serialize :audited_changes
21
+
22
+ before_create :set_version_number, :set_audit_user
23
+
24
+ # Return all audits older than the current one.
25
+ def ancestors
26
+ self.class.where(['auditable_id = ? and auditable_type = ? and version <= ?',
27
+ auditable_id, auditable_type, version])
28
+ end
29
+
30
+ private
31
+ def set_version_number
32
+ max = self.class.auditable_finder(auditable_id, auditable_type).maximum(:version) || 0
33
+ self.version = max + 1
34
+ end
35
+
36
+ def set_audit_user
37
+ self.user = RequestStore.store[:audited_user] if RequestStore.store[:audited_user]
38
+ self.remote_address = RequestStore.store[:audited_ip] if RequestStore.store[:audited_ip]
39
+
40
+ nil # prevent stopping callback chains
41
+ end
42
+
43
+ end
44
+ 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' %>
data/config/routes.rb ADDED
@@ -0,0 +1,2 @@
1
+ Tamaudit::Engine.routes.draw do
2
+ end
@@ -0,0 +1,14 @@
1
+ class CreateAuditableAudits < ActiveRecord::Migration
2
+ def change
3
+ create_table :audits do |t|
4
+ t.references :auditable, polymorphic: true, index: true
5
+ t.references :user, polymorphic: true, index: true
6
+ t.text :audited_changes
7
+ t.string :comment
8
+ t.integer :version
9
+ t.string :action
10
+ t.string :remote_address
11
+ t.timestamps
12
+ end
13
+ end
14
+ end
data/lib/tamaudit.rb ADDED
@@ -0,0 +1,20 @@
1
+ require "tamaudit/engine"
2
+ require "request_store"
3
+
4
+ module Tamaudit
5
+
6
+ autoload :Auditor, "tamaudit/auditor"
7
+ autoload :AuditorBehavior, "tamaudit/auditor_behavior"
8
+ autoload :AuditorRequest, "tamaudit/auditor_request"
9
+
10
+ class << self
11
+
12
+ attr_accessor :current_user_method
13
+
14
+ def current_user_method
15
+ @current_user_method ||= :current_user
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,7 @@
1
+ module Tamaudit
2
+ module Auditor
3
+ extend ActiveSupport::Concern
4
+ include Tamaudit::AuditorBehavior
5
+ end
6
+ end
7
+
@@ -0,0 +1,98 @@
1
+ module Tamaudit
2
+ module AuditorBehavior
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ class_attribute :excluded_cols
7
+ class_attribute :audit_callbacks
8
+ attr_accessor :audit_comment
9
+ end
10
+
11
+ module ClassMethods
12
+
13
+ @@default_excluded = %w(lock_version created_at updated_at created_on updated_on)
14
+
15
+ def auditable(options = {})
16
+
17
+ self.audit_callbacks = []
18
+ self.audit_callbacks << options[:on] unless options[:on].blank?
19
+ self.audit_callbacks.flatten!
20
+
21
+ after_create :audit_create if self.audit_callbacks.blank? || self.audit_callbacks.include?(:create)
22
+ before_update :audit_update if self.audit_callbacks.blank? || self.audit_callbacks.include?(:update)
23
+ before_destroy :audit_destroy if self.audit_callbacks.blank? || self.audit_callbacks.include?(:destroy)
24
+
25
+ self.excluded_cols = (@@default_excluded)
26
+
27
+ if options[:only]
28
+ options[:only] = [options[:only]].flatten.map { |x| x.to_s }
29
+ self.excluded_cols = (self.column_names - options[:only] )
30
+ end
31
+
32
+ if options[:except]
33
+ options[:except] = [options[:except]].flatten.map { |x| x.to_s }
34
+ self.excluded_cols = (@@default_excluded) + options[:except]
35
+ end
36
+
37
+ has_many :audits, :as => :auditable, :class_name => Tamaudit::Audit.name
38
+ #attr_accessor :audited_user, :audited_ip
39
+ accepts_nested_attributes_for :audits
40
+
41
+ end
42
+
43
+ def permited_columns
44
+ self.column_names - self.excluded_cols.to_a
45
+ end
46
+
47
+ # All audits made during the block called will be recorded as made
48
+ # by +user+. This method is hopefully threadsafe, making it ideal
49
+ # for background operations that require audit information.
50
+ def as_user(user, &block)
51
+ RequestStore.store[:audited_user] = user
52
+ yield
53
+ ensure
54
+ RequestStore.store[:audited_user] = nil
55
+ end
56
+
57
+ end
58
+
59
+ # audited attributes detected against permited columns
60
+ def audited_attributes
61
+ self.changes.keys & self.class.permited_columns
62
+ end
63
+
64
+ def audited_hash
65
+ Hash[ audited_attributes.map{|o| [o.to_sym, self.changes[o.to_sym] ] } ]
66
+ end
67
+
68
+
69
+ def audit_create
70
+ #puts self.class.audit_callbacks
71
+ write_audit(:action => 'create',
72
+ :audited_changes => audited_hash,
73
+ :comment => audit_comment)
74
+ end
75
+
76
+ def audit_update
77
+ #puts self.class.audit_callbacks
78
+ write_audit(:action => 'update',
79
+ :audited_changes => audited_hash,
80
+ :comment => audit_comment)
81
+ end
82
+
83
+ def audit_destroy
84
+ write_audit(:action => 'destroy',
85
+ :audited_changes => audited_hash,
86
+ :comment => audit_comment)
87
+ end
88
+
89
+ def write_audit(options)
90
+ if options[:action] == 'destroy'
91
+ self.audits.create(options)
92
+ else
93
+ self.audits.create(options) unless audited_hash.blank?
94
+ end
95
+ end
96
+
97
+ end
98
+ end
@@ -0,0 +1,13 @@
1
+ module Tamaudit::AuditorRequest
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ before_filter :store_audited_user
6
+ end
7
+
8
+ def store_audited_user
9
+ RequestStore.store[:audited_user] = self.send(Tamaudit.current_user_method) #current_user
10
+
11
+ RequestStore.store[:audited_ip] = self.try(:request).try(:remote_ip)
12
+ end
13
+ end
@@ -0,0 +1,19 @@
1
+ module Tamaudit
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Tamaudit
4
+
5
+ config.generators do |g|
6
+ g.test_framework :rspec,
7
+ :fixture_replacement => :factory_girl ,
8
+ :dir => "spec/factories"
9
+ g.integration_tool :rspec
10
+ end
11
+
12
+ initializer "include Auditor request into action controller" do |app|
13
+ ActionController::Base.send(:include, Tamaudit::AuditorRequest)
14
+ ActiveRecord::Base.send(:include, Tamaudit::Auditor)
15
+ end
16
+
17
+
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module Tamaudit
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :tamaudit do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ class AuditsController < ActionController::Base
4
+ def audit
5
+ @general_model = FactoryGirl.create(:general_model)
6
+ render :nothing => true
7
+ end
8
+
9
+ def update_user
10
+ current_user.update_attributes( :password => 'foo')
11
+ render :nothing => true
12
+ end
13
+
14
+ private
15
+
16
+ attr_accessor :current_user
17
+ attr_accessor :custom_user
18
+ end
19
+
20
+
21
+ describe AuditsController do
22
+
23
+ before :each do
24
+ GeneralModel.auditable
25
+ end
26
+
27
+ let( :general_model ){
28
+ FactoryFirl.create(:general_model)
29
+ }
30
+
31
+ let( :user ) { FactoryGirl.create(:user) }
32
+
33
+ describe "POST audit" do
34
+
35
+ it "should audit user" do
36
+ controller.send(:current_user=, user)
37
+ expect {
38
+ post :audit
39
+ }.to change( Tamaudit::Audit, :count )
40
+
41
+ assigns(:general_model).audits.last.user.should == user
42
+ assigns(:general_model).audits.last.remote_address.should == "0.0.0.0"
43
+
44
+ end
45
+ end
46
+
47
+ end
@@ -0,0 +1,28 @@
1
+ == README
2
+
3
+ This README would normally document whatever steps are necessary to get the
4
+ application up and running.
5
+
6
+ Things you may want to cover:
7
+
8
+ * Ruby version
9
+
10
+ * System dependencies
11
+
12
+ * Configuration
13
+
14
+ * Database creation
15
+
16
+ * Database initialization
17
+
18
+ * How to run the test suite
19
+
20
+ * Services (job queues, cache servers, search engines, etc.)
21
+
22
+ * Deployment instructions
23
+
24
+ * ...
25
+
26
+
27
+ Please feel free to use a different markup language if you do not plan to run
28
+ <tt>rake doc:app</tt>.