approvable 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
- metadata.gz: b8c9ad5fa11f3a6ca079cc935124013edace0ce5
4
- data.tar.gz: 727445aa21a877ea3340f9f07aeb83a87188b1ab
3
+ metadata.gz: 44c15bf9a39dd40e95b9d8455bf00c5bdcd44824
4
+ data.tar.gz: b91c51047334ead882747819116a498858a42c88
5
5
  SHA512:
6
- metadata.gz: 4151b5d043c9766856283cee5cbb6e837cb5600e68cbf4ef82e4664b4c76911038af4cefd84c3d71c5abd12647d8b18f4805bca3859e15b488ca46f9adff51ad
7
- data.tar.gz: af85d4ccc38564186c27f9b7978733fd3dd75ee0719454d93602d00c3e361b496a1804c7f088231bf0e5d2ddbf308f9b096eedcf862265825c157a9582f5053e
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, :dependent => :destroy, :polymorphic => true
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
- state_machine :initial => :pending do
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
- transition [:rejected, :pending] => :submitted
23
+ transitions from: [:rejected, :pending], to: :submitted
16
24
  end
17
25
 
18
26
  event :unsubmit do
19
- transition :submitted => :pending
27
+ transitions from: :submitted, to: :pending
20
28
  end
21
29
 
22
30
  event :approve do
23
- transition :submitted => :approved
31
+ transitions from: :submitted, to: :approved
24
32
  end
25
-
33
+
26
34
  event :reject do
27
- transition :submitted => :rejected
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
- transition :rejected => :pending
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? || submitted?
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
@@ -0,0 +1,5 @@
1
+ class AddNotesToApprovableChangeRequests < ActiveRecord::Migration
2
+ def change
3
+ add_column :approvable_change_requests, :notes, :json, default: {}
4
+ end
5
+ end
@@ -6,78 +6,143 @@ module Approvable
6
6
  end
7
7
 
8
8
  module ClassMethods
9
- def acts_as_approvable(options = {})
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
- alias_method_chain :save, :change_request
16
- alias_method_chain :save!, :change_request
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 if current_change_request
47
+ current_change_request ? current_change_request.state : 'approved'
26
48
  end
27
-
28
- def with_changes
29
- self.attributes = current_change_request.requested_changes || {}
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
- current_change_request.submit
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
- current_change_request.unsubmit
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
- if with_changes.save_without_change_request
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
- private
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
- def existing_changes
54
- current_change_request.try(:requested_changes) || {}
55
- end
95
+ assign_attributes_without_change_request ignored_params
96
+ end
97
+
98
+ private
56
99
 
57
- def new_changes
58
- self.attributes.select {|k,v| changed_attributes.keys.include? k}
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 all_changes
63
- existing_changes.merge new_changes
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
- def move_changes_to_change_request
67
- self.current_change_request_attributes = {requested_changes: all_changes}
68
- self.attributes = self.changed_attributes
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 save_with_change_request
72
- move_changes_to_change_request
73
- save_without_change_request
138
+ def auto_approve?
139
+ Approvable.auto_approve == true
74
140
  end
75
141
 
76
- def save_with_change_request!
77
- move_changes_to_change_request
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
@@ -1,9 +1,10 @@
1
1
  require 'approvable/acts_as_approvable'
2
- require 'state_machine'
3
- require 'graphviz' # Optional: only required for graphing
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
@@ -1,3 +1,3 @@
1
1
  module Approvable
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -0,0 +1,6 @@
1
+ class Foobar < ActiveRecord::Base
2
+ acts_as_approvable
3
+
4
+ store_accessor :json_hash, :foo, :bar
5
+
6
+ end
@@ -1,3 +1,5 @@
1
1
  class Listing < ActiveRecord::Base
2
2
  acts_as_approvable
3
+
4
+ validates :title, presence: true
3
5
  end
@@ -0,0 +1,9 @@
1
+ class CreateFoobars < ActiveRecord::Migration
2
+ def change
3
+ create_table :foobars do |t|
4
+ t.json :json_hash
5
+
6
+ t.timestamps
7
+ end
8
+ end
9
+ end
@@ -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: 20140807144654) do
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
- after(:create) {|c| c.submit; c.approve}
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
- after(:create) {|c| c.submit}
19
+ state :submitted
18
20
  end
19
21
 
20
22
  trait :rejected do
21
- after(:create) {|c| c.submit; c.reject}
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