quby-compiler 0.4.5 → 0.4.9

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c8a3b1948f1047978b5362a22a102cadcf1e73614008ab01ba4c9700ca8a2f49
4
- data.tar.gz: 854cd8345cf21de03ba5262ce08db475b39c106d9ea1874a2106c24a33196820
3
+ metadata.gz: 8adddc572c511a6145f59379fa1c54d49f53fcd3487a3d9a5026fb0634b54c54
4
+ data.tar.gz: 5b173fb296fd7a7b88d9d60eb2357117c6f9f80eb1c800a5fe781a427d359efa
5
5
  SHA512:
6
- metadata.gz: df9bf74043519c50553993a151d30c75ad98d894ed56504705f32d525a5f927e67c54d8af83b287eafe6d05bfef5e744e107527b7dbeb2f898eb1665124b1c5f
7
- data.tar.gz: 2eae0ecda1fa65a11b0fb97189f006136f8c8bff684e6884b8b9b25cbd20c09ef143e3c6a4d0729a4c43b00718994b2d2c85fa2321a02fae6e1fad6cd1093f21
6
+ metadata.gz: 6e23e0347aa449053801f045533b26d10108d8f4a62783a71a121b9cc41215dab348ca3da8e8acf528a58e4689f05219c96237218478c2368acbfcff07c220ce
7
+ data.tar.gz: 47e5d45f09c1452db50e29e3933967256b890090991ed715f91003b7622076aafa0adbe0d0f414506b79707187c03bc37c7217c94bad39bfd5f3a2d681dec72d
data/CHANGELOG.md CHANGED
@@ -1,3 +1,33 @@
1
+ # 0.4.9
2
+
3
+ * roqua.json: Fix html stripping for string with no whitespace
4
+
5
+ # 0.4.8
6
+
7
+ * roqua.json:
8
+ * sort parent questions before it's children, to make the order more logical to just dumbly show
9
+ * parse markdown before stripping tags (questiom.title, option.description)
10
+ * context-free-titles no longer contain the title as fallback
11
+ * quby2.json
12
+ * sort visibility rules: flags first, then in question order, so we can apply them in order without issue
13
+
14
+ # 0.4.7
15
+
16
+ * seeds.yml: Fix missing title questions (broken by 0.4.6)
17
+
18
+ # 0.4.6
19
+
20
+ * Added type validations for boolean attributes
21
+ * roqua.json
22
+ * add unit to questions
23
+ * add title_question_key to questions
24
+ * add parent_question_key and parent_option_key to questions
25
+ * add child_question_keys to question_options
26
+ * models
27
+ * Made title_question attribute on question instead of adding options.last.questions.
28
+ * Made v1 serializer create backward compatible json (for now)
29
+ * Made v1 serializer add the full title_question json to question.title_question.
30
+
1
31
  # 0.4.5
2
32
 
3
33
  * roqua.json: Don't add inner titles and placeholder options
@@ -114,16 +114,6 @@ module Quby
114
114
  def initialize(key, **options, &block)
115
115
  super
116
116
  @default_question_options = options[:default_question_options] || {}
117
- @title_question = nil
118
- end
119
-
120
- def build
121
- if @title_question
122
- @question.options.last.questions << @title_question
123
- @title_question = nil
124
- end
125
-
126
- super
127
117
  end
128
118
 
129
119
  def title_question(key, **options, &block)
@@ -138,7 +128,7 @@ module Quby
138
128
  question = QuestionBuilder.build(key, **options, &block)
139
129
 
140
130
  @questionnaire.register_question(question)
141
- @title_question = question
131
+ @question.title_question = question
142
132
  end
143
133
 
144
134
  def question(key, **options, &block)
@@ -31,17 +31,6 @@ module Quby
31
31
  "#{description} (true - '#{description_true}', false - '#{description_false}')"
32
32
  end
33
33
 
34
- def to_codebook(_options = {})
35
- output = []
36
- output << "#{key} flag"
37
- output << "'#{description}'" if description.present?
38
- output << " 'true' - #{description_true}"
39
- output << " 'false' - #{description_false}"
40
- output << " '' (leeg) - Vlag niet ingesteld, informatie onbekend"
41
- output << ""
42
- output.join("\n")
43
- end
44
-
45
34
  private
46
35
 
47
36
  def ensure_valid_descriptions
@@ -30,10 +30,6 @@ module Quby
30
30
  class: self.class.to_s
31
31
  }
32
32
  end
33
-
34
- def to_codebook(questionnaire, options = {})
35
- ""
36
- end
37
33
  end
38
34
  end
39
35
  end
@@ -29,9 +29,11 @@ module Quby
29
29
 
30
30
  # To hide old questions
31
31
  attr_accessor :hidden
32
+ validates :hidden, inclusion: {in: [true, false, nil], message: "must be boolean"}
32
33
 
33
34
  # Whether to skip the uniqueness validation on radio and select option values
34
35
  attr_reader :allow_duplicate_option_values
36
+ validates :allow_duplicate_option_values, inclusion: {in: [true, false, nil], message: "must be boolean"}
35
37
 
36
38
  # In what modes do we display this question
37
39
  # NOTE We always display questions in print-view (if they have an answer)
@@ -40,9 +42,13 @@ module Quby
40
42
  # Multiple-choice questions have options to choose from
41
43
  attr_accessor :options
42
44
 
45
+ # string question that is displayed after the title of this question.
46
+ attr_accessor :title_question
47
+
43
48
  # Question validation fails when there are no title and no context_free_title.
44
49
  # When :allow_blank_titles => true passed, validation does not fail. Any other value will raise the failure.
45
50
  attr_accessor :allow_blank_titles
51
+ validates :allow_blank_titles, inclusion: {in: [true, false, nil], message: "must be boolean"}
46
52
 
47
53
  # Minimum and maximum values for float and integer types
48
54
  attr_accessor :minimum
@@ -72,6 +78,7 @@ module Quby
72
78
 
73
79
  # Whether this radio question is deselectable
74
80
  attr_accessor :deselectable
81
+ validates :deselectable, inclusion: {in: [true, false, nil], message: "must be boolean"}
75
82
 
76
83
  # Some questions are a tree.
77
84
  attr_accessor :parent
@@ -79,6 +86,7 @@ module Quby
79
86
 
80
87
  # Whether we can collapse this in bulk view
81
88
  attr_accessor :disallow_bulk
89
+ validates :disallow_bulk, inclusion: {in: [true, false, nil], message: "must be boolean"}
82
90
 
83
91
  # This question should not validate itself unless the depends_on question is answered.
84
92
  # May also be an array of "#{question_key}_#{option_key}" strings that specify options
@@ -115,6 +123,7 @@ module Quby
115
123
  attr_accessor :row_span
116
124
 
117
125
  attr_accessor :default_invisible
126
+ validates :default_invisible, inclusion: {in: [true, false], message: "must be boolean"}
118
127
 
119
128
  # Slider only: where to place the sliding thing by default
120
129
  # Can have value :hidden for a hidden handle.
@@ -200,6 +209,10 @@ module Quby
200
209
  end
201
210
  # rubocop:enable CyclomaticComplexity, Metrics/MethodLength
202
211
 
212
+ def context_free_title_or_title
213
+ context_free_title || title
214
+ end
215
+
203
216
  # rubocop:disable AccessorMethodName
204
217
  def set_depends_on(keys)
205
218
  return if keys.blank?
@@ -208,10 +221,6 @@ module Quby
208
221
  end
209
222
  # rubocop:enable AccessorMethodName
210
223
 
211
- def context_free_title
212
- @context_free_title || @title
213
- end
214
-
215
224
  def expand_depends_on_input_keys
216
225
  return unless @depends_on
217
226
  @depends_on = questionnaire.expand_input_keys(@depends_on)
@@ -247,6 +256,10 @@ module Quby
247
256
  end
248
257
  end
249
258
 
259
+ def title_question?
260
+ presentation == :next_to_title
261
+ end
262
+
250
263
  # Returns all keys belonging to html inputs generated by this question.
251
264
  def input_keys
252
265
  if options.blank?
@@ -316,39 +329,8 @@ module Quby
316
329
  !parent_option_key.nil?
317
330
  end
318
331
 
319
- def to_codebook(questionnaire, opts = {})
320
- output = []
321
- question_key = codebook_key(key, questionnaire, opts)
322
- output << "#{question_key} #{codebook_output_type} #{codebook_output_range}#{' deprecated' if hidden}"
323
- output << "\"#{context_free_title}\"" unless context_free_title.blank?
324
- options_string = options.map do |option|
325
- option.to_codebook(questionnaire, opts)
326
- end.compact.join("\n")
327
- output << options_string unless options.blank?
328
- output.join("\n")
329
- end
330
-
331
- def codebook_key(key, questionnaire, opts = {})
332
- key.to_s.gsub(/^v_/, "#{opts[:roqua_key] || questionnaire.key.to_s}_")
333
- end
334
-
335
- def codebook_output_type
336
- type
337
- end
338
-
339
- def codebook_output_range
340
- range_min = validations.find { |i| i[:type] == :minimum }&.fetch(:value, nil)
341
- range_max = validations.find { |i| i[:type] == :maximum }&.fetch(:value, nil)
342
-
343
- if range_min || range_max
344
- "(#{[range_min, "value", range_max].compact.join(" &lt;= ")})"
345
- else
346
- ""
347
- end
348
- end
349
-
350
332
  def variable_descriptions
351
- {key => context_free_title}.with_indifferent_access
333
+ {key => context_free_title_or_title}.with_indifferent_access
352
334
  end
353
335
  end
354
336
  end
@@ -4,23 +4,26 @@ module Quby
4
4
  module Compiler
5
5
  module Entities
6
6
  class QuestionOption
7
+ include ActiveModel::Validations
8
+
7
9
  MARKDOWN_ATTRIBUTES = %w(description).freeze
8
10
 
9
11
  attr_reader :key
10
12
  attr_reader :value
11
- attr_reader :description
13
+ attr_reader :description, :context_free_description
12
14
  attr_reader :questions
13
15
  attr_reader :inner_title
16
+ validates :inner_title, inclusion: {in: [true, false, nil], message: "must be boolean"}
14
17
  attr_reader :hides_questions
15
18
  attr_reader :shows_questions
16
19
  attr_reader :hidden
20
+ validates :hidden, inclusion: {in: [true, false], message: "must be boolean"}
17
21
  attr_reader :placeholder
22
+ validates :placeholder, inclusion: {in: [true, false], message: "must be boolean"}
18
23
  attr_reader :question
19
24
  attr_reader :view_id
20
25
  attr_reader :input_key
21
26
 
22
- attr_reader :start_chosen
23
-
24
27
  def initialize(key, question, options = {})
25
28
  @key = key
26
29
  @question = question
@@ -49,10 +52,6 @@ module Quby
49
52
  false
50
53
  end
51
54
 
52
- def context_free_description
53
- @context_free_description || @description
54
- end
55
-
56
55
  def as_json(options = {})
57
56
  {
58
57
  key: key,
@@ -72,28 +71,6 @@ module Quby
72
71
  viewId: view_id
73
72
  }
74
73
  end
75
-
76
- def to_codebook(questionnaire, opts)
77
- return nil if inner_title
78
- output = []
79
-
80
- if question.type == :check_box
81
- option_key = question.codebook_key(key, questionnaire, opts)
82
- output << "#{option_key} #{question.codebook_output_type}#{' deprecated' if hidden || question.hidden }"
83
- output << "\"#{question.title} -- #{description}\"" unless question.title.blank? and description.blank?
84
- output << "1\tChecked"
85
- output << "0\tUnchecked"
86
- output << "empty\tUnchecked"
87
- else
88
- output << "#{value || key}\t\"#{description}\"#{' deprecated' if hidden}"
89
- end
90
-
91
- questions.each do |subquestion|
92
- output << "\t#{subquestion.to_codebook(questionnaire, opts).gsub("\n", "\n\t")}"
93
- end
94
-
95
- output.join("\n")
96
- end
97
74
  end
98
75
  end
99
76
  end
@@ -172,6 +172,19 @@ module Quby
172
172
  end)
173
173
  end
174
174
 
175
+ # sorts parents before children, so showing makes more sense and visiblity rules are ordered correctly
176
+ def sorted_questions
177
+ return @sorted_questions if @sorted_questions
178
+
179
+ key_to_order_by = questions.map.with_index.to_h { [_1.key, [_2]] }
180
+ questions.each do |question|
181
+ if question.parent
182
+ key_to_order_by[question.key].unshift(key_to_order_by[question.parent.key].first)
183
+ end
184
+ end
185
+ @sorted_questions = questions.sort_by { key_to_order_by[_1.key] }
186
+ end
187
+
175
188
  def questions
176
189
  question_hash.values
177
190
  end
@@ -201,43 +214,6 @@ module Quby
201
214
  }
202
215
  end
203
216
 
204
- # rubocop:disable Metrics/MethodLength
205
- def to_codebook(options = {})
206
- output = []
207
- output << title
208
- output << "Date unknown"
209
- output << ""
210
-
211
- options[:extra_vars]&.each do |var|
212
- output << "#{var[:key]} #{var[:type]}"
213
- output << "\"#{var[:description]}\""
214
- output << ""
215
- end
216
-
217
- top_questions = panels.map do |panel|
218
- panel.items.select { |item| item.is_a? Question }
219
- end.flatten.compact
220
-
221
- top_questions.each do |question|
222
- output << question.to_codebook(self, options)
223
- output << ""
224
- end
225
-
226
- flags.each_value do |flag|
227
- output << flag.to_codebook(options)
228
- output << ""
229
- end
230
-
231
- textvars.each_value do |textvar|
232
- output << textvar.to_codebook(options)
233
- output << ""
234
- end
235
-
236
- output = output.join("\n")
237
- strip_tags(output.gsub(/\<([ 1-9])/, '&lt;\1')).gsub("&lt;", "<")
238
- end
239
- # rubocop:enable Metrics/MethodLength
240
-
241
217
  def key_in_use?(key)
242
218
  fields.key_in_use?(key) || score_calculations.key?(key)
243
219
  end
@@ -415,9 +391,14 @@ module Quby
415
391
  end.uniq(&:config)
416
392
  end
417
393
 
394
+ # Order is important
395
+ # quby2 follows them one by one and ignores rules by hidden question, so needs to be in order of interface.
396
+ # Flags don't have this issue, so as long as they are first, their order doesn't matter.
418
397
  def visibility_rules
419
- @visibility_rules ||= fields.question_hash.values.flat_map { |question| VisibilityRule.from(question) } \
420
- + flags.values.flat_map { |flag| VisibilityRule.from_flag(flag) }
398
+ @visibility_rules ||= [
399
+ *flags.values.flat_map { |flag| VisibilityRule.from_flag(flag) },
400
+ *sorted_questions.flat_map { |question| VisibilityRule.from(question) }
401
+ ]
421
402
  end
422
403
 
423
404
  private
@@ -51,9 +51,9 @@ module Quby
51
51
  end
52
52
 
53
53
  def variable_descriptions
54
- options.each_with_object(key => context_free_title) do |option, hash|
54
+ options.each_with_object(key => context_free_title_or_title) do |option, hash|
55
55
  next if option.input_key.blank?
56
- hash[option.input_key] = "#{context_free_title} - #{option.description}"
56
+ hash[option.input_key] = "#{context_free_title_or_title} - #{option.description}"
57
57
  end.with_indifferent_access
58
58
  end
59
59
 
@@ -69,12 +69,6 @@ module Quby
69
69
  def as_json(options = {})
70
70
  super.merge(options: @options.as_json)
71
71
  end
72
-
73
- def to_codebook(questionnaire, opts = {})
74
- options.map do |option|
75
- option.to_codebook(questionnaire, opts)
76
- end.compact.join("\n\n")
77
- end
78
72
  end
79
73
  end
80
74
  end
@@ -53,9 +53,9 @@ module Quby
53
53
  end
54
54
 
55
55
  def variable_descriptions
56
- components.each_with_object(key => context_free_title) do |component, hash|
56
+ components.each_with_object(key => context_free_title_or_title) do |component, hash|
57
57
  key = send("#{component}_key")
58
- hash[key] = "#{context_free_title} (#{I18n.t component})"
58
+ hash[key] = "#{context_free_title_or_title} (#{I18n.t component})"
59
59
  end.with_indifferent_access
60
60
  end
61
61
 
@@ -65,18 +65,6 @@ module Quby
65
65
  end
66
66
  super.merge(components: components).merge(component_keys)
67
67
  end
68
-
69
- def to_codebook(questionnaire, opts = {})
70
- output = []
71
- components.each do |component|
72
- output << "#{codebook_key(send("#{component}_key"), questionnaire, opts)} " \
73
- "#{type}_#{component} #{codebook_output_range}"
74
- output << "\"#{title}\"" unless title.blank?
75
- output << options.map(&:to_codebook).join("\n") unless options.blank?
76
- output << ""
77
- end
78
- output.join("\n")
79
- end
80
68
  end
81
69
  end
82
70
  end
@@ -8,10 +8,6 @@ module Quby
8
8
  def as_json(options = {})
9
9
  super.merge(options: @options.as_json)
10
10
  end
11
-
12
- def codebook_output_type
13
- :radio
14
- end
15
11
  end
16
12
  end
17
13
  end
@@ -8,10 +8,6 @@ module Quby
8
8
  def as_json(options = {})
9
9
  super.merge(options: @options.as_json)
10
10
  end
11
-
12
- def codebook_output_type
13
- :radio
14
- end
15
11
  end
16
12
  end
17
13
  end
@@ -10,13 +10,6 @@ module Quby
10
10
  super(key, description, default, depends_on_flag)
11
11
  end
12
12
  # rubocop:enable ParameterLists
13
-
14
- def to_codebook(_options = {})
15
- output = []
16
- output << "#{key} Textvariabele"
17
- output << description
18
- output.join("\n")
19
- end
20
13
  end
21
14
  end
22
15
  end
@@ -61,6 +61,7 @@ module Quby
61
61
  question_type: question.type,
62
62
  key: question.key,
63
63
  title: question.title,
64
+ title_question: (question_as_json(question.title_question) if question.title_question),
64
65
  context_free_title: question.context_free_title,
65
66
  description: question.description,
66
67
  presentation: question.presentation,
@@ -96,7 +97,7 @@ module Quby
96
97
  case question
97
98
  when Quby::Compiler::Entities::Questions::CheckboxQuestion
98
99
  base_options.merge(
99
- options: question.options.map { |option| option_as_json(option) },
100
+ options: options_as_json(question),
100
101
  check_all_option: question.check_all_option,
101
102
  uncheck_all_option: question.uncheck_all_option,
102
103
  maximum_checked_allowed: question.maximum_checked_allowed,
@@ -114,7 +115,7 @@ module Quby
114
115
  )
115
116
  when Quby::Compiler::Entities::Questions::DeprecatedQuestion
116
117
  base_options.merge(
117
- options: question.options.map { |option| option_as_json(option) }
118
+ options: options_as_json(question)
118
119
  )
119
120
  when Quby::Compiler::Entities::Questions::FloatQuestion
120
121
  base_options.merge(
@@ -130,11 +131,11 @@ module Quby
130
131
  )
131
132
  when Quby::Compiler::Entities::Questions::RadioQuestion
132
133
  base_options.merge(
133
- options: question.options.map { |option| option_as_json(option) }
134
+ options: options_as_json(question)
134
135
  )
135
136
  when Quby::Compiler::Entities::Questions::SelectQuestion
136
137
  base_options.merge(
137
- options: question.options.map { |option| option_as_json(option) }
138
+ options: options_as_json(question)
138
139
  )
139
140
  when Quby::Compiler::Entities::Questions::StringQuestion
140
141
  base_options.merge(
@@ -177,6 +178,16 @@ module Quby
177
178
  end
178
179
  end
179
180
 
181
+ # Also adds the title question under the last option.
182
+ # TODO old dsl put it there, remove after quby gem uses title_question
183
+ def options_as_json(question)
184
+ question.options.map { |option| option_as_json(option) }.tap do |options_json|
185
+ if question.title_question
186
+ options_json.last[:questions] << question_as_json(question.title_question)
187
+ end
188
+ end
189
+ end
190
+
180
191
  def option_as_json(option)
181
192
  {
182
193
  key: option.key,
@@ -160,31 +160,39 @@ module Quby
160
160
  # sbg_key [nil/string] _not_ defaulted to key
161
161
  # type [string]
162
162
  # title [nil/string] no html
163
- # context_free_title [nil/string] no html, defaulted to title
163
+ # context_free_title [nil/string] no html, not defaulted to title!
164
+ # unit [nil/string] no html (not in quby either)
164
165
  # deprecated [nil/true] only show if filled for old answers
165
166
  # default_invisible [nil/true] only show if filled or when all questions are shown
167
+ # parent_question_key [nil/string] title_questions or question defined under option
168
+ # parent_option_key [nil/string] question defined under option
169
+ # title_question_key [nil/string]
166
170
  # date_parts [nil/{..}]
167
171
  # options: [nil/{..}]
168
172
  def questions
169
- questionnaire.questions.reject{_1.type == :hidden}.to_h { |question|
173
+ questionnaire.sorted_questions.reject{_1.type == :hidden}.to_h { |question|
170
174
  [
171
175
  question.key,
172
176
  {
173
177
  key: question.key,
174
178
  sbg_key: question.sbg_key,
175
179
  type: QUBY_TYPE_TO_ROQUA_TYPE.fetch(question.type),
176
- title: strip_tags_without_html_encode(question.title),
177
- context_free_title: strip_tags_without_html_encode(question.context_free_title),
180
+ title: parse_markdown_and_strip_tags(question.title),
181
+ context_free_title: question.context_free_title,
182
+ unit: question.unit,
178
183
  deprecated: question.hidden.presence,
179
184
  default_invisible: question.default_invisible.presence,
185
+ parent_question_key: question.parent&.key,
186
+ parent_option_key: question.parent_option_key,
187
+ title_question_key: question.title_question&.key,
180
188
  date_parts: date_parts_for(question),
181
- options: options_for(question),
189
+ options: options_for(question)
182
190
  }.compact
183
191
  ]
184
192
  }
185
193
  end
186
194
 
187
- # {year_key: {key: "v_date_yyyy"}, ..}
195
+ # {year: {key: "v_date_yyyy"}, ..}
188
196
  # key [string]
189
197
  def date_parts_for(question)
190
198
  return nil unless question.type == :date
@@ -202,7 +210,8 @@ module Quby
202
210
  # {a1: {..}}
203
211
  # key [string]
204
212
  # value [nil/string] nil for check_box, string otherwise
205
- # description [nil/string] context_free_description, no html
213
+ # description [nil/string] context_free_description or parsed/stripped description, no html
214
+ # child_question_keys [nil/[string]]
206
215
  def options_for(question)
207
216
  return nil if question.options.empty?
208
217
 
@@ -213,7 +222,8 @@ module Quby
213
222
  {
214
223
  key: option.key,
215
224
  value: (option.value.to_s unless question.type == :check_box),
216
- description: strip_tags_without_html_encode(option.context_free_description)
225
+ description: option.context_free_description || parse_markdown_and_strip_tags(option.description),
226
+ child_question_keys: option.questions.map(&:key).presence
217
227
  }.compact
218
228
  ]
219
229
  }
@@ -245,8 +255,17 @@ module Quby
245
255
  }
246
256
  end
247
257
 
258
+ def parse_markdown_and_strip_tags(markdown)
259
+ strip_tags_without_html_encode(Quby::MarkdownParser.new(markdown).to_html)
260
+ end
261
+
248
262
  def strip_tags_without_html_encode(html)
249
- Nokogiri::HTML(html).text
263
+ Nokogiri::HTML(html).tap do |doc|
264
+ doc.css('br, hr, img').each { |node| node.replace(' ') }
265
+ end \
266
+ .text
267
+ .gsub(/\s+/, ' ')
268
+ .presence
250
269
  end
251
270
  end
252
271
  end
@@ -100,6 +100,9 @@ module Quby
100
100
  def validate_question_options(questionnaire, question)
101
101
  question.options.each do |option|
102
102
  msg_base = "Question #{option.question.key} option #{option.key}"
103
+ unless option.valid?
104
+ fail "#{msg_base} #{option.errors.full_messages.to_sentence}"
105
+ end
103
106
  to_be_hidden_questions_exist_and_not_subquestion?(questionnaire, option, msg_base: msg_base)
104
107
  to_be_shown_questions_exist_and_not_subquestion?(questionnaire, option, msg_base: msg_base)
105
108
  end
@@ -40,7 +40,7 @@ module Quby
40
40
  when :select
41
41
  d_qtypes[question.key.to_s] = { type: :discrete }
42
42
  for option in question.options
43
- d_qtypes[question.key.to_s][option.value.to_s] = strip_p_tag(option.context_free_description || "") unless option.placeholder
43
+ d_qtypes[question.key.to_s][option.value.to_s] = strip_p_tag(option.context_free_description || option.description || "") unless option.placeholder
44
44
  end
45
45
  update_hidden_questions_for(question)
46
46
  when :check_box
@@ -49,16 +49,19 @@ module Quby
49
49
  next if option.inner_title
50
50
  vars << option.key.to_s
51
51
  if question.hidden
52
- question_titles[option.key.to_s] = strip_tags question.context_free_title
52
+ question_titles[option.key.to_s] = strip_tags(question.context_free_title_or_title)
53
53
  end
54
54
  value = 1
55
55
  option_type = { type: :discrete }
56
- option_type[value.to_s] = (option.context_free_description || "")
56
+ option_type[value.to_s] = (option.context_free_description || option.description || "")
57
57
  option_type[:depends] = { values: [value, value.to_s].uniq, variable: option.key.to_s } unless options[:without_depends]
58
58
  d_qtypes[option.key.to_s] = option_type
59
59
  values = [value, value.to_s].uniq
60
60
  handle_subquestions(question, question_titles, d_qtypes, vars, option, values, option.key.to_s)
61
61
  end
62
+ if question.title_question
63
+ subquestion(question, question_titles, d_qtypes, vars, question.title_question, nil, nil)
64
+ end
62
65
  update_hidden_questions_for(question, for_checkbox: true)
63
66
  when :textarea
64
67
  d_qtypes[question.key.to_s] = { type: :text_field }
@@ -72,7 +75,7 @@ module Quby
72
75
  end
73
76
  when :hidden
74
77
  if question.options.blank? # string
75
- question_titles[question.key.to_s] = strip_tags question.context_free_title
78
+ question_titles[question.key.to_s] = strip_tags(question.context_free_title_or_title)
76
79
  vars << question.key.to_s unless vars.include? question.key.to_s
77
80
  d_qtypes[question.key.to_s] = { type: :text }
78
81
  d_qtypes[question.key.to_s][:depends] = :present unless options[:without_depends]
@@ -85,16 +88,16 @@ module Quby
85
88
  next if option.inner_title
86
89
  d_qtypes[question.key.to_s] ||= { type: :scale }
87
90
  values << option.value.to_s
88
- d_qtypes[question.key.to_s][option.value.to_s] = strip_p_tag(option.context_free_description || "")
91
+ d_qtypes[question.key.to_s][option.value.to_s] = strip_p_tag(option.context_free_description || option.description || "")
89
92
  # TODO: missing sub-questions
90
93
  else # check_box
91
94
  d_qtypes[question.key.to_s] ||= { type: :check_box }
92
95
  no_keys = false
93
- question_titles[option.key.to_s] = strip_tags question.context_free_title
96
+ question_titles[option.key.to_s] = strip_tags(question.context_free_title_or_title)
94
97
  vars << option.key.to_s
95
98
  value = option.value || 1
96
99
  option_type = { type: :discrete }
97
- option_type[value.to_s] = (option.context_free_description || "")
100
+ option_type[value.to_s] = (option.context_free_description || option.description || "")
98
101
  option_type[:depends] = { values: [value, value.to_s].uniq, variable: option.key.to_s } unless options[:without_depends]
99
102
  d_qtypes[option.key.to_s] = option_type
100
103
  # TODO: missing sub-questions
@@ -102,7 +105,7 @@ module Quby
102
105
  end
103
106
  if no_keys # scale or radio
104
107
  d_qtypes[question.key.to_s][:depends] = { values: values, variable: question.key.to_s } unless options[:without_depends]
105
- question_titles[question.key.to_s] = strip_tags question.context_free_title
108
+ question_titles[question.key.to_s] = strip_tags(question.context_free_title_or_title)
106
109
  end
107
110
  end
108
111
  else
@@ -191,7 +194,7 @@ module Quby
191
194
 
192
195
  for question in questions_flat
193
196
  unless question.hidden && (question.type == :check_box || question.type == :hidden)
194
- title = question.context_free_title || question.description || ""
197
+ title = question.context_free_title_or_title || question.description || ""
195
198
  question_titles[question.key.to_s] = strip_tags(title)
196
199
  end
197
200
  end
@@ -237,24 +240,24 @@ module Quby
237
240
  end
238
241
  end
239
242
  d_qtypes[quest.key.to_s][:label] = quest.unit unless quest.unit.blank?
240
- quests[quest.key.to_s] = strip_tags(quest.context_free_title || "")
243
+ quests[quest.key.to_s] = strip_tags(quest.context_free_title || quest.title || "")
241
244
  vars << quest.key.to_s
242
245
  end
243
246
 
244
247
  def sub_textfield(question, quests, d_qtypes, vars, quest, values, key)
245
248
  d_qtypes[quest.key.to_s] = { type: :text_field }
246
249
  d_qtypes[quest.key.to_s][:depends] = { values: values, variable: key } unless options[:without_depends]
247
- quests[quest.key.to_s] = strip_tags(quest.context_free_title || "")
250
+ quests[quest.key.to_s] = strip_tags(quest.context_free_title || quest.title || "")
248
251
  vars << quest.key.to_s
249
252
  end
250
253
 
251
254
  def sub_radio(question, quests, d_qtypes, vars, quest, values, key)
252
255
  d_qtypes[quest.key.to_s] = { type: :scale }
253
256
  d_qtypes[quest.key.to_s][:depends] = { values: values, variable: key } unless options[:without_depends]
254
- quests[quest.key.to_s] = strip_tags(quest.context_free_title || "")
257
+ quests[quest.key.to_s] = strip_tags(quest.context_free_title || quest.title || "")
255
258
  for option in quest.options
256
259
  next if option.inner_title
257
- d_qtypes[quest.key.to_s][option.value.to_s] = strip_p_tag(option.context_free_description || "")
260
+ d_qtypes[quest.key.to_s][option.value.to_s] = strip_p_tag(option.context_free_description || option.description || "")
258
261
  end
259
262
  vars << quest.key.to_s
260
263
  update_hidden_questions_for(quest)
@@ -266,7 +269,7 @@ module Quby
266
269
  vars << key
267
270
  hash[component] = key.to_s
268
271
  end
269
- quests[quest.key.to_s] = strip_tags(quest.context_free_title || "")
272
+ quests[quest.key.to_s] = strip_tags(quest.context_free_title || quest.title || "")
270
273
  end
271
274
 
272
275
  def handle_scale(question, quests, d_qtypes, vars)
@@ -275,11 +278,14 @@ module Quby
275
278
  update_hidden_questions_for(question)
276
279
  for option in question.options
277
280
  next if option.inner_title
278
- d_qtypes[question.key.to_s][option.value.to_s] = strip_p_tag(option.context_free_description || "")
281
+ d_qtypes[question.key.to_s][option.value.to_s] = strip_p_tag(option.context_free_description || option.description || "")
279
282
  values << option.value.to_s
280
283
  key = question.key.to_s
281
284
  handle_subquestions(question, quests, d_qtypes, vars, option, [option.value.to_s], key)
282
285
  end
286
+ if question.title_question
287
+ subquestion(question, quests, d_qtypes, vars, question.title_question, nil, nil)
288
+ end
283
289
  end
284
290
 
285
291
  def handle_textfield(question, d_qtypes)
@@ -1,5 +1,5 @@
1
1
  module Quby
2
2
  module Compiler
3
- VERSION = "0.4.5"
3
+ VERSION = "0.4.9"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: quby-compiler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.5
4
+ version: 0.4.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marten Veldthuis
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-01-21 00:00:00.000000000 Z
11
+ date: 2022-02-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel