stair_master 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +80 -0
- data/LICENSE +20 -0
- data/LICENSE.txt +22 -0
- data/README.md +128 -0
- data/Rakefile +1 -0
- data/app/concerns/stair_master/controller.rb +29 -0
- data/lib/stair_master/concerns/getters.rb +63 -0
- data/lib/stair_master/concerns/ordering.rb +24 -0
- data/lib/stair_master/concerns/tests.rb +33 -0
- data/lib/stair_master/concerns/traversal.rb +62 -0
- data/lib/stair_master/concerns.rb +10 -0
- data/lib/stair_master/conditions/availability.rb +13 -0
- data/lib/stair_master/conditions/base.rb +43 -0
- data/lib/stair_master/conditions/skippable.rb +13 -0
- data/lib/stair_master/conditions.rb +10 -0
- data/lib/stair_master/engine.rb +6 -0
- data/lib/stair_master/step.rb +61 -0
- data/lib/stair_master/version.rb +3 -0
- data/lib/stair_master/workflow_map.rb +37 -0
- data/lib/stair_master.rb +9 -0
- data/stair_master.gemspec +30 -0
- metadata +167 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
stair_master (0.0.1)
|
5
|
+
activesupport (>= 3.1)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activesupport (4.0.1)
|
11
|
+
i18n (~> 0.6, >= 0.6.4)
|
12
|
+
minitest (~> 4.2)
|
13
|
+
multi_json (~> 1.3)
|
14
|
+
thread_safe (~> 0.1)
|
15
|
+
tzinfo (~> 0.3.37)
|
16
|
+
atomic (1.1.14)
|
17
|
+
celluloid (0.15.2)
|
18
|
+
timers (~> 1.1.0)
|
19
|
+
childprocess (0.3.9)
|
20
|
+
ffi (~> 1.0, >= 1.0.11)
|
21
|
+
coderay (1.1.0)
|
22
|
+
diff-lcs (1.2.5)
|
23
|
+
ffi (1.9.3)
|
24
|
+
formatador (0.2.4)
|
25
|
+
guard (2.2.3)
|
26
|
+
formatador (>= 0.2.4)
|
27
|
+
listen (~> 2.1)
|
28
|
+
lumberjack (~> 1.0)
|
29
|
+
pry (>= 0.9.12)
|
30
|
+
thor (>= 0.18.1)
|
31
|
+
guard-rspec (4.0.3)
|
32
|
+
guard (>= 2.1.1)
|
33
|
+
rspec (~> 2.14)
|
34
|
+
guard-spork (1.5.1)
|
35
|
+
childprocess (>= 0.2.3)
|
36
|
+
guard (>= 1.1)
|
37
|
+
spork (>= 0.8.4)
|
38
|
+
i18n (0.6.9)
|
39
|
+
listen (2.2.0)
|
40
|
+
celluloid (>= 0.15.2)
|
41
|
+
rb-fsevent (>= 0.9.3)
|
42
|
+
rb-inotify (>= 0.9)
|
43
|
+
lumberjack (1.0.4)
|
44
|
+
method_source (0.8.2)
|
45
|
+
minitest (4.7.5)
|
46
|
+
multi_json (1.8.4)
|
47
|
+
pry (0.9.12.3)
|
48
|
+
coderay (~> 1.0)
|
49
|
+
method_source (~> 0.8)
|
50
|
+
slop (~> 3.4)
|
51
|
+
rake (10.1.1)
|
52
|
+
rb-fsevent (0.9.3)
|
53
|
+
rb-inotify (0.9.2)
|
54
|
+
ffi (>= 0.5.0)
|
55
|
+
rspec (2.14.1)
|
56
|
+
rspec-core (~> 2.14.0)
|
57
|
+
rspec-expectations (~> 2.14.0)
|
58
|
+
rspec-mocks (~> 2.14.0)
|
59
|
+
rspec-core (2.14.7)
|
60
|
+
rspec-expectations (2.14.4)
|
61
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
62
|
+
rspec-mocks (2.14.4)
|
63
|
+
slop (3.4.6)
|
64
|
+
spork (0.9.2)
|
65
|
+
thor (0.18.1)
|
66
|
+
thread_safe (0.1.3)
|
67
|
+
atomic
|
68
|
+
timers (1.1.0)
|
69
|
+
tzinfo (0.3.38)
|
70
|
+
|
71
|
+
PLATFORMS
|
72
|
+
ruby
|
73
|
+
|
74
|
+
DEPENDENCIES
|
75
|
+
bundler (~> 1.3)
|
76
|
+
guard-rspec
|
77
|
+
guard-spork (~> 1.5.0)
|
78
|
+
rake
|
79
|
+
rspec
|
80
|
+
stair_master!
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Digital Opera
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 JD Hendrickson
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
# StairMaster
|
2
|
+
|
3
|
+
[ ![Codeship Status for digitalopera/stair-master](https://www.codeship.io/projects/a4655e20-60fd-0131-2319-5ede98f174ff/status?branch=master)](https://www.codeship.io/projects/12227)
|
4
|
+
|
5
|
+
StairMaster is designed to make building wizard, or step styled navigation in your Rails applicaiton dead simple.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'stair_master'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle install
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
StairMaster attempts to maintain RESTful concepts in the wizard implementation. Each step has it's own controller that should inherit from a "base" controller which will define the rules governing the workflow.
|
20
|
+
|
21
|
+
#### Workflow Maps
|
22
|
+
A workflow map will tell StairMaster what steps are available, define any rules that may govern that availability as well as define the order to which the steps will be presented to the user.
|
23
|
+
|
24
|
+
Workflow maps must inherit from `StairMaster::WorkflowMap` which provides some simple methods for for defining your steps and order.
|
25
|
+
|
26
|
+
There are two methods that we will use to define our map.
|
27
|
+
|
28
|
+
##### add_step(controller_name, label, named_route, *conditions)
|
29
|
+
Use this method to add steps to your workflow map.
|
30
|
+
|
31
|
+
###### Method Parameters
|
32
|
+
**controller_name** :symbol
|
33
|
+
|
34
|
+
This is the name of the controller for the step. Should be a symbol.
|
35
|
+
|
36
|
+
**label** :string
|
37
|
+
|
38
|
+
This is the label to be used for the step. Can be accessed by the view, and will also be used in the rendering of the breadcrumbs hash.
|
39
|
+
|
40
|
+
**named_route** :symbol
|
41
|
+
|
42
|
+
The named route for the path of this step.
|
43
|
+
|
44
|
+
**conditions** :hash
|
45
|
+
|
46
|
+
Define the conditions that are to be used when resolving which steps will be available, and/or should be skipped based on the current context. Available conditions are:
|
47
|
+
|
48
|
+
- available_if/unless
|
49
|
+
- skip_if/unless
|
50
|
+
|
51
|
+
**Note** we recommend placing your maps in `app/maps` however you can place them where ever you would like.
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
# app/maps/my_process_map.rb
|
55
|
+
#
|
56
|
+
class MyProcessMap < StairMaster::WorkflowMap
|
57
|
+
# We must override the define_map! method with our mapping rules
|
58
|
+
add_step :home, "First Step", :first_step_path, available_if: :method_name
|
59
|
+
...
|
60
|
+
|
61
|
+
set_order :home, ...
|
62
|
+
end
|
63
|
+
```
|
64
|
+
|
65
|
+
#### Controllers
|
66
|
+
When setting up your controllers, we would recommend setting up a base controller which you can use to define which map to use and any methods that will be called by your conditionals to determine your workflow at runtime.
|
67
|
+
|
68
|
+
##### Base Controller
|
69
|
+
```ruby
|
70
|
+
# app/controllers/my_process/base_controller.rb
|
71
|
+
#
|
72
|
+
class MyProcess::BaseController < ApplicationController
|
73
|
+
# Include the stair master controller concern
|
74
|
+
include StairMaster::Controller
|
75
|
+
|
76
|
+
# Tell stair master what map to use
|
77
|
+
stair_master_map_class MyProcessMap
|
78
|
+
end
|
79
|
+
```
|
80
|
+
|
81
|
+
##### Step Controller
|
82
|
+
```ruby
|
83
|
+
# app/controllers/my_process/home_controller.rb
|
84
|
+
#
|
85
|
+
class MyProcess::HomeController < MyProcess::BaseController
|
86
|
+
def show
|
87
|
+
end
|
88
|
+
|
89
|
+
def update
|
90
|
+
end
|
91
|
+
end
|
92
|
+
```
|
93
|
+
|
94
|
+
Our step controller has two actions: `show` and `update`. You can absolutely add other actions to this controller at your descretion, however StairMaster is going to need only these two. We will use the `show` view to present the user with the step, and the `update` action to process any data the user submits.
|
95
|
+
|
96
|
+
#### Helper Methods
|
97
|
+
StepMaster is going to make available serveral different herlper methods that we can use from our view to help the user navigate from step to step using the business logic you have defined in the WorkflowMap.
|
98
|
+
|
99
|
+
##### Step Traversal
|
100
|
+
###### current_step
|
101
|
+
###### next_step
|
102
|
+
###### previous_step
|
103
|
+
<br/>
|
104
|
+
Each of these helpers will give you access to your current, next and previous step. When accessing the step you're going to have a few methods available to you.
|
105
|
+
|
106
|
+
**label**
|
107
|
+
|
108
|
+
You can access the step label to retrieve the label text that you assigned to the step in your WorkflowMap.
|
109
|
+
|
110
|
+
**url_for(resources=[], options={})**
|
111
|
+
|
112
|
+
You can render the named_route you identified for the step by calling this method. It will accept any of the same parameters you would normally pass into a named route in your code.
|
113
|
+
|
114
|
+
<br/>
|
115
|
+
##### Ordering
|
116
|
+
###### available_steps
|
117
|
+
<br/>
|
118
|
+
##### Testing
|
119
|
+
###### on_first_step?
|
120
|
+
###### on_last_step?
|
121
|
+
<br/>
|
122
|
+
## Contributing
|
123
|
+
|
124
|
+
1. Fork it
|
125
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
126
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
127
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
128
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# app/concerns/stair_master/controller.rb
|
2
|
+
##
|
3
|
+
##
|
4
|
+
##
|
5
|
+
module StairMaster
|
6
|
+
module Controller
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
include ::StairMaster::Concerns::Traversal
|
10
|
+
include ::StairMaster::Concerns::Getters
|
11
|
+
include ::StairMaster::Concerns::Ordering
|
12
|
+
include ::StairMaster::Concerns::Tests
|
13
|
+
|
14
|
+
included do
|
15
|
+
helper_method :workflow_map
|
16
|
+
end
|
17
|
+
|
18
|
+
def workflow_map
|
19
|
+
@stair_master_workflow_map ||= self.class.workflow_map.new
|
20
|
+
end
|
21
|
+
|
22
|
+
module ClassMethods
|
23
|
+
def workflow_map(map_class=nil)
|
24
|
+
return (@@stair_master_workflow_map || ::StairMaster::WorkflowMap) if map_class.nil?
|
25
|
+
@@stair_master_workflow_map = map_class.to_s.camelize.constantize
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# lib/stair_master/concerns/getters.rb
|
2
|
+
##
|
3
|
+
##
|
4
|
+
##
|
5
|
+
module StairMaster
|
6
|
+
module Concerns
|
7
|
+
module Getters
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
def get_step_index_by_name(step_name)
|
11
|
+
available_steps.find_index step_name
|
12
|
+
end
|
13
|
+
|
14
|
+
def get_step_name_by_index(step_index)
|
15
|
+
available_steps[step_index]
|
16
|
+
end
|
17
|
+
|
18
|
+
def get_step_by_index(step_index)
|
19
|
+
get_step_by_name available_steps[step_index]
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_step_by_name(step_name)
|
23
|
+
workflow_map.steps[step_name]
|
24
|
+
end
|
25
|
+
|
26
|
+
def get_next_step_index
|
27
|
+
on_last_step? ? nil : find_next_step_index
|
28
|
+
end
|
29
|
+
|
30
|
+
def get_previous_step_index(step_index=nil)
|
31
|
+
on_first_step? ? nil : find_previous_step_index
|
32
|
+
end
|
33
|
+
|
34
|
+
private # -------------------------------------------
|
35
|
+
|
36
|
+
def find_previous_step_index(step_index=nil)
|
37
|
+
previous_index = step_index.nil? ? current_step_index.pred : step_index.pred
|
38
|
+
step = get_step_by_index previous_index
|
39
|
+
|
40
|
+
if step.nil?
|
41
|
+
nil
|
42
|
+
elsif step.skip?(self)
|
43
|
+
find_previous_step_index(previous_index)
|
44
|
+
else
|
45
|
+
previous_index
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def find_next_step_index(step_index=nil)
|
50
|
+
next_index = step_index.nil? ? current_step_index.next : step_index.next
|
51
|
+
step = get_step_by_index next_index
|
52
|
+
|
53
|
+
if step.nil?
|
54
|
+
nil
|
55
|
+
elsif step.skip?(self)
|
56
|
+
find_next_step_index(next_index)
|
57
|
+
else
|
58
|
+
next_index
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# lib/stair_master/concerns/ordering.rb
|
2
|
+
##
|
3
|
+
##
|
4
|
+
##
|
5
|
+
module StairMaster
|
6
|
+
module Concerns
|
7
|
+
module Ordering
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
helper_method :available_steps
|
12
|
+
end
|
13
|
+
|
14
|
+
def available_steps
|
15
|
+
@stair_master_available_steps ||= workflow_map.order.map{ |step_name| step_name if step_is_available?(step_name) }.compact
|
16
|
+
end
|
17
|
+
|
18
|
+
def step_is_available?(step_name)
|
19
|
+
step = get_step_by_name(step_name)
|
20
|
+
step.available?(self)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# lib/stair_master/concerns/tests.rb
|
2
|
+
##
|
3
|
+
##
|
4
|
+
##
|
5
|
+
module StairMaster
|
6
|
+
module Concerns
|
7
|
+
module Tests
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
helper_method :on_first_step?,
|
12
|
+
:on_last_step?
|
13
|
+
end
|
14
|
+
|
15
|
+
## Methods ------------------------------------------
|
16
|
+
def on_last_step?
|
17
|
+
@stair_master_on_last_step ||= current_step_name == available_steps.last
|
18
|
+
end
|
19
|
+
|
20
|
+
def on_first_step?
|
21
|
+
@stair_master_on_first_step ||= current_step_name == available_steps.first
|
22
|
+
end
|
23
|
+
|
24
|
+
def is_last_step?(step_name)
|
25
|
+
step_name == available_steps.last
|
26
|
+
end
|
27
|
+
|
28
|
+
def is_first_step?(step_name)
|
29
|
+
step_name == available_steps.first
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# lib/stair_master/concerns/traverser.rb
|
2
|
+
##
|
3
|
+
##
|
4
|
+
##
|
5
|
+
module StairMaster
|
6
|
+
module Concerns
|
7
|
+
module Traversal
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
helper_method :current_step,
|
12
|
+
:current_step_name,
|
13
|
+
:current_step_index,
|
14
|
+
:next_step,
|
15
|
+
:next_step_index,
|
16
|
+
:next_step_name,
|
17
|
+
:previous_step,
|
18
|
+
:previous_step_index,
|
19
|
+
:previous_step_name
|
20
|
+
end
|
21
|
+
|
22
|
+
## Current Step -------------------------------------
|
23
|
+
def current_step_index
|
24
|
+
@stair_master_current_step_index ||= get_step_index_by_name current_step_name
|
25
|
+
end
|
26
|
+
|
27
|
+
def current_step
|
28
|
+
@stair_master_current_step ||= get_step_by_index current_step_index
|
29
|
+
end
|
30
|
+
|
31
|
+
def current_step_name
|
32
|
+
@stair_master_current_step_name ||= controller_name.to_sym
|
33
|
+
end
|
34
|
+
|
35
|
+
## Next Step ----------------------------------------
|
36
|
+
def next_step_index
|
37
|
+
@stair_master_next_step_index ||= get_next_step_index
|
38
|
+
end
|
39
|
+
|
40
|
+
def next_step
|
41
|
+
@stair_master_next_step ||= ( next_step_index.nil? ? nil : get_step_by_index(next_step_index) )
|
42
|
+
end
|
43
|
+
|
44
|
+
def next_step_name
|
45
|
+
@stair_master_next_step_name ||= ( next_step_index.nil? ? nil : get_step_name_by_index(next_step_index) )
|
46
|
+
end
|
47
|
+
|
48
|
+
## Previous Step ------------------------------------
|
49
|
+
def previous_step_index(base_step_index=nil)
|
50
|
+
@stair_master_previous_step_index ||= get_previous_step_index
|
51
|
+
end
|
52
|
+
|
53
|
+
def previous_step
|
54
|
+
@stair_master_previous_step ||= ( previous_step_index.nil? ? nil : get_step_by_index(previous_step_index) )
|
55
|
+
end
|
56
|
+
|
57
|
+
def previous_step_name
|
58
|
+
@stair_master_previous_step_name ||= ( previous_step_index.nil? ? nil : get_step_name_by_index(previous_step_index) )
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# lib/stair_master/conditions/availability.rb
|
2
|
+
##
|
3
|
+
##
|
4
|
+
##
|
5
|
+
module StairMaster
|
6
|
+
module Conditions
|
7
|
+
class Availability < StairMaster::Conditions::Base
|
8
|
+
def description
|
9
|
+
"The step '#{ @step.name }' is only available #{ @type } #{ @method_name } returns TRUE"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# lib/stair_master/conditions/base.rb
|
2
|
+
##
|
3
|
+
##
|
4
|
+
##
|
5
|
+
module StairMaster
|
6
|
+
module Conditions
|
7
|
+
class Base
|
8
|
+
attr_reader :type # :if || :unless
|
9
|
+
attr_accessor :method_name
|
10
|
+
|
11
|
+
def initialize(step, type, method_name)
|
12
|
+
@step = step
|
13
|
+
@type = type
|
14
|
+
@method_name = method_name
|
15
|
+
end
|
16
|
+
|
17
|
+
## Methods ------------------------------------------
|
18
|
+
|
19
|
+
def available?
|
20
|
+
raise NotImplmenetedError
|
21
|
+
end
|
22
|
+
|
23
|
+
def not_available?
|
24
|
+
raise NotImplmenetedError
|
25
|
+
end
|
26
|
+
|
27
|
+
def description
|
28
|
+
raise NotImplementedError
|
29
|
+
end
|
30
|
+
|
31
|
+
def test(context)
|
32
|
+
case @type.to_sym
|
33
|
+
when :if
|
34
|
+
return context.send(@method_name.to_sym)
|
35
|
+
when :unless
|
36
|
+
return !context.send(@method_name.to_sym)
|
37
|
+
else
|
38
|
+
return false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# lib/stair_master/conditions/skippable.rb
|
2
|
+
##
|
3
|
+
##
|
4
|
+
##
|
5
|
+
module StairMaster
|
6
|
+
module Conditions
|
7
|
+
class Skippable < StairMaster::Conditions::Base
|
8
|
+
def description
|
9
|
+
"The step '#{ @step.name }' will be skipped #{ @type } #{ @method_name } returns TRUE"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
|
2
|
+
module StairMaster
|
3
|
+
class Step
|
4
|
+
attr_accessor :label, :path, :conditions
|
5
|
+
|
6
|
+
def initialize(label, path, conditions)
|
7
|
+
@label = label
|
8
|
+
@path = path
|
9
|
+
@conditions = build_conditions conditions
|
10
|
+
end
|
11
|
+
|
12
|
+
## Methods --------------------------------------------
|
13
|
+
|
14
|
+
def available?(context)
|
15
|
+
rules = @conditions[:availability]
|
16
|
+
( rules.empty? ? [true] : test_rules(rules, context) ).all?
|
17
|
+
end
|
18
|
+
|
19
|
+
def skip?(context)
|
20
|
+
rules = @conditions[:skippable]
|
21
|
+
( rules.empty? ? [false] : test_rules(rules, context) ).all?
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_rules(rules, context)
|
25
|
+
results = []
|
26
|
+
rules.each{ |condition| results << condition.test(context) }
|
27
|
+
results
|
28
|
+
end
|
29
|
+
|
30
|
+
def url_for(resources=[], options={})
|
31
|
+
begin
|
32
|
+
Rails.application.routes.url_helpers.send @path.to_sym, resources, options
|
33
|
+
rescue
|
34
|
+
raise RuntimeError, "Could not find the route '#{ @path }' in your application."
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private # ---------------------------------------------
|
39
|
+
|
40
|
+
def build_conditions(conditions)
|
41
|
+
conditions = conditions.empty? ? {} : conditions.first
|
42
|
+
availability_rules = []
|
43
|
+
skip_rules = []
|
44
|
+
|
45
|
+
conditions.each do |k,v|
|
46
|
+
condition_type, type = k.to_s.split('_').map{ |v| v.to_sym }
|
47
|
+
|
48
|
+
if condition_type == :available ## It is an "availability" condition
|
49
|
+
availability_rules << ::StairMaster::Conditions::Availability.new(self, type, v)
|
50
|
+
elsif condition_type == :skip ## It is a "skip" condition
|
51
|
+
skip_rules << ::StairMaster::Conditions::Skippable.new(self, type, v)
|
52
|
+
else
|
53
|
+
## We don't know what this type of condition is so raise an exception
|
54
|
+
raise RuntimeError, "Unknown type for: #{ k }"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
{ availability: availability_rules, skippable: skip_rules }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# lib/stair_master/mapping.rb
|
2
|
+
|
3
|
+
module StairMaster
|
4
|
+
class WorkflowMap
|
5
|
+
attr_reader :steps, :order
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@steps = {}
|
9
|
+
@order = []
|
10
|
+
|
11
|
+
# ---------------------------------------------------
|
12
|
+
define_map!
|
13
|
+
end
|
14
|
+
|
15
|
+
## Methods --------------------------------------------
|
16
|
+
|
17
|
+
def define_map!
|
18
|
+
raise RuntimeError, "You need to define your map."
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_step(controller_name, label, named_path, *conditions)
|
22
|
+
@steps[controller_name] = ::StairMaster::Step.new(label, named_path, conditions)
|
23
|
+
end
|
24
|
+
|
25
|
+
def set_order(*steps)
|
26
|
+
@order = steps
|
27
|
+
end
|
28
|
+
|
29
|
+
def set_context(view_context)
|
30
|
+
@view_context = view_context
|
31
|
+
end
|
32
|
+
|
33
|
+
def get_context
|
34
|
+
@view_context
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/stair_master.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'stair_master/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "stair_master"
|
8
|
+
spec.version = StairMaster::VERSION
|
9
|
+
spec.authors = ["JD Hendrickson", "Grant Klinsing"]
|
10
|
+
spec.email = ["jd@digitalopera.com", "grant@digitalopera.com"]
|
11
|
+
spec.description = "Build complex controller workflows in your Rails app."
|
12
|
+
spec.summary = "Build complex controller workflows in your Rails app."
|
13
|
+
spec.homepage = "http://www.digitalopera.com/"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
## Dependencies -----------------------------------------
|
22
|
+
spec.add_dependency 'activesupport', '>= 3.1'
|
23
|
+
|
24
|
+
## Development Dependencies -----------------------------
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
26
|
+
spec.add_development_dependency "rake"
|
27
|
+
spec.add_development_dependency "rspec"
|
28
|
+
spec.add_development_dependency "guard-rspec"
|
29
|
+
spec.add_development_dependency "guard-spork", "~> 1.5.0"
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: stair_master
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- JD Hendrickson
|
9
|
+
- Grant Klinsing
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2014-01-16 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: activesupport
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '3.1'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ! '>='
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '3.1'
|
31
|
+
- !ruby/object:Gem::Dependency
|
32
|
+
name: bundler
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ~>
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '1.3'
|
39
|
+
type: :development
|
40
|
+
prerelease: false
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '1.3'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rake
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: rspec
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ! '>='
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
type: :development
|
72
|
+
prerelease: false
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
- !ruby/object:Gem::Dependency
|
80
|
+
name: guard-rspec
|
81
|
+
requirement: !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ! '>='
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
type: :development
|
88
|
+
prerelease: false
|
89
|
+
version_requirements: !ruby/object:Gem::Requirement
|
90
|
+
none: false
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: guard-spork
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ~>
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 1.5.0
|
103
|
+
type: :development
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ~>
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 1.5.0
|
111
|
+
description: Build complex controller workflows in your Rails app.
|
112
|
+
email:
|
113
|
+
- jd@digitalopera.com
|
114
|
+
- grant@digitalopera.com
|
115
|
+
executables: []
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- .gitignore
|
120
|
+
- Gemfile
|
121
|
+
- Gemfile.lock
|
122
|
+
- LICENSE
|
123
|
+
- LICENSE.txt
|
124
|
+
- README.md
|
125
|
+
- Rakefile
|
126
|
+
- app/concerns/stair_master/controller.rb
|
127
|
+
- lib/stair_master.rb
|
128
|
+
- lib/stair_master/concerns.rb
|
129
|
+
- lib/stair_master/concerns/getters.rb
|
130
|
+
- lib/stair_master/concerns/ordering.rb
|
131
|
+
- lib/stair_master/concerns/tests.rb
|
132
|
+
- lib/stair_master/concerns/traversal.rb
|
133
|
+
- lib/stair_master/conditions.rb
|
134
|
+
- lib/stair_master/conditions/availability.rb
|
135
|
+
- lib/stair_master/conditions/base.rb
|
136
|
+
- lib/stair_master/conditions/skippable.rb
|
137
|
+
- lib/stair_master/engine.rb
|
138
|
+
- lib/stair_master/step.rb
|
139
|
+
- lib/stair_master/version.rb
|
140
|
+
- lib/stair_master/workflow_map.rb
|
141
|
+
- stair_master.gemspec
|
142
|
+
homepage: http://www.digitalopera.com/
|
143
|
+
licenses:
|
144
|
+
- MIT
|
145
|
+
post_install_message:
|
146
|
+
rdoc_options: []
|
147
|
+
require_paths:
|
148
|
+
- lib
|
149
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
150
|
+
none: false
|
151
|
+
requirements:
|
152
|
+
- - ! '>='
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
version: '0'
|
155
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
156
|
+
none: false
|
157
|
+
requirements:
|
158
|
+
- - ! '>='
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '0'
|
161
|
+
requirements: []
|
162
|
+
rubyforge_project:
|
163
|
+
rubygems_version: 1.8.24
|
164
|
+
signing_key:
|
165
|
+
specification_version: 3
|
166
|
+
summary: Build complex controller workflows in your Rails app.
|
167
|
+
test_files: []
|