quby-compiler 0.5.14 → 0.5.15
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 +4 -4
- data/CHANGELOG.md +8 -0
- data/lib/quby/compiler/dsl/questions/base.rb +4 -0
- data/lib/quby/compiler/entities/panel.rb +0 -18
- data/lib/quby/compiler/entities/question.rb +3 -24
- data/lib/quby/compiler/entities/question_option.rb +1 -34
- data/lib/quby/compiler/entities/questionnaire.rb +1 -0
- data/lib/quby/compiler/entities/questions/checkbox_question.rb +0 -8
- data/lib/quby/compiler/entities/questions/concerns/slider.rb +0 -14
- data/lib/quby/compiler/entities/questions/date_question.rb +0 -13
- data/lib/quby/compiler/entities/questions/deprecated_question.rb +0 -4
- data/lib/quby/compiler/entities/questions/float_question.rb +0 -7
- data/lib/quby/compiler/entities/questions/integer_question.rb +0 -7
- data/lib/quby/compiler/entities/questions/radio_question.rb +0 -3
- data/lib/quby/compiler/entities/questions/select_question.rb +0 -6
- data/lib/quby/compiler/entities/questions/string_question.rb +0 -6
- data/lib/quby/compiler/entities/questions/text_question.rb +0 -3
- data/lib/quby/compiler/outputs/quby_frontend_v2_serializer.rb +215 -4
- data/lib/quby/compiler/version.rb +1 -1
- data/lib/quby/compiler.rb +1 -0
- data/lib/quby/inspect_except.rb +12 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 126c7a3a5ef150e332a888dfef85acc264a46fe4a886b2f5ef9b0a19f460b14d
|
4
|
+
data.tar.gz: 9bc6fad59d9da077cca02ab744bed4497dea3764e09f82d4aaf42016f7479dbc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 183182375a43d83e98bb4667865c78b958fb2443d8f028ea4411b59d2caaf894825072d764753539811bc659d68a8294c284d4226b7dddfdfb56635049a60a01
|
7
|
+
data.tar.gz: '066826a89ee2dd03f8dc8b76b2d284a583331e4e3181823935d9afb3e48f970a8aeeece44ca7b6bc6f479ccd3ee179b47b39a65b26e60fef5489efb8106b41d3'
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
# 0.5.15
|
2
|
+
|
3
|
+
* Add context_description to questions, to have a text item that is hidden together with the question.
|
4
|
+
* quuby2.json
|
5
|
+
* Moved quby2 serialization to the serializer, compact everything, no markdown for v2 title/descriptions.
|
6
|
+
* Sanitize Quby2 html in v2.
|
7
|
+
* Add contextDescription to questions.
|
8
|
+
|
1
9
|
# 0.5.14
|
2
10
|
|
3
11
|
* Support for activemodel 7.1 and 7.2
|
@@ -16,10 +16,6 @@ module Quby
|
|
16
16
|
@items = options[:items] || []
|
17
17
|
end
|
18
18
|
|
19
|
-
def as_json(options = {})
|
20
|
-
super.merge(title: title, index: index, items: json_items)
|
21
|
-
end
|
22
|
-
|
23
19
|
def index
|
24
20
|
@questionnaire.panels.index(self)
|
25
21
|
end
|
@@ -44,20 +40,6 @@ module Quby
|
|
44
40
|
end
|
45
41
|
end
|
46
42
|
|
47
|
-
def json_items
|
48
|
-
items.map do |item|
|
49
|
-
case item
|
50
|
-
when Text
|
51
|
-
{ type: 'html', html: item.html }
|
52
|
-
when Question
|
53
|
-
next if item.table # things inside a table are added to the table, AND ALSO to the panel. skip them.
|
54
|
-
{ type: 'question', key: item.key }
|
55
|
-
when Table
|
56
|
-
{ type: "table" }
|
57
|
-
end
|
58
|
-
end.compact
|
59
|
-
end
|
60
|
-
|
61
43
|
def validations
|
62
44
|
vals = {}
|
63
45
|
items.each do |item|
|
@@ -6,6 +6,8 @@ module Quby
|
|
6
6
|
module Compiler
|
7
7
|
module Entities
|
8
8
|
class Question < Item
|
9
|
+
include ::Quby::InspectExcept.new(*%i[@dependencies @options @questionnaire @table @validations @parent])
|
10
|
+
|
9
11
|
MARKDOWN_ATTRIBUTES = %w(description title).freeze
|
10
12
|
|
11
13
|
set_callback :after_dsl_enhance, :expand_depends_on_input_keys
|
@@ -18,6 +20,7 @@ module Quby
|
|
18
20
|
attr_accessor :title
|
19
21
|
attr_accessor :context_free_title
|
20
22
|
attr_accessor :description
|
23
|
+
attr_accessor :context_description
|
21
24
|
|
22
25
|
attr_accessor :labels
|
23
26
|
|
@@ -229,30 +232,6 @@ module Quby
|
|
229
232
|
options.length > 0 && type != :select ? options.length : @col_span
|
230
233
|
end
|
231
234
|
|
232
|
-
def as_json(options = {})
|
233
|
-
# rubocop:disable SymbolName
|
234
|
-
super.merge(
|
235
|
-
key: key,
|
236
|
-
title: Quby::Compiler::MarkdownParser.new(title).to_html,
|
237
|
-
description: Quby::Compiler::MarkdownParser.new(description).to_html,
|
238
|
-
type: type,
|
239
|
-
size: size.presence && Integer(size), # 2022-11: 4k string and 7k integer
|
240
|
-
hidden: hidden?,
|
241
|
-
displayModes: display_modes,
|
242
|
-
defaultInvisible: default_invisible,
|
243
|
-
viewSelector: view_selector,
|
244
|
-
parentKey: parent&.key,
|
245
|
-
parentOptionKey: parent_option_key,
|
246
|
-
deselectable: deselectable,
|
247
|
-
presentation: presentation,
|
248
|
-
as: as || type, # default to type so typescript can narrow on it.
|
249
|
-
questionGroup: question_group
|
250
|
-
).tap do |json|
|
251
|
-
json[:unit] = unit if %i[integer float string].include?(type) && as != :slider
|
252
|
-
json[:showValues] = [true, :all].include?(show_values) if %i[radio scale].include?(type) && show_values
|
253
|
-
end
|
254
|
-
end
|
255
|
-
|
256
235
|
def title_question?
|
257
236
|
presentation == :next_to_title
|
258
237
|
end
|
@@ -5,6 +5,7 @@ module Quby
|
|
5
5
|
module Entities
|
6
6
|
class QuestionOption
|
7
7
|
include ActiveModel::Validations
|
8
|
+
include ::Quby::InspectExcept.new(:@question, :@questions)
|
8
9
|
|
9
10
|
MARKDOWN_ATTRIBUTES = %w(description).freeze
|
10
11
|
|
@@ -53,40 +54,6 @@ module Quby
|
|
53
54
|
@questions.each { |q| return true if q.key_in_use?(k) }
|
54
55
|
false
|
55
56
|
end
|
56
|
-
|
57
|
-
def as_json(options = {})
|
58
|
-
if inner_title
|
59
|
-
inner_title_as_json(options)
|
60
|
-
elsif placeholder
|
61
|
-
nil # placeholder attr on question.
|
62
|
-
else
|
63
|
-
option_as_json(options)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def inner_title_as_json(options = {})
|
68
|
-
{
|
69
|
-
type: 'html',
|
70
|
-
key: SecureRandom.uuid,
|
71
|
-
html: Quby::Compiler::MarkdownParser.new(description).to_html
|
72
|
-
}
|
73
|
-
end
|
74
|
-
|
75
|
-
def option_as_json(options = {})
|
76
|
-
{
|
77
|
-
type: 'option',
|
78
|
-
key: key,
|
79
|
-
value: value,
|
80
|
-
description: question.type == :select \
|
81
|
-
? description
|
82
|
-
: Quby::Compiler::MarkdownParser.new(description).to_html,
|
83
|
-
questions: questions,
|
84
|
-
hidesQuestions: hides_questions,
|
85
|
-
showsQuestions: shows_questions,
|
86
|
-
hidden: hidden,
|
87
|
-
viewId: view_id
|
88
|
-
}
|
89
|
-
end
|
90
57
|
end
|
91
58
|
end
|
92
59
|
end
|
@@ -17,6 +17,7 @@ module Quby
|
|
17
17
|
class Questionnaire
|
18
18
|
extend ActiveModel::Naming
|
19
19
|
include ActiveModel::Validations
|
20
|
+
include ::Quby::InspectExcept.new(*%i[@attributes @charts @fields @flags @panels @questions @sourcecode @score_calculations @score_schemas @textvars])
|
20
21
|
|
21
22
|
class ValidationError < StandardError; end
|
22
23
|
class UnknownInputKey < ValidationError; end
|
@@ -58,14 +58,6 @@ module Quby
|
|
58
58
|
# Some options don't have a key (inner_title), they are stripped.
|
59
59
|
options.map { |opt| opt.input_key }.compact
|
60
60
|
end
|
61
|
-
|
62
|
-
def as_json(options = {})
|
63
|
-
super.tap do |json|
|
64
|
-
json[:children] = @options.as_json
|
65
|
-
json[:checkAllOption] = check_all_option if check_all_option.present?
|
66
|
-
json[:uncheckAllOption] = uncheck_all_option if uncheck_all_option.present?
|
67
|
-
end
|
68
|
-
end
|
69
61
|
end
|
70
62
|
end
|
71
63
|
end
|
@@ -5,19 +5,5 @@ module Quby::Compiler::Entities::Questions::Concerns
|
|
5
5
|
included do
|
6
6
|
validates :minimum, :maximum, presence: true, if: -> { as == :slider }
|
7
7
|
end
|
8
|
-
|
9
|
-
def as_json(options = {})
|
10
|
-
if as == :slider
|
11
|
-
super.merge(
|
12
|
-
step: step,
|
13
|
-
defaultPosition: default_position.is_a?(Numeric) ? default_position : minimum,
|
14
|
-
startThumbHidden: default_position == :hidden,
|
15
|
-
valueTooltip: input_data[:value_tooltip] || false,
|
16
|
-
labels: labels
|
17
|
-
)
|
18
|
-
else
|
19
|
-
super
|
20
|
-
end
|
21
|
-
end
|
22
8
|
end
|
23
9
|
end
|
@@ -53,19 +53,6 @@ module Quby
|
|
53
53
|
send("#{component}_key").to_sym
|
54
54
|
end
|
55
55
|
end
|
56
|
-
|
57
|
-
def as_json(options = {})
|
58
|
-
super.merge(
|
59
|
-
type: 'date_parts',
|
60
|
-
as: 'date_parts',
|
61
|
-
dateParts: components.map { |component|
|
62
|
-
{
|
63
|
-
part: component,
|
64
|
-
key: send("#{component}_key")
|
65
|
-
}
|
66
|
-
}
|
67
|
-
)
|
68
|
-
end
|
69
56
|
end
|
70
57
|
end
|
71
58
|
end
|
@@ -5,12 +5,6 @@ module Quby
|
|
5
5
|
module Entities
|
6
6
|
module Questions
|
7
7
|
class SelectQuestion < Question
|
8
|
-
def as_json(options = {})
|
9
|
-
super.merge(
|
10
|
-
children: @options.as_json.compact, # for now just options, but we'll add optgroups later.
|
11
|
-
placeholder: @options.find { _1.placeholder }&.description # nil for no placeholder
|
12
|
-
)
|
13
|
-
end
|
14
8
|
end
|
15
9
|
end
|
16
10
|
end
|
@@ -5,12 +5,6 @@ module Quby
|
|
5
5
|
module Entities
|
6
6
|
module Questions
|
7
7
|
class StringQuestion < Question
|
8
|
-
def as_json(options = {})
|
9
|
-
super.merge(autocomplete: @autocomplete)
|
10
|
-
super.merge(autocomplete: @autocomplete).tap do |json|
|
11
|
-
json[:setsTextvar] = sets_textvar if sets_textvar
|
12
|
-
end
|
13
|
-
end
|
14
8
|
end
|
15
9
|
end
|
16
10
|
end
|
@@ -19,12 +19,202 @@ module Quby
|
|
19
19
|
description: description,
|
20
20
|
shortDescription: short_description,
|
21
21
|
defaultAnswerValue: Services::TransformQuby1ValuesIntoQuby2Values.run!(@questionnaire, default_answer_value),
|
22
|
-
panels: panels,
|
23
|
-
questions:
|
24
|
-
flags: flags,
|
22
|
+
panels: panels.map { panel(_1) },
|
23
|
+
questions: questions,
|
25
24
|
textvars: textvars,
|
26
25
|
validations: validations,
|
27
|
-
visibilityRules: visibility_rules
|
26
|
+
visibilityRules: visibility_rules.as_json
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
def panel(panel)
|
31
|
+
{
|
32
|
+
title: panel.title,
|
33
|
+
items: panel.items.map { panel_item(_1) }.compact,
|
34
|
+
}.compact
|
35
|
+
end
|
36
|
+
|
37
|
+
def panel_item(item)
|
38
|
+
case item
|
39
|
+
when Quby::Compiler::Entities::Text
|
40
|
+
{ type: 'html', html: handle_html(item.html, type: :prose, v1_markdown: false) }
|
41
|
+
when Quby::Compiler::Entities::Question
|
42
|
+
return if item.table # things inside a table are added to the table, AND ALSO to the panel. skip them.
|
43
|
+
{ type: 'question', key: item.key }
|
44
|
+
when Quby::Compiler::Entities::Table
|
45
|
+
{ type: "table" }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def questions
|
50
|
+
fields.question_hash \
|
51
|
+
.to_h { |k, question| [k, question(question)] } \
|
52
|
+
.compact
|
53
|
+
end
|
54
|
+
|
55
|
+
def question(question)
|
56
|
+
send(:"#{question_type(question)}_question", question)
|
57
|
+
end
|
58
|
+
|
59
|
+
def check_box_question(question)
|
60
|
+
{
|
61
|
+
**base_question(question),
|
62
|
+
children: children(question),
|
63
|
+
checkAllOption: question.check_all_option,
|
64
|
+
uncheckAllOption: question.uncheck_all_option,
|
65
|
+
}.compact
|
66
|
+
end
|
67
|
+
|
68
|
+
def date_parts_question(question)
|
69
|
+
{
|
70
|
+
**base_question(question),
|
71
|
+
dateParts: question.components.map { |component|
|
72
|
+
{
|
73
|
+
part: component,
|
74
|
+
key: question.send("#{component}_key"),
|
75
|
+
}
|
76
|
+
},
|
77
|
+
}.compact
|
78
|
+
end
|
79
|
+
|
80
|
+
# deprecated
|
81
|
+
def hidden_question(question)
|
82
|
+
nil
|
83
|
+
end
|
84
|
+
|
85
|
+
def float_question(question)
|
86
|
+
integer_question(question)
|
87
|
+
end
|
88
|
+
|
89
|
+
def integer_question(question)
|
90
|
+
{
|
91
|
+
**base_question(question),
|
92
|
+
**slider_question(question),
|
93
|
+
minimum: question.minimum,
|
94
|
+
maximum: question.maximum,
|
95
|
+
size: size(question),
|
96
|
+
unit: question.as != :slider && question.unit,
|
97
|
+
}.compact
|
98
|
+
end
|
99
|
+
|
100
|
+
def radio_question(question)
|
101
|
+
{
|
102
|
+
**base_question(question),
|
103
|
+
children: children(question),
|
104
|
+
showValues: [true, :all].include?(question.show_values),
|
105
|
+
}.compact
|
106
|
+
end
|
107
|
+
|
108
|
+
def scale_question(question)
|
109
|
+
radio_question(question)
|
110
|
+
end
|
111
|
+
|
112
|
+
def select_question(question)
|
113
|
+
{
|
114
|
+
**base_question(question),
|
115
|
+
children: children(question),
|
116
|
+
placeholder: question.options.find { _1.placeholder }&.description,
|
117
|
+
}.compact
|
118
|
+
end
|
119
|
+
|
120
|
+
def string_question(question)
|
121
|
+
{
|
122
|
+
**base_question(question),
|
123
|
+
setsTextvar: question.sets_textvar,
|
124
|
+
size: size(question),
|
125
|
+
unit: question.as != :slider && question.unit,
|
126
|
+
}.compact
|
127
|
+
end
|
128
|
+
|
129
|
+
def textarea_question(question)
|
130
|
+
{
|
131
|
+
**base_question(question),
|
132
|
+
autocomplete: question.autocomplete,
|
133
|
+
lines: question.lines,
|
134
|
+
}.compact
|
135
|
+
end
|
136
|
+
|
137
|
+
def base_question(question)
|
138
|
+
{
|
139
|
+
key: question.key,
|
140
|
+
title: handle_html(question.title),
|
141
|
+
description: handle_html(question.description, type: :question_description),
|
142
|
+
contextDescription: handle_html(question.context_description, type: :prose, v1_markdown: false),
|
143
|
+
type: question_type(question),
|
144
|
+
hidden: question.hidden?,
|
145
|
+
displayModes: question.display_modes,
|
146
|
+
viewSelector: question.view_selector,
|
147
|
+
parentKey: question.parent&.key,
|
148
|
+
parentOptionKey: question.parent_option_key,
|
149
|
+
deselectable: question.deselectable,
|
150
|
+
presentation: question.presentation,
|
151
|
+
as: question.as || question_type(question), # default to type so typescript can narrow on it.
|
152
|
+
questionGroup: question.question_group,
|
153
|
+
}
|
154
|
+
end
|
155
|
+
|
156
|
+
def slider_question(question)
|
157
|
+
return {} unless question.as == :slider
|
158
|
+
|
159
|
+
{
|
160
|
+
step: question.step,
|
161
|
+
defaultPosition: question.default_position.is_a?(Numeric) ? question.default_position : question.minimum,
|
162
|
+
startThumbHidden: question.default_position == :hidden,
|
163
|
+
valueTooltip: question.input_data[:value_tooltip] || false,
|
164
|
+
labels: question.labels,
|
165
|
+
}
|
166
|
+
end
|
167
|
+
|
168
|
+
def question_type(question)
|
169
|
+
{
|
170
|
+
date: 'date_parts',
|
171
|
+
}[question.type] || question.type
|
172
|
+
end
|
173
|
+
|
174
|
+
def size(question)
|
175
|
+
question.size.presence && Integer(question.size) # 2022-11: 4k string and 7k integer
|
176
|
+
end
|
177
|
+
|
178
|
+
def children(question)
|
179
|
+
question.options.map { |child|
|
180
|
+
if child.inner_title
|
181
|
+
inner_title_as_json(child)
|
182
|
+
elsif child.placeholder
|
183
|
+
nil # placeholder attr on question.
|
184
|
+
else
|
185
|
+
option_as_json(child)
|
186
|
+
end
|
187
|
+
}.compact
|
188
|
+
end
|
189
|
+
|
190
|
+
def inner_title_as_json(option)
|
191
|
+
{
|
192
|
+
type: 'html',
|
193
|
+
key: SecureRandom.uuid,
|
194
|
+
html: handle_html(option.description)
|
195
|
+
}
|
196
|
+
end
|
197
|
+
|
198
|
+
def option_as_json(option)
|
199
|
+
{
|
200
|
+
type: 'option',
|
201
|
+
key: option.key,
|
202
|
+
value: option.question.type != :check_box && option.value,
|
203
|
+
description: option.question.type == :select ? option.description : handle_html(option.description),
|
204
|
+
questions: option.question.type != :select && option.questions.map{ question(_1) },
|
205
|
+
viewId: option.view_id
|
206
|
+
}.compact
|
207
|
+
end
|
208
|
+
|
209
|
+
def textvars
|
210
|
+
@questionnaire.textvars.to_h { |key, textvar|
|
211
|
+
[
|
212
|
+
key,
|
213
|
+
{
|
214
|
+
key: textvar.key,
|
215
|
+
default: textvar.default,
|
216
|
+
}
|
217
|
+
]
|
28
218
|
}
|
29
219
|
end
|
30
220
|
|
@@ -40,6 +230,27 @@ module Quby
|
|
40
230
|
end.as_json
|
41
231
|
end
|
42
232
|
end
|
233
|
+
|
234
|
+
def handle_html(html, type: :simple, v1_markdown: true)
|
235
|
+
if layout_version == :v2
|
236
|
+
case type
|
237
|
+
when :simple
|
238
|
+
html_sanitizer.sanitize(html, tags: %w[strong em sup sub br span], attributes: %w[class])
|
239
|
+
when :question_description
|
240
|
+
html_sanitizer.sanitize(html, tags: %w[strong em b i u sup sub p span br ul ol li], attributes: %w[class])
|
241
|
+
when :prose
|
242
|
+
html_sanitizer.sanitize(html, tags: %w[strong em b i u sup sub pre blockquote p span br ul ol li a h1 h2 h3 h4], attributes: %w[href class target])
|
243
|
+
end
|
244
|
+
elsif v1_markdown
|
245
|
+
Quby::Compiler::MarkdownParser.new(html).to_html
|
246
|
+
else
|
247
|
+
html
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def html_sanitizer
|
252
|
+
@html_sanitize ||= Rails::HTML5::SafeListSanitizer.new
|
253
|
+
end
|
43
254
|
end
|
44
255
|
end
|
45
256
|
end
|
data/lib/quby/compiler.rb
CHANGED
@@ -0,0 +1,12 @@
|
|
1
|
+
class Quby::InspectExcept < Module
|
2
|
+
def initialize(*excepts)
|
3
|
+
define_method :inspect do
|
4
|
+
prefix = "#<#{self.class}:0x#{self.__id__.to_s(16)}"
|
5
|
+
|
6
|
+
parts = (instance_variables - excepts).map do |var|
|
7
|
+
"#{var}=#{instance_variable_get(var).inspect}"
|
8
|
+
end
|
9
|
+
"#{prefix}\n #{parts.join(", ")}>"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: quby-compiler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.15
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marten Veldthuis
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-02-
|
10
|
+
date: 2025-02-27 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: activemodel
|
@@ -220,6 +220,7 @@ files:
|
|
220
220
|
- lib/quby/compiler/services/transform_quby1_values_into_quby2_values.rb
|
221
221
|
- lib/quby/compiler/type_validator.rb
|
222
222
|
- lib/quby/compiler/version.rb
|
223
|
+
- lib/quby/inspect_except.rb
|
223
224
|
- lib/quby/range_categories.rb
|
224
225
|
- lib/quby/settings.rb
|
225
226
|
- lib/quby/text_transformation.rb
|