tamaudit 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 (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>.