datashift_journey 0.1.2

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 (167) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +20 -0
  3. data/README.md +391 -0
  4. data/Rakefile +26 -0
  5. data/app/assets/stylesheets/datashift_journey/partials/_state_jumper_toolbar.scss.erb +27 -0
  6. data/app/controllers/concerns/datashift_journey/error_renderer.rb +9 -0
  7. data/app/controllers/concerns/datashift_journey/review_renderer.rb +21 -0
  8. data/app/controllers/concerns/datashift_journey/token_based_access.rb +23 -0
  9. data/app/controllers/concerns/datashift_journey/validate_state.rb +59 -0
  10. data/app/controllers/datashift_journey/abandon_enrollments_controller.rb +14 -0
  11. data/app/controllers/datashift_journey/abandonments_controller.rb +17 -0
  12. data/app/controllers/datashift_journey/api/v1/states_controller.rb +49 -0
  13. data/app/controllers/datashift_journey/application_controller.rb +53 -0
  14. data/app/controllers/datashift_journey/errors_controller.rb +24 -0
  15. data/app/controllers/datashift_journey/journey_ends_controller.rb +19 -0
  16. data/app/controllers/datashift_journey/journey_plans_controller.rb +166 -0
  17. data/app/controllers/datashift_journey/page_states_controller.rb +49 -0
  18. data/app/controllers/datashift_journey/reviews_controller.rb +32 -0
  19. data/app/controllers/datashift_journey/state_jumper_controller.rb +51 -0
  20. data/app/factories/datashift_journey/form_object_factory.rb +68 -0
  21. data/app/forms/datashift_journey/collector/base_collector_form.rb +60 -0
  22. data/app/forms/datashift_journey/concerns/form_mixin.rb +64 -0
  23. data/app/forms/datashift_journey/null_form.rb +20 -0
  24. data/app/helpers/datashift_journey/application_helper.rb +50 -0
  25. data/app/helpers/datashift_journey/back_link_helper.rb +9 -0
  26. data/app/models/datashift_journey/collector/data_node.rb +18 -0
  27. data/app/models/datashift_journey/collector/form_definition.rb +35 -0
  28. data/app/models/datashift_journey/collector/form_field.rb +61 -0
  29. data/app/models/datashift_journey/journey_review.rb +65 -0
  30. data/app/models/datashift_journey/review_data_section.rb +32 -0
  31. data/app/serializers/datashift_journey/collector/page_state_serializer.rb +9 -0
  32. data/app/serializers/state_machines/state/state_serializer.rb +5 -0
  33. data/app/views/datashift_journey/collector/_generic_form.html.erb +14 -0
  34. data/app/views/datashift_journey/errors/401.html.erb +13 -0
  35. data/app/views/datashift_journey/errors/403.html.erb +13 -0
  36. data/app/views/datashift_journey/errors/404.html.erb +13 -0
  37. data/app/views/datashift_journey/errors/422.html.erb +11 -0
  38. data/app/views/datashift_journey/errors/500.html.erb +13 -0
  39. data/app/views/datashift_journey/errors/503.html.erb +11 -0
  40. data/app/views/datashift_journey/errors/invalid_authenticity_token.html.erb +14 -0
  41. data/app/views/datashift_journey/journey_ends/new.html.erb +5 -0
  42. data/app/views/datashift_journey/journey_ends/show.html.erb +5 -0
  43. data/app/views/datashift_journey/journey_plans/_form.html.erb +14 -0
  44. data/app/views/datashift_journey/journey_plans/_render_fields.html.erb +24 -0
  45. data/app/views/datashift_journey/journey_plans/edit.html.erb +6 -0
  46. data/app/views/datashift_journey/journey_plans/new.html.erb +6 -0
  47. data/app/views/datashift_journey/shared/_default_actions.html.erb +6 -0
  48. data/app/views/datashift_journey/shared/_errors.html.erb +19 -0
  49. data/app/views/datashift_journey/shared/_submit_action.html.erb +5 -0
  50. data/app/views/datashift_journey/state_jumper/_toolbar.html.erb +16 -0
  51. data/config/brakeman.ignore +42 -0
  52. data/config/i18n-tasks.yml +103 -0
  53. data/config/initializers/exceptions_app.rb +3 -0
  54. data/config/initializers/mime_types.rb +1 -0
  55. data/config/initializers/rswag-api.rb +14 -0
  56. data/config/initializers/rswag-ui.rb +9 -0
  57. data/config/locales/en.yml +39 -0
  58. data/config/routes.rb +44 -0
  59. data/lib/datashift_journey/collector/field_snippet.rb +12 -0
  60. data/lib/datashift_journey/collector/page_state_snippet.rb +12 -0
  61. data/lib/datashift_journey/collector/snippet.rb +14 -0
  62. data/lib/datashift_journey/configuration.rb +103 -0
  63. data/lib/datashift_journey/engine.rb +38 -0
  64. data/lib/datashift_journey/exceptions.rb +26 -0
  65. data/lib/datashift_journey/helpers/back_link.rb +58 -0
  66. data/lib/datashift_journey/journey/machine_builder.rb +59 -0
  67. data/lib/datashift_journey/prepare_data_for_review.rb +219 -0
  68. data/lib/datashift_journey/reference_generator.rb +51 -0
  69. data/lib/datashift_journey/state_machines/branch_sequence_map.rb +35 -0
  70. data/lib/datashift_journey/state_machines/extensions.rb +40 -0
  71. data/lib/datashift_journey/state_machines/planner.rb +206 -0
  72. data/lib/datashift_journey/state_machines/sequence.rb +86 -0
  73. data/lib/datashift_journey/state_machines/state_machine_core_ext.rb +72 -0
  74. data/lib/datashift_journey/version.rb +3 -0
  75. data/lib/datashift_journey.rb +57 -0
  76. data/lib/generators/datashift_journey/collector/collector_generator.rb +43 -0
  77. data/lib/generators/datashift_journey/collector/install_collector_generator.rb +61 -0
  78. data/lib/generators/datashift_journey/collector/install_mongo_collector_generator.rb +44 -0
  79. data/lib/generators/datashift_journey/collector/templates/collector_concern.rb.tt +34 -0
  80. data/lib/generators/datashift_journey/collector/templates/collector_migration.rb.tt +46 -0
  81. data/lib/generators/datashift_journey/forms_generator.rb +45 -0
  82. data/lib/generators/datashift_journey/generate_common.rb +33 -0
  83. data/lib/generators/datashift_journey/setup/USAGE +12 -0
  84. data/lib/generators/datashift_journey/setup/setup_generator.rb +44 -0
  85. data/lib/generators/datashift_journey/setup/templates/initializer.rb.tt +17 -0
  86. data/lib/generators/datashift_journey/setup/templates/model_concern.rb.tt +37 -0
  87. data/lib/generators/datashift_journey/templates/base_form.rb.tt +7 -0
  88. data/lib/generators/datashift_journey/templates/collector_form.rb.tt +18 -0
  89. data/lib/generators/datashift_journey/templates/collector_view.rb.tt +15 -0
  90. data/lib/generators/datashift_journey/templates/journey_plan_form.rb.tt +23 -0
  91. data/lib/generators/datashift_journey/templates/journey_plan_view.rb.tt +15 -0
  92. data/lib/generators/datashift_journey/views_generator.rb +35 -0
  93. data/lib/tasks/state_machine.thor +48 -0
  94. data/spec/datashift_journey/complex_journey_spec.rb +132 -0
  95. data/spec/datashift_journey/machine_builder_spec.rb +268 -0
  96. data/spec/datashift_journey/planner_spec.rb +129 -0
  97. data/spec/datashift_journey/sequence_spec.rb +20 -0
  98. data/spec/dummy/Rakefile +6 -0
  99. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  100. data/spec/dummy/app/assets/stylesheets/application.css +16 -0
  101. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  102. data/spec/dummy/app/forms/base_form.rb +8 -0
  103. data/spec/dummy/app/forms/business_details_form.rb +17 -0
  104. data/spec/dummy/app/forms/business_type_form.rb +17 -0
  105. data/spec/dummy/app/forms/contact_details_form.rb +17 -0
  106. data/spec/dummy/app/forms/enter_reg_number_form.rb +17 -0
  107. data/spec/dummy/app/forms/new_or_renew_form.rb +17 -0
  108. data/spec/dummy/app/forms/postal_address_form.rb +17 -0
  109. data/spec/dummy/app/forms/question1_form.rb +9 -0
  110. data/spec/dummy/app/forms/question2_form.rb +12 -0
  111. data/spec/dummy/app/forms/sole_trader_name_form.rb +17 -0
  112. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  113. data/spec/dummy/app/models/payment.rb +5 -0
  114. data/spec/dummy/app/services/datashift_journey/models/collector_journey.rb +51 -0
  115. data/spec/dummy/app/views/_question1.html.erb +13 -0
  116. data/spec/dummy/app/views/_question2.html.erb +9 -0
  117. data/spec/dummy/app/views/layouts/alternative.html.erb +14 -0
  118. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  119. data/spec/dummy/app/views/pages/home.html.erb +6 -0
  120. data/spec/dummy/app/views/pages/start.html.erb +5 -0
  121. data/spec/dummy/bin/bundle +3 -0
  122. data/spec/dummy/bin/rails +4 -0
  123. data/spec/dummy/bin/rake +4 -0
  124. data/spec/dummy/bin/setup +29 -0
  125. data/spec/dummy/config/application.rb +39 -0
  126. data/spec/dummy/config/boot.rb +5 -0
  127. data/spec/dummy/config/database.yml +22 -0
  128. data/spec/dummy/config/environment.rb +5 -0
  129. data/spec/dummy/config/environments/development.rb +47 -0
  130. data/spec/dummy/config/environments/production.rb +79 -0
  131. data/spec/dummy/config/environments/test.rb +47 -0
  132. data/spec/dummy/config/initializers/assets.rb +11 -0
  133. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  134. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  135. data/spec/dummy/config/initializers/datashift_journey.rb +6 -0
  136. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  137. data/spec/dummy/config/initializers/inflections.rb +16 -0
  138. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  139. data/spec/dummy/config/initializers/session_store.rb +3 -0
  140. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  141. data/spec/dummy/config/locales/en.yml +27 -0
  142. data/spec/dummy/config/routes.rb +28 -0
  143. data/spec/dummy/config/secrets.yml +22 -0
  144. data/spec/dummy/config.ru +4 -0
  145. data/spec/dummy/db/migrate/20160101091218_create_dummy_checkout.rb +43 -0
  146. data/spec/dummy/db/migrate/20161221100703_datashift_journey_create_collector.rb +56 -0
  147. data/spec/dummy/db/schema.rb +142 -0
  148. data/spec/dummy/lib/version.rb +1 -0
  149. data/spec/factories/collector_factory.rb +16 -0
  150. data/spec/factories/collector_snippet_factory.rb +9 -0
  151. data/spec/factories/collector_state_page_factory.rb +12 -0
  152. data/spec/factories/data_node_factory.rb +9 -0
  153. data/spec/factories/form_factory.rb +6 -0
  154. data/spec/features/basic_navigation_spec.rb +125 -0
  155. data/spec/helpers/application_helper_spec.rb +40 -0
  156. data/spec/integration/collector/page_state_spec.rb +45 -0
  157. data/spec/models/collector/collector_spec.rb +100 -0
  158. data/spec/models/collector/page_state_spec.rb +30 -0
  159. data/spec/rails_helper.rb +73 -0
  160. data/spec/requests/collector/api/v1/page_state_spec.rb +85 -0
  161. data/spec/requests/collector/api/v1/states_spec.rb +28 -0
  162. data/spec/spec_helper.rb +63 -0
  163. data/spec/support/asserts.rb +27 -0
  164. data/spec/support/mailer_macros.rb +25 -0
  165. data/spec/support/page_objects/base_page_object.rb +77 -0
  166. data/spec/swagger_helper.rb +25 -0
  167. metadata +425 -0
@@ -0,0 +1,46 @@
1
+ class DatashiftJourneyCreateCollector < ActiveRecord::Migration[<%= @migration_version %>]
2
+
3
+ # rubocop:disable Metrics/MethodLength
4
+
5
+ def change
6
+
7
+ create_table :dsj_form_definitions do |t|
8
+ t.string :state, index: true, null: false
9
+ t.string :klass, index: true, null: false
10
+ t.timestamps null: false
11
+ end
12
+
13
+ create_table :dsj_form_fields do |t|
14
+ t.references :form_definition, index: true, null: false
15
+ t.string :name, index: true, null: false, limit: 100
16
+ t.integer :category, index: true, null: false
17
+ t.text :presentation
18
+ t.timestamps null: false
19
+ end
20
+
21
+ # The plan is an instance of a JourneyPlan class
22
+ create_table :dsj_data_nodes do |t|
23
+ t.references :plan, null: false, polymorphic: true
24
+ t.references :form_field, null: false
25
+ t.text :field_value
26
+ t.timestamps null: false
27
+ end
28
+
29
+ create_table :dsj_snippets do |t|
30
+ t.text :raw_text
31
+ t.string :I18n_key, index: true
32
+ t.timestamps null: false
33
+ end
34
+
35
+ create_table :dsj_forms_snippets do |t|
36
+ t.references :form_model, null: false
37
+ t.references :snippet, null: false
38
+ end
39
+
40
+ create_table :dsj_fields_snippets do |t|
41
+ t.references :form_field, null: false
42
+ t.references :snippet, null: false
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,45 @@
1
+ require 'datashift_journey/collector/base_collector_form'
2
+
3
+ module DatashiftJourney
4
+
5
+ class FormsGenerator < Rails::Generators::Base
6
+
7
+ attr_accessor :state
8
+
9
+ source_root File.expand_path('templates', __dir__)
10
+
11
+ class_option :base_class, type: :string, banner: "ClassName", desc: "Class to use as the Base class for generated Forms"
12
+ class_option :"no-collector", type: :boolean, default: false, desc: "Do not generate form to use inbuilt data Collector"
13
+
14
+ desc 'This generator creates an initializer and concern to setup and manage the journey Model'
15
+
16
+ def create_form_per_state
17
+
18
+ method_ptr = if options["no-collector"] == false || DatashiftJourney.journey_plan_class == DatashiftJourney::Collector::Collector
19
+ ->() { state_forms_for_collector_definition }
20
+ else
21
+ ->() { state_form_definition }
22
+ end
23
+
24
+ DatashiftJourney.journey_plan_class.state_machine(:state).states.map(&:name).each do |state|
25
+ @state = state
26
+ method_ptr.call
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def state_forms_for_collector_definition
33
+ @datashift_journey_baseform = "DatashiftJourney::Collector::BaseCollectorForm"
34
+ template 'base_form.rb', "app/forms/base_form.rb"
35
+ template 'collector_form.rb', "app/forms/#{state}_form.rb"
36
+ end
37
+
38
+ def state_form_definition
39
+ @datashift_journey_baseform = "DatashiftJourney::BaseForm"
40
+ template 'base_form.rb', "app/forms/base_form.rb"
41
+ template 'journey_plan_form.rb', "app/forms/#{state}_form.rb"
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,33 @@
1
+ module DatashiftJourney
2
+
3
+ module GenerateCommon
4
+
5
+ # Copied from https://github.com/rails/rails/blob/6-0-stable/activerecord/lib/rails/generators/active_record/migration.rb#L12-L16
6
+
7
+ # Implement the required interface for Rails::Generators::Migration.
8
+ def next_migration_number(dirname)
9
+ next_migration_number = current_migration_number(dirname) + 1
10
+ ActiveRecord::Migration.next_migration_number(next_migration_number)
11
+ end
12
+
13
+ def model_path
14
+ @model_path ||= File.join(destination_root, "app", "models", journey_plan_filename)
15
+ end
16
+
17
+ # Module name = options[:journey_class]Journey
18
+
19
+ def journey_plan_filename
20
+ "#{DatashiftJourney.journey_plan_class.to_s.underscore}.rb"
21
+ end
22
+ #
23
+ # def concern_file(journey_class)
24
+ # "#{journey_class.underscore}_journey.rb"
25
+ # end
26
+ #
27
+ # def decorator_file(journey_class)
28
+ # "#{journey_class.underscore}_decorator.rb"
29
+ # end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,12 @@
1
+ Description:
2
+ This generator will setup a full Datashift Journey Plan.
3
+
4
+ Creates a model file to hold the plan.
5
+
6
+ Creates an initializer file at config/initializers
7
+
8
+ Example:
9
+ bin/rails generate setup Model
10
+
11
+ This will create :
12
+ app/models/datashift_journey/model.rb
@@ -0,0 +1,44 @@
1
+ module DatashiftJourney
2
+ class SetupGenerator < Rails::Generators::NamedBase
3
+
4
+ source_root File.expand_path('templates', __dir__)
5
+
6
+ class_option :migration, type: :boolean, default: true, aliases: '-m'
7
+
8
+ # This class automatically runs every method defined in it,
9
+ def generate_model
10
+ Rails::Generators.invoke "active_record:model", [name, 'state:string', 'reference:string:index:unique'], migration: options[:migration] unless model_exists? && behavior == :invoke
11
+
12
+ plan = File.open(File.join(SetupGenerator.source_root, "model_concern.rb.tt")).read
13
+
14
+ inject_into_file model_path, :after => /class.* < ApplicationRecord/ do
15
+ "\n\tinclude DatashiftJourney::ReferenceGenerator.new(prefix: 'C')\n\tvalidates_presence_of :reference\n\tvalidates_uniqueness_of :reference\n\n#{plan}"
16
+ end
17
+ end
18
+
19
+ def create_files
20
+ template 'initializer.rb', 'config/initializers/datashift_journey.rb'
21
+ end
22
+
23
+ def add_engine_to_routes
24
+ return if File.readlines(File.join(destination_root, "config/routes.rb")).grep(/mount DatashiftJourney::Engine =>/).present?
25
+
26
+ route("mount DatashiftJourney::Engine => '/journey'")
27
+
28
+ puts '*' * 50
29
+ puts "We added the following line to your application's config/routes.rb file:\n"
30
+ puts " mount DatashiftJourney::Engine => '/journey'"
31
+ end
32
+
33
+ private
34
+
35
+ def model_exists?
36
+ File.exist?(File.join(destination_root, model_path))
37
+ end
38
+
39
+ def model_path
40
+ @model_path ||= File.join(destination_root, "app", "models", "#{file_path}.rb")
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,17 @@
1
+ DatashiftJourney.journey_plan_class = '<%= class_name %>'
2
+
3
+ DatashiftJourney::Configuration.configure do |config|
4
+ config.partial_location = '<%= class_name.pluralize.underscore %>'
5
+
6
+ config.add_state_jumper_toolbar = false
7
+
8
+ config.state_jumper_states = {}
9
+ end
10
+
11
+ # For Form validation options see - https://github.com/trailblazer/reform#installation
12
+ #
13
+ require "reform/form/dry"
14
+
15
+ Reform::Form.class_eval do
16
+ feature Reform::Form::Dry
17
+ end
@@ -0,0 +1,37 @@
1
+
2
+ DatashiftJourney::Journey::MachineBuilder.create_journey_plan(initial: :TO_DO_SET_INITIAL_STATE) do
3
+
4
+ =begin
5
+ The available API is defined in : datashift_journey/lib/datashift_journey/state_machines/planner.rb
6
+
7
+ A basic example with 2 simple steps, followed by one set of branches, which reconnect to another common
8
+ section starting at :review
9
+
10
+ DatashiftJourney::Journey::MachineBuilder.create_journey_plan(initial: :ship_address) do
11
+
12
+ # Two simple sequential steps
13
+ sequence [:ship_address, :bill_address]
14
+
15
+ # first define the sequences
16
+ branch_sequence :visa_sequence, [:visa_page1, :visa_page2]
17
+
18
+ branch_sequence :mastercard_sequence, [:page_mastercard1, :page_mastercard2, :page_mastercard3]
19
+
20
+ branch_sequence :paypal_sequence, []
21
+
22
+ # now define the parent state and the routing criteria to each sequence
23
+ # So after bill address we reach payment - then we split to a single step, depending on the card type entered
24
+
25
+ split_on_equality( :payment,
26
+ "payment_card", # Helper method on Checkout that returns card type from Payment
27
+ visa_sequence: 'visa',
28
+ mastercard_sequence: 'mastercard',
29
+ paypal_sequence: 'paypal'
30
+ )
31
+
32
+ # All different card type branches, recombine here at review
33
+ sequence [:review, :complete ]
34
+ end
35
+ =end
36
+
37
+ end
@@ -0,0 +1,7 @@
1
+ class BaseForm <%= "< #{@datashift_journey_baseform}" if @datashift_journey_baseform %>
2
+
3
+ def initialize(model, journey_plan = nil)
4
+ super(model, journey_plan)
5
+ end
6
+
7
+ end
@@ -0,0 +1,18 @@
1
+ class <%= state.to_s.camelize %>Form < ::BaseForm
2
+
3
+ def params_key
4
+ :<%= state %>
5
+ end
6
+
7
+ # Data collection performed by base class but if you need custom processing, the forms
8
+ # fields are represented as a series of DataNode objects, with the actual data saved in :field_value
9
+ #
10
+ # collection :data_nodes do
11
+ # property :field_value
12
+ # end
13
+
14
+ # Example basic validation - has field been filled in :
15
+ # validates :field_value, presence: true
16
+
17
+ end
18
+
@@ -0,0 +1,15 @@
1
+ <!--
2
+ You can access the Reform form object via local : form
3
+ You can access the Reform form model object via : form.model
4
+ You have access to main DSJ journey object via local : journey_plan
5
+ -->
6
+
7
+ <header class="text">
8
+ <h1 class="form-title heading-large" id="groupLabel"><%%= t(".header") %></h1>
9
+ </header>
10
+
11
+ <fieldset>
12
+ <legend class="visuallyhidden"><%%= t(".legend") %></legend>
13
+ <%%= f.label :field_value, t(".field_value_label"), class: 'form-label' %>
14
+ <%%= f.text_field :field_value, class: 'form-control form-control-char-50' %>
15
+ </fieldset>
@@ -0,0 +1,23 @@
1
+ class <%= state.to_s.camelize %>Form < ::BaseForm
2
+
3
+ def self.factory(journey)
4
+
5
+ # Auto generated by DatashiftJourney - check the Model this Form is managing
6
+
7
+ # state_model = #{state.classify}.new
8
+ # new(state_model, journey)
9
+
10
+ # For example if this page concerned with collecting data for an Organisation model use :
11
+
12
+ # organisation = journey.organisation
13
+ # new(organisation, journey)
14
+
15
+ # If you have no separate model just use
16
+ new(journey)
17
+ end
18
+
19
+ def params_key
20
+ :<%= state %>
21
+ end
22
+ end
23
+
@@ -0,0 +1,15 @@
1
+ <!--
2
+ You can access the Reform form object via local : form
3
+ You can access the Reform form model object via : form.model
4
+ You have access to main DSJ journey object via local : journey_plan
5
+ -->
6
+
7
+ <header class="text">
8
+ <h1 class="form-title heading-large" id="groupLabel"><%%= t(".header") %></h1>
9
+ </header>
10
+
11
+ <fieldset>
12
+ <legend class="visuallyhidden"><%%= t(".legend") %></legend>
13
+ <%%= f.label :field_value, t(".field_value_label"), class: 'form-label' %>
14
+ <%%= f.text_field :field_value, class: 'form-control form-control-char-50' %>
15
+ </fieldset>
@@ -0,0 +1,35 @@
1
+ module DatashiftJourney
2
+
3
+ class ViewsGenerator < Rails::Generators::Base
4
+
5
+ source_root File.expand_path('templates', __dir__)
6
+
7
+ desc 'This generator creates an initializer and concern to setup and manage the journey Model'
8
+
9
+ def create_form_per_state
10
+ method_ptr = if DatashiftJourney.journey_plan_class == DatashiftJourney::Collector::Collector
11
+ ->(p) { view_for_collector_definition(p) }
12
+ else
13
+ ->(p) { view_for_journey_plan_definition(p) }
14
+ end
15
+
16
+ partial_location = DatashiftJourney::Configuration.call.partial_location
17
+
18
+ path = 'app/views'
19
+ path = File.join(path, partial_location) if partial_location.present?
20
+
21
+ DatashiftJourney.state_names.each { |state| method_ptr.call(state.to_s, File.join(path, "_#{state}.html.erb")) }
22
+ end
23
+
24
+ private
25
+
26
+ def view_for_collector_definition(path)
27
+ template 'collector_view.rb', path
28
+ end
29
+
30
+ def view_for_journey_plan_definition(path)
31
+ template 'journey_plan_view.rb', path
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,48 @@
1
+ require'datashift_journey'
2
+ require 'thor'
3
+
4
+ class StateMachine < Thor
5
+
6
+ desc "report", "Report on the states and transitions availblke for current DSJ Journey Plan"
7
+
8
+ def report
9
+
10
+ environment
11
+
12
+ journey_plan = DatashiftJourney.journey_plan_class.new
13
+
14
+ state_machine = DatashiftJourney.journey_plan_class.state_machine
15
+
16
+ puts "\nINITIAL STATE", journey_plan.state.inspect
17
+
18
+ puts "\nEVENTS",journey_plan.state_events.inspect
19
+
20
+ puts "\nSTATE PATHS",journey_plan.state_paths.inspect
21
+
22
+ puts "\nSTATES", state_machine.states.map(&:name).inspect
23
+
24
+ puts "\nEVENT KEYS", state_machine.events.keys.sort
25
+
26
+ puts "\nBACK", state_machine.events[:back].known_states.sort
27
+ end
28
+
29
+ no_commands do
30
+
31
+ def environment
32
+
33
+ if File.exist?(File.expand_path('config/environment.rb'))
34
+ begin
35
+ require File.expand_path('config/environment.rb')
36
+ rescue => e
37
+ puts ("Failed to initialise ActiveRecord : #{e.message}")
38
+ exit -1
39
+ #raise ConnectionError.new("Failed to initialise ActiveRecord : #{e.message}")
40
+ end
41
+
42
+ else
43
+ raise PathError.new('No config/environment.rb found - cannot initialise ActiveRecord')
44
+ end
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,132 @@
1
+ require 'rails_helper'
2
+
3
+ module DatashiftJourney
4
+
5
+ module Journey
6
+
7
+ RSpec.describe 'Complex Machine Builder Examples' do
8
+ before(:each) do
9
+ DatashiftJourney.use_default_journey_plan_class
10
+ # puts DatashiftJourney.journey_plan_class.state_machine.methods.sort
11
+ # puts DatashiftJourney.journey_plan_class.state_machine
12
+
13
+ # N.B This is bit of a hack cos DatashiftJourney.journey_plan_class.state_machines
14
+ # only available after at least one Machine defined
15
+
16
+ DatashiftJourney.journey_plan_class.state_machines.clear if DatashiftJourney.journey_plan_class.respond_to?(:state_machines)
17
+ end
18
+
19
+ context 'Multiple Sequences' do
20
+ it 'reports that branches are missing when not defined' do
21
+ pending('create a test with a splitter but where some branch_sequences are not defined')
22
+
23
+ DatashiftJourney::Journey::MachineBuilder.create_journey_plan(initial: :splitter) do
24
+ branch_sequence :renew_sequence, [:business_type]
25
+
26
+ split_on_equality(:splitter,
27
+ 'new_or_renew_value', # Helper method on Collector
28
+ new_sequence: 'new',
29
+ renew_sequence: 'renew')
30
+ end
31
+
32
+ journey = DatashiftJourney.journey_plan_class.new
33
+
34
+ expect_state_matches(journey, :new_or_renew)
35
+ expect(journey.can_back?).to eq false # this is the initial state
36
+
37
+ journey.next!
38
+ end
39
+
40
+ it 'enables the first state to be a branch splitter' do
41
+ DatashiftJourney::Journey::MachineBuilder.create_journey_plan(initial: :new_or_renew) do
42
+ branch_sequence :new_sequence, [:business_type]
43
+
44
+ branch_sequence :renew_sequence, [:enter_reg_number]
45
+
46
+ # now define the parent state and the routing criteria to each sequence
47
+
48
+ split_on_equality(:new_or_renew,
49
+ 'new_or_renew_value', # Helper method on Collector
50
+ new_sequence: 'new',
51
+ renew_sequence: 'renew')
52
+
53
+ # Define the sequences for Business Type split
54
+ # Partnership Limited Company Public Body Charity Authority Other
55
+
56
+ split_on_equality(:business_type,
57
+ 'business_type_value', # Helper method on Collector,
58
+ authority: 'authority',
59
+ other_sequence: 'other',
60
+ sole_trader_sequence: 'sole_trader',
61
+ partnership_sequence: 'partnership',
62
+ limited_company: 'limited_company',
63
+ public_body: 'public_body',
64
+ charity: 'charity')
65
+
66
+ # SOLE TRADER
67
+ branch_sequence :sole_trader_sequence, [:other_businesses, :service_provided]
68
+
69
+ split_on_equality(:service_provided,
70
+ 'service_provided_value', # Helper method on Collector
71
+ service_provided_no_sequence: 'no',
72
+ service_provided_yes_sequence: 'yes')
73
+
74
+ branch_sequence :service_provided_no_sequence, [] # => construction_demolition
75
+
76
+ # OTHER
77
+
78
+ branch_sequence :other_sequence, [:other_business]
79
+
80
+ # Construction Demolition
81
+
82
+ split_on_equality(:construction_demolition,
83
+ :construction_demolition_value,
84
+ registration_type_sequence: 'yes') # => registration_type
85
+
86
+ # This branch may actually simplify down to just
87
+ # sequence [ :registration_type, :business_details]
88
+
89
+ split_on_equality(:registration_type,
90
+ :registration_type_value,
91
+ carrier_dealer_sequence: 'carrier_dealer',
92
+ broker_dealer_sequence: 'broker_dealer',
93
+ carrier_broker_dealer_sequence: 'carrier_broker_dealer')
94
+
95
+ branch_sequence :carrier_dealer_sequence, [:business_details]
96
+ branch_sequence :broker_dealer_sequence, [:business_details]
97
+ branch_sequence :carrier_broker_dealer_sequence, [:business_details]
98
+
99
+ # RESTART COMMON JOURNEY AFTER BUSINESS TYPES
100
+
101
+ sequence [
102
+ :contact_details,
103
+ :postal_address
104
+ ]
105
+ end
106
+
107
+ journey = DatashiftJourney.journey_plan_class.new(reference: 'UNIQUE_1234')
108
+
109
+ expect_state_matches(journey, :new_or_renew)
110
+ expect(journey.can_back?).to eq false # this is the initial state
111
+ expect(journey.can_next?).to eq true
112
+ journey.next!
113
+
114
+ expect_state_canback_cannext_and_next!(journey, :business_type)
115
+
116
+ # First state of a branch - We should be able to go back to the Split - based on same conditions
117
+ expect_state_matches(journey, :other_businesses)
118
+ expect(journey.can_back?).to eq true
119
+ journey.back!
120
+
121
+ expect_state_canback_cannext_and_next!(journey, :business_type)
122
+
123
+ # Now in Sole Trader branhc
124
+ expect_state_canback_cannext_and_next!(journey, :other_businesses)
125
+ expect_state_canback_cannext_and_next!(journey, :service_provided)
126
+
127
+ expect_state_matches(journey, :construction_demolition)
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end