approvable 0.0.1 → 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.
- checksums.yaml +4 -4
- data/app/models/approvable/change_request.rb +28 -13
- data/db/migrate/20140807145534_create_approvable_change_requests.rb +1 -1
- data/db/migrate/20140809183112_add_notes_to_approvable_change_requests.rb +5 -0
- data/lib/approvable/acts_as_approvable.rb +101 -36
- data/lib/approvable/engine.rb +4 -16
- data/lib/approvable/version.rb +1 -1
- data/spec/dummy/app/models/foobar.rb +6 -0
- data/spec/dummy/app/models/listing.rb +2 -0
- data/spec/dummy/db/migrate/20140816143802_create_foobars.rb +9 -0
- data/spec/dummy/db/schema.rb +33 -1
- data/spec/factories/change_requests.rb +5 -28
- data/spec/factories/listing.rb +4 -1
- data/spec/models/acts_as_approvable_spec.rb +344 -159
- data/spec/models/approvable/change_request_spec.rb +19 -20
- metadata +23 -9
- data/README.rdoc +0 -3
- data/spec/dummy/log/development.log +0 -339
- data/spec/dummy/log/test.log +0 -33704
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 44c15bf9a39dd40e95b9d8455bf00c5bdcd44824
|
4
|
+
data.tar.gz: b91c51047334ead882747819116a498858a42c88
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7f2c4b284e223bd22b5139b0d1d98cc2597fa3d5a580c6294d11510f52a618b773632dbf40e040060ee375ddc106c9dafc2efc80a303e5aaf2b83e640197f157
|
7
|
+
data.tar.gz: 8155feb1924e39a3aa114c67d5147481fdf3036e3645c32394e68a942489246b51060f1b5f784c422c2ff8acb3597463f28a1487502143b69f4058a26ee72d85
|
@@ -1,54 +1,69 @@
|
|
1
1
|
module Approvable
|
2
2
|
class ChangeRequest < ActiveRecord::Base
|
3
|
-
belongs_to :approvable, :
|
3
|
+
belongs_to :approvable, :polymorphic => true
|
4
4
|
belongs_to :approver, :polymorphic => true
|
5
5
|
|
6
6
|
validate :no_outstanding_change_requests, on: :create
|
7
|
-
validate :not_submitted_or_approved, if: :requested_changes_changed?
|
7
|
+
# validate :not_submitted_or_approved, if: :requested_changes_changed?
|
8
8
|
|
9
9
|
after_save :update_rejected_to_pending, if: :requested_changes_changed?
|
10
10
|
|
11
11
|
scope :unapproved, -> {where.not(state: 'approved')}
|
12
12
|
|
13
|
-
|
13
|
+
include AASM
|
14
|
+
|
15
|
+
aasm column: :state do
|
16
|
+
|
17
|
+
state :pending, :initial => true
|
18
|
+
state :submitted
|
19
|
+
state :rejected
|
20
|
+
state :approved
|
21
|
+
|
14
22
|
event :submit do
|
15
|
-
|
23
|
+
transitions from: [:rejected, :pending], to: :submitted
|
16
24
|
end
|
17
25
|
|
18
26
|
event :unsubmit do
|
19
|
-
|
27
|
+
transitions from: :submitted, to: :pending
|
20
28
|
end
|
21
29
|
|
22
30
|
event :approve do
|
23
|
-
|
31
|
+
transitions from: :submitted, to: :approved
|
24
32
|
end
|
25
|
-
|
33
|
+
|
26
34
|
event :reject do
|
27
|
-
|
35
|
+
transitions from: :submitted, to: :rejected, :on_transition => Proc.new {|obj, *args| obj.transition_options(*args)}
|
28
36
|
end
|
29
37
|
|
30
38
|
event :unreject do
|
31
|
-
|
39
|
+
transitions from: :rejected, to: :pending
|
32
40
|
end
|
33
41
|
end
|
34
42
|
|
43
|
+
def transition_options(options = {})
|
44
|
+
note = options[:note] if options
|
45
|
+
self.notes_will_change! if note
|
46
|
+
self.notes ||= {}
|
47
|
+
self.notes[Time.now.to_s] = note if note
|
48
|
+
end
|
49
|
+
|
35
50
|
private
|
36
51
|
|
37
52
|
def not_submitted_or_approved
|
38
|
-
if approved
|
53
|
+
if ['approved', 'submitted'].include? state_was
|
39
54
|
errors.add(:base, "cannot change a #{state} request")
|
40
55
|
end
|
41
56
|
end
|
42
|
-
|
57
|
+
|
43
58
|
def no_outstanding_change_requests
|
44
59
|
if self.class.where(approvable: approvable).unapproved.any?
|
45
60
|
errors.add(:base, 'please use the existing change request')
|
46
|
-
end
|
61
|
+
end
|
47
62
|
end
|
48
63
|
|
49
64
|
def update_rejected_to_pending
|
50
65
|
if rejected?
|
51
|
-
unreject
|
66
|
+
unreject!
|
52
67
|
end
|
53
68
|
end
|
54
69
|
|
@@ -3,7 +3,7 @@ class CreateApprovableChangeRequests < ActiveRecord::Migration
|
|
3
3
|
create_table :approvable_change_requests do |t|
|
4
4
|
t.string :approvable_type
|
5
5
|
t.integer :approvable_id
|
6
|
-
t.json :requested_changes
|
6
|
+
t.json :requested_changes, default: {}
|
7
7
|
t.string :state
|
8
8
|
t.string :approver_type
|
9
9
|
t.integer :approver_id
|
@@ -6,78 +6,143 @@ module Approvable
|
|
6
6
|
end
|
7
7
|
|
8
8
|
module ClassMethods
|
9
|
-
def acts_as_approvable
|
9
|
+
def acts_as_approvable **options
|
10
10
|
include Approvable::ActsAsApprovable::LocalInstanceMethods
|
11
11
|
|
12
|
-
has_many :change_requests, as: :approvable, class_name: 'Approvable::ChangeRequest'
|
13
|
-
has_one :current_change_request, -> {where.not(state: 'approved') }, as: :approvable, class_name: 'Approvable::ChangeRequest'
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
accepts_nested_attributes_for :current_change_request, update_only: true
|
12
|
+
has_many :change_requests, as: :approvable, class_name: 'Approvable::ChangeRequest', dependent: :destroy
|
13
|
+
has_one :current_change_request, -> {where.not(state: 'approved') }, as: :approvable, class_name: 'Approvable::ChangeRequest', autosave: true
|
14
|
+
|
15
|
+
before_save :apply_changes, if: :auto_approve?
|
16
|
+
after_save :force_approve!, if: :auto_approve?
|
18
17
|
|
18
|
+
cattr_accessor :filter_attrs, :filter_type
|
19
|
+
if options[:except]
|
20
|
+
self.filter_type = :except
|
21
|
+
self.filter_attrs = options[:except]
|
22
|
+
elsif options[:only]
|
23
|
+
self.filter_type = :only
|
24
|
+
self.filter_attrs = options[:only]
|
25
|
+
else
|
26
|
+
self.filter_type = :except
|
27
|
+
self.filter_attrs = []
|
28
|
+
end
|
29
|
+
|
30
|
+
unless method_defined?(:assign_attributes_without_change_request)
|
31
|
+
alias_method_chain :assign_attributes, :change_request
|
32
|
+
alias_method :attributes=, :assign_attributes_with_change_request
|
33
|
+
|
34
|
+
end
|
19
35
|
end
|
36
|
+
|
20
37
|
end
|
21
38
|
|
22
39
|
module LocalInstanceMethods
|
40
|
+
|
41
|
+
def requested_changes
|
42
|
+
current_change_request ? current_change_request.requested_changes.with_indifferent_access : {}
|
43
|
+
end
|
23
44
|
|
45
|
+
# use current_change_request here so that we dont get pending from a newly built change_request
|
24
46
|
def change_status
|
25
|
-
current_change_request.state
|
47
|
+
current_change_request ? current_change_request.state : 'approved'
|
26
48
|
end
|
27
|
-
|
28
|
-
def
|
29
|
-
|
49
|
+
|
50
|
+
def change_status_notes
|
51
|
+
current_change_request ? current_change_request.notes : {}
|
52
|
+
end
|
53
|
+
|
54
|
+
def apply_changes
|
55
|
+
self.assign_attributes_without_change_request requested_changes
|
30
56
|
self
|
31
57
|
end
|
32
58
|
|
33
59
|
def submit_changes
|
34
|
-
|
60
|
+
transaction do
|
61
|
+
current_change_request.submit! if current_change_request
|
62
|
+
reload
|
63
|
+
end
|
35
64
|
end
|
36
65
|
|
37
66
|
def unsubmit_changes
|
38
|
-
|
67
|
+
transaction do
|
68
|
+
current_change_request.unsubmit! if current_change_request
|
69
|
+
reload
|
70
|
+
end
|
39
71
|
end
|
40
72
|
|
41
73
|
def approve_changes
|
42
|
-
|
43
|
-
current_change_request.approve
|
74
|
+
transaction do
|
75
|
+
current_change_request.approve! if current_change_request
|
76
|
+
apply_changes.save
|
77
|
+
reload
|
44
78
|
end
|
45
79
|
end
|
46
80
|
|
47
|
-
def reject_changes
|
48
|
-
current_change_request.reject
|
81
|
+
def reject_changes options = {}
|
82
|
+
current_change_request.reject! :rejected, options if current_change_request
|
83
|
+
reload
|
49
84
|
end
|
50
85
|
|
51
|
-
|
86
|
+
def assign_attributes_with_change_request new_attributes
|
87
|
+
ignored_params = ignored_attributes(new_attributes)
|
88
|
+
approvable_params = approvable_attributes(new_attributes)
|
89
|
+
|
90
|
+
if approvable_params.any?
|
91
|
+
current_change_request || build_current_change_request(requested_changes: {})
|
92
|
+
current_change_request.requested_changes = requested_changes.merge approvable_params
|
93
|
+
end
|
52
94
|
|
53
|
-
|
54
|
-
|
55
|
-
|
95
|
+
assign_attributes_without_change_request ignored_params
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
56
99
|
|
57
|
-
def
|
58
|
-
self.
|
100
|
+
def ignored_attributes(new_attributes)
|
101
|
+
process_nested_hash(new_attributes, self.class.filter_attrs, filter_type == :except)
|
59
102
|
end
|
60
|
-
|
61
103
|
|
62
|
-
def
|
63
|
-
|
104
|
+
def approvable_attributes(new_attributes)
|
105
|
+
process_nested_hash(new_attributes, self.class.filter_attrs, filter_type == :only )
|
64
106
|
end
|
65
107
|
|
66
|
-
|
67
|
-
|
68
|
-
|
108
|
+
# h = {"first_name"=>"Leif", "last_name"=>"Gensert", "address"=>{"street"=>"Preysinstraße", "city"=>"München"}}
|
109
|
+
def process_nested_hash(attributes, keys, should_match)
|
110
|
+
attributes = attributes.dup.stringify_keys!
|
111
|
+
hash = {}
|
112
|
+
[*keys].each do |key|
|
113
|
+
if key.is_a? Hash
|
114
|
+
key.each do |k,v|
|
115
|
+
hash[k.to_s] = process_nested_hash(attributes[k.to_s], v, should_match) if attributes[k.to_s]
|
116
|
+
end
|
117
|
+
elsif key.is_a? Array
|
118
|
+
key.each do|k|
|
119
|
+
process_nested_hash(attributes[k.to_s], k, should_match) if attributes[k.to_s]
|
120
|
+
end
|
121
|
+
else
|
122
|
+
value = attributes.delete(key.to_s)
|
123
|
+
hash[key.to_s] = value if value
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
if should_match
|
128
|
+
return hash
|
129
|
+
else
|
130
|
+
return attributes
|
131
|
+
end
|
132
|
+
|
69
133
|
end
|
134
|
+
|
135
|
+
# process_nested_hash h, [:first_name, {address: :street}], true
|
136
|
+
# process_nested_hash h, [:first_name, {address: :street}], false
|
70
137
|
|
71
|
-
def
|
72
|
-
|
73
|
-
save_without_change_request
|
138
|
+
def auto_approve?
|
139
|
+
Approvable.auto_approve == true
|
74
140
|
end
|
75
141
|
|
76
|
-
def
|
77
|
-
|
78
|
-
save_without_change_request!
|
142
|
+
def force_approve!
|
143
|
+
current_change_request.update_column :state, 'approved' if current_change_request
|
79
144
|
end
|
80
|
-
|
145
|
+
|
81
146
|
end
|
82
147
|
end
|
83
148
|
end
|
data/lib/approvable/engine.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
require 'approvable/acts_as_approvable'
|
2
|
-
require '
|
3
|
-
require '
|
4
|
-
require 'state_machine/version'
|
2
|
+
require 'aasm'
|
3
|
+
require 'activesupport/json_encoder'
|
5
4
|
|
6
5
|
module Approvable
|
6
|
+
cattr_accessor :disabled, :auto_approve
|
7
|
+
|
7
8
|
class Engine < ::Rails::Engine
|
8
9
|
isolate_namespace Approvable
|
9
10
|
|
@@ -15,19 +16,6 @@ module Approvable
|
|
15
16
|
end
|
16
17
|
end
|
17
18
|
|
18
|
-
|
19
|
-
initializer :state_machine do |app|
|
20
|
-
unless StateMachine::VERSION == '1.2.0'
|
21
|
-
# If you see this message, please test removing this file
|
22
|
-
# If it's still required, please bump up the version above
|
23
|
-
Rails.logger.warn "Please remove me, StateMachine version has changed"
|
24
|
-
end
|
25
|
-
|
26
|
-
module StateMachine::Integrations::ActiveModel
|
27
|
-
public :around_validation
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
19
|
|
32
20
|
config.generators do |g|
|
33
21
|
g.test_framework :rspec, :fixture => false
|
data/lib/approvable/version.rb
CHANGED
data/spec/dummy/db/schema.rb
CHANGED
@@ -11,11 +11,43 @@
|
|
11
11
|
#
|
12
12
|
# It's strongly recommended that you check this file into your version control system.
|
13
13
|
|
14
|
-
ActiveRecord::Schema.define(version:
|
14
|
+
ActiveRecord::Schema.define(version: 20140816143802) do
|
15
15
|
|
16
16
|
# These are extensions that must be enabled in order to support this database
|
17
17
|
enable_extension "plpgsql"
|
18
18
|
|
19
|
+
create_table "approvable_change_requests", force: true do |t|
|
20
|
+
t.string "approvable_type"
|
21
|
+
t.integer "approvable_id"
|
22
|
+
t.json "requested_changes", default: {}
|
23
|
+
t.string "state"
|
24
|
+
t.string "approver_type"
|
25
|
+
t.integer "approver_id"
|
26
|
+
t.datetime "created_at"
|
27
|
+
t.datetime "updated_at"
|
28
|
+
t.json "notes", default: {}
|
29
|
+
end
|
30
|
+
|
31
|
+
create_table "bars", force: true do |t|
|
32
|
+
t.string "title"
|
33
|
+
t.integer "listing_id"
|
34
|
+
t.datetime "created_at"
|
35
|
+
t.datetime "updated_at"
|
36
|
+
end
|
37
|
+
|
38
|
+
create_table "foobars", force: true do |t|
|
39
|
+
t.json "json_hash"
|
40
|
+
t.datetime "created_at"
|
41
|
+
t.datetime "updated_at"
|
42
|
+
end
|
43
|
+
|
44
|
+
create_table "foos", force: true do |t|
|
45
|
+
t.string "title"
|
46
|
+
t.integer "listing_id"
|
47
|
+
t.datetime "created_at"
|
48
|
+
t.datetime "updated_at"
|
49
|
+
end
|
50
|
+
|
19
51
|
create_table "listings", force: true do |t|
|
20
52
|
t.string "title"
|
21
53
|
t.text "description"
|
@@ -7,44 +7,21 @@ FactoryGirl.define do
|
|
7
7
|
|
8
8
|
|
9
9
|
trait :approved do
|
10
|
-
|
10
|
+
state :approved
|
11
11
|
end
|
12
12
|
|
13
13
|
trait :pending do
|
14
|
+
state :pending
|
15
|
+
|
14
16
|
end
|
15
17
|
|
16
18
|
trait :submitted do
|
17
|
-
|
19
|
+
state :submitted
|
18
20
|
end
|
19
21
|
|
20
22
|
trait :rejected do
|
21
|
-
|
23
|
+
state :rejected
|
22
24
|
end
|
23
|
-
|
24
|
-
|
25
|
-
# trait :pending do
|
26
|
-
# submitted_at nil
|
27
|
-
# approved_at nil
|
28
|
-
# rejected_at nil
|
29
|
-
# end
|
30
|
-
#
|
31
|
-
# trait :submitted do
|
32
|
-
# submitted_at {Time.now}
|
33
|
-
# approved_at nil
|
34
|
-
# rejected_at nil
|
35
|
-
# end
|
36
|
-
#
|
37
|
-
# trait :approved do
|
38
|
-
# submitted_at {Time.now}
|
39
|
-
# approved_at {Time.now}
|
40
|
-
# rejected_at nil
|
41
|
-
# end
|
42
|
-
#
|
43
|
-
# trait :rejected do
|
44
|
-
# submitted_at {Time.now}
|
45
|
-
# approved_at nil
|
46
|
-
# rejected_at {Time.now}
|
47
|
-
# end
|
48
25
|
|
49
26
|
end
|
50
27
|
end
|