workflowable 1.0

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 (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