juice10-action_flow 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. data/.document +5 -0
  2. data/Gemfile +13 -0
  3. data/Gemfile.lock +20 -0
  4. data/LICENSE.txt +20 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.rdoc +206 -0
  7. data/Rakefile +53 -0
  8. data/VERSION +1 -0
  9. data/action_flow.gemspec +149 -0
  10. data/init.rb +1 -0
  11. data/install.rb +1 -0
  12. data/lib/action_flow.rb +59 -0
  13. data/lib/action_flow/blank_slate.rb +33 -0
  14. data/lib/action_flow/expression.rb +135 -0
  15. data/lib/action_flow/filters.rb +25 -0
  16. data/lib/action_flow/flow.rb +54 -0
  17. data/lib/action_flow/flow/controller.rb +91 -0
  18. data/lib/action_flow/flow/state.rb +73 -0
  19. data/lib/action_flow/helpers.rb +15 -0
  20. data/lib/action_flow/variable.rb +14 -0
  21. data/test/helper.rb +18 -0
  22. data/test/test_action_flow.rb +7 -0
  23. data/test_app/.gitignore +4 -0
  24. data/test_app/Gemfile +34 -0
  25. data/test_app/Gemfile.lock +131 -0
  26. data/test_app/README +256 -0
  27. data/test_app/Rakefile +7 -0
  28. data/test_app/app/controllers/application_controller.rb +6 -0
  29. data/test_app/app/controllers/settings_controller.rb +33 -0
  30. data/test_app/app/helpers/application_helper.rb +4 -0
  31. data/test_app/app/views/layouts/application.html.erb +14 -0
  32. data/test_app/app/views/settings/_step.html.erb +22 -0
  33. data/test_app/app/views/settings/intro.html.erb +12 -0
  34. data/test_app/app/views/settings/outro.html.erb +8 -0
  35. data/test_app/config.ru +4 -0
  36. data/test_app/config/application.rb +42 -0
  37. data/test_app/config/boot.rb +6 -0
  38. data/test_app/config/cucumber.yml +8 -0
  39. data/test_app/config/database.yml +25 -0
  40. data/test_app/config/environment.rb +5 -0
  41. data/test_app/config/environments/cucumber.rb +35 -0
  42. data/test_app/config/environments/development.rb +26 -0
  43. data/test_app/config/environments/production.rb +49 -0
  44. data/test_app/config/environments/test.rb +35 -0
  45. data/test_app/config/initializers/backtrace_silencers.rb +7 -0
  46. data/test_app/config/initializers/inflections.rb +10 -0
  47. data/test_app/config/initializers/mime_types.rb +5 -0
  48. data/test_app/config/initializers/secret_token.rb +7 -0
  49. data/test_app/config/initializers/session_store.rb +8 -0
  50. data/test_app/config/locales/en.yml +5 -0
  51. data/test_app/config/routes.rb +58 -0
  52. data/test_app/db/schema.rb +15 -0
  53. data/test_app/db/seeds.rb +7 -0
  54. data/test_app/features/flow_management.feature +122 -0
  55. data/test_app/features/flow_with_array_alternatives.feature +13 -0
  56. data/test_app/features/flows_with_conditions.feature +60 -0
  57. data/test_app/features/mutual_exclusion.feature +39 -0
  58. data/test_app/features/step_definitions/flow_steps.rb +20 -0
  59. data/test_app/features/step_definitions/url_steps.rb +7 -0
  60. data/test_app/features/step_definitions/web_steps.rb +211 -0
  61. data/test_app/features/support/env.rb +39 -0
  62. data/test_app/features/support/hooks.rb +3 -0
  63. data/test_app/features/support/paths.rb +33 -0
  64. data/test_app/features/support/selectors.rb +39 -0
  65. data/test_app/lib/tasks/.gitkeep +0 -0
  66. data/test_app/lib/tasks/cucumber.rake +57 -0
  67. data/test_app/public/404.html +26 -0
  68. data/test_app/public/422.html +26 -0
  69. data/test_app/public/500.html +26 -0
  70. data/test_app/public/favicon.ico +0 -0
  71. data/test_app/public/images/rails.png +0 -0
  72. data/test_app/public/index.html +239 -0
  73. data/test_app/public/javascripts/application.js +2 -0
  74. data/test_app/public/javascripts/controls.js +965 -0
  75. data/test_app/public/javascripts/dragdrop.js +974 -0
  76. data/test_app/public/javascripts/effects.js +1123 -0
  77. data/test_app/public/javascripts/prototype.js +6001 -0
  78. data/test_app/public/javascripts/rails.js +191 -0
  79. data/test_app/public/robots.txt +5 -0
  80. data/test_app/public/stylesheets/.gitkeep +0 -0
  81. data/test_app/script/about +4 -0
  82. data/test_app/script/console +3 -0
  83. data/test_app/script/cucumber +10 -0
  84. data/test_app/script/dbconsole +3 -0
  85. data/test_app/script/destroy +3 -0
  86. data/test_app/script/generate +3 -0
  87. data/test_app/script/performance/benchmarker +3 -0
  88. data/test_app/script/performance/profiler +3 -0
  89. data/test_app/script/plugin +3 -0
  90. data/test_app/script/rails +6 -0
  91. data/test_app/script/runner +3 -0
  92. data/test_app/script/server +3 -0
  93. data/test_app/test/performance/browsing_test.rb +9 -0
  94. data/test_app/test/test_helper.rb +13 -0
  95. data/test_app/vendor/plugins/.gitkeep +0 -0
  96. data/test_app/vendor/plugins/action_flow/init.rb +2 -0
  97. data/uninstall.rb +1 -0
  98. metadata +224 -0
@@ -0,0 +1,15 @@
1
+ # This file is auto-generated from the current state of the database. Instead
2
+ # of editing this file, please use the migrations feature of Active Record to
3
+ # incrementally modify your database, and then regenerate this schema definition.
4
+ #
5
+ # Note that this schema.rb definition is the authoritative source for your
6
+ # database schema. If you need to create the application database on another
7
+ # system, you should be using db:schema:load, not running all the migrations
8
+ # from scratch. The latter is a flawed and unsustainable approach (the more migrations
9
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
10
+ #
11
+ # It's strongly recommended to check this file into your version control system.
12
+
13
+ ActiveRecord::Schema.define(:version => 0) do
14
+
15
+ end
@@ -0,0 +1,7 @@
1
+ # This file should contain all the record creation needed to seed the database with its default values.
2
+ # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
3
+ #
4
+ # Examples:
5
+ #
6
+ # cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }])
7
+ # Mayor.create(:name => 'Daley', :city => cities.first)
@@ -0,0 +1,122 @@
1
+ Feature: Manage flow transitions
2
+
3
+ Background: Setup default flows
4
+ Given I have the flows:
5
+ """
6
+ flow :steps, settings.one,
7
+ settings.two,
8
+ settings.three
9
+
10
+ flow :large, settings.four,
11
+ settings.five,
12
+ settings.six,
13
+ settings.seven,
14
+ settings.eight
15
+
16
+ flow :process, settings.intro + application.other,
17
+ :steps,
18
+ settings.outro(:id => find(:text))
19
+
20
+ flow :pass, settings.passthrough,
21
+ settings.intro
22
+ """
23
+
24
+ Scenario: Simple flow sequence terminated at end
25
+ When I visit "/settings/one"
26
+ And I follow "Next"
27
+ Then I should be at "/settings/two"
28
+ When I follow "Next"
29
+ Then I should be at "/settings/three"
30
+ And I should not see "Next"
31
+
32
+ Scenario: One sequence nested inside another
33
+ When I visit "/settings/intro"
34
+ And I follow "Next"
35
+ Then I should be at "/settings/one"
36
+ When I follow "Next"
37
+ Then I should be at "/settings/two"
38
+ When I follow "Next"
39
+ Then I should be at "/settings/three"
40
+ When I follow "Next"
41
+ Then I should be at "/settings/outro"
42
+
43
+ Scenario: Skipping the outer sequence from the controller
44
+ When I visit "/settings/intro"
45
+ And I follow "Next"
46
+ Then I should be at "/settings/one"
47
+ When I visit "/settings/two?skip=true"
48
+ Then I should be at "/settings/outro"
49
+
50
+ Scenario: Skip a subflow
51
+ When I visit "/settings/intro"
52
+ And I follow "Next"
53
+ And I follow "Skip"
54
+ Then I should be at "/settings/outro"
55
+
56
+ Scenario: Get partway through a subflow before skipping
57
+ When I visit "/settings/intro"
58
+ And I follow "Next"
59
+ And I follow "Next"
60
+ And I follow "Skip"
61
+ Then I should be at "/settings/outro"
62
+
63
+ Scenario: Get to the final page of a subflow before skipping
64
+ When I visit "/settings/intro"
65
+ And I follow "Next"
66
+ And I follow "Next"
67
+ And I follow "Next"
68
+ And I follow "Skip"
69
+ Then I should be at "/settings/outro"
70
+
71
+ Scenario: Pass through variables to flow expressions
72
+ When I visit "/settings/intro/Hello"
73
+ And I follow "Next"
74
+ And I follow "Skip"
75
+ Then I should be at "/settings/outro/Hello"
76
+ Then I should see "Hello"
77
+
78
+ Scenario: Last page of a flow is still part of the flow
79
+ When I visit "/settings/intro"
80
+ Then I should see "You're in the :process flow"
81
+ When I follow "Next"
82
+ And I follow "Skip"
83
+ Then I should see "You're in the :process flow"
84
+ When I visit "/settings/outro"
85
+ Then I should see "You're in the :process flow"
86
+
87
+ Scenario: Backtracking still picks the right next action
88
+ When I visit "/settings/one"
89
+ And I follow "Next"
90
+ When I follow "Next"
91
+ Then I should be at "/settings/three"
92
+ When I visit "/settings/two"
93
+ When I follow "Next"
94
+ Then I should be at "/settings/three"
95
+
96
+ Scenario: Skip back and forth between visited pages
97
+ When I visit "/settings/four"
98
+ And I follow "Next"
99
+ And I follow "Next"
100
+ And I follow "Next"
101
+ Then I should be at "/settings/seven"
102
+ When I visit "/settings/five"
103
+ And I follow "Next"
104
+ Then I should be at "/settings/six"
105
+ When I visit "/settings/five"
106
+ And I visit "/settings/seven"
107
+ And I follow "Next"
108
+ Then I should be at "/settings/eight"
109
+
110
+ Scenario: Pass params through #next_in_flow
111
+ When I visit "/settings/passthrough/milk"
112
+ Then I should see "Received milk"
113
+
114
+ Scenario: Session contains invalid objects
115
+ Given I visit "/settings/poison"
116
+ And I visit "/settings/one"
117
+
118
+ Scenario: Session contains data for nonexistent flows
119
+ Given I visit "/settings/delete"
120
+ And I visit "/settings/one"
121
+ Then I should see "This is a step"
122
+
@@ -0,0 +1,13 @@
1
+ Feature: Use arrays instead of '+' to manage alternatives that reference flows using symbols
2
+
3
+ Background:
4
+ Given I have the flows:
5
+ """
6
+ flow :has_alternates, [:start, anything.action]
7
+ flow :start, settings.one
8
+ """
9
+
10
+ Scenario: Enter a flow when I match a group of alternatives
11
+ When I visit "/settings/one"
12
+ Then I should see "In the alternatives flow"
13
+
@@ -0,0 +1,60 @@
1
+ Feature: Flows with conditions
2
+ In order to have finer grained control of what flows are active
3
+ I want to specify boolean conditions on flow expressions
4
+
5
+ Scenario: Mutually exclusive flows
6
+ Given I have the flows:
7
+ """
8
+ flow :steps, settings.one,
9
+ settings.two,
10
+ settings.three
11
+
12
+ flow :mess, settings.two { not in_any_flow? },
13
+ settings.one
14
+ """
15
+ When I visit "/settings/one"
16
+ And I follow "Next"
17
+ When I follow "Next"
18
+ Then I should be at "/settings/three"
19
+ When I visit "/settings/two"
20
+ When I follow "Next"
21
+ Then I should be at "/settings/three"
22
+ And I should not see "Next"
23
+
24
+ Scenario: Terminate flow on given expression
25
+ Given I have the flows:
26
+ """
27
+ flow :steps, settings.three
28
+
29
+ flow :mess, settings.two,
30
+ settings.three,
31
+ settings.four
32
+
33
+ terminate :mess, :on => settings.five
34
+ """
35
+ When I visit "/settings/two"
36
+ And I visit "/settings/five"
37
+ And I visit "/settings/three"
38
+ Then I am at the end of the "mess" flow
39
+
40
+ Scenario: Specify list of flows to terminate flow on given expression
41
+ Given I have the flows:
42
+ """
43
+ flow :flow_one, settings.one,
44
+ settings.two,
45
+ settings.five
46
+
47
+ flow :flow_two, settings.four,
48
+ settings.five,
49
+ settings.two
50
+
51
+ terminate :flow_one, :flow_two, :on => settings.seven
52
+ """
53
+ When I visit "/settings/one"
54
+ And I visit "/settings/seven"
55
+ And I visit "/settings/two"
56
+ Then I am at the end of the "flow_two" flow
57
+ When I visit "/settings/four"
58
+ And I visit "/settings/seven"
59
+ And I visit "/settings/five"
60
+ Then I am at the end of the "flow_one" flow
@@ -0,0 +1,39 @@
1
+ Feature: Mutually exclusive flows
2
+ In order to simplify managment of overlapping flows
3
+ I want to mark sets of flows as mtually exclusive
4
+
5
+ Background:
6
+ Given I have the flows:
7
+ """
8
+ flow :one, settings.one,
9
+ settings.two
10
+
11
+ flow :two, settings.two
12
+
13
+ flow :others, settings.four,
14
+ settings.five
15
+
16
+ terminate :others, :on => settings.one
17
+
18
+ mutex :one, :two, :others
19
+ """
20
+
21
+ Scenario: Don't enter a flow excluded by another
22
+ When I visit "/settings/one"
23
+ And I visit "/settings/two"
24
+ Then I should see "In flow one"
25
+ And I should not see "In flow two"
26
+
27
+ Scenario: Locked flow becomes available after locking flow exits
28
+ When I visit "/settings/one"
29
+ And I visit "/settings/two"
30
+ And I visit "/settings/three"
31
+ And I visit "/settings/two"
32
+ Then I should not see "In flow one"
33
+ And I should see "In flow two"
34
+
35
+ Scenario: Locked flow becomes available if it terminates another
36
+ When I visit "/settings/four"
37
+ And I visit "/settings/one"
38
+ Then I should see "Flow number 1"
39
+
@@ -0,0 +1,20 @@
1
+ module ActionFlow
2
+ def define_flows(flow_dsl_string)
3
+ ApplicationController.instance_eval do
4
+
5
+ ActionFlow.configure do
6
+ instance_eval(flow_dsl_string)
7
+ end
8
+ end
9
+ end
10
+ end
11
+
12
+ World(ActionFlow)
13
+
14
+ Given "I have the flows:" do |dsl|
15
+ define_flows(dsl)
16
+ end
17
+
18
+ Then /^I am at the end of the "([^\"]+)" flow$/ do |_|
19
+ Then 'I should not see "Next"'
20
+ end
@@ -0,0 +1,7 @@
1
+ When /^(?:|I )visit "([^\"]+)"$/ do |url|
2
+ visit(url)
3
+ end
4
+
5
+ Then /^I should be at "([^\"]+)"$/ do |url|
6
+ URI.parse(current_url).path.should == url
7
+ end
@@ -0,0 +1,211 @@
1
+ # TL;DR: YOU SHOULD DELETE THIS FILE
2
+ #
3
+ # This file was generated by Cucumber-Rails and is only here to get you a head start
4
+ # These step definitions are thin wrappers around the Capybara/Webrat API that lets you
5
+ # visit pages, interact with widgets and make assertions about page content.
6
+ #
7
+ # If you use these step definitions as basis for your features you will quickly end up
8
+ # with features that are:
9
+ #
10
+ # * Hard to maintain
11
+ # * Verbose to read
12
+ #
13
+ # A much better approach is to write your own higher level step definitions, following
14
+ # the advice in the following blog posts:
15
+ #
16
+ # * http://benmabey.com/2008/05/19/imperative-vs-declarative-scenarios-in-user-stories.html
17
+ # * http://dannorth.net/2011/01/31/whose-domain-is-it-anyway/
18
+ # * http://elabs.se/blog/15-you-re-cuking-it-wrong
19
+ #
20
+
21
+
22
+ require 'uri'
23
+ require 'cgi'
24
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "paths"))
25
+ require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "selectors"))
26
+
27
+ module WithinHelpers
28
+ def with_scope(locator)
29
+ locator ? within(*selector_for(locator)) { yield } : yield
30
+ end
31
+ end
32
+ World(WithinHelpers)
33
+
34
+ # Single-line step scoper
35
+ When /^(.*) within ([^:]+)$/ do |step, parent|
36
+ with_scope(parent) { When step }
37
+ end
38
+
39
+ # Multi-line step scoper
40
+ When /^(.*) within ([^:]+):$/ do |step, parent, table_or_string|
41
+ with_scope(parent) { When "#{step}:", table_or_string }
42
+ end
43
+
44
+ Given /^(?:|I )am on (.+)$/ do |page_name|
45
+ visit path_to(page_name)
46
+ end
47
+
48
+ When /^(?:|I )go to (.+)$/ do |page_name|
49
+ visit path_to(page_name)
50
+ end
51
+
52
+ When /^(?:|I )press "([^"]*)"$/ do |button|
53
+ click_button(button)
54
+ end
55
+
56
+ When /^(?:|I )follow "([^"]*)"$/ do |link|
57
+ click_link(link)
58
+ end
59
+
60
+ When /^(?:|I )fill in "([^"]*)" with "([^"]*)"$/ do |field, value|
61
+ fill_in(field, :with => value)
62
+ end
63
+
64
+ When /^(?:|I )fill in "([^"]*)" for "([^"]*)"$/ do |value, field|
65
+ fill_in(field, :with => value)
66
+ end
67
+
68
+ # Use this to fill in an entire form with data from a table. Example:
69
+ #
70
+ # When I fill in the following:
71
+ # | Account Number | 5002 |
72
+ # | Expiry date | 2009-11-01 |
73
+ # | Note | Nice guy |
74
+ # | Wants Email? | |
75
+ #
76
+ # TODO: Add support for checkbox, select og option
77
+ # based on naming conventions.
78
+ #
79
+ When /^(?:|I )fill in the following:$/ do |fields|
80
+ fields.rows_hash.each do |name, value|
81
+ When %{I fill in "#{name}" with "#{value}"}
82
+ end
83
+ end
84
+
85
+ When /^(?:|I )select "([^"]*)" from "([^"]*)"$/ do |value, field|
86
+ select(value, :from => field)
87
+ end
88
+
89
+ When /^(?:|I )check "([^"]*)"$/ do |field|
90
+ check(field)
91
+ end
92
+
93
+ When /^(?:|I )uncheck "([^"]*)"$/ do |field|
94
+ uncheck(field)
95
+ end
96
+
97
+ When /^(?:|I )choose "([^"]*)"$/ do |field|
98
+ choose(field)
99
+ end
100
+
101
+ When /^(?:|I )attach the file "([^"]*)" to "([^"]*)"$/ do |path, field|
102
+ attach_file(field, File.expand_path(path))
103
+ end
104
+
105
+ Then /^(?:|I )should see "([^"]*)"$/ do |text|
106
+ if page.respond_to? :should
107
+ page.should have_content(text)
108
+ else
109
+ assert page.has_content?(text)
110
+ end
111
+ end
112
+
113
+ Then /^(?:|I )should see \/([^\/]*)\/$/ do |regexp|
114
+ regexp = Regexp.new(regexp)
115
+
116
+ if page.respond_to? :should
117
+ page.should have_xpath('//*', :text => regexp)
118
+ else
119
+ assert page.has_xpath?('//*', :text => regexp)
120
+ end
121
+ end
122
+
123
+ Then /^(?:|I )should not see "([^"]*)"$/ do |text|
124
+ if page.respond_to? :should
125
+ page.should have_no_content(text)
126
+ else
127
+ assert page.has_no_content?(text)
128
+ end
129
+ end
130
+
131
+ Then /^(?:|I )should not see \/([^\/]*)\/$/ do |regexp|
132
+ regexp = Regexp.new(regexp)
133
+
134
+ if page.respond_to? :should
135
+ page.should have_no_xpath('//*', :text => regexp)
136
+ else
137
+ assert page.has_no_xpath?('//*', :text => regexp)
138
+ end
139
+ end
140
+
141
+ Then /^the "([^"]*)" field(?: within (.*))? should contain "([^"]*)"$/ do |field, parent, value|
142
+ with_scope(parent) do
143
+ field = find_field(field)
144
+ field_value = (field.tag_name == 'textarea') ? field.text : field.value
145
+ if field_value.respond_to? :should
146
+ field_value.should =~ /#{value}/
147
+ else
148
+ assert_match(/#{value}/, field_value)
149
+ end
150
+ end
151
+ end
152
+
153
+ Then /^the "([^"]*)" field(?: within (.*))? should not contain "([^"]*)"$/ do |field, parent, value|
154
+ with_scope(parent) do
155
+ field = find_field(field)
156
+ field_value = (field.tag_name == 'textarea') ? field.text : field.value
157
+ if field_value.respond_to? :should_not
158
+ field_value.should_not =~ /#{value}/
159
+ else
160
+ assert_no_match(/#{value}/, field_value)
161
+ end
162
+ end
163
+ end
164
+
165
+ Then /^the "([^"]*)" checkbox(?: within (.*))? should be checked$/ do |label, parent|
166
+ with_scope(parent) do
167
+ field_checked = find_field(label)['checked']
168
+ if field_checked.respond_to? :should
169
+ field_checked.should be_true
170
+ else
171
+ assert field_checked
172
+ end
173
+ end
174
+ end
175
+
176
+ Then /^the "([^"]*)" checkbox(?: within (.*))? should not be checked$/ do |label, parent|
177
+ with_scope(parent) do
178
+ field_checked = find_field(label)['checked']
179
+ if field_checked.respond_to? :should
180
+ field_checked.should be_false
181
+ else
182
+ assert !field_checked
183
+ end
184
+ end
185
+ end
186
+
187
+ Then /^(?:|I )should be on (.+)$/ do |page_name|
188
+ current_path = URI.parse(current_url).path
189
+ if current_path.respond_to? :should
190
+ current_path.should == path_to(page_name)
191
+ else
192
+ assert_equal path_to(page_name), current_path
193
+ end
194
+ end
195
+
196
+ Then /^(?:|I )should have the following query string:$/ do |expected_pairs|
197
+ query = URI.parse(current_url).query
198
+ actual_params = query ? CGI.parse(query) : {}
199
+ expected_params = {}
200
+ expected_pairs.rows_hash.each_pair{|k,v| expected_params[k] = v.split(',')}
201
+
202
+ if actual_params.respond_to? :should
203
+ actual_params.should == expected_params
204
+ else
205
+ assert_equal expected_params, actual_params
206
+ end
207
+ end
208
+
209
+ Then /^show me the page$/ do
210
+ save_and_open_page
211
+ end