workflowable 1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +202 -0
  3. data/README.md +23 -0
  4. data/Rakefile +39 -0
  5. data/app/assets/images/workflowable/select2-spinner.gif +0 -0
  6. data/app/assets/images/workflowable/select2.png +0 -0
  7. data/app/assets/images/workflowable/select2x2.png +0 -0
  8. data/app/assets/javascripts/workflowable/application.js +118 -0
  9. data/app/assets/javascripts/workflowable/d3.min.js +5 -0
  10. data/app/assets/javascripts/workflowable/dagre-min.js +2 -0
  11. data/app/assets/javascripts/workflowable/foundation/fastclick.js +9 -0
  12. data/app/assets/javascripts/workflowable/foundation/foundation.min.js +10 -0
  13. data/app/assets/javascripts/workflowable/foundation/modernizr.js +8 -0
  14. data/app/assets/javascripts/workflowable/foundation/placeholder.js +2 -0
  15. data/app/assets/javascripts/workflowable/select2.min.js +22 -0
  16. data/app/assets/javascripts/workflowable/workflow.js +2 -0
  17. data/app/assets/stylesheets/workflowable/application.css +26 -0
  18. data/app/assets/stylesheets/workflowable/dagre-d3.css +19 -0
  19. data/app/assets/stylesheets/workflowable/foundation/foundation.min.css +1 -0
  20. data/app/assets/stylesheets/workflowable/foundation/normalize.css +423 -0
  21. data/app/assets/stylesheets/workflowable/select2.css +646 -0
  22. data/app/assets/stylesheets/workflowable/workflow.css +4 -0
  23. data/app/controllers/workflowable/actions_controller.rb +35 -0
  24. data/app/controllers/workflowable/application_controller.rb +18 -0
  25. data/app/controllers/workflowable/workflows_controller.rb +137 -0
  26. data/app/helpers/workflowable/application_helper.rb +19 -0
  27. data/app/helpers/workflowable/workflow_helper.rb +19 -0
  28. data/app/models/workflowable/action.rb +113 -0
  29. data/app/models/workflowable/stage.rb +104 -0
  30. data/app/models/workflowable/stage_action.rb +24 -0
  31. data/app/models/workflowable/stage_next_step.rb +21 -0
  32. data/app/models/workflowable/workflow.rb +57 -0
  33. data/app/models/workflowable/workflow_action.rb +23 -0
  34. data/app/views/layouts/workflowable/application.html.erb +24 -0
  35. data/app/views/shared/actions/_details.html.erb +30 -0
  36. data/app/views/shared/actions/_fields.html.erb +26 -0
  37. data/app/views/workflowable/actions/_options.html.erb +39 -0
  38. data/app/views/workflowable/actions/options.js.erb +2 -0
  39. data/app/views/workflowable/workflows/_form.html.erb +57 -0
  40. data/app/views/workflowable/workflows/configure_stages.html.erb +34 -0
  41. data/app/views/workflowable/workflows/edit.html.erb +2 -0
  42. data/app/views/workflowable/workflows/index.html.erb +16 -0
  43. data/app/views/workflowable/workflows/new.html.erb +2 -0
  44. data/app/views/workflowable/workflows/show.html.erb +49 -0
  45. data/app/views/workflowable/workflows/stages.json.jbuilder +8 -0
  46. data/config/routes.rb +34 -0
  47. data/db/migrate/20140406195941_create_workflowable_workflows.rb +25 -0
  48. data/db/migrate/20140407170846_create_workflowable_stages.rb +25 -0
  49. data/db/migrate/20140407184821_rename_workflow_initial_stage_to_initial_stage_id.rb +20 -0
  50. data/db/migrate/20140407191620_create_workflowable_stage_next_steps.rb +25 -0
  51. data/db/migrate/20140407193057_create_workflowable_stage_actions.rb +26 -0
  52. data/db/migrate/20140407194247_create_workflowable_actions.rb +26 -0
  53. data/db/migrate/20140408164900_create_workflowable_workflow_actions.rb +25 -0
  54. data/db/migrate/20140708173311_add_order_to_action.rb +20 -0
  55. data/db/migrate/20140818062426_add_position_to_stage_action.rb +20 -0
  56. data/lib/tasks/workflowable_tasks.rake +4 -0
  57. data/lib/workflowable.rb +26 -0
  58. data/lib/workflowable/actions/action.rb +76 -0
  59. data/lib/workflowable/engine.rb +47 -0
  60. data/lib/workflowable/model_additions.rb +152 -0
  61. data/lib/workflowable/railtie.rb +25 -0
  62. data/lib/workflowable/version.rb +18 -0
  63. metadata +301 -0
@@ -0,0 +1,4 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
@@ -0,0 +1,35 @@
1
+ # Copyright 2014 Netflix, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ require_dependency "workflowable/application_controller"
17
+
18
+ module Workflowable
19
+ class ActionsController < ApplicationController
20
+
21
+ def options
22
+
23
+ action = Action.new(action_plugin: params[:action_plugin])
24
+ @options = action.available_options
25
+
26
+ respond_to do |format|
27
+ format.json { render json: @options }
28
+ format.js
29
+
30
+ end
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -0,0 +1,18 @@
1
+ # Copyright 2014 Netflix, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Workflowable
16
+ class ApplicationController < ActionController::Base
17
+ end
18
+ end
@@ -0,0 +1,137 @@
1
+ # Copyright 2014 Netflix, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ require_dependency "workflowable/application_controller"
17
+
18
+ module Workflowable
19
+ class WorkflowsController < ApplicationController
20
+ before_filter :load_workflow, only: [:show, :edit, :update, :destroy, :stages, :configure_stages]
21
+ def index
22
+ @workflows = Workflowable::Workflow.order(:name)
23
+ end
24
+
25
+ def new
26
+ @workflow = Workflow.new
27
+
28
+ respond_to do |format|
29
+ format.html # new.html.erb
30
+ format.json { render json: @workflow }
31
+ end
32
+ end
33
+
34
+ # GET /workflows/1/edit
35
+ def edit
36
+
37
+ end
38
+
39
+ # POST /workflows
40
+ # POST /workflows.json
41
+ def create
42
+ @workflow = Workflow.new(workflow_params)
43
+
44
+
45
+ respond_to do |format|
46
+ if @workflow.save
47
+ format.html {
48
+ if(@workflow.stages.count > 1)
49
+ redirect_to configure_stages_workflow_path(@workflow), notice: 'Workflow was successfully created. Please configure transitions.'
50
+ else
51
+ redirect_to @workflow, notice: 'Workflow was successfully created.'
52
+ end
53
+
54
+ }
55
+ format.json { render json: @workflow, status: :created, location: @workflow }
56
+ else
57
+ format.html { render action: "new" }
58
+ format.json { render json: @workflow.errors, status: :unprocessable_entity }
59
+ end
60
+ end
61
+ end
62
+
63
+ # PUT /workflows/1
64
+ # PUT /workflows/1.json
65
+ def update
66
+
67
+
68
+ respond_to do |format|
69
+ if @workflow.update_attributes(workflow_params)
70
+ format.html { redirect_to @workflow, notice: 'Workflow was successfully updated.' }
71
+ format.json { head :no_content }
72
+ else
73
+ format.html { render action: "edit" }
74
+ format.json { render json: @workflow.errors, status: :unprocessable_entity }
75
+ end
76
+ end
77
+ end
78
+
79
+ def show
80
+
81
+
82
+ respond_to do |format|
83
+ format.html # new.html.erb
84
+ format.json { render json: @workflow }
85
+ end
86
+
87
+ end
88
+
89
+ def stages
90
+
91
+ end
92
+
93
+ def configure_stages
94
+
95
+ end
96
+
97
+
98
+ # DELETE /workflows/1
99
+ # DELETE /workflows/1.json
100
+ def destroy
101
+ @workflow.destroy
102
+
103
+ respond_to do |format|
104
+ format.html { redirect_to workflows_url }
105
+ format.json { head :no_content }
106
+ end
107
+ end
108
+
109
+ private
110
+
111
+
112
+ def workflow_params
113
+
114
+ #params.require(:search).permit(:name, :description, :provider, :query, :tag_list).merge(:options =>all_options)
115
+
116
+ params.require(:workflow).permit!
117
+
118
+ # params.require(:workflow).permit(:name, :workflow_id,
119
+ # :actions_attributes=>[:id, :name, :action_plugin, :_destroy],
120
+ # :stages_attributes=>[:id, :name, :_destroy,
121
+ # :before_actions_attributes=>[:id, :name, :action_plugin, :_destroy],
122
+ # :after_actions_attributes=>[:id, :name, :action_plugin, :_destroy],
123
+ # :stage_next_steps_attributes=>[:next_stage_id, :id, :_destroy]
124
+
125
+ # ]
126
+ # ).tap do |whitelisted|
127
+ # whitelisted[:actions_attributes] = params[:workflow].try(:[],:actions_attributes)
128
+ # whitelisted[:stages_attributes] = params[:workflow].try(:[], :stages_attributes)
129
+ # end
130
+ end
131
+
132
+ def load_workflow
133
+ @workflow = Workflow.find(params[:id])
134
+ end
135
+ end
136
+
137
+ end
@@ -0,0 +1,19 @@
1
+ # Copyright 2014 Netflix, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ module Workflowable
17
+ module ApplicationHelper
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # Copyright 2014 Netflix, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ module Workflowable
17
+ module WorkflowHelper
18
+ end
19
+ end
@@ -0,0 +1,113 @@
1
+ # Copyright 2014 Netflix, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ module Workflowable
17
+ class Action < ActiveRecord::Base
18
+ has_many :workflow_actions
19
+ has_many :workflows, :through=>:workflow_actions
20
+ has_many :stage_actions
21
+ has_many :stages, :through=>:stage_actions
22
+ validate :validate_action_plugin
23
+ validate :name, :uniquness=>true
24
+
25
+ before_save :reject_blank_values
26
+
27
+
28
+ serialize :options, Hash
29
+
30
+ def reject_blank_values
31
+ options.each{|k,v| v.reject!{ |k,v| v.blank? }}
32
+ options.reject!{|k,v| v.blank?}
33
+ end
34
+
35
+ def validate_action_plugin
36
+ begin
37
+ if(Workflowable::Actions.constants.include?(action_plugin.to_sym))
38
+ #if(Workflowable::Actions::Action.subclasses.include?(("Workflowable::Actions::"+action_plugin).constantize))
39
+ return true
40
+ end
41
+ rescue
42
+ end
43
+ errors.add :action_plugin, "is invalid"
44
+ end
45
+
46
+ def autocomplete(field_type, value, options={}, workflow=nil, object=nil, current_stage=nil, next_stage=nil, user=nil)
47
+ ("Workflowable::Actions::"+action_plugin).constantize.autocomplete(field_type, value, options, workflow, object, current_stage, next_stage, user)
48
+ end
49
+
50
+
51
+ def run(options={}, workflow, object, current_stage, next_stage, user)
52
+ options ||={}
53
+ plugin = ("Workflowable::Actions::"+action_plugin).constantize#("Workflowable::Actions::" + self.action_plugin.to_s).constantize
54
+ plugin.new(self.available_options(options), workflow, object, current_stage, next_stage, user).run
55
+
56
+ end
57
+
58
+
59
+ def validate_options(options={}, workflow=nil, object=nil, current_stage=nil, next_stage=nil, user=nil)
60
+ plugin = ("Workflowable::Actions::"+action_plugin).constantize#("Workflowable::Actions::" + self.action_plugin.to_s).constantize
61
+ results = plugin.new(self.available_options(options), workflow, object, current_stage, next_stage, user).validate_options
62
+
63
+ return results
64
+ end
65
+
66
+
67
+ def available_options(options={}, workflow=nil, object=nil, current_stage=nil, next_stage=nil, user=nil)
68
+ options ||={}
69
+
70
+ # value_options = options.with_indifferent_access.deep_dup.each{|k1,v1|
71
+ # v1.reject!{|k2,v2| k2!= "value" || v2.blank?};
72
+ # v1[:user_specified]=true;
73
+ # }
74
+ # default_options = options.with_indifferent_access.deep_dup.each{ |k1,v1|
75
+ # v1.reject!{|k2,v2| k2!= "default" || v2.blank? }
76
+ # }
77
+
78
+
79
+
80
+ value_options = options.with_indifferent_access.each{|k1,v1|
81
+ v1.reject{|k2,v2| k2 != "value" || v2.blank?};
82
+ v1[:user_specified]=true;
83
+ }
84
+
85
+
86
+
87
+ default_options = options.with_indifferent_access.each{ |k1,v1|
88
+ v1.reject{|k2,v2| k2 != "default" || v2.blank? }
89
+ }
90
+
91
+ options = self.options.with_indifferent_access.deep_merge(default_options)
92
+
93
+ options = value_options.with_indifferent_access.deep_merge(options)
94
+ if(action_plugin)
95
+ all_options = ("Workflowable::Actions::"+action_plugin).constantize.options(options, workflow, object, current_stage, next_stage, user)
96
+ else
97
+ all_options = {}
98
+ end
99
+
100
+
101
+ end
102
+
103
+ def action_plugin_options
104
+ ("Workflowable::Actions::"+action_plugin).constantize.options
105
+ end
106
+
107
+
108
+ def action_plugin_name
109
+ ("Workflowable::Actions::"+action_plugin).constantize.name
110
+ end
111
+
112
+ end
113
+ end
@@ -0,0 +1,104 @@
1
+ # Copyright 2014 Netflix, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ module Workflowable
17
+ class Stage < ActiveRecord::Base
18
+ belongs_to :workflow, :inverse_of=>:stages
19
+ has_many :stage_next_steps, :foreign_key=>:current_stage_id
20
+ has_many :next_steps,
21
+ :through=>:stage_next_steps,
22
+ :class_name=>"Stage",
23
+ :source=>:next_stage
24
+
25
+
26
+
27
+
28
+
29
+
30
+ has_many :before_stage_actions, -> { where(event: 'before').order("position ASC") }, class_name: 'StageAction'
31
+ has_many :after_stage_actions, -> { where(event: 'after').order("position ASC") }, class_name: 'StageAction'
32
+
33
+ has_many :before_actions, :through=>:before_stage_actions, :class_name=>'Action', :source=>:action
34
+ has_many :after_actions, :through=>:after_stage_actions, :class_name=>'Action', :source=>:action
35
+
36
+ validates :name, :presence=>true
37
+ validates :name, :uniqueness=>{:scope=> :workflow_id}
38
+
39
+ #validates :workflow_id, :presence=>true
40
+ validates_presence_of :workflow_id, :unless => lambda {|stage| stage.workflow.try(:valid?)}
41
+ validates_associated :workflow
42
+
43
+ accepts_nested_attributes_for :before_actions, :allow_destroy => true
44
+ accepts_nested_attributes_for :after_actions, :allow_destroy => true
45
+ accepts_nested_attributes_for :stage_next_steps, :allow_destroy => true
46
+
47
+
48
+
49
+
50
+
51
+ def run_before_actions(options={}, object, current_stage, user)
52
+ options ||= {}
53
+ self.before_actions.each do |action|
54
+ action.run(options[action.name], self.workflow, object, current_stage, self, user)
55
+ end
56
+ end
57
+
58
+ def run_after_actions(options={}, object, next_stage, user)
59
+ options ||= {}
60
+ self.after_actions.each do |action|
61
+ action.run(options[action.name], self.workflow, object, self, next_stage, user)
62
+ end
63
+ end
64
+
65
+ def validate_after_actions(options={}, object, next_stage, user)
66
+ options ||= {}
67
+ after_action_errors = {}
68
+ after_actions.each do |action|
69
+ errors = action.validate_options(options[action.name] || {}, self.workflow, object, self, next_stage, user)
70
+ after_action_errors[action.name] = errors if errors.present?
71
+ end
72
+ after_action_errors
73
+ end
74
+
75
+ def validate_before_actions(options={}, object, current_stage, user)
76
+ options ||= {}
77
+ before_action_errors = {}
78
+ before_actions.each do |action|
79
+ errors = action.validate_options(options[action.name] || {}, self.workflow, object, current_stage, self, user)
80
+ before_action_errors[action.name] = errors if errors.present?
81
+ end
82
+ before_action_errors
83
+ end
84
+
85
+ def run_after_options(options={}, object, next_stage, user)
86
+ options ||= {}
87
+ after_action_options = {}
88
+ after_actions.each do |action|
89
+ after_action_options[action.name] = action.available_options(options[action.name] || {}, self.workflow, object, self, next_stage, user)
90
+ end
91
+ after_action_options
92
+ end
93
+
94
+ def run_before_options(options={}, object, current_stage, user)
95
+ options ||= {}
96
+ before_actions_options = {}
97
+ before_actions.each do |action|
98
+ before_actions_options [action.name] = action.available_options(options[action.name] || {}, self.workflow, object, current_stage, self, user)
99
+ end
100
+ before_actions_options
101
+ end
102
+
103
+ end
104
+ end