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,24 @@
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 StageAction < ActiveRecord::Base
18
+ belongs_to :stage
19
+ belongs_to :action
20
+
21
+ delegate :position, to: :action
22
+
23
+ end
24
+ end
@@ -0,0 +1,21 @@
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 StageNextStep < ActiveRecord::Base
18
+ belongs_to :current_stage, :class_name=>'Stage'
19
+ belongs_to :next_stage, :class_name=>'Stage'
20
+ end
21
+ end
@@ -0,0 +1,57 @@
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 Workflow < ActiveRecord::Base
18
+ has_many :stages, :inverse_of=>:workflow
19
+ belongs_to :initial_stage, :class_name=>Stage
20
+ has_many :workflow_actions, -> { order("position ASC") }
21
+ has_many :actions, :through=>:workflow_actions
22
+ accepts_nested_attributes_for :stages, :allow_destroy => true
23
+ accepts_nested_attributes_for :actions, :allow_destroy => true
24
+
25
+
26
+
27
+ validates :name, :presence=>true
28
+ validates :name, :uniqueness=>true
29
+
30
+
31
+
32
+ def run_workflow_actions(options={}, object, current_stage, next_stage, user)
33
+ actions.each do |action|
34
+ action.run(options.try(:[],action.name), self, object, current_stage, next_stage, user)
35
+ end
36
+ end
37
+
38
+ def workflow_action_options(options={}, object, current_stage, next_stage, user)
39
+ options ||= {}
40
+ workflow_action_options = {}
41
+ actions.each do |action|
42
+ workflow_action_options[action.name] = action.available_options(options.try(:[],action.name) || {}, self, object, current_stage, next_stage, user)
43
+ end
44
+ workflow_action_options
45
+ end
46
+
47
+ def validate_action_options(options={}, object, current_stage, next_stage, user)
48
+ options ||= {}
49
+ action_errors = {}
50
+ actions.each do |action|
51
+ errors = action.validate_options(options[action.name] || {}, self, object, current_stage, next_stage, user)
52
+ action_errors[action.name] = errors if errors.present?
53
+ end
54
+ action_errors
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,23 @@
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 WorkflowAction < ActiveRecord::Base
18
+ belongs_to :workflow
19
+ belongs_to :action
20
+
21
+
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
5
+ <title>Workflowable</title>
6
+ <%= stylesheet_link_tag "workflowable/application", media: "all" %>
7
+ <%= javascript_include_tag "workflowable/application" %>
8
+ <%= csrf_meta_tags %>
9
+ </head>
10
+ <body>
11
+ <nav class="top-bar hide-for-small" data-topbar="">
12
+ <ul class="title-area">
13
+ <li class="name">
14
+ <h1><%= link_to "Workflowable", workflows_path %></h1>
15
+ </li>
16
+ </ul>
17
+ </nav>
18
+ <div class="row">
19
+ <div class="large-12 columns">
20
+ <%= yield %>
21
+ </div>
22
+ </div>
23
+ </body>
24
+ </html>
@@ -0,0 +1,30 @@
1
+ <% if actions.count == 0 %>
2
+ <b>No actions defined</b>
3
+ <% else %>
4
+ <% actions.each do |action| %>
5
+ <h6><b><%= action.name %></b>
6
+ (<%= action.action_plugin %>)</h6>
7
+ <table>
8
+ <thead>
9
+ <tr>
10
+ <th>Option</th>
11
+ <th>Value Specified</th>
12
+ <th>Default Value</th>
13
+ </tr>
14
+ </thead>
15
+ <tbody>
16
+ <% if action.options.present? %>
17
+ <% action.options.each do |k,v| %>
18
+ <tr>
19
+ <td><%= k %></td>
20
+ <td><%= v.try(:[],:value) %></td>
21
+ <td><%= v.try(:[],:default) %></td>
22
+ </tr>
23
+ <% end %>
24
+ <% else %>
25
+ <tr ><td colspan="3">No options defined</td></tr>
26
+ <% end %>
27
+ </tbody>
28
+ </table>
29
+ <% end %>
30
+ <% end %>
@@ -0,0 +1,26 @@
1
+ <%= f.fields_for association do |action_form| %>
2
+ <fieldset>
3
+ <legend><%= label.humanize %>
4
+ details -
5
+ <%= action_form.link_to_remove "Remove" %></legend>
6
+ <div class="field">
7
+ <%= action_form.label :action_name %>
8
+ <%= action_form.text_field :name %>
9
+ <%= action_form.label :position %>
10
+ <%= action_form.text_field :position %>
11
+ </div>
12
+ <div class="field">
13
+ <%= action_form.label :action %>
14
+ <%= action_form.collection_select :action_plugin, Workflowable::Actions.constants, :to_s, :to_s, {include_blank: true}, {class: "action_plugin_select"} %>
15
+ </div>
16
+ <div class="action_options_fields" data-url="<%= options_actions_url %>" data-context="<%= action_form.object_name %>">
17
+ <% available_options = action_form.object.available_options(action_form.object.options) %>
18
+ <% if available_options %>
19
+ <%= render :partial=>"workflowable/actions/options", :locals => {:options=>available_options , context: action_form.object_name } %>
20
+ <% end %>
21
+ </div>
22
+ <br/>
23
+ </fieldset>
24
+ <br/>
25
+ <% end %>
26
+ <p><%= f.link_to_add "Add #{label}", association %></p>
@@ -0,0 +1,39 @@
1
+ <%= fields_for "#{context}[options]", OpenStruct.new({}) do |options_form| %>
2
+ <% options.each do |key, value| %>
3
+ <div class="field">
4
+ <div class="row">
5
+ <div class="medium-5 columns">
6
+ <%= label_tag do %>
7
+ Specify Value:
8
+ <%= value.try(:[],:name) || key.to_s %>
9
+ <%= "(required)" if value[:required] == true %>
10
+ <% end %>
11
+ <% if value[:type] == :choice %>
12
+ <%= options_form.select "#{key}][value", value[:choices].collect{|v| [v[:name],v.to_json]},{ include_blank: true, selected: value[:value].to_json} %>
13
+ <% elsif value[:type] == :boolean %>
14
+ <%= options_form.check_box "#{key}][value", :checked=>value[:value] == "1" %>
15
+ <% else %>
16
+ <%= options_form.text_field "#{key}][value", :value=>value[:value] %>
17
+ <% end %>
18
+ </div>
19
+ <div class="medium-1 columns">
20
+ <br/>
21
+ <b>OR</b>
22
+ </div>
23
+ <div class="medium-6 columns">
24
+ <%= label_tag do %>
25
+ Default Value:
26
+ <%= value.try(:[],:name) || key.to_s %>
27
+ <% end %>
28
+ <% if value[:type] == :choice %>
29
+ <%= options_form.select "#{key}][default", value[:choices].collect{|v| [v[:name],v.to_json]},{ include_blank: true, selected: value[:default].to_json} %>
30
+ <% elsif value[:type] == :boolean %>
31
+ <%= options_form.check_box "#{key}][default", :checked=>value[:default] == "1" %>
32
+ <% else %>
33
+ <%= options_form.text_field "#{key}][default", :value=>value[:default] %>
34
+ <% end %>
35
+ </div>
36
+ </div>
37
+ </div>
38
+ <% end %>
39
+ <% end %>
@@ -0,0 +1,2 @@
1
+ $('.action_options_fields.active').html("<%= escape_javascript(render :partial=>"options", :locals => {:options=>@options, context: params[:context]}) %>");
2
+ $('.action_options_fields.active').removeClass("active");
@@ -0,0 +1,57 @@
1
+ <%= nested_form_for(@workflow) do |f| %>
2
+ <% if @workflow.errors.any? %>
3
+ <div id="error_explanation">
4
+ <h2><%= pluralize(@workflow.errors.count, "error") %>
5
+ prohibited this workflow from being saved:</h2>
6
+ <ul>
7
+ <% @workflow.errors.full_messages.each do |msg| %>
8
+ <li><%= msg %></li>
9
+ <% end %>
10
+ </ul>
11
+ </div>
12
+ <% end %>
13
+ <div data-magellan-expedition="fixed">
14
+ <dl class="sub-nav">
15
+ <dd data-magellan-arrival="basic"><a href="#basic">Basic Details</a></dd>
16
+ <dd data-magellan-arrival="global_actions"><a href="#global_actions">Global Actions</a></dd>
17
+ <dd data-magellan-arrival="stages"><a href="#stages">Stages</a></dd>
18
+ </dl>
19
+ </div>
20
+ <a name="basic"></a>
21
+ <fieldset>
22
+ <legend data-magellan-destination="basic">Basic Details</legend>
23
+ <div class="field">
24
+ <%= f.label :name %>
25
+ <%= f.text_field :name %>
26
+ </div>
27
+ </fieldset>
28
+ <hr/>
29
+ <a name="global_actions"></a>
30
+ <h4 data-magellan-destination="global_actions">Global Actions<br/><small>These actions will be run every time the stage changes</small></h4>
31
+ <%= render partial: 'shared/actions/fields', locals: {f: f, label: 'global action', association: :actions} %>
32
+ <hr/>
33
+ <a name="stages"></a>
34
+ <h4 data-magellan-destination="stages">Stages<br/><small>The available states for the workflow</small></h4>
35
+ <%= f.fields_for :stages do |stage_form| %>
36
+ <fieldset>
37
+ <legend>Stage Details -
38
+ <%= stage_form.link_to_remove "Remove this stage" %></legend>
39
+ <div class="field">
40
+ <%= stage_form.label :name %>
41
+ <%= stage_form.text_field :name %>
42
+ </div>
43
+ <hr/>
44
+ <h5>Before Actions<br/><small>These actions will be run when leaving this stage</small></h5>
45
+ <%= render partial: 'shared/actions/fields', locals: {f: stage_form, association: :before_actions, label: 'before action'} %>
46
+ <hr/>
47
+ <h5>After Actions<br/><small>These actions will run before entering this stage</small></h5>
48
+ <%= render partial: 'shared/actions/fields', locals: {f: stage_form, association: :after_actions, label: 'after action'} %>
49
+ <hr/>
50
+ </fieldset>
51
+ <br/>
52
+ <% end %>
53
+ <br/>
54
+ <p><%= f.link_to_add "Add a stage", :stages %></p>
55
+ <%= f.submit class: "button" %>
56
+ <%= link_to 'Back', workflows_path, class: "button secondary" %>
57
+ <% end %>
@@ -0,0 +1,34 @@
1
+ <%= nested_form_for(@workflow) do |f| %>
2
+ <% if @workflow.errors.any? %>
3
+ <div id="error_explanation">
4
+ <h2><%= pluralize(@workflow.errors.count, "error") %>
5
+ prohibited this workflow from being saved:</h2>
6
+ <ul>
7
+ <% @workflow.errors.full_messages.each do |msg| %>
8
+ <li><%= msg %></li>
9
+ <% end %>
10
+ </ul>
11
+ </div>
12
+ <% end %>
13
+ <h2>Workflow Options</h2>
14
+ <div class="field">
15
+ <%= f.label :initial_stage %>
16
+ <%= f.collection_select :initial_stage_id, @workflow.stages, :id, :name, include_blank: true %>
17
+ </div>
18
+ <h2>Stage Transitions</h2>
19
+ <%= f.fields_for :stages do |stage_form| %>
20
+ <div class="field">
21
+ <h3><%= stage_form.object.name %></h3>
22
+ <%= stage_form.fields_for :stage_next_steps do |next_steps_form| %>
23
+ <div class="field">
24
+ <%= next_steps_form.label :next_step %>
25
+ <%= next_steps_form.collection_select :next_stage_id, @workflow.stages, :id, :name, include_blank: true %>
26
+ </div>
27
+ <%= next_steps_form.link_to_remove "Remove this step" %>
28
+ <% end %>
29
+ <p><%= stage_form.link_to_add "Add a next step", :stage_next_steps %></p>
30
+ </div>
31
+ <% end %>
32
+ <%= f.submit class: "button" %>
33
+ <%= link_to 'Back', workflows_path, class: "button secondary" %>
34
+ <% end %>
@@ -0,0 +1,2 @@
1
+ <h1>Edit workflow</h1>
2
+ <%= render 'form' %>
@@ -0,0 +1,16 @@
1
+ <table>
2
+ <tr>
3
+ <th>Name</th>
4
+ <th></th>
5
+ </tr>
6
+ <% @workflows.each do |workflow| %>
7
+ <tr>
8
+ <td><%= link_to workflow.name, workflow %></td>
9
+ <td>
10
+ <%= link_to 'Edit', edit_workflow_path(workflow), class: "button tiny" %>
11
+ <%= link_to 'Destroy', workflow, method: :delete, data: { confirm: 'Are you sure?' }, class: "button tiny alert" %></td>
12
+ </tr>
13
+ <% end %>
14
+ </table>
15
+ <br />
16
+ <%= link_to 'New Workflow', new_workflow_path, class: "button" %>
@@ -0,0 +1,2 @@
1
+ <h1>New workflow</h1>
2
+ <%= render 'form' %>
@@ -0,0 +1,49 @@
1
+ <h1>Workflow:
2
+ <%= @workflow.name %></h1>
3
+ <h5>Global Actions</h5>
4
+ <dl class="accordion" data-accordion>
5
+ <dd>
6
+ <a href="<%= "#workflow_#{@workflow.id}_actions" %>">Actions (<%= @workflow.actions.count %>)</a>
7
+ <div id="<%= "workflow_#{@workflow.id}_actions" %>" class="content">
8
+ <%= render partial: 'shared/actions/details', locals: {actions: @workflow.actions, uuid: "workflow_#{@workflow.id}_actions"} %>
9
+ </div>
10
+ </dd>
11
+ </dl>
12
+ <% @workflow.stages.each do |stage| %>
13
+ <dl class="accordion" data-accordion>
14
+ <dd>
15
+ <a href="#<%= "stage_#{stage.id}" %>"><%= stage.name %></a>
16
+ <div id="<%= "stage_#{stage.id}" %>" class="content">
17
+ <dl class="tabs" data-tab>
18
+ <dd class="active"><a href="<%= "#stage_#{stage.id}_before" %>">Before Actions (<%= stage.before_actions.count %>)</a></dd>
19
+ <dd><a href="<%= "#stage_#{stage.id}_after" %>">After Actions (<%= stage.after_actions.count %>)</a></dd>
20
+ <dd><a href="<%= "#stage_#{stage.id}_next_steps" %>">Next Steps (<%= stage.next_steps.count %>)</a></dd>
21
+ </dl>
22
+ <div class="tabs-content">
23
+ <div id="<%= "stage_#{stage.id}_before" %>" class="content active">
24
+ <%= render partial: 'shared/actions/details', locals: {actions: stage.before_actions, uuid: "stage_#{stage.id}_before"} %>
25
+ </div>
26
+ <div id="<%= "stage_#{stage.id}_after" %>" class="content">
27
+ <%= render partial: 'shared/actions/details', locals: {actions: stage.after_actions, uuid: "stage_#{stage.id}_after"} %>
28
+ </div>
29
+ <div id="<%= "stage_#{stage.id}_next_steps" %>" class="content">
30
+ <% if stage.next_steps.count == 0 %>
31
+ <b>No next steps defined</b>
32
+ <% else %>
33
+ <% stage.next_steps.each do |step| %>
34
+ <%= step.name %><br/>
35
+ <% end %>
36
+ <% end %>
37
+ </div>
38
+ </div>
39
+ </div>
40
+ </dd>
41
+ </dl>
42
+ <% end %>
43
+ <br/>
44
+ <%= link_to "Edit", edit_workflow_path(@workflow), class:"button" %>
45
+ <%= link_to "Configure Stages", configure_stages_workflow_path(@workflow), class:"button" %>
46
+ <%= link_to "Back", workflows_path , class:"button secondary"%>
47
+ <%= content_tag :svg, class: "workflow_diagram", data: {url: stages_workflow_url(@workflow, format: :json)} do %>
48
+ <g transform="translate(20, 20)" />
49
+ <% end %>
@@ -0,0 +1,8 @@
1
+ json.nodes @workflow.stages, :id, :name
2
+
3
+
4
+
5
+ next_steps=[]
6
+ @workflow.stages.each{|s| next_steps += s.stage_next_steps}
7
+
8
+ json.edges next_steps, :current_stage_id, :next_stage_id
data/config/routes.rb ADDED
@@ -0,0 +1,34 @@
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
+ Workflowable::Engine.routes.draw do
17
+ resources :workflows do
18
+ member do
19
+ get 'stages'
20
+ get 'configure_stages'
21
+ post 'update_stages'
22
+ end
23
+ end
24
+
25
+ resources :actions, only: [] do
26
+ collection do
27
+ get 'options'
28
+ end
29
+ end
30
+
31
+ root to: "workflows#index"
32
+
33
+
34
+ end