espinita 0.0.1

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 (70) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +60 -0
  4. data/Rakefile +31 -0
  5. data/app/models/espinita/audit.rb +41 -0
  6. data/config/database.yml +24 -0
  7. data/config/routes.rb +2 -0
  8. data/db/migrate/20131029200927_create_auditable_audits.rb +15 -0
  9. data/lib/espinita.rb +16 -0
  10. data/lib/espinita/auditor.rb +18 -0
  11. data/lib/espinita/auditor_behavior.rb +83 -0
  12. data/lib/espinita/auditor_request.rb +13 -0
  13. data/lib/espinita/engine.rb +18 -0
  14. data/lib/espinita/version.rb +3 -0
  15. data/lib/tasks/espinita_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/development.sqlite3 +0 -0
  51. data/spec/dummy/db/migrate/20131029211126_create_general_models.rb +12 -0
  52. data/spec/dummy/db/migrate/20131030014901_create_users.rb +11 -0
  53. data/spec/dummy/db/schema.rb +52 -0
  54. data/spec/dummy/log/development.log +28 -0
  55. data/spec/dummy/log/test.log +17268 -0
  56. data/spec/dummy/public/404.html +58 -0
  57. data/spec/dummy/public/422.html +58 -0
  58. data/spec/dummy/public/500.html +57 -0
  59. data/spec/dummy/public/favicon.ico +0 -0
  60. data/spec/factories/auditable_audits.rb +11 -0
  61. data/spec/factories/general_models.rb +10 -0
  62. data/spec/factories/users.rb +9 -0
  63. data/spec/integration/navigation_test.rb +10 -0
  64. data/spec/models/espinita/audit_spec.rb +8 -0
  65. data/spec/models/models/general_model_spec.rb +160 -0
  66. data/spec/models/models/user_spec.rb +5 -0
  67. data/spec/spec_helper.rb +24 -0
  68. data/spec/support/models.rb +8 -0
  69. data/spec/support/schema.rb +34 -0
  70. metadata +291 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7183b60c3fd3a7d44f78dcb555200525c5dd8030
4
+ data.tar.gz: b9bd2cf0e545a2f111082d14a094691b02aa9bca
5
+ SHA512:
6
+ metadata.gz: bfb5056db551350c66078a54de5e644da72d58730cf6ebe06ce7aee533e5fdb76022548737755579c6edbc25c27e1c6a54325d3d9d60c2d82ad30ad021b2391b
7
+ data.tar.gz: 6899fc45eaf3413e0fd068443a20f2e4d2ac185ed535b3ef449a11ed1241fb742a6c36cbe0c1a0e49acdd704a41d4f28ae50acf7e7d491ba123fefccc8349e56
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,60 @@
1
+ # Espinita
2
+
3
+ [![Build Status](https://secure.travis-ci.org/continuum/espinita.png)](http://travis-ci.org/continuum/espinita) [![Dependency Status](https://gemnasium.com/continuum/espinita.png)](https://gemnasium.com/continuum/espinita) [![Coverage Status](https://coveralls.io/repos/continuum/espinita/badge.png?branch=master)](https://coveralls.io/r/continuum/espinita?branch=master)
4
+ =======
5
+
6
+ ## Audits activerecord models like a boss
7
+
8
+ ![Alt text](./espinita.jpg)
9
+
10
+ Audit activerecord models like a boss, tested in rails 4 and ruby 2.0.0.
11
+
12
+
13
+ This proyect is heavily based in audited gem.
14
+
15
+ ## Installation
16
+
17
+ In your gemfile
18
+
19
+ gem "espinita"
20
+
21
+ In console
22
+
23
+ $ rake espinita:install:migrations
24
+ $ rake db:migrate
25
+
26
+ ## Usage
27
+
28
+ class Post < ActiveRecord::Base
29
+ auditable
30
+ end
31
+
32
+ @post.create(title: "an awesome blog post" )
33
+
34
+ Espinita will create an audit by default on creation , edition and destroy:
35
+
36
+ @post.audits.size #=> 1
37
+
38
+ Espinita provides options to include or exclude columns to trigger the creation of audit.
39
+
40
+ class Post < ActiveRecord::Base
41
+ auditable only: [:title] # except: [:some_column]
42
+ end
43
+
44
+ And let you declare the callbacks you want for audit creation:
45
+
46
+ class Post < ActiveRecord::Base
47
+ auditable on: [:create] # on: [:create, :update]
48
+ end
49
+
50
+ You can find the audits records easily:
51
+
52
+ @post.audits.first #=> #<Espinita::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]}
53
+
54
+ Espinita will save the model changes in a serialized column called audited_changes:
55
+
56
+ @post.audits.firt.audited_changed #=> {"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]}
57
+
58
+ Espinita will detect current user when records saved from rails controllers. by default Espinita use current_user method but you can change it
59
+
60
+ Espinita.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 = 'Espinita'
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,41 @@
1
+ module Espinita
2
+ class Audit < ActiveRecord::Base
3
+ belongs_to :auditable, polymorphic: true
4
+ belongs_to :user, polymorphic: true
5
+
6
+
7
+ scope :descending, ->{ reorder("version DESC")}
8
+ scope :creates, ->{ where({:action => 'create'})}
9
+ scope :updates, ->{ where({:action => 'update'})}
10
+ scope :destroys, ->{ where({:action => 'destroy'})}
11
+
12
+ scope :up_until, ->(date_or_time){where("created_at <= ?", date_or_time) }
13
+ scope :from_version, ->(version){where(['version >= ?', version]) }
14
+ scope :to_version, ->(version){where(['version <= ?', version]) }
15
+ scope :auditable_finder, ->(auditable_id, auditable_type){where(auditable_id: auditable_id, auditable_type: auditable_type)}
16
+
17
+ serialize :audited_changes
18
+
19
+ before_create :set_version_number, :set_audit_user
20
+
21
+ # Return all audits older than the current one.
22
+ def ancestors
23
+ self.class.where(['auditable_id = ? and auditable_type = ? and version <= ?',
24
+ auditable_id, auditable_type, version])
25
+ end
26
+
27
+ private
28
+ def set_version_number
29
+ max = self.class.auditable_finder(auditable_id, auditable_type).maximum(:version) || 0
30
+ self.version = max + 1
31
+ end
32
+
33
+ def set_audit_user
34
+ self.user = RequestStore.store[:audited_user] if RequestStore.store[:audited_user]
35
+ self.remote_address = RequestStore.store[:audited_ip] if RequestStore.store[:audited_ip]
36
+
37
+ nil # prevent stopping callback chains
38
+ end
39
+
40
+ end
41
+ 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
+ Espinita::Engine.routes.draw do
2
+ end
@@ -0,0 +1,15 @@
1
+ class CreateAuditableAudits < ActiveRecord::Migration
2
+ def change
3
+ create_table :espinita_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
+
12
+ t.timestamps
13
+ end
14
+ end
15
+ end
data/lib/espinita.rb ADDED
@@ -0,0 +1,16 @@
1
+ require "espinita/engine"
2
+ require "request_store"
3
+
4
+ module Espinita
5
+
6
+ autoload :Auditor, "espinita/auditor"
7
+ autoload :AuditorBehavior, "espinita/auditor_behavior"
8
+ autoload :AuditorRequest, "espinita/auditor_request"
9
+
10
+ attr_accessor :current_user_method
11
+
12
+ def self.current_user_method
13
+ @current_user_method ||= :current_user
14
+ end
15
+
16
+ end
@@ -0,0 +1,18 @@
1
+ module Espinita
2
+ module Auditor
3
+ extend ActiveSupport::Concern
4
+ include Espinita::AuditorBehavior
5
+
6
+ included do
7
+ has_many :audits, :as => :auditable, :class_name => Espinita::Audit.name
8
+ #attr_accessor :audited_user, :audited_ip
9
+ accepts_nested_attributes_for :audits
10
+ end
11
+
12
+ module ClassMethods
13
+
14
+ end
15
+
16
+ end
17
+ end
18
+
@@ -0,0 +1,83 @@
1
+ module Espinita
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
+ end
38
+
39
+ def permited_columns
40
+ self.column_names - self.excluded_cols.to_a
41
+ end
42
+
43
+ # All audits made during the block called will be recorded as made
44
+ # by +user+. This method is hopefully threadsafe, making it ideal
45
+ # for background operations that require audit information.
46
+ def as_user(user, &block)
47
+ RequestStore.store[:audited_user] = user
48
+ yield
49
+ ensure
50
+ RequestStore.store[:audited_user] = nil
51
+ end
52
+
53
+ end
54
+
55
+ # audited attributes detected against permited columns
56
+ def audited_attributes
57
+ self.changes.keys & self.class.permited_columns
58
+ end
59
+
60
+
61
+ def audit_create
62
+ puts self.class.audit_callbacks
63
+ write_audit(:action => 'create', :audited_changes => changes,
64
+ :comment => audit_comment)
65
+ end
66
+
67
+ def audit_update
68
+ puts self.class.audit_callbacks
69
+ write_audit(:action => 'update', :audited_changes => changes,
70
+ :comment => audit_comment)
71
+ end
72
+
73
+ def audit_destroy
74
+ write_audit(:action => 'destroy', :audited_changes => changes,
75
+ :comment => audit_comment)
76
+ end
77
+
78
+ def write_audit(options)
79
+ self.audits.create(options) unless audited_attributes.blank?
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,13 @@
1
+ module Espinita::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(Espinita.current_user_method) #current_user
10
+
11
+ RequestStore.store[:audited_ip] = self.try(:request).try(:remote_ip)
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ module Espinita
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Espinita
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, Espinita::AuditorRequest)
14
+ end
15
+
16
+
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ module Espinita
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :espinita 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( Espinita::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>.