approval2 0.1.2 → 0.1.3
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/README.md +8 -8
- data/approval2.gemspec +2 -0
- data/lib/approval2/active_record_adapter.rb +10 -0
- data/lib/approval2/controller_additions.rb +61 -0
- data/lib/approval2/model_additions.rb +90 -0
- data/lib/approval2/version.rb +1 -1
- data/lib/approval2.rb +3 -4
- data/lib/generators/approval2/install/install_generator.rb +37 -0
- data/lib/generators/approval2/install/templates/_approve.html.haml +15 -0
- data/lib/generators/approval2/install/templates/create_unapproved_records.rb +11 -0
- data/lib/generators/approval2/install/templates/index.html.haml +23 -0
- data/lib/generators/approval2/install/templates/unapproved_record.rb +3 -0
- data/lib/generators/approval2/install/templates/unapproved_records_controller.rb +15 -0
- metadata +38 -1
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NDljMWE3ODNiZjFjMzJhODEyN2MwZDRiYTQ1ZWUxM2IxNzZkZmY4MA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YzYxODYyNjBlNmJkZWM5MDc4MmRmMDc3YmZiZjhiY2FjZjEyMWMzOA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ODE3M2FlNDMzMTM0YzU2NGQ0NGQwZTUyYTVhZTMzODdhZTkzODM0N2MzY2E3
|
10
|
+
OWMxYmFmZGY2NzcwNTJhNDY3ZWMwYjNjZjY5MGE4OTI5MTdjY2Y3ZmRiYjJj
|
11
|
+
MTI4MTUxZTkxYjE5ODcyOWU5ZGFmZjQzNTA2ZTI4OGQyMjg1MDg=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
OGFlOGQyOTlhYjQ5NzBmMDQ4OGIwMmU4Mzg3ZWY0YjZlZjkwYzQ0MDI2NzQ5
|
14
|
+
Nzk0ZDg5MGFlZmI1OTA1ZTVkYzNiZjRkZTcyMzc2NTE4YTQ4ZWFjNmQwYmI2
|
15
|
+
Yzg2MmQ0MTYwYmIzZTNkNzg3OTdhOWJlY2ZkY2E3ZDUzMDUwM2I=
|
data/README.md
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
# Approval2
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
TODO: Delete this and the text above, and describe your gem
|
3
|
+
A trivial implementation of the 4 eyes principle. All record actions require an approval to take effect.
|
6
4
|
|
5
|
+
|
7
6
|
## Installation
|
8
7
|
|
9
8
|
Add this line to your application's Gemfile:
|
@@ -22,13 +21,14 @@ Or install it yourself as:
|
|
22
21
|
|
23
22
|
## Usage
|
24
23
|
|
25
|
-
|
26
|
-
|
27
|
-
## Development
|
24
|
+
Models that need an approval, should have the standard approval columns (can be added by included t.approval_columns) and should include include Approval2::ModelAdditions in the class.
|
28
25
|
|
29
|
-
|
26
|
+
Doing so adds the following columns to the model
|
30
27
|
|
31
|
-
|
28
|
+
1. approval_status
|
29
|
+
2. approved_version
|
30
|
+
3. approved_id
|
31
|
+
4. last_action
|
32
32
|
|
33
33
|
## Contributing
|
34
34
|
|
data/approval2.gemspec
CHANGED
@@ -0,0 +1,10 @@
|
|
1
|
+
module ActiveRecord::ConnectionAdapters
|
2
|
+
class TableDefinition
|
3
|
+
def approval_columns(*args)
|
4
|
+
column(:approval_status, :string, limit: 1, default: 'U', null: false, comment: "the approval status of the record, A (approved), U (unapproved)")
|
5
|
+
column(:last_action, :string, limit: 1, default: 'C', comment: "the last action on the record, C (create), U (update), D (delete)")
|
6
|
+
column(:approved_id, :integer, comment: "the id of the approved record that was edited, and resulted in this unapproved record ")
|
7
|
+
column(:approved_version, :integer, comment: "the lock_version of the approved record at the time it was edited, and resulted in this unapproved record")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Approval2
|
2
|
+
module ControllerAdditions
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
before_filter :before_edit, only: :edit
|
7
|
+
before_filter :before_approve, only: :approve
|
8
|
+
before_filter :before_index, only: :index
|
9
|
+
end
|
10
|
+
|
11
|
+
def approve
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def before_index
|
17
|
+
if (params[:approval_status].present? and params[:approval_status] == 'U')
|
18
|
+
modelName = self.class.name.sub("Controller", "").underscore.split('/').last.singularize
|
19
|
+
modelKlass = modelName.classify.constantize
|
20
|
+
|
21
|
+
x = modelKlass.unscoped.where("approval_status =?",'U').order("id desc")
|
22
|
+
instance_variable_set("@#{modelName}s", x.paginate(:per_page => 10, :page => params[:page]))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def before_edit
|
27
|
+
modelName = self.class.name.sub("Controller", "").underscore.split('/').last.singularize
|
28
|
+
modelKlass = modelName.classify.constantize
|
29
|
+
|
30
|
+
x = modelKlass.unscoped.find_by_id(params[:id])
|
31
|
+
if x.approval_status == 'A' && x.unapproved_record.nil?
|
32
|
+
params = (x.attributes).merge({:approved_id => x.id,:approved_version => x.lock_version})
|
33
|
+
x = modelKlass.new(params)
|
34
|
+
end
|
35
|
+
|
36
|
+
instance_variable_set("@#{modelName}", x)
|
37
|
+
end
|
38
|
+
|
39
|
+
def before_approve
|
40
|
+
modelName = self.class.name.sub("Controller", "").underscore.split('/').last.singularize
|
41
|
+
modelKlass = modelName.classify.constantize
|
42
|
+
|
43
|
+
x = modelKlass.unscoped.find(params[:id]) rescue nil
|
44
|
+
modelKlass.transaction do
|
45
|
+
approval = x.approve
|
46
|
+
if approval.empty?
|
47
|
+
flash[:alert] = "#{modelName.humanize.titleize} record was approved successfully"
|
48
|
+
else
|
49
|
+
msg = x.errors.full_messages << approval
|
50
|
+
flash[:alert] = msg
|
51
|
+
raise ActiveRecord::Rollback
|
52
|
+
end
|
53
|
+
end
|
54
|
+
redirect_to x
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
ActionController::Base.class_eval do
|
60
|
+
include Approval2::ControllerAdditions
|
61
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Approval2
|
2
|
+
module ModelAdditions
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
audited except: [:approval_status, :last_action]
|
7
|
+
|
8
|
+
# refers to the unapproved record in the common unapproved_record model, this is true only for U records
|
9
|
+
has_one :unapproved_record_entry, :as => :approvable, :class_name => '::UnapprovedRecord'
|
10
|
+
|
11
|
+
# refers to the approved/unapproved record in the model
|
12
|
+
belongs_to :unapproved_record, :primary_key => 'approved_id', :foreign_key => 'id', :class_name => self.name, :unscoped => true
|
13
|
+
belongs_to :approved_record, :foreign_key => 'approved_id', :primary_key => 'id', :class_name => self.name, :unscoped => true
|
14
|
+
|
15
|
+
validates_uniqueness_of :approved_id, :allow_blank => true
|
16
|
+
validate :validate_unapproved_record
|
17
|
+
|
18
|
+
def self.default_scope
|
19
|
+
where approval_status: 'A'
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
after_create :on_create_create_unapproved_record_entry
|
24
|
+
after_destroy :on_destory_remove_unapproved_record_entries
|
25
|
+
after_update :on_update_remove_unapproved_record_entries
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
def validate_unapproved_record
|
30
|
+
errors.add(:base,"Unapproved Record Already Exists for this record") if !unapproved_record.nil? and (approval_status == 'A' and approval_status_was == 'A')
|
31
|
+
end
|
32
|
+
|
33
|
+
def approve
|
34
|
+
return "The record version is different from that of the approved version" if !self.approved_record.nil? and self.approved_version != self.approved_record.lock_version
|
35
|
+
|
36
|
+
# make the U the A record, also assign the id of the A record, this looses history
|
37
|
+
# self.approval_status = 'A'
|
38
|
+
# self.approved_record.delete unless self.approved_record.nil?
|
39
|
+
# self.update_column(:id, self.approved_id) unless self.approved_id.nil?
|
40
|
+
# self.approved_id = nil
|
41
|
+
|
42
|
+
|
43
|
+
if self.approved_record.nil?
|
44
|
+
# create action, all we need to do is set the status to approved
|
45
|
+
self.approval_status = 'A'
|
46
|
+
else
|
47
|
+
# copy all attributes of the U record to the A record, and delete the U record
|
48
|
+
attributes = self.attributes.select do |attr, value|
|
49
|
+
self.class.column_names.include?(attr.to_s) and
|
50
|
+
['id', 'approved_id', 'approval_status', 'lock_version', 'approved_version', 'created_at', 'updated_at', 'updated_by', 'created_by'].exclude?(attr)
|
51
|
+
end
|
52
|
+
|
53
|
+
self.class.unscoped do
|
54
|
+
approved_record = self.approved_record
|
55
|
+
approved_record.assign_attributes(attributes)
|
56
|
+
approved_record.last_action = 'U'
|
57
|
+
approved_record.updated_by = self.created_by
|
58
|
+
self.destroy
|
59
|
+
# not enought time to test cases where the approval is being done after changes in validations of the model, such that the saving of the approved
|
60
|
+
# record fails, this can be fixed to return the errors so that they can be shown to the user
|
61
|
+
approved_record.save!
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
return ""
|
66
|
+
end
|
67
|
+
|
68
|
+
def enable_approve_button?
|
69
|
+
self.approval_status == 'U' ? true : false
|
70
|
+
end
|
71
|
+
|
72
|
+
def on_create_create_unapproved_record_entry
|
73
|
+
if approval_status == 'U'
|
74
|
+
UnapprovedRecord.create!(:approvable => self)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def on_destory_remove_unapproved_record_entries
|
79
|
+
if approval_status == 'U'
|
80
|
+
unapproved_record_entry.delete
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def on_update_remove_unapproved_record_entries
|
85
|
+
if approval_status == 'A' and approval_status_was == 'U'
|
86
|
+
unapproved_record_entry.delete
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/approval2/version.rb
CHANGED
data/lib/approval2.rb
CHANGED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/migration'
|
3
|
+
|
4
|
+
module Approval2
|
5
|
+
module Generators
|
6
|
+
class InstallGenerator < ::Rails::Generators::Base
|
7
|
+
include Rails::Generators::Migration
|
8
|
+
source_root File.expand_path('../templates', __FILE__)
|
9
|
+
desc "Add the migrations for unapproved_records"
|
10
|
+
|
11
|
+
def self.next_migration_number(path)
|
12
|
+
next_migration_number = current_migration_number(path) + 1
|
13
|
+
ActiveRecord::Migration.next_migration_number(next_migration_number)
|
14
|
+
end
|
15
|
+
|
16
|
+
def copy_migrations
|
17
|
+
migration_template "create_unapproved_records.rb", "db/migrate/create_unapproved_records.rb"
|
18
|
+
end
|
19
|
+
|
20
|
+
def copy_models
|
21
|
+
copy_file "unapproved_record.rb", "#{Rails.root}/app/models/unapproved_record.rb"
|
22
|
+
end
|
23
|
+
|
24
|
+
def copy_views
|
25
|
+
["index.html.haml", "_approve.html.haml"].each do |v|
|
26
|
+
copy_file v, "#{Rails.root}/app/views/unapproved_records/#{v}"
|
27
|
+
copy_file v, "#{Rails.root}/app/views/unapproved_records/#{v}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def copy_controllers
|
32
|
+
copy_file "unapproved_records_controller.rb", "#{Rails.root}/app/controllers/unapproved_records_controller.rb"
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
- can = can? :approve, @record
|
2
|
+
- approve_flag = @record.enable_approve_button?
|
3
|
+
%a.btn{"data-toggle" => "modal", :href => "#{!(can && approve_flag) ? '#' : '#myModalApprove'}", :role => "button", :class => "btn btn-primary #{(can && approve_flag) ? '' : 'disabled'}"} Approve
|
4
|
+
.modal.hide.fade{"id" => "myModalApprove", "aria-hidden" => "true", "aria-labelledby" => " myModalLabel", :role => "dialog", :tabindex => "-1"}
|
5
|
+
.modal-header
|
6
|
+
%button.close{"aria-hidden" => "true", "data-dismiss" => "modal", :type => "button"} ×
|
7
|
+
%h3#myModalLabel Acknowledge
|
8
|
+
#error_message{:style => 'color:red'}
|
9
|
+
.modal-body
|
10
|
+
= simple_form_for @record, :url => {:action => 'approve'}, :method => :put, :html=>{:id=>"transition"} do |ef|
|
11
|
+
= ef.input :updated_by, :as => :hidden, :input_html => {:value => current_user.id}
|
12
|
+
= submit_tag "Confirm", :class=>"btn btn-primary transition_button", :id => "transition_button"
|
13
|
+
%p{:style => 'color:green;'}
|
14
|
+
= created_or_edited_by(@record)
|
15
|
+
%br
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class CreateUnApprovedRecords < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :unapproved_records, {:sequence_start_value => '1 cache 20 order increment by 1'} do |t|
|
4
|
+
t.integer :approvable_id
|
5
|
+
t.string :approvable_type
|
6
|
+
t.timestamps null: false
|
7
|
+
|
8
|
+
t.index([:approvable_id, :approvable_type], unique: true, name: :uk_unapproved_records)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
%br
|
2
|
+
%h1 Unapproved Records
|
3
|
+
%br
|
4
|
+
- unless @records.nil?
|
5
|
+
%br
|
6
|
+
= will_paginate @records
|
7
|
+
%br
|
8
|
+
%table.table.table-bordered.table-striped.table-hover
|
9
|
+
.thead
|
10
|
+
%th{:style=>'text-align:left; background-color: lightblue;'}
|
11
|
+
Record Type
|
12
|
+
%th{:style=>'text-align:left; background-color: lightblue;'}
|
13
|
+
Record Count
|
14
|
+
.tbody
|
15
|
+
- @records.each do |record|
|
16
|
+
%tr
|
17
|
+
%td{:style=>'text-align:left;'}
|
18
|
+
= record[:record_type]
|
19
|
+
%td{:style=>'text-align:left;'}
|
20
|
+
- if record[:record_type] == 'IncomingFile'
|
21
|
+
= link_to record[:record_count], {:controller => record[:record_type].tableize, :action => :index, :approval_status => 'U', :sc_service => 'AML'}
|
22
|
+
- else
|
23
|
+
= link_to record[:record_count], {:controller => record[:record_type].tableize, :action => :index, :approval_status => 'U'}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'will_paginate/array'
|
2
|
+
|
3
|
+
class UnapprovedRecordsController < ApplicationController
|
4
|
+
def index
|
5
|
+
result = []
|
6
|
+
UnapprovedRecord.distinct.select(:approvable_type).each do |m|
|
7
|
+
if m.approvable_type.constantize.column_names.include?('approval_status')
|
8
|
+
count = UnapprovedRecord.where("approvable_type =?", m.approvable_type).count
|
9
|
+
result << {:record_type => m.approvable_type, :record_count => count}
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
@records = result.paginate(:per_page => 10, :page => params[:page]) rescue []
|
14
|
+
end
|
15
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: approval2
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- akil
|
@@ -38,6 +38,34 @@ dependencies:
|
|
38
38
|
- - ~>
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: audited
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: will_paginate
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
41
69
|
description:
|
42
70
|
email:
|
43
71
|
- akhilesh.kataria@quantiguous.com
|
@@ -55,7 +83,16 @@ files:
|
|
55
83
|
- bin/console
|
56
84
|
- bin/setup
|
57
85
|
- lib/approval2.rb
|
86
|
+
- lib/approval2/active_record_adapter.rb
|
87
|
+
- lib/approval2/controller_additions.rb
|
88
|
+
- lib/approval2/model_additions.rb
|
58
89
|
- lib/approval2/version.rb
|
90
|
+
- lib/generators/approval2/install/install_generator.rb
|
91
|
+
- lib/generators/approval2/install/templates/_approve.html.haml
|
92
|
+
- lib/generators/approval2/install/templates/create_unapproved_records.rb
|
93
|
+
- lib/generators/approval2/install/templates/index.html.haml
|
94
|
+
- lib/generators/approval2/install/templates/unapproved_record.rb
|
95
|
+
- lib/generators/approval2/install/templates/unapproved_records_controller.rb
|
59
96
|
homepage: http://quantiguous.com
|
60
97
|
licenses:
|
61
98
|
- MIT
|