dcidev_approval 0.0.8 → 0.0.12

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 (4) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +93 -1
  3. data/lib/dcidev_approval.rb +145 -165
  4. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 113b68a6c6cc3aa09902b2ede950d44deaa4a3f9cfe8a72c4ab4e1a9051e5ca7
4
- data.tar.gz: 489f1c91e45864e4d88ab12bd32dd34f9eafc1c1e381d9d8be92884703e7f316
3
+ metadata.gz: acf499a815402aa955b457d3fa2db0a97a6018135c1c8814ecad4c4894eeaf62
4
+ data.tar.gz: 12c8dffb9dea52b6d335e0a38194344f780dbf9e7d13259d4c92892d92dbdc60
5
5
  SHA512:
6
- metadata.gz: 6b59254605c479585ecd5289dfd6e1f41c15aa4cde5888a4c1f834633a1f56e13d04fe1570762507adf249411d11234e0aaf102bd13e150e9f2dfbe98c40aa98
7
- data.tar.gz: c1af9bc61fb9ae9fc0ce9da4daef4b1c9dc44975f04a4a68ca07371f4109ea7c3569f508243ed8fd799c0cb0b354aa2b3e106f27083973ff9875b307bb5becd5
6
+ metadata.gz: 1cd2869a8e8709375a4b82fde759ab37a7e43a41f1ceedbdf1f563e68a6ac58976c950b8b1088ca8bd7c50ee81e42a8d744bd207d5a5819d23d967fa6be8f242
7
+ data.tar.gz: 844432fe9092b9918d238ce1ff730dd4028993c087665f26c8361518f49d7c5a189362a7a987c2fc3a92fcb81ecd94cadcd651d3311649fa62e4a79357718e80
data/README.md CHANGED
@@ -1 +1,93 @@
1
- dcidev_approval
1
+ # Setup
2
+ ##### 1. Add new column named `status` to every model requiring this gem
3
+ `db/migrate/migration_file.rb`
4
+ ```ruby
5
+ class AddStatusToProduk < ActiveRecord::Migration[6.0]
6
+ def change
7
+ add_column :produk, :status, :string
8
+ add_column :produk, :change_status, :string
9
+ end
10
+ end
11
+ ```
12
+ `app/models/model.rb`
13
+ ```ruby
14
+ STATUS = %w[waiting approved rejected].freeze
15
+ enum status: STATUS.zip(STATUS).to_h, _prefix: true
16
+
17
+ CHANGE_STATUS = %w(pending_delete pending_update).freeze
18
+ enum change_status: CHANGE_STATUS.zip(CHANGE_STATUS).to_h, _prefix: true
19
+ ```
20
+
21
+
22
+
23
+ ##### 2. Format your `Agent` and `Role` with one-many relationship.
24
+ ##### 3. Declare instance method `is_admin?` to `Agent` to find out whether an agent is an admin or not
25
+
26
+ ```ruby
27
+ def is_admin?
28
+ # add your custom logic here
29
+ self.approved? && ["admin", "super_admin", "checker"].include?(self.try(:roles).try(:first).try(:code))
30
+ end
31
+ ```
32
+
33
+ ##### 4. Include the module to every model requiring approval. Or just put it in `ApplicationRecord`
34
+ `app/models/application_record.rb`
35
+ ```ruby
36
+ class ApplicationRecord < ActiveRecord::Base
37
+ include DcidevApproval
38
+ self.abstract_class = true
39
+ # ...
40
+ end
41
+ ```
42
+
43
+ # Features
44
+ * Create: `Model.create_data(declared(params), current_user, bypass)`
45
+ * Update: `model.edit_data(declared(params), current_user, bypass)`
46
+ * Delete: `model.delete_data(declared(params), current_user, bypass)`
47
+ * Approval: `model.approval(declared(params))`
48
+ * Compare current database value and argument to check if there are any update: `model.changes_present?(params)`
49
+ * Check approval status: `model.waiting_approval?`, `model.pending_insert?`, `model.pending_update?`, `model.pending_delete?`
50
+ * Find last lodifier & timestamp: `model.last_modified_by`
51
+ * Find author: `model.created_by`
52
+ * Find approval agent & timestamp: `model.last_approved_by`
53
+
54
+ Explanation
55
+ * `declared(params)`: is a hash value from Grape Parameters, plain ruby hash can also be used
56
+ * `current_user`: the agent responsible for the changes
57
+ * `bypass`: boolean value to toogle the approval system. If not sent, the default value is `true`
58
+
59
+ To track changes peformed to a record, call
60
+ # Callbacks
61
+ To execute code before/after the CRUD, include module `DcidevApproval` in `ApplicationRecord` and peform overide and or overload on it's child model.
62
+
63
+ `app/models/application_record.rb`
64
+ ```ruby
65
+ class ApplicationRecord < ActiveRecord::Base
66
+ include DcidevApproval
67
+ self.abstract_class = true
68
+ # ...
69
+ end
70
+ ```
71
+
72
+ `app/models/child_model.rb`
73
+ ```ruby
74
+ class ChildModel < ApplicationRecord
75
+ # ...
76
+ def self.create_data(params, agent, request)
77
+ super(params, agent, false) do |data|
78
+ # do something after the record is successfully created
79
+ # in this case, write an activity log
80
+ # the data variable will return the created record
81
+ ActivityLog.write("#{agent.is_admin? || params.bypass ? nil : "Request "} Add #{self.class.to_s}", request, agent, menu, data) if params.log
82
+ end
83
+ end
84
+
85
+ def edit_data(params, agent, request)
86
+ super(params, agent, false) do |_|
87
+ # do something after the record is successfully edited and require approval
88
+ end
89
+ end
90
+ # ...
91
+ end
92
+
93
+ ```
@@ -1,170 +1,150 @@
1
- module DcidevApproval
2
- def self.included base
3
- base.send :include, InstanceMethods
4
- base.extend ClassMethods
5
- end
6
-
7
- module InstanceMethods
8
-
9
- def changes_present?(changes)
10
- present = false
11
- changes.each do |k, v|
12
- begin
13
- if eval("self.#{k}") != v
14
- present = true
15
- break
16
- end
17
- rescue => _
18
- end
19
- end
20
- return present
21
- end
22
-
23
- def waiting_approval?
24
- %w[pending_update pending_delete].include?(self.change_status) || self.status == "waiting"
25
- end
26
-
27
- def pending_insert?
28
- self.change_status.nil? && %w[waiting rejected].include?(self.status)
29
- end
30
-
31
- def pending_update?
32
- self.change_status == "pending_update"
33
- end
34
-
35
- def pending_delete?
36
- self.change_status == "pending_delete"
37
- end
38
-
39
- def approved?
40
- self.status == "approved"
41
- end
42
-
43
- def rejected?
44
- self.status == "rejected"
45
- end
46
-
47
- def waiting?
48
- self.status == "waiting"
49
- end
50
-
51
- def last_modified_by
52
- # p self.audit_trail
53
- if self.try(:change_status).present? && self.try(:change_status) == 'pending_delete'
54
- log = self.activity_logs.where("activity LIKE '%delete%'").limit(1).order(created_at: :desc).try(:first)
55
- else
56
- log = self.activity_logs.where("activity LIKE '%edit%'").limit(1).order(created_at: :desc).try(:first)
57
- end
58
- {
59
- modified_by: log.present? ? log.try(:agent).try(:name).to_s + " (#{log.try(:agent).try(:username).to_s} | #{log.try(:agent).try(:roles).try(:first).try(:name)})" : "System",
60
- modified_at: log.present? ? log.try(:created_at) || self.try(:updated_at) || self.try(:created_at) : nil
61
- }
62
- end
63
-
64
- def created_by
65
- log = self.activity_logs.try(:first)
66
- {
67
- created_by: log.present? && log.try(:agent).try(:name).present? ? log.try(:agent).try(:name).to_s + " (#{log.try(:agent).try(:username).to_s} | #{log.try(:agent).try(:roles).try(:first).try(:name)})" : "System",
68
- created_at: self.try(:created_at) || log.try(:created_at)
69
- }
70
- end
71
-
72
- def last_approved_by
73
- last_approve = self.activity_logs.where("activity LIKE '%approv%'").limit(1).order(created_at: :desc).try(:first)
74
- last_entry = self.activity_logs.last
75
- {
76
- approved_by: last_approve.try(:id) == last_entry.try(:id) ? last_approve.try(:agent).try(:name).to_s + " (#{last_approve.try(:agent).try(:username).to_s} | #{last_approve.try(:agent).try(:roles).try(:first).try(:name)})" : nil,
77
- approved_at: last_approve.try(:id) == last_entry.try(:id) ? last_approve.try(:created_at) : nil
78
- }
79
- end
80
-
81
- def approve_changes
82
-
83
- if self.change_status.nil? && %w[waiting rejected].include?(self.status)
84
- raise self.errors.full_messages.join(", ") unless self.update(status: :approved, data_changes: nil, change_status: nil)
85
- # ActivityLog.write("Approve insert to #{self.class.to_s}", request, agent, menu, self) if params.log
86
- # self.delay(queue: "reorder_#{self.id}", priority: 0).reorder if self.class.column_names.include?("view_order")
87
-
88
- end
89
- if self.change_status == "pending_update"
90
- raise self.errors.full_messages.join(", ") unless self.update_by_params(self.data_changes, false)
91
- raise self.errors.full_messages.join(", ") unless self.update(status: :approved, data_changes: nil, change_status: nil)
92
- # ActivityLog.write("Approve update to #{self.class.to_s}", request, agent, menu, self) if params.log
93
- # self.delay(queue: "reorder_#{self.id}", priority: 0).reorder if self.class.column_names.include?("view_order")
1
+ # frozen_string_literal: true
94
2
 
95
- elsif self.change_status == "pending_delete"
96
- raise self.errors.full_messages.join(", ") unless self.update(change_status: nil, data_changes: nil)
97
- ActiveRecord::Base.transaction do
98
- # ActivityLog.write("Approve delete to #{self.class.to_s}", request, agent, menu, self) if params.log
99
- self.try(:destroy)
3
+ module DcidevApproval
4
+ def self.included(base)
5
+ base.send :include, InstanceMethods
6
+ base.extend ClassMethods
7
+ end
8
+
9
+ module InstanceMethods
10
+ def changes_present?(changes)
11
+ present = false
12
+ changes.each do |k, v|
13
+ if eval("self.#{k}") != v
14
+ present = true
15
+ break
16
+ end
17
+ rescue StandardError => _e
18
+ end
19
+ present
20
+ end
21
+
22
+ def waiting_approval?
23
+ %w[pending_update pending_delete].include?(change_status) || status == 'waiting'
24
+ end
25
+
26
+ def pending_insert?
27
+ change_status.nil? && %w[waiting rejected].include?(status)
28
+ end
29
+
30
+ def pending_update?
31
+ change_status == 'pending_update'
32
+ end
33
+
34
+ def pending_delete?
35
+ change_status == 'pending_delete'
36
+ end
37
+
38
+ def approved?
39
+ status == 'approved' || change_status.nil?
40
+ end
41
+
42
+ def rejected?
43
+ status == 'rejected'
44
+ end
45
+
46
+ def waiting?
47
+ status == 'waiting'
48
+ end
49
+
50
+ def last_modified_by
51
+ log = audit_trails.where(activity_type: %w[update delete]).limit(1).try(:first)
52
+ {
53
+ modified_by: log.present? ? log.try(:agent).try(:name).to_s + " (#{log.try(:agent).try(:roles).try(:first).try(:name)})" : nil,
54
+ modified_at: log.present? ? log.try(:created_at) || try(:updated_at) || try(:created_at) : nil
55
+ }
56
+ end
57
+
58
+ def created_by
59
+ log = audit_trails(:asc).where(activity_type: :create).limit(1).try(:first)
60
+ {
61
+ created_by: log.present? && log.try(:agent).try(:name).present? ? log.try(:agent).try(:name).to_s + " (#{log.try(:agent_role).try(:name)})" : 'System',
62
+ created_at: self.try(:created_at) || log.try(:created_at)
63
+ }
64
+ end
65
+
66
+ def last_approved_by
67
+ last_approve = audit_trails.where(activity_type: :approve).limit(1).try(:first)
68
+ last_entry = audit_trails.limit(1).order(created_at: :desc).try(:first)
69
+ {
70
+ approved_by: last_approve.try(:id) == last_entry.try(:id) ? last_approve.try(:agent).try(:name).to_s + " (#{last_approve.try(:agent_role).try(:name)})" : nil,
71
+ approved_at: last_approve.try(:id) == last_entry.try(:id) ? last_approve.try(:created_at) : nil
72
+ }
73
+ end
74
+
75
+ def approve_changes
76
+ if change_status.nil? && %w[waiting rejected].include?(status) && !update(status: :approved, data_changes: nil, change_status: nil)
77
+ raise errors.full_messages.join(', ')
78
+ end
79
+
80
+ case change_status
81
+ when 'pending_update'
82
+ raise errors.full_messages.join(', ') unless update_by_params(data_changes, false)
83
+ raise errors.full_messages.join(', ') unless update(status: :approved, data_changes: nil, change_status: nil)
84
+ when 'pending_delete'
85
+ destroy
86
+ end
87
+ end
88
+
89
+ def delete_changes
90
+ raise errors.full_messages.join(', ') unless update(data_changes: nil, change_status: nil, status: status == 'waiting' ? :rejected : :approved)
91
+ end
92
+
93
+ def edit_data(params, agent, bypass = true)
94
+ raise 'data still waiting for approval' if waiting_approval?
95
+
96
+ if bypass
97
+ raise errors.full_messages.join(', ') unless update_by_params(params, false)
98
+ elsif changes_present?(params)
99
+ ActiveRecord::Base.transaction do
100
+ data = agent.is_admin? || status == 'waiting' ? params : { change_status: :pending_update, data_changes: agent.is_admin? ? nil : params }
101
+ raise errors.full_messages.join(', ') unless update_by_params(data, false)
102
+ end
103
+ end
104
+ yield self
105
+ end
106
+
107
+ def approval(params)
108
+ case params.status
109
+ when 'approved'
110
+ approve_changes
111
+ when 'rejected'
112
+ delete_changes
113
+ end
114
+ yield self
115
+ end
116
+
117
+ def delete_data(agent, bypass = true)
118
+ raise 'data still waiting for approval' if waiting_approval?
119
+
120
+ if bypass || agent.is_admin?
121
+ ActiveRecord::Base.transaction do
122
+ raise errors.full_messages.join(', ') unless destroy
123
+ end
124
+ else
125
+ raise errors.full_messages.join(', ') unless update(change_status: :pending_delete)
126
+ end
127
+ yield true
100
128
  end
101
- end
102
- end
103
-
104
- def delete_changes
105
- # return unless %w[pending_update pending_delete].include? self.change_status
106
- raise self.errors.full_messages.join(", ") unless self.update(data_changes: nil, change_status: nil, status: self.status == "waiting" ? :rejected : :approved)
107
- # ActivityLog.write("Reject changes to #{self.class.to_s}", request, agent, menu, self) if params.log
108
- end
109
-
110
- def edit_data(params, agent, bypass = true)
111
- raise "data still waiting for approval" if self.waiting_approval?
112
- if bypass
113
- raise self.errors.full_messages.join(", ") unless self.update_by_params(params, false)
114
- # ActivityLog.write("Edit #{self.class.to_s}", request, agent, menu, self) if params.log
115
- else
116
- if self.changes_present?(params)
117
- ActiveRecord::Base.transaction do
118
- data = (agent.is_admin? || self.status == "waiting") ? params : { change_status: :pending_update, data_changes: agent.is_admin? ? nil : params }
119
- raise self.errors.full_messages.join(", ") unless self.update_by_params(data, false)
120
- end
121
- # ActivityLog.write("#{agent.is_admin? ? nil : "Request "}Edit #{self.class.to_s}", request, agent, menu, self) if params.log
122
- end
123
- end
124
- yield true
125
- end
126
-
127
- def approval(params)
128
- if params.status == "approved"
129
- self.approve_changes
130
- elsif params.status == "rejected"
131
- self.delete_changes
132
- end
133
- yield true
134
- end
135
-
136
- def delete_data(agent, bypass = true)
137
- raise "data still waiting for approval" if self.waiting_approval?
138
- if bypass || agent.is_admin?
139
- ActiveRecord::Base.transaction do
140
- # ActivityLog.write("Delete #{self.class.to_s}", request, agent, menu, self) if params.log
141
- raise self.errors.full_messages.join(", ") unless self.destroy
142
- end
143
- else
144
- raise self.errors.full_messages.join(", ") unless self.update(change_status: :pending_delete)
145
- # ActivityLog.write("Request Delete #{self.class.to_s}", request, agent, menu, self) if params.log
146
- end
147
- yield true
148
129
  end
149
- end
150
-
151
- module ClassMethods
152
- def create_data(params, agent, bypass = true)
153
- if bypass
154
- ActiveRecord::Base.transaction do
155
- data = params.merge!({ status: :approved })
156
- d = self.new_from_params(data)
157
- raise d.errors.full_messages.join(", ") unless d.save
158
- # ActivityLog.write("#{agent.is_admin? ? nil : "Request "} Add #{self.to_s}", request, agent, menu, d) if params.log
159
- end
160
- else
161
- d = self.new_from_params(params)
162
- d.status = agent.is_admin? ? :approved : :waiting
163
- raise d.errors.full_messages.join(", ") unless d.save
164
- # ActivityLog.write("Add #{self.to_s}", request, agent, menu, d) if params.log
165
- end
166
- yield d
130
+
131
+ module ClassMethods
132
+ def create_data(params, agent, bypass = true)
133
+ if bypass
134
+ ActiveRecord::Base.transaction do
135
+ data = params.merge!({ status: :approved })
136
+ d = new_from_params(data)
137
+ raise d.errors.full_messages.join(', ') unless d.save
138
+
139
+ yield d
140
+ end
141
+ else
142
+ d = new_from_params(params)
143
+ d.status = agent.is_admin? ? :approved : :waiting
144
+ raise d.errors.full_messages.join(', ') unless d.save
145
+
146
+ yield d
147
+ end
148
+ end
167
149
  end
168
- end
169
150
  end
170
-
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dcidev_approval
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.0.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Punto Damar P
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-04 00:00:00.000000000 Z
11
+ date: 2022-06-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dcidev_active_record