Skip to content

Commit 022dc95

Browse files
committed
Merge pull request #2103 from alphagov/use-erb-templates-for-questions
Allow questions to use ERB templates
2 parents 3049071 + 6994b3e commit 022dc95

File tree

98 files changed

+741
-330
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

98 files changed

+741
-330
lines changed

app/presenters/checkbox_question_presenter.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ def response_labels(values)
44
if value == SmartAnswer::Question::Checkbox::NONE_OPTION
55
value.to_s
66
else
7-
translate_option(value)
7+
render_option(value)
88
end
99
end
1010
end

app/presenters/flow_registration_presenter.rb

+8-7
Original file line numberDiff line numberDiff line change
@@ -35,17 +35,18 @@ def description
3535

3636
NODE_PRESENTER_METHODS = [:title, :body, :hint]
3737

38+
module MethodMissingHelper
39+
def method_missing(method, *args, &block)
40+
MethodMissingObject.new(method, parent_method = nil, blank_to_s = true)
41+
end
42+
end
43+
3844
def indexable_content
3945
HTMLEntities.new.decode(
4046
text = @flow.questions.inject([start_node.body]) { |acc, node|
41-
pres = QuestionPresenter.new(@i18n_prefix, node)
47+
pres = QuestionPresenter.new(@i18n_prefix, node, nil, helpers: [MethodMissingHelper])
4248
acc.concat(NODE_PRESENTER_METHODS.map { |method|
43-
begin
44-
pres.send(method)
45-
rescue I18n::MissingInterpolationArgument
46-
# We can't do much about this, so we ignore these text nodes
47-
nil
48-
end
49+
pres.send(method)
4950
})
5051
}.compact.join(" ").gsub(/(?:<[^>]+>|\s)+/, " ")
5152
)

app/presenters/question_presenter.rb

+65-15
Original file line numberDiff line numberDiff line change
@@ -6,56 +6,106 @@ class QuestionPresenter < NodePresenter
66
:translate_option
77
] => :@renderer
88

9+
delegate use_erb_template?: :@node
10+
911
def initialize(i18n_prefix, node, state = nil, options = {})
1012
super(i18n_prefix, node, state)
11-
@renderer = options[:renderer] || SmartAnswer::I18nRenderer.new(
12-
i18n_prefix: @i18n_prefix,
13-
node: @node,
14-
state: @state
15-
)
13+
@renderer = options[:renderer]
14+
helpers = options[:helpers] || []
15+
if use_erb_template?
16+
@renderer ||= SmartAnswer::ErbRenderer.new(
17+
template_directory: @node.template_directory.join('questions'),
18+
template_name: @node.filesystem_friendly_name,
19+
locals: @state.to_hash,
20+
helpers: helpers
21+
)
22+
else
23+
@renderer ||= SmartAnswer::I18nRenderer.new(
24+
i18n_prefix: @i18n_prefix,
25+
node: @node,
26+
state: @state
27+
)
28+
end
1629
end
1730

1831
def title
19-
translate!('title', rescue_exception: false)
32+
if use_erb_template?
33+
@renderer.single_line_of_content_for(:title)
34+
else
35+
translate!('title', rescue_exception: false)
36+
end
2037
end
2138

2239
def error
2340
if @state.error.present?
24-
translate!(@state.error.to_sym) || error_message || I18n.translate('flow.defaults.error_message')
41+
error_message_for(@state.error) || error_message_for('error_message') || I18n.translate('flow.defaults.error_message')
2542
end
2643
end
2744

28-
def error_message
29-
translate!('error_message')
45+
def error_message_for(key)
46+
if use_erb_template?
47+
message = @renderer.single_line_of_content_for(key.to_sym)
48+
message.blank? ? nil : message
49+
else
50+
translate!(key)
51+
end
3052
end
3153

3254
def hint
33-
translate!('hint')
55+
if use_erb_template?
56+
@renderer.single_line_of_content_for(:hint)
57+
else
58+
translate!('hint')
59+
end
3460
end
3561

3662
def label
37-
translate!('label')
63+
if use_erb_template?
64+
@renderer.single_line_of_content_for(:label)
65+
else
66+
translate!('label')
67+
end
3868
end
3969

4070
def suffix_label
41-
translate!('suffix_label')
71+
if use_erb_template?
72+
@renderer.single_line_of_content_for(:suffix_label)
73+
else
74+
translate!('suffix_label')
75+
end
4276
end
4377

4478
def has_labels?
4579
label.present? || suffix_label.present?
4680
end
4781

4882
def body(html: true)
49-
translate_and_render('body', html: html)
83+
if use_erb_template?
84+
@renderer.content_for(:body, html: html)
85+
else
86+
translate_and_render('body', html: html)
87+
end
5088
end
5189

5290
def post_body
53-
translate_and_render('post_body', html: true)
91+
if use_erb_template?
92+
@renderer.content_for(:post_body, html: true)
93+
else
94+
translate_and_render('post_body', html: true)
95+
end
5496
end
5597

5698
def options
5799
@node.options.map do |option|
58-
OpenStruct.new(label: translate_option(option), value: option)
100+
OpenStruct.new(label: render_option(option), value: option)
101+
end
102+
end
103+
104+
def render_option(key)
105+
if use_erb_template?
106+
@renderer.option_text(key.to_sym)
107+
else
108+
translate_option(key)
59109
end
60110
end
61111

lib/graph_presenter.rb

+6-25
Original file line numberDiff line numberDiff line change
@@ -74,34 +74,15 @@ def word_wrap(text, line_width = 40)
7474
end * "\n"
7575
end
7676

77-
def allow_missing_interpolations(&block)
78-
old = I18n.config.missing_interpolation_argument_handler
79-
I18n.config.missing_interpolation_argument_handler = ->(key) { "((#{key}))" }
80-
block.call
81-
ensure
82-
I18n.config.missing_interpolation_argument_handler = old
83-
end
84-
85-
def i18n_prefix(node)
86-
"flow.#{@flow.name}.#{node.name}"
87-
end
88-
89-
def node_title(node)
90-
allow_missing_interpolations do
91-
I18n.translate!("#{i18n_prefix(node)}.title", {})
77+
module MethodMissingHelper
78+
def method_missing(method, *args, &block)
79+
MethodMissingObject.new(method)
9280
end
93-
rescue I18n::MissingTranslationData
94-
""
9581
end
9682

97-
def translate_option(node, option)
98-
allow_missing_interpolations do
99-
begin
100-
I18n.translate!("flow.#{@flow.name}.options.#{option}")
101-
rescue I18n::MissingTranslationData
102-
I18n.translate("#{i18n_prefix(node)}.options.#{option}")
103-
end
104-
end
83+
def node_title(node)
84+
presenter = QuestionPresenter.new(nil, node, nil, helpers: [MethodMissingHelper])
85+
presenter.title
10586
end
10687

10788
def presenter

lib/method_missing_object.rb

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
class MethodMissingObject
2+
def initialize(method, parent_method = nil, blank_to_s = false)
3+
@method = method
4+
@parent_method = parent_method
5+
@blank_to_s = blank_to_s
6+
end
7+
8+
def method_missing(method, *args, &block)
9+
MethodMissingObject.new(method, parent_method = self, blank_to_s = @blank_to_s)
10+
end
11+
12+
def description
13+
@parent_method ? "#{@parent_method.description}.#{@method}" : @method
14+
end
15+
16+
def to_s
17+
@blank_to_s ? "" : "<%= #{description} %>".html_safe
18+
end
19+
end

lib/smart_answer/erb_renderer.rb

+16
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
11
module SmartAnswer
22
class ErbRenderer
3+
module QuestionOptionsHelper
4+
def options(options = nil)
5+
if options
6+
@options = options
7+
else
8+
@options || {}
9+
end
10+
end
11+
end
12+
313
def initialize(template_directory:, template_name:, locals: {}, helpers: [])
414
@template_directory = template_directory
515
@template_name = template_name
616
@locals = locals
717
@view = ActionView::Base.new([@template_directory])
818
helpers.each { |helper| @view.extend(helper) }
19+
@view.extend(QuestionOptionsHelper)
920
end
1021

1122
def single_line_of_content_for(key)
@@ -18,6 +29,11 @@ def content_for(key, html: true)
1829
html ? GovspeakPresenter.new(content).html : normalize_blank_lines(content).html_safe
1930
end
2031

32+
def option_text(key)
33+
rendered_view
34+
@view.options.fetch(key)
35+
end
36+
2137
def erb_template_path
2238
@template_directory.join(erb_template_name)
2339
end

lib/smart_answer/flow.rb

+27-17
Original file line numberDiff line numberDiff line change
@@ -48,36 +48,40 @@ def status(s = nil)
4848
@status
4949
end
5050

51+
def use_erb_templates_for_questions
52+
@use_erb_templates_for_questions = true
53+
end
54+
5155
def multiple_choice(name, options = {}, &block)
52-
add_node Question::MultipleChoice.new(self, name, options, &block)
56+
add_question(Question::MultipleChoice, name, options, &block)
5357
end
5458

5559
def country_select(name, options = {}, &block)
56-
add_node Question::CountrySelect.new(self, name, options, &block)
60+
add_question(Question::CountrySelect, name, options, &block)
5761
end
5862

59-
def date_question(name, &block)
60-
add_node Question::Date.new(self, name, &block)
63+
def date_question(name, options = {}, &block)
64+
add_question(Question::Date, name, options, &block)
6165
end
6266

6367
def value_question(name, options = {}, &block)
64-
add_node Question::Value.new(self, name, options, &block)
68+
add_question(Question::Value, name, options, &block)
6569
end
6670

67-
def money_question(name, &block)
68-
add_node Question::Money.new(self, name, &block)
71+
def money_question(name, options = {}, &block)
72+
add_question(Question::Money, name, options, &block)
6973
end
7074

71-
def salary_question(name, &block)
72-
add_node Question::Salary.new(self, name, &block)
75+
def salary_question(name, options = {}, &block)
76+
add_question(Question::Salary, name, options, &block)
7377
end
7478

75-
def checkbox_question(name, &block)
76-
add_node Question::Checkbox.new(self, name, &block)
79+
def checkbox_question(name, options = {}, &block)
80+
add_question(Question::Checkbox, name, options, &block)
7781
end
7882

79-
def postcode_question(name, &block)
80-
add_node Question::Postcode.new(self, name, &block)
83+
def postcode_question(name, options = {}, &block)
84+
add_question(Question::Postcode, name, options, &block)
8185
end
8286

8387
def outcome(name, &block)
@@ -130,9 +134,15 @@ def normalize_responses(responses)
130134
class InvalidStatus < StandardError; end
131135

132136
private
133-
def add_node(node)
134-
raise "Node #{node.name} already defined" if node_exists?(node)
135-
@nodes << node
136-
end
137+
138+
def add_question(klass, name, options = {}, &block)
139+
options.reverse_merge!(use_erb_template: @use_erb_templates_for_questions)
140+
add_node klass.new(self, name, options, &block)
141+
end
142+
143+
def add_node(node)
144+
raise "Node #{node.name} already defined" if node_exists?(node)
145+
@nodes << node
146+
end
137147
end
138148
end

lib/smart_answer/node.rb

+4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ def to_s
2121
name.to_s
2222
end
2323

24+
def filesystem_friendly_name
25+
to_s.sub(/\?$/, '')
26+
end
27+
2428
def calculate(variable_name, &block)
2529
@calculations << Calculation.new(variable_name, &block)
2630
end

lib/smart_answer/question/base.rb

+5
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ def initialize(flow, name, options = {}, &block)
1212
@permitted_next_nodes = []
1313
@predicate_stack = []
1414
@predicates = {}
15+
@uses_erb_template = options[:use_erb_template]
1516
super
1617
end
1718

@@ -126,6 +127,10 @@ def question?
126127
true
127128
end
128129

130+
def use_erb_template?
131+
@uses_erb_template
132+
end
133+
129134
private
130135
def permitted_next_node?(next_node)
131136
@permitted_next_nodes.include?(next_node)

0 commit comments

Comments
 (0)