journea 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +8 -0
  3. data/README.md +131 -0
  4. data/Rakefile +26 -0
  5. data/app/controllers/journea/engine_controller.rb +19 -0
  6. data/app/controllers/journea/journeys_controller.rb +10 -0
  7. data/app/controllers/journea/steps_controller.rb +52 -0
  8. data/app/helpers/journea/form_helper.rb +25 -0
  9. data/app/models/journea/journey.rb +54 -0
  10. data/app/models/journea/journey_transition.rb +17 -0
  11. data/app/models/journea/keyvalue.rb +5 -0
  12. data/app/views/application/_back_link.html.haml +5 -0
  13. data/config/initializers/simple_form.rb +179 -0
  14. data/config/locales/simple_form.en.yml +31 -0
  15. data/config/routes.rb +5 -0
  16. data/db/migrate/20161201153630_create_journey_transitions.rb +22 -0
  17. data/db/migrate/20161201162349_create_journeys_table.rb +10 -0
  18. data/db/migrate/20161202162350_create_keyvalues_table.rb +11 -0
  19. data/lib/generators/journea/install_generator.rb +76 -0
  20. data/lib/generators/journea/templates/assets/application.js +23 -0
  21. data/lib/generators/journea/templates/assets/application.scss +49 -0
  22. data/lib/generators/journea/templates/config/initializers/journea.rb +23 -0
  23. data/lib/generators/journea/templates/config/initializers/journea2.rb +24 -0
  24. data/lib/generators/journea/templates/controllers/pages_controller.rb.example +2 -0
  25. data/lib/generators/journea/templates/views/application.html.haml +20 -0
  26. data/lib/generators/journea/templates/views/pages/start.html.haml.example +12 -0
  27. data/lib/generators/journea/templates/views/shared/error_messages.html.haml.example +7 -0
  28. data/lib/generators/journea/templates/views/steps/step.html.haml.example +8 -0
  29. data/lib/journea.rb +25 -0
  30. data/lib/journea/data.rb +20 -0
  31. data/lib/journea/engine.rb +27 -0
  32. data/lib/journea/state_machine.rb +40 -0
  33. data/lib/journea/step.rb +32 -0
  34. data/lib/journea/version.rb +3 -0
  35. data/lib/simple_form_object.rb +146 -0
  36. data/lib/tasks/journea_tasks.rake +4 -0
  37. data/lib/templates/erb/scaffold/_form.html.erb +13 -0
  38. data/spec/dummy/Rakefile +6 -0
  39. data/spec/dummy/app/assets/config/manifest.js +5 -0
  40. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  41. data/spec/dummy/app/assets/javascripts/cable.js +13 -0
  42. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  43. data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
  44. data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
  45. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  46. data/spec/dummy/app/controllers/pages_controller.rb +4 -0
  47. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  48. data/spec/dummy/app/jobs/application_job.rb +2 -0
  49. data/spec/dummy/app/mailers/application_mailer.rb +4 -0
  50. data/spec/dummy/app/models/application_record.rb +3 -0
  51. data/spec/dummy/app/steps/address_step.rb +4 -0
  52. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  53. data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
  54. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  55. data/spec/dummy/app/views/pages/start.html.erb +1 -0
  56. data/spec/dummy/app/views/steps/address.html.erb +5 -0
  57. data/spec/dummy/app/views/steps/name.html.erb +5 -0
  58. data/spec/dummy/app/views/steps/phone_number.html.erb +0 -0
  59. data/spec/dummy/bin/bundle +3 -0
  60. data/spec/dummy/bin/rails +4 -0
  61. data/spec/dummy/bin/rake +4 -0
  62. data/spec/dummy/bin/setup +34 -0
  63. data/spec/dummy/bin/update +29 -0
  64. data/spec/dummy/config.ru +5 -0
  65. data/spec/dummy/config/application.rb +14 -0
  66. data/spec/dummy/config/boot.rb +5 -0
  67. data/spec/dummy/config/cable.yml +9 -0
  68. data/spec/dummy/config/database.yml +25 -0
  69. data/spec/dummy/config/environment.rb +5 -0
  70. data/spec/dummy/config/environments/development.rb +54 -0
  71. data/spec/dummy/config/environments/production.rb +86 -0
  72. data/spec/dummy/config/environments/test.rb +42 -0
  73. data/spec/dummy/config/initializers/application_controller_renderer.rb +6 -0
  74. data/spec/dummy/config/initializers/assets.rb +11 -0
  75. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  76. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  77. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  78. data/spec/dummy/config/initializers/inflections.rb +16 -0
  79. data/spec/dummy/config/initializers/journea.rb +8 -0
  80. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  81. data/spec/dummy/config/initializers/new_framework_defaults.rb +24 -0
  82. data/spec/dummy/config/initializers/session_store.rb +3 -0
  83. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  84. data/spec/dummy/config/locales/en.yml +23 -0
  85. data/spec/dummy/config/puma.rb +47 -0
  86. data/spec/dummy/config/routes.rb +4 -0
  87. data/spec/dummy/config/secrets.yml +22 -0
  88. data/spec/dummy/config/spring.rb +6 -0
  89. data/spec/dummy/db/development.sqlite3 +0 -0
  90. data/spec/dummy/db/schema.rb +42 -0
  91. data/spec/dummy/db/test.sqlite3 +0 -0
  92. data/spec/dummy/log/development.log +57 -0
  93. data/spec/dummy/log/test.log +448 -0
  94. data/spec/dummy/public/404.html +67 -0
  95. data/spec/dummy/public/422.html +67 -0
  96. data/spec/dummy/public/500.html +66 -0
  97. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  98. data/spec/dummy/public/apple-touch-icon.png +0 -0
  99. data/spec/dummy/public/favicon.ico +0 -0
  100. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/EA/EAR2KaBsPQau6HwaTGrxV7EGMwAWyS-4C8pGVDU1Z-Q.cache +1 -0
  101. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/JZ/JZuPFgM2mLQgAHB-r7Pt_TdGW3DRWPrplT7xPjnCJSE.cache +1 -0
  102. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/LT/LTt1oE1gi9m65YRwUp78DuufkCOfi3sa3Qa05_ykADk.cache +1 -0
  103. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Lj/LjRKOqtrClPtb97phuIWI7HHA8-p5J5nKHVE_zEycxo.cache +1 -0
  104. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/QM/QMxUWUtpFaqwG3RI9t3m38QtFAOvHpiY3LphKJiVRTc.cache +1 -0
  105. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/St/StFuOi5bd69ceiqz2D6EOCmRzHgwY0IxERsMfiCneGM.cache +1 -0
  106. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Vj/VjCUmI9pp5eD8BlYATbwJ4ZOk0438rwvtDrsttyWbfQ.cache +0 -0
  107. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Wl/WlLcUIADXcUxaOt8Gq59uQbO0mquikyz7jxiJjXjhaI.cache +1 -0
  108. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/YH/YHo__TrFq5y8rpx49T8d8ecyvMLbHFbnhz7IBV7pM98.cache +1 -0
  109. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Zu/ZuLXgeX6x_Dx8sCUbzuTtWu42HzQXWe7eooTgsonTnc.cache +0 -0
  110. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/aG/aG1tp1pv4kBiSKx9ixZH5xiRXH-rGa0Hi6sdl4SHw6g.cache +3 -0
  111. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/aw/awTDtuorxLsRLIGEYCefR-PTN7yptoaGRLEk13vHVLA.cache +1 -0
  112. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/b_/b_Itlk9QZZd7Rvf8kcA4yLP1R5Acu7jB-m1xQiSU0qE.cache +2 -0
  113. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/cW/cWHxYOqms7abJ-dzXyrn2G7nadJicU5kkC8SduS06yc.cache +0 -0
  114. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/cb/cbX7RFavWZoWojlrsXhsy4FN46UOfMwZYStkLHT5HJw.cache +1 -0
  115. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/eY/eYTHvo0Wl2TD2vipDcJ966HHXGrA4ZlJaiA2DnftKhM.cache +1 -0
  116. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/f4/f4CpFuYmsdh1EvlFVjsl1WmM2QA9_z9AuOebEOw4mts.cache +0 -0
  117. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/gH/gHSjqTBRSW22rP9mSj8XW6huEXKpcIOehvF-2fvuShQ.cache +1 -0
  118. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/m0/m0smDeM83DcmOVpX9zos6AIX40E9Tf2j26tyvKFgMwc.cache +1 -0
  119. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/mW/mWrwQwWUQpAlMCLQ8dGv5VkAXd-TxvAtB5WPg9l09-A.cache +1 -0
  120. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/mX/mX1nlsL_SWOB4y22W5FheRX0YEONKyOY7xUeIvRiHco.cache +2 -0
  121. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/n_/n_xYqQYhwEMQknb3jFQnjlxxBE9TzMNHCdJ-bEyZFIw.cache +2 -0
  122. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/nx/nxTv3sKVUQZADJyM3dPaVmUA78MIsMLD_K279yN_GsI.cache +2 -0
  123. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/oA/oAtzk5ZeF7lWCwNufspgWqtv_dQN_n5GumFNB4ScfBE.cache +2 -0
  124. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/rA/rACdJeX3jZNkhx4awGhdZeMdOfmARubkoVKVAZdwYog.cache +0 -0
  125. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/sC/sCEBZXwTjRpEzThf7YHzHNfkAupMVOx_Ko7WNMAFHS0.cache +0 -0
  126. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/x7/x7PYh8DJvPykcEqpVab2vcY9-GFz-3cqtoMlRAu94Uc.cache +2 -0
  127. data/spec/dummy/tmp/cache/assets/sprockets/v3.0/yT/yTOf3QInGfDlRZlwZNDBLlbma19FQRHM035AKRxcRyw.cache +0 -0
  128. data/spec/features/journey_spec.rb +9 -0
  129. data/spec/lib/simple_form_object/attribute_spec.rb +74 -0
  130. data/spec/lib/simple_form_object/delegation_spec.rb +50 -0
  131. data/spec/lib/simple_form_object/model_spec.rb +90 -0
  132. data/spec/lib/steps_spec.rb +0 -0
  133. data/spec/requests/journeys_spec.rb +6 -0
  134. data/spec/spec_helper.rb +44 -0
  135. metadata +449 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b62952533c42e385316284af3e0e14a3e2ed34dd
4
+ data.tar.gz: bd27f82ed21bd76b53012e7f46a02f0d43981706
5
+ SHA512:
6
+ metadata.gz: baa167b6d1f4b4a96b61bbc5d02650be086ecc0e316820df4a751ed3339dc59ec62b522de07aace8582f295a97dc8e411c8f231deebe2148854ee79ebbf63f38
7
+ data.tar.gz: b305cae30727a93ad44efd909cd145e13768f44e70b616e36e13988a2edd4dbd92cfa432e3852dc14d40b00bab1fe37987f6f3355b9cdea4cb0f7e5453a2b13f
data/LICENSE ADDED
@@ -0,0 +1,8 @@
1
+ The Open Government Licence (OGL) Version 3
2
+
3
+ Copyright (c) 2016 Environment Agency
4
+
5
+ This source code is licensed under the Open Government Licence v3.0. To view this
6
+ licence, visit www.nationalarchives.gov.uk/doc/open-government-licence/version/3
7
+ or write to the Information Policy Team, The National Archives, Kew, Richmond,
8
+ Surrey, TW9 4DU.
@@ -0,0 +1,131 @@
1
+ # Journea (Alpha)
2
+
3
+ [![Build Status](https://travis-ci.org/DEFRA/journea.svg?branch=master)](https://travis-ci.org/DEFRA/journea)
4
+ [![Code Climate](https://codeclimate.com/github/DEFRA/journea/badges/gpa.svg)](https://codeclimate.com/github/DEFRA/journea)
5
+ [![Test Coverage](https://codeclimate.com/github/DEFRA/journea/badges/coverage.svg)](https://codeclimate.com/github/DEFRA/journea/coverage)
6
+ [![security](https://hakiri.io/github/DEFRA/journea/master.svg)](https://hakiri.io/github/DEFRA/journea/master)
7
+ [![Dependency Status](https://dependencyci.com/github/DEFRA/journea/badge)](https://dependencyci.com/github/DEFRA/journea)
8
+ [![Gem Version](https://badge.fury.io/rb/journea.svg)](https://badge.fury.io/rb/journea)
9
+
10
+ **Journea** is a Ruby on Rails engine (tested on Rails 5 only) which you can use to define steps and the transitions between them. Each step (or page) is tracked in a state machine, [Statesman](https://github.com/gocardless/statesman).
11
+
12
+ **Journea** handles rendering the views you design and the transitions between them are defined in an initializer.
13
+
14
+ ## Installation
15
+
16
+ Add the gem to your gemfile.
17
+
18
+ Run the install generator and answer the questions
19
+
20
+ ```
21
+ # Only use in new, fresh Rails projects
22
+ rails generate journea:install
23
+ ```
24
+
25
+ ## Getting started
26
+
27
+ For each step you need to setup a state in the state machine
28
+
29
+ ```ruby
30
+ # config/initializers/journea.rb
31
+
32
+ Journea.configure_steps do
33
+ step :import_or_export, initial: true
34
+ step :company_type
35
+ step :limited_company_details
36
+ step :company_address
37
+ step :finish
38
+
39
+ transition from: :import_or_export, to: :company_type
40
+ transition from: :company_type, to: [:limited_company_details, :company_address]
41
+ transition from: :limited_company_details, to: :company_address
42
+ transition from: :company_address, to: :finish
43
+
44
+ guard_transition(to: :limited_company_details) do |journea|
45
+ journea.data.is_limited_company?
46
+ end
47
+
48
+ after_transition(to: :finish) do |journea|
49
+ Mailer.application_details(journea.data).deliver
50
+ end
51
+ end
52
+ ```
53
+
54
+ Each step can optionally have a step object, based on [simple form object](https://github.com/reinteractive-open/simple_form_object):
55
+
56
+ ```ruby
57
+ # app/steps/company_address_step.rb
58
+
59
+ class CompanyAddress < Journea::Step
60
+ attribute :address_line1, :string
61
+ attribute :address_line2, :string
62
+ attribute :city, :string
63
+ attribute :postcode, :string
64
+ attribute :is_business_address, :boolean
65
+
66
+ attribute :created_date, :datetime, default: Time.now
67
+
68
+ validates :address_line1, :address_line2, presence: true
69
+ end
70
+ ```
71
+
72
+ And each step needs a view
73
+
74
+ ```ruby
75
+
76
+ # app/views/layouts/application.html.haml
77
+
78
+ # Optional back link helper can be used anywhere
79
+
80
+ = render 'back_link'
81
+
82
+ # app/views/steps/company_address.html.haml
83
+
84
+ .grid-row
85
+ .column-two-thirds
86
+ = simple_form_for @form do |f|
87
+ = render 'shared/error_messages'
88
+
89
+ %h1.heading-large What is your company address?
90
+
91
+ = f.input :address_line1
92
+ = f.input :address_line2, hint: 'Optional'
93
+ = f.input :city
94
+ = f.input :postcode
95
+ = f.input :is_business_address
96
+
97
+ = f.button :submit, 'Continue'
98
+
99
+ ```
100
+
101
+ You can access data stored against the journey object like so
102
+
103
+ ```ruby
104
+ journey = Journea::Journey.find(1)
105
+ puts journey.data.certifcate_type
106
+
107
+ # or
108
+ puts @journea.data.certifcate_type
109
+ ```
110
+
111
+ ## Contributing to this project
112
+
113
+ If you have an idea you'd like to contribute please log an issue.
114
+
115
+ All contributions should be submitted via a pull request.
116
+
117
+ ## License
118
+
119
+ THIS INFORMATION IS LICENSED UNDER THE CONDITIONS OF THE OPEN GOVERNMENT LICENCE found at:
120
+
121
+ http://www.nationalarchives.gov.uk/doc/open-government-licence/version/3
122
+
123
+ The following attribution statement MUST be cited in your products and applications when using this information.
124
+
125
+ > Contains public sector information licensed under the Open Government license v3
126
+
127
+ ### About the license
128
+
129
+ The Open Government Licence (OGL) was developed by the Controller of Her Majesty's Stationery Office (HMSO) to enable information providers in the public sector to license the use and re-use of their information under a common open licence.
130
+
131
+ It is designed to encourage use and re-use of information freely and flexibly, with only a few conditions.
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require "bundler/setup"
4
+ rescue LoadError
5
+ puts "You must `gem install bundler` and `bundle install` to run rake tasks"
6
+ end
7
+
8
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
9
+ load "rails/tasks/engine.rake"
10
+
11
+ Bundler::GemHelper.install_tasks
12
+
13
+ Dir[File.join(File.dirname(__FILE__), "tasks/**/*.rake")].each {|f| load f }
14
+
15
+ require "rspec/core"
16
+ require "rspec/core/rake_task"
17
+
18
+ desc "Run all specs in spec directory (excluding plugin specs)"
19
+ RSpec::Core::RakeTask.new(:spec => "app:db:test:prepare")
20
+
21
+ task :default => :spec
22
+
23
+ require 'github_changelog_generator/task'
24
+
25
+ GitHubChangelogGenerator::RakeTask.new :changelog do |config|
26
+ end
@@ -0,0 +1,19 @@
1
+ module Journea
2
+ class EngineController < ::ApplicationController
3
+ helper Journea::Engine.helpers
4
+
5
+ protect_from_forgery with: :exception
6
+
7
+ before_action :set_view_paths
8
+
9
+ private
10
+
11
+ def model
12
+ ::Journea::Journey
13
+ end
14
+
15
+ def set_view_paths
16
+ prepend_view_path Rails.root.join("app", "views", "steps")
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,10 @@
1
+ module Journea
2
+ class JourneysController < EngineController
3
+
4
+ def create
5
+ journey = model.create
6
+ redirect_to edit_journey_step_path(journey, journey.state_machine.current_state)
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,52 @@
1
+ module Journea
2
+ class StepsController < EngineController
3
+
4
+ before_action :set_journey
5
+
6
+ def edit
7
+ @step = current_step_class.new(@journea.data_for_form(current_step_class.new))
8
+ @step.journey = @journea
9
+ render template: @step.step_name
10
+ end
11
+
12
+ def update
13
+ @step = current_step_class.new(form_params)
14
+ @step.pre_save(form_params)
15
+ @step.journey = @journea
16
+ if @journea.save_data(@step)
17
+ @journea.transition_to_next_step
18
+ redirect_to edit_journey_step_path(@journea, @journea.state_machine.current_state)
19
+ else
20
+ render template: @step.step_name
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def set_journey
27
+ @journea = model.find(params[:journey_id])
28
+
29
+ requested_state = params[:id]
30
+ return unless requested_state != @journea.current_state
31
+
32
+ @journea.transition_to!(requested_state)
33
+ end
34
+
35
+ def current_step_class
36
+ class_name = "#{@journea.current_state}_step".classify
37
+ begin
38
+ class_name.constantize
39
+ rescue NameError
40
+ # If no custom class is available then use the default
41
+ klass = Step
42
+ klass.custom_name = @journea.current_state
43
+ klass
44
+ end
45
+ end
46
+
47
+ def form_params
48
+ params.fetch(current_step_class.step_name.to_sym, {}).permit(current_step_class.permitted_params)
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,25 @@
1
+ module Journea
2
+ module FormHelper
3
+
4
+ # So that we don't have to put url: on every simple form, override the url
5
+ # generator
6
+ def polymorphic_path(*args)
7
+ if args.present?
8
+ record = args.first
9
+ return journey_step_path(@journea, @step.step_name) if record.is_a? Step
10
+ end
11
+ super(*args)
12
+ end
13
+
14
+ def link_to_input_with_error(element, messages)
15
+ capture do
16
+ messages.map do |message|
17
+ concat(content_tag(:li) do
18
+ link_to message, "##{@step.step_name}_#{element}"
19
+ end)
20
+ end
21
+ end
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,54 @@
1
+ module Journea
2
+ class Journey < ActiveRecord::Base
3
+
4
+ include Statesman::Adapters::ActiveRecordQueries
5
+
6
+ has_many :journea_journey_transitions, class_name: "Journea::JourneyTransition", autosave: false
7
+ has_many :journea_keyvalues, class_name: "Journea::Keyvalue"
8
+
9
+ # State Machine related
10
+
11
+ def state_machine
12
+ @state_machine ||= StateMachine.new(self, transition_class: JourneyTransition)
13
+ end
14
+
15
+ delegate :can_transition_to?,
16
+ :transition_to!,
17
+ :transition_to,
18
+ :current_state,
19
+ :transition_to_next_step,
20
+ :previous_step,
21
+ :in_initial_state?,
22
+ to: :state_machine
23
+
24
+ def self.transition_class
25
+ Journea::JourneyTransition
26
+ end
27
+ private_class_method :transition_class
28
+
29
+ # Instance methods
30
+
31
+ def save_data(step_object)
32
+ return false unless step_object.valid?
33
+ step_object.attributes.each do |k, v|
34
+ key_value_pair = Keyvalue.find_or_initialize_by(key: k.to_s, journey: self)
35
+ key_value_pair.value = v
36
+ key_value_pair.save
37
+ end
38
+
39
+ true
40
+ end
41
+
42
+ def data_for_form(step_object)
43
+ data = Keyvalue.where(key: step_object.attributes.keys, journey: self).map do |kv|
44
+ [kv.key.to_sym, kv.value]
45
+ end
46
+ data.to_h
47
+ end
48
+
49
+ def data
50
+ Data.new(Keyvalue.where(journey: self))
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,17 @@
1
+ module Journea
2
+ class JourneyTransition < ActiveRecord::Base
3
+ include Statesman::Adapters::ActiveRecordTransition
4
+
5
+ belongs_to :journey, inverse_of: :journea_journey_transitions
6
+
7
+ after_destroy :update_most_recent, if: :most_recent?
8
+
9
+ private
10
+
11
+ def update_most_recent
12
+ last_transition = journey.journey_transitions.order(:sort_key).last
13
+ return unless last_transition.present?
14
+ last_transition.update_column(:most_recent, true)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,5 @@
1
+ module Journea
2
+ class Keyvalue < ActiveRecord::Base
3
+ belongs_to :journey
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ - if @journea.present?
2
+ - if @journea.previous_step.present?
3
+ = link_to 'Back', edit_journey_step_path(@journea, @journea.previous_step), class: 'link-back back-link'
4
+ - elsif @journea.in_initial_state?
5
+ = link_to 'Back', main_app.root_path, class: 'link-back back-link'
@@ -0,0 +1,179 @@
1
+ # Use this setup block to configure all options available in SimpleForm.
2
+ # rubocop:disable Metrics/BlockLength
3
+ SimpleForm.setup do |config|
4
+ # Wrappers are used by the form builder to generate a
5
+ # complete input. You can remove any component from the
6
+ # wrapper, change the order or even add your own to the
7
+ # stack. The options given below are used to wrap the
8
+ # whole input.
9
+ config.wrappers :default, class: ["form-group", :input],
10
+ hint_class: :field_with_hint, error_class: :error do |b|
11
+ ## Extensions enabled by default
12
+ # Any of these extensions can be disabled for a
13
+ # given input by passing: `f.input EXTENSION_NAME => false`.
14
+ # You can make any of these extensions optional by
15
+ # renaming `b.use` to `b.optional`.
16
+
17
+ # Determines whether to use HTML5 (:email, :url, ...)
18
+ # and required attributes
19
+ b.use :html5
20
+
21
+ # Calculates placeholders automatically from I18n
22
+ # You can also pass a string as f.input placeholder: "Placeholder"
23
+ b.use :placeholder
24
+
25
+ ## Optional extensions
26
+ # They are disabled unless you pass `f.input EXTENSION_NAME => true`
27
+ # to the input. If so, they will retrieve the values from the model
28
+ # if any exists. If you want to enable any of those
29
+ # extensions by default, you can change `b.optional` to `b.use`.
30
+
31
+ # Calculates maxlength from length validations for string inputs
32
+ b.optional :maxlength
33
+
34
+ # Calculates pattern from format validations for string inputs
35
+ b.optional :pattern
36
+
37
+ # Calculates min and max from length validations for numeric inputs
38
+ b.optional :min_max
39
+
40
+ # Calculates readonly automatically from readonly attributes
41
+ b.optional :readonly
42
+
43
+ ## Inputs
44
+ b.use :label, class: "form-label"
45
+ b.use :hint, wrap_with: { tag: :span, class: "form-hint" }
46
+ b.use :input, class: "form-control"
47
+ b.use :error, wrap_with: { tag: :span, class: "error-message" }
48
+
49
+ ## full_messages_for
50
+ # If you want to display the full error message for the attribute, you can
51
+ # use the component :full_error, like:
52
+ #
53
+ # b.use :full_error, wrap_with: { tag: :span, class: :error }
54
+ end
55
+
56
+ # The default wrapper to be used by the FormBuilder.
57
+ config.default_wrapper = :default
58
+
59
+ # Define the way to render check boxes / radio buttons with labels.
60
+ # Defaults to :nested for bootstrap config.
61
+ # inline: input + label
62
+ # nested: label > input
63
+ config.boolean_style = :nested
64
+
65
+ # Default class for buttons
66
+ config.button_class = "button"
67
+
68
+ # Method used to tidy up errors. Specify any Rails Array method.
69
+ # :first lists the first message for each field.
70
+ # Use :to_sentence to list all errors for each field.
71
+ # config.error_method = :first
72
+
73
+ # Default tag used for error notification helper.
74
+ config.error_notification_tag = :div
75
+
76
+ # CSS class to add for error notification helper.
77
+ config.error_notification_class = "error_notification"
78
+
79
+ # ID to add for error notification helper.
80
+ # config.error_notification_id = nil
81
+
82
+ # Series of attempts to detect a default label method for collection.
83
+ # config.collection_label_methods = [ :to_label, :name, :title, :to_s ]
84
+
85
+ # Series of attempts to detect a default value method for collection.
86
+ # config.collection_value_methods = [ :id, :to_s ]
87
+
88
+ # You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none.
89
+ # config.collection_wrapper_tag = :fieldset
90
+
91
+ config.wrappers :vertical_radio_and_checkboxes, tag: "div", class: "form-group", error_class: "error" do |b|
92
+ b.use :error, wrap_with: { tag: "span", class: "error-message" }
93
+ b.use :input
94
+ end
95
+
96
+ # You can define the class to use on all collection wrappers. Defaulting to none.
97
+ # config.collection_wrapper_class = nil
98
+
99
+ # You can wrap each item in a collection of radio/check boxes with a tag,
100
+ # defaulting to :span.
101
+ # config.item_wrapper_tag = :span
102
+
103
+ # You can define a class to use in all item wrappers. Defaulting to none.
104
+ # config.item_wrapper_class = nil
105
+
106
+ # How the label text should be generated altogether with the required text.
107
+ config.label_text = ->(label, _required, _explicit_label) { label }
108
+
109
+ # You can define the class to use on all labels. Default is nil.
110
+ # config.label_class = nil
111
+
112
+ # You can define the default class to be used on forms. Can be overriden
113
+ # with `html: { :class }`. Defaulting to none.
114
+ # config.default_form_class = nil
115
+
116
+ # You can define which elements should obtain additional classes
117
+ # config.generate_additional_classes_for = [:wrapper, :label, :input]
118
+
119
+ # Whether attributes are required by default (or not). Default is true.
120
+ # config.required_by_default = true
121
+
122
+ # Tell browsers whether to use the native HTML5 validations (novalidate form option).
123
+ # These validations are enabled in SimpleForm's internal config but disabled by default
124
+ # in this configuration, which is recommended due to some quirks from different browsers.
125
+ # To stop SimpleForm from generating the novalidate option, enabling the HTML5 validations,
126
+ # change this configuration to true.
127
+ config.browser_validations = false
128
+
129
+ # Collection of methods to detect if a file type was given.
130
+ # config.file_methods = [ :mounted_as, :file?, :public_filename ]
131
+
132
+ # Custom mappings for input types. This should be a hash containing a regexp
133
+ # to match as key, and the input type that will be used when the field name
134
+ # matches the regexp as value.
135
+ # config.input_mappings = { /count/ => :integer }
136
+
137
+ # Custom wrappers for input types. This should be a hash containing an input
138
+ # type as key and the wrapper that will be used for all inputs with specified type.
139
+ # config.wrapper_mappings = { string: :prepend }
140
+
141
+ # Namespaces where SimpleForm should look for custom input classes that
142
+ # override default inputs.
143
+ # config.custom_inputs_namespaces << "CustomInputs"
144
+
145
+ # Default priority for time_zone inputs.
146
+ # config.time_zone_priority = nil
147
+
148
+ # Default priority for country inputs.
149
+ # config.country_priority = nil
150
+
151
+ # When false, do not use translations for labels.
152
+ # config.translate_labels = true
153
+
154
+ # Automatically discover new inputs in Rails' autoload path.
155
+ # config.inputs_discovery = true
156
+
157
+ # Cache SimpleForm inputs discovery
158
+ # config.cache_discovery = !Rails.env.development?
159
+
160
+ # Default class for inputs
161
+ # config.input_class = nil
162
+
163
+ # Define the default class of the input wrapper of the boolean input.
164
+ config.boolean_label_class = "checkbox"
165
+
166
+ # Defines if the default input wrapper class should be included in radio
167
+ # collection wrappers.
168
+ # config.include_default_input_wrapper_class = true
169
+
170
+ # Defines which i18n scope will be used in Simple Form.
171
+ # config.i18n_scope = 'simple_form'
172
+
173
+ config.wrapper_mappings = {
174
+ radio_buttons: :vertical_radio_and_checkboxes,
175
+ check_boxes: :vertical_radio_and_checkboxes,
176
+ boolean: :radio
177
+ }
178
+ end
179
+ # rubocop:enable Metrics/BlockLength