quby-compiler 0.3.1 → 0.3.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/CHANGELOG.md +21 -0
  4. data/Gemfile.lock +25 -25
  5. data/exe/quby-compile +2 -3
  6. data/lib/quby/compiler/dsl.rb +3 -3
  7. data/lib/quby/compiler/dsl/base.rb +2 -2
  8. data/lib/quby/compiler/dsl/calls_custom_methods.rb +1 -2
  9. data/lib/quby/compiler/dsl/charting/chart_builder.rb +3 -3
  10. data/lib/quby/compiler/dsl/charting/overview_chart_builder.rb +1 -1
  11. data/lib/quby/compiler/dsl/helpers.rb +3 -3
  12. data/lib/quby/compiler/dsl/panel_builder.rb +13 -10
  13. data/lib/quby/compiler/dsl/question_builder.rb +2 -2
  14. data/lib/quby/compiler/dsl/questionnaire_builder.rb +24 -24
  15. data/lib/quby/compiler/dsl/questions/base.rb +7 -7
  16. data/lib/quby/compiler/dsl/questions/checkbox_question_builder.rb +1 -1
  17. data/lib/quby/compiler/dsl/questions/date_question_builder.rb +1 -1
  18. data/lib/quby/compiler/dsl/questions/deprecated_question_builder.rb +1 -1
  19. data/lib/quby/compiler/dsl/questions/float_question_builder.rb +1 -1
  20. data/lib/quby/compiler/dsl/questions/integer_question_builder.rb +1 -1
  21. data/lib/quby/compiler/dsl/questions/radio_question_builder.rb +1 -1
  22. data/lib/quby/compiler/dsl/questions/select_question_builder.rb +1 -1
  23. data/lib/quby/compiler/dsl/questions/string_question_builder.rb +1 -1
  24. data/lib/quby/compiler/dsl/questions/text_question_builder.rb +1 -1
  25. data/lib/quby/compiler/dsl/score_builder.rb +1 -1
  26. data/lib/quby/compiler/dsl/table_builder.rb +5 -5
  27. data/lib/quby/compiler/entities/definition.rb +3 -2
  28. data/lib/quby/compiler/entities/outcome_table.rb +12 -0
  29. data/lib/quby/compiler/entities/questionnaire.rb +2 -2
  30. data/lib/quby/compiler/entities/questions/float_question.rb +9 -0
  31. data/lib/quby/compiler/entities/questions/integer_question.rb +9 -0
  32. data/lib/quby/compiler/entities/subscore_schema.rb +2 -1
  33. data/lib/quby/compiler/instance.rb +2 -2
  34. data/lib/quby/compiler/outputs/roqua_serializer.rb +39 -2
  35. data/lib/quby/compiler/services/definition_validator.rb +8 -4
  36. data/lib/quby/compiler/services/quby_proxy.rb +6 -6
  37. data/lib/quby/compiler/version.rb +1 -1
  38. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4d882604750c6b9e14ef80ebf8251f0cd921b7f9e7db31b843b1c279676904c4
4
- data.tar.gz: db31a32e46f675814f9ea97d00b0c8b35a45d60b895834c0aeaf0eec58c4ae57
3
+ metadata.gz: a56013c91225af82fd15a27e8195ca88b22dcb3ea94354ab2ef62cef50532bf2
4
+ data.tar.gz: 6412e7e3ef45dd548dc10eea9e2b72da4b7cc64d94d9de2a3ede13db70c5be25
5
5
  SHA512:
6
- metadata.gz: 816477fed5ba4f5e9c1c36a0766e9e832bb8c9a37ab22e8547f8980937e98b915188a51e5154cde2c88cb0af578fb16d42d0709133c6e1b1622054cb2415e5ae
7
- data.tar.gz: 97cf01e8fd73955ce8f51e399db8017705a69785f7e48c2f4dc6c40652f17d9638b17542725803442d1c6ca567d7c79bbc49e72d2df7f60cd9481e9aa0ac11c2
6
+ metadata.gz: 67d746a71ccbd1c0780a836579000efdf50e825a10906a10992abaf3612340039e9670320814d6cb7f6baf3eb041e85c3872496e4d95a70ed4195d61632b969a
7
+ data.tar.gz: 51151607042665ecbdd7f4b9448e24a220350cd132ec1e5ef509dfd71b5abca5668686de5231cfd40a27e43ecead4003e1f41ab04e01f952fae95ecc3f96a7cf
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.7.1
1
+ 2.7.2
data/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ # 0.3.6
2
+
3
+ * `outcome_table` panel dsl method now exports the given outcome table definition to `roqua.json`
4
+ This can't be used in combination with the `outcome_table: ` attibute in score schemas.
5
+
6
+ # 0.3.5
7
+
8
+ * Allow score_schema's outcome_table to be set to nil to hide values from outcome tables
9
+
10
+ # 0.3.4
11
+
12
+ * Added preliminary support for sliders to the v2 output.
13
+
14
+ # 0.3.3
15
+
16
+ * Replace `options={}` with `**options` in the builders to prevent prepare for ruby 3.0
17
+
18
+ # 0.3.2
19
+
20
+ * Fix slashes-after-numbers removal when question does not have a title
21
+
1
22
  # 0.3.1
2
23
 
3
24
  * Slashes after numbers are now removed from question titles when making RoQua seeds (quests attribute).
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- quby-compiler (0.3.1)
4
+ quby-compiler (0.3.5)
5
5
  actionview (>= 5.0)
6
6
  activemodel (>= 5.0)
7
7
  activesupport (>= 5.0)
@@ -15,42 +15,42 @@ PATH
15
15
  GEM
16
16
  remote: https://rubygems.org/
17
17
  specs:
18
- actionview (6.0.3.5)
19
- activesupport (= 6.0.3.5)
18
+ actionview (6.0.4)
19
+ activesupport (= 6.0.4)
20
20
  builder (~> 3.1)
21
21
  erubi (~> 1.4)
22
22
  rails-dom-testing (~> 2.0)
23
23
  rails-html-sanitizer (~> 1.1, >= 1.2.0)
24
- active_interaction (3.8.3)
25
- activemodel (>= 4, < 7)
26
- activemodel (6.0.3.5)
27
- activesupport (= 6.0.3.5)
28
- activerecord (6.0.3.5)
29
- activemodel (= 6.0.3.5)
30
- activesupport (= 6.0.3.5)
31
- activesupport (6.0.3.5)
24
+ active_interaction (4.0.2)
25
+ activemodel (>= 5, < 7)
26
+ activemodel (6.0.4)
27
+ activesupport (= 6.0.4)
28
+ activerecord (6.0.4)
29
+ activemodel (= 6.0.4)
30
+ activesupport (= 6.0.4)
31
+ activesupport (6.0.4)
32
32
  concurrent-ruby (~> 1.0, >= 1.0.2)
33
33
  i18n (>= 0.7, < 2)
34
34
  minitest (~> 5.1)
35
35
  tzinfo (~> 1.1)
36
36
  zeitwerk (~> 2.2, >= 2.2.2)
37
- appsignal (2.10.12)
37
+ appsignal (3.0.8)
38
38
  rack
39
39
  builder (3.2.4)
40
40
  coderay (1.1.3)
41
- concurrent-ruby (1.1.8)
41
+ concurrent-ruby (1.1.9)
42
42
  crass (1.0.6)
43
43
  diff-lcs (1.4.4)
44
44
  dry-configurable (0.12.1)
45
45
  concurrent-ruby (~> 1.0)
46
46
  dry-core (~> 0.5, >= 0.5.0)
47
- dry-container (0.7.2)
47
+ dry-container (0.8.0)
48
48
  concurrent-ruby (~> 1.0)
49
49
  dry-configurable (~> 0.1, >= 0.1.3)
50
- dry-core (0.5.0)
50
+ dry-core (0.6.0)
51
51
  concurrent-ruby (~> 1.0)
52
52
  dry-inflector (0.2.0)
53
- dry-logic (1.1.0)
53
+ dry-logic (1.2.0)
54
54
  concurrent-ruby (~> 1.0)
55
55
  dry-core (~> 0.5, >= 0.5)
56
56
  dry-struct (1.4.0)
@@ -64,20 +64,20 @@ GEM
64
64
  dry-inflector (~> 0.1, >= 0.1.2)
65
65
  dry-logic (~> 1.0, >= 1.0.2)
66
66
  erubi (1.10.0)
67
- i18n (1.8.9)
67
+ i18n (1.8.10)
68
68
  concurrent-ruby (~> 1.0)
69
69
  ice_nine (0.11.2)
70
- loofah (2.9.0)
70
+ loofah (2.10.0)
71
71
  crass (~> 1.0.2)
72
72
  nokogiri (>= 1.5.9)
73
73
  method_source (1.0.0)
74
- mini_portile2 (2.5.0)
74
+ mini_portile2 (2.5.3)
75
75
  minitest (5.14.4)
76
76
  naught (1.1.0)
77
- nokogiri (1.11.1)
77
+ nokogiri (1.11.7)
78
78
  mini_portile2 (~> 2.5.0)
79
79
  racc (~> 1.4)
80
- nokogumbo (2.0.4)
80
+ nokogumbo (2.0.5)
81
81
  nokogiri (~> 1.8, >= 1.8.4)
82
82
  pry (0.13.1)
83
83
  coderay (~> 1.1)
@@ -91,10 +91,10 @@ GEM
91
91
  loofah (~> 2.3)
92
92
  rake (12.3.3)
93
93
  redcarpet (3.5.1)
94
- roqua-support (0.3.5)
95
- active_interaction (~> 3.0)
96
- activesupport (>= 5.1, < 6.1)
97
- appsignal (>= 2.9, < 2.11)
94
+ roqua-support (0.4.0)
95
+ active_interaction (>= 3.0, < 5.0)
96
+ activesupport (>= 5.2, < 6.2)
97
+ appsignal (>= 2.9, < 3.1)
98
98
  naught (~> 1.0)
99
99
  with_advisory_lock (~> 3.2)
100
100
  rspec (3.9.0)
data/exe/quby-compile CHANGED
@@ -21,7 +21,7 @@ OptionParser.new do |opts|
21
21
  end
22
22
 
23
23
  opts.on("--seeds=INPUT") do |value|
24
- seeds_path = value
24
+ puts '--seeds is a deprecated option, questionnaire definitions should use seed_patches to conserve seed tweaks'
25
25
  end
26
26
  end.parse!
27
27
 
@@ -38,7 +38,7 @@ lookup_tables = Quby::Compiler::Entities::LookupTables.new(lookup_tables_path)
38
38
  paths.each do |path|
39
39
  puts "Compiling #{path}"
40
40
 
41
- key = File.basename(path, File.extname(path))
41
+ key = File.basename(File.dirname(path))
42
42
  sourcecode = File.read(path)
43
43
  last_update = File.mtime(path)
44
44
  seed_path = File.join(seeds_path, "#{key}.yml")
@@ -46,7 +46,6 @@ paths.each do |path|
46
46
  compiled = Quby::Compiler.compile(key, sourcecode, path: path, seeds: seeds, lookup_tables: lookup_tables, last_update: last_update)
47
47
 
48
48
  FileUtils.mkdir_p(File.join(output_path, key))
49
-
50
49
  compiled[:outputs].each do |type, output|
51
50
  next unless output
52
51
  File.open(File.join(output_path, key, output.filename), 'w') do |file|
@@ -10,15 +10,15 @@ module Quby
10
10
  module DSL
11
11
  def self.build_from_definition(definition)
12
12
  Entities::Questionnaire.new(definition.key, last_update: definition.timestamp).tap do |questionnaire|
13
- builder = QuestionnaireBuilder.new(questionnaire)
13
+ builder = QuestionnaireBuilder.new(questionnaire, lookup_tables: definition.lookup_tables)
14
14
  builder.instance_eval(definition.sourcecode, definition.path) if definition.sourcecode
15
15
  questionnaire.callback_after_dsl_enhance_on_questions
16
16
  end
17
17
  end
18
18
 
19
- def self.build(key, sourcecode = nil, path: nil, timestamp: nil, &block)
19
+ def self.build(key, sourcecode = nil, path: nil, timestamp: nil, lookup_tables: {}, &block)
20
20
  Entities::Questionnaire.new(key, last_update: timestamp).tap do |questionnaire|
21
- builder = QuestionnaireBuilder.new(questionnaire)
21
+ builder = QuestionnaireBuilder.new(questionnaire, lookup_tables: lookup_tables)
22
22
  builder.instance_eval(sourcecode, path || key) if sourcecode
23
23
  builder.instance_eval(&block) if block
24
24
  questionnaire.callback_after_dsl_enhance_on_questions
@@ -9,8 +9,8 @@ module Quby
9
9
  class Base
10
10
  include Helpers
11
11
 
12
- def self.build(*args, &block)
13
- builder = new(*args)
12
+ def self.build(*args, **kwargs, &block)
13
+ builder = new(*args, **kwargs)
14
14
  builder.instance_eval(&block) if block
15
15
  builder.build
16
16
  end
@@ -6,8 +6,7 @@ module Quby
6
6
  module CallsCustomMethods
7
7
  attr_reader :custom_methods
8
8
 
9
- def initialize(*args)
10
- options = args.last.is_a?(::Hash) ? args.last : {}
9
+ def initialize(*args, **options)
11
10
  @custom_methods = options[:custom_methods] || {}
12
11
  super
13
12
  end
@@ -16,8 +16,8 @@ module Quby
16
16
  @chart_class
17
17
  end
18
18
 
19
- def initialize(questionnaire, key, options = {})
20
- @chart = self.class.chart_class.new(key, options)
19
+ def initialize(questionnaire, key, **options)
20
+ @chart = self.class.chart_class.new(key, **options)
21
21
  @questionnaire = questionnaire
22
22
  end
23
23
 
@@ -49,7 +49,7 @@ module Quby
49
49
  @chart.plotlines << {value: value, color: color}
50
50
  end
51
51
 
52
- def plot(key, options = {})
52
+ def plot(key, **options)
53
53
  unless plottable = @questionnaire.find_plottable(key)
54
54
  fail "Questionnaire #{@questionnaire.key} chart #{@chart.key} references unknown score or question #{key}"
55
55
  end
@@ -7,7 +7,7 @@ module Quby
7
7
  class OverviewChartBuilder < ChartBuilder
8
8
  set_chart_class(Entities::Charting::OverviewChart)
9
9
 
10
- def initialize(questionnaire, options = {})
10
+ def initialize(questionnaire, **options)
11
11
  @questionnaire = questionnaire
12
12
  @chart = self.class.chart_class.new
13
13
  end
@@ -7,8 +7,8 @@ module Quby
7
7
  include ActionView::Helpers::TagHelper
8
8
  include ActionView::Helpers::OutputSafetyHelper
9
9
 
10
- def image_tag(source, options = {})
11
- tag.img(options.reverse_merge(src: source, alt: image_alt(source)))
10
+ def image_tag(source, **options)
11
+ tag.img(**options.reverse_merge(src: source, alt: image_alt(source)))
12
12
  end
13
13
 
14
14
  # Copied from ActionController::Base.helpers.image_alt, because it will be removed from Rails 6.0, but we want
@@ -18,7 +18,7 @@ module Quby
18
18
  end
19
19
 
20
20
  def check_question_keys_uniqueness(key, options, questionnaire)
21
- keys = QuestionBuilder.build(key, options).claimed_keys
21
+ keys = QuestionBuilder.build(key, **options).claimed_keys
22
22
  if keys.any? { |k| questionnaire.key_in_use? k }
23
23
  fail "#{questionnaire.key}:#{key}: A question or option with input key #{key} is already defined."
24
24
  end
@@ -9,9 +9,9 @@ module Quby
9
9
  attr_reader :title
10
10
  attr_reader :questionnaire
11
11
 
12
- def initialize(title, options = {})
13
- @panel = Entities::Panel.new(options.merge(title: title, items: []))
14
- @default_question_options = options[:default_question_options] || {}
12
+ def initialize(title, default_question_options: {}, **options)
13
+ @panel = Entities::Panel.new({title: title, items: [], **options})
14
+ @default_question_options = default_question_options
15
15
  @questionnaire = options[:questionnaire]
16
16
  @custom_methods = options[:custom_methods] || {}
17
17
  end
@@ -41,25 +41,28 @@ module Quby
41
41
  @panel.items << Entities::Text.new('', raw_content: video_html)
42
42
  end
43
43
 
44
- def default_question_options(options = {})
44
+ def default_question_options(**options)
45
45
  @default_question_options = @default_question_options.merge(options)
46
46
  end
47
47
 
48
- def question(key, options = {}, &block)
48
+ def question(key, **options, &block)
49
49
  options = @default_question_options.merge(options).merge(questionnaire: @panel.questionnaire)
50
50
 
51
51
  check_question_keys_uniqueness key, options, @questionnaire
52
52
 
53
- question = QuestionBuilder.build(key, options, &block)
53
+ question = QuestionBuilder.build(key, **options, &block)
54
54
 
55
55
  @questionnaire.register_question(question)
56
56
  @panel.items << question
57
57
  end
58
58
 
59
- def table(options = {}, &block)
60
- table_builder = TableBuilder.new(@panel, options.merge(questionnaire: @panel.questionnaire,
61
- default_question_options: @default_question_options,
62
- custom_methods: @custom_methods))
59
+ def table(**options, &block)
60
+ table_builder = TableBuilder.new \
61
+ @panel,
62
+ questionnaire: @panel.questionnaire,
63
+ default_question_options: @default_question_options,
64
+ custom_methods: @custom_methods,
65
+ **options
63
66
  table_builder.instance_eval(&block) if block
64
67
  end
65
68
 
@@ -31,8 +31,8 @@ module Quby
31
31
  'hidden' => Questions::DeprecatedQuestionBuilder
32
32
  }
33
33
 
34
- def self.build(key, options = {}, &block)
35
- BUILDERS.fetch(options.fetch(:type).to_s).build(key, options, &block)
34
+ def self.build(key, **options, &block)
35
+ BUILDERS.fetch(options.fetch(:type).to_s).build(key, **options, &block)
36
36
  end
37
37
  end
38
38
  end
@@ -19,7 +19,7 @@ module Quby
19
19
  include StandardizedPanelGenerators
20
20
  include Helpers
21
21
 
22
- def initialize(target_instance, lookup_tables: nil)
22
+ def initialize(target_instance, lookup_tables:)
23
23
  @questionnaire = target_instance
24
24
  @lookup_tables = lookup_tables
25
25
  @default_question_options = {}
@@ -148,7 +148,7 @@ module Quby
148
148
  end
149
149
 
150
150
  def panel(title = nil, options = {}, &block)
151
- panel = PanelBuilder.build(title, options.merge(default_panel_options), &block)
151
+ panel = PanelBuilder.build(title, **options, **default_panel_options, &block)
152
152
  @questionnaire.add_panel(panel)
153
153
  end
154
154
 
@@ -167,28 +167,28 @@ module Quby
167
167
  @questionnaire.lookup_tables[key] = {levels: levels, tree: tree}
168
168
  end
169
169
 
170
- def default_question_options(options = {})
170
+ def default_question_options(**options)
171
171
  @default_question_options.merge!(options)
172
172
  end
173
173
 
174
174
  # Short-circuit the question command to perform an implicit panel
175
- def question(key, options = {}, &block)
175
+ def question(key, **options, &block)
176
176
  panel(nil, default_panel_options) do
177
- question(key, @default_question_options.merge(options).merge(questionnaire: @questionnaire), &block)
177
+ question(key, questionnaire: @questionnaire, **@default_question_options, **options, &block)
178
178
  end
179
179
  end
180
180
 
181
181
  # Short-circuit the text command to perform an implicit panel
182
- def text(value, options = {})
183
- panel(nil, default_panel_options) do
184
- text(value, options)
182
+ def text(value, **options)
183
+ panel(nil, **default_panel_options) do
184
+ text(value, **options)
185
185
  end
186
186
  end
187
187
 
188
188
  # Short-circuit the table command to perform an implicit panel
189
- def table(options = {}, &block)
190
- panel(nil, default_panel_options) do
191
- table(options, &block)
189
+ def table(**options, &block)
190
+ panel(nil, **default_panel_options) do
191
+ table(**options, &block)
192
192
  end
193
193
  end
194
194
 
@@ -197,15 +197,15 @@ module Quby
197
197
  # # variables are private to the score calculation
198
198
  # q01 + q02 + q03
199
199
  # end
200
- def variable(key, options = {}, &block)
201
- @questionnaire.add_score_calculation ScoreBuilder.new(key, options, &block).build
200
+ def variable(key, **options, &block)
201
+ @questionnaire.add_score_calculation ScoreBuilder.new(key, **options, &block).build
202
202
  end
203
203
 
204
- def score(key, options = {}, &block)
204
+ def score(key, **options, &block)
205
205
  @questionnaire.errors.add "Score #{key}", 'misses label in score call' if options[:label].blank?
206
206
  schema = options.delete(:schema)
207
207
  score_schema(key, options[:label], schema) if schema.present?
208
- variable(key, options.reverse_merge(score: true), &block)
208
+ variable(key, score: true, **options, &block)
209
209
  end
210
210
 
211
211
  def score_schema(key, label, options = nil, &block)
@@ -222,16 +222,16 @@ module Quby
222
222
  end
223
223
  end
224
224
 
225
- def attention(options = {}, &block)
226
- variable(:attention, options.reverse_merge(action: true), &block)
225
+ def attention(**options, &block)
226
+ variable(:attention, action: true, **options, &block)
227
227
  end
228
228
 
229
- def alarm(options = {}, &block)
230
- variable(:alarm, options.reverse_merge(action: true), &block)
229
+ def alarm(**options, &block)
230
+ variable(:alarm, action: true, **options, &block)
231
231
  end
232
232
 
233
- def completion(options = {}, &block)
234
- variable(:completion, options.reverse_merge(completion: true), &block)
233
+ def completion(**options, &block)
234
+ variable(:completion, completion: true, **options, &block)
235
235
  end
236
236
 
237
237
  def overview_chart(*args, &block)
@@ -255,15 +255,15 @@ module Quby
255
255
  @questionnaire.add_chart(builder.build(&block))
256
256
  end
257
257
 
258
- def flag(flag_options)
258
+ def flag(**flag_options)
259
259
  @questionnaire.add_flag flag_options
260
260
  end
261
261
 
262
- def textvar(textvar_options)
262
+ def textvar(**textvar_options)
263
263
  @questionnaire.add_textvar textvar_options
264
264
  end
265
265
 
266
- def outcome_table(table_options)
266
+ def outcome_table(**table_options)
267
267
  @questionnaire.add_outcome_table table_options
268
268
  end
269
269
 
@@ -10,8 +10,8 @@ module Quby
10
10
  attr_reader :type
11
11
  attr_reader :questionnaire
12
12
 
13
- def initialize(key, options = {})
14
- @questionnaire = options[:questionnaire]
13
+ def initialize(key, questionnaire:, **options)
14
+ @questionnaire = questionnaire
15
15
  end
16
16
 
17
17
  def build
@@ -111,7 +111,7 @@ module Quby
111
111
  end
112
112
 
113
113
  module Subquestions
114
- def initialize(key, options = {}, &block)
114
+ def initialize(key, **options, &block)
115
115
  super
116
116
  @default_question_options = options[:default_question_options] || {}
117
117
  @title_question = nil
@@ -126,7 +126,7 @@ module Quby
126
126
  super
127
127
  end
128
128
 
129
- def title_question(key, options = {}, &block)
129
+ def title_question(key, **options, &block)
130
130
  options = @default_question_options.merge({depends_on: @question.key,
131
131
  questionnaire: @questionnaire,
132
132
  parent: @question,
@@ -135,13 +135,13 @@ module Quby
135
135
 
136
136
  check_question_keys_uniqueness key, options, @questionnaire
137
137
 
138
- question = QuestionBuilder.build(key, options, &block)
138
+ question = QuestionBuilder.build(key, **options, &block)
139
139
 
140
140
  @questionnaire.register_question(question)
141
141
  @title_question = question
142
142
  end
143
143
 
144
- def question(key, options = {}, &block)
144
+ def question(key, **options, &block)
145
145
  options = @default_question_options.merge(options)
146
146
  .merge(questionnaire: @questionnaire,
147
147
  parent: @question,
@@ -149,7 +149,7 @@ module Quby
149
149
 
150
150
  check_question_keys_uniqueness key, options, @questionnaire
151
151
 
152
- question = QuestionBuilder.build(key, options, &block)
152
+ question = QuestionBuilder.build(key, **options, &block)
153
153
 
154
154
  @questionnaire.register_question(question)
155
155
  @question.options.last.questions << question
@@ -9,7 +9,7 @@ module Quby
9
9
  include Subquestions
10
10
  include InnerTitles
11
11
 
12
- def initialize(key, options = {}, &block)
12
+ def initialize(key, **options, &block)
13
13
  super
14
14
  @question = Entities::Questions::CheckboxQuestion.new(key, options)
15
15
  end
@@ -7,7 +7,7 @@ module Quby
7
7
  class DateQuestionBuilder < Base
8
8
  include MinMaxValidations
9
9
 
10
- def initialize(key, options = {}, &block)
10
+ def initialize(key, **options, &block)
11
11
  super
12
12
  @question = Entities::Questions::DateQuestion.new(key, options)
13
13
  end
@@ -7,7 +7,7 @@ module Quby
7
7
  class DeprecatedQuestionBuilder < Base
8
8
  include MultipleChoice
9
9
 
10
- def initialize(key, options = {}, &block)
10
+ def initialize(key, **options, &block)
11
11
  super
12
12
  @question = Entities::Questions::DeprecatedQuestion.new(key, options)
13
13
  end
@@ -10,7 +10,7 @@ module Quby
10
10
  include Units
11
11
  include Sizes
12
12
 
13
- def initialize(key, options = {}, &block)
13
+ def initialize(key, **options, &block)
14
14
  super
15
15
  @question = Entities::Questions::FloatQuestion.new(key, options)
16
16
  end
@@ -10,7 +10,7 @@ module Quby
10
10
  include Units
11
11
  include Sizes
12
12
 
13
- def initialize(key, options = {}, &block)
13
+ def initialize(key, **options, &block)
14
14
  super
15
15
  @question = Entities::Questions::IntegerQuestion.new(key, options)
16
16
  end
@@ -9,7 +9,7 @@ module Quby
9
9
  include Subquestions
10
10
  include InnerTitles
11
11
 
12
- def initialize(key, options = {}, &block)
12
+ def initialize(key, **options, &block)
13
13
  super
14
14
  @question = Entities::Questions::RadioQuestion.new(key, options)
15
15
  end
@@ -7,7 +7,7 @@ module Quby
7
7
  class SelectQuestionBuilder < Base
8
8
  include MultipleChoice
9
9
 
10
- def initialize(key, options = {}, &block)
10
+ def initialize(key, **options, &block)
11
11
  super
12
12
  @question = Entities::Questions::SelectQuestion.new(key, options)
13
13
  end
@@ -9,7 +9,7 @@ module Quby
9
9
  include Units
10
10
  include Sizes
11
11
 
12
- def initialize(key, options = {}, &block)
12
+ def initialize(key, **options, &block)
13
13
  super
14
14
  @question = Entities::Questions::StringQuestion.new(key, options)
15
15
  end
@@ -7,7 +7,7 @@ module Quby
7
7
  class TextQuestionBuilder < Base
8
8
  include RegexpValidations
9
9
 
10
- def initialize(key, options = {}, &block)
10
+ def initialize(key, **options, &block)
11
11
  super
12
12
  @question = Entities::Questions::TextQuestion.new(key, options)
13
13
  end
@@ -9,7 +9,7 @@ module Quby
9
9
  attr_reader :key
10
10
  attr_reader :calculation
11
11
 
12
- def initialize(key, options = {}, &block)
12
+ def initialize(key, **options, &block)
13
13
  @score = Entities::ScoreCalculation.new(key, options, &block)
14
14
  end
15
15
 
@@ -9,10 +9,10 @@ module Quby
9
9
  prepend CallsCustomMethods
10
10
  include Helpers
11
11
 
12
- def initialize(panel, options = {})
12
+ def initialize(panel, default_question_options: {}, **options)
13
13
  @panel = panel
14
14
  @table = Entities::Table.new(options)
15
- @default_question_options = options[:default_question_options] || {}
15
+ @default_question_options = default_question_options
16
16
  @panel.items << @table
17
17
  end
18
18
 
@@ -24,11 +24,11 @@ module Quby
24
24
  @table.description = value
25
25
  end
26
26
 
27
- def text(value, options = {})
27
+ def text(value, **options)
28
28
  @table.items << Entities::Text.new(value.to_s, options)
29
29
  end
30
30
 
31
- def question(key, options = {}, &block)
31
+ def question(key, **options, &block)
32
32
  options = @default_question_options.merge(options)
33
33
  .merge(table: @table,
34
34
  questionnaire: @panel.questionnaire)
@@ -36,7 +36,7 @@ module Quby
36
36
  check_question_keys_uniqueness key, options, @panel.questionnaire
37
37
  fail "You can't create a slider in a table at the moment" if options[:as] == :slider
38
38
 
39
- question = QuestionBuilder.build(key, options, &block)
39
+ question = QuestionBuilder.build(key, **options, &block)
40
40
 
41
41
  @panel.questionnaire.register_question(question)
42
42
  @table.items << question
@@ -10,13 +10,14 @@ module Quby
10
10
  extend ActiveModel::Naming
11
11
  include ActiveModel::Validations
12
12
 
13
- attr_accessor :key, :sourcecode, :timestamp, :path
13
+ attr_accessor :key, :sourcecode, :timestamp, :path, :lookup_tables
14
14
 
15
- def initialize(key:, path:, sourcecode: "", timestamp: nil)
15
+ def initialize(key:, path:, sourcecode: "", timestamp: nil, lookup_tables: {})
16
16
  @path = path
17
17
  @key = key
18
18
  @sourcecode = sourcecode
19
19
  @timestamp = timestamp
20
+ @lookup_tables = lookup_tables
20
21
  end
21
22
 
22
23
  validates_with Services::DefinitionValidator
@@ -15,6 +15,7 @@ module Quby
15
15
  validates :score_keys, :subscore_keys, :questionnaire, :key, presence: true
16
16
  validates :name, presence: true, if: proc { |table| table.default_collapsed }
17
17
  validate :references_existing_score_keys
18
+ validate :no_outcome_tables_defined_in_score_schemas
18
19
 
19
20
  def references_existing_score_keys
20
21
  (score_keys - questionnaire.score_schemas.values.map(&:key)).each do |missing_key|
@@ -25,6 +26,17 @@ module Quby
25
26
  errors.add :subscore_keys, "#{missing_key.inspect} not found in subscore schemas"
26
27
  end
27
28
  end
29
+
30
+ def no_outcome_tables_defined_in_score_schemas
31
+ if questionnaire.score_schemas.values.any? do |schema|
32
+ schema.subscore_schemas.any? do |subscore|
33
+ subscore.outcome_table != :main
34
+ end
35
+ end
36
+ errors.add :score_schemas,
37
+ "Outcome table associations defined in score schemas should not be combined with explicit outcome tables"
38
+ end
39
+ end
28
40
  end
29
41
  end
30
42
  end
@@ -283,7 +283,7 @@ module Quby
283
283
  end
284
284
  flag_options[:key] = flag_key
285
285
  fail(ArgumentError, "Flag '#{flag_key}' already defined") if flags.key?(flag_key)
286
- flags[flag_key] = Flag.new(flag_options)
286
+ flags[flag_key] = Flag.new(**flag_options)
287
287
  end
288
288
 
289
289
  def filter_flags(given_flags)
@@ -297,7 +297,7 @@ module Quby
297
297
  textvar_options[:key] = textvar_key
298
298
  validate_textvar_keys_unique(textvar_key)
299
299
  validate_depends_on_flag(textvar_key, textvar_options)
300
- textvars[textvar_key] = Textvar.new(textvar_options)
300
+ textvars[textvar_key] = Textvar.new(**textvar_options)
301
301
  end
302
302
 
303
303
  def filter_textvars(given_textvars)
@@ -5,6 +5,15 @@ module Quby
5
5
  module Entities
6
6
  module Questions
7
7
  class FloatQuestion < Question
8
+ def as_json(options = {})
9
+ super.merge(
10
+ minimum: minimum,
11
+ maximum: maximum,
12
+ step: 0.01, # fixed in v1.
13
+ # defaultPosition: default_position # Needs discussion, can be number or string "hidden"
14
+ )
15
+ end
16
+
8
17
  def size
9
18
  @size || 30
10
19
  end
@@ -5,6 +5,15 @@ module Quby
5
5
  module Entities
6
6
  module Questions
7
7
  class IntegerQuestion < Question
8
+ def as_json(options = {})
9
+ super.merge(
10
+ minimum: minimum,
11
+ maximum: maximum,
12
+ step: 1, # fixed in v1.
13
+ # defaultPosition: default_position # Needs discussion, can be number or string "hidden"
14
+ )
15
+ end
16
+
8
17
  def size
9
18
  @size || 30
10
19
  end
@@ -16,7 +16,8 @@ module Quby
16
16
  attribute :calculation_key?, Types::Symbol
17
17
  # [Optional argument] The name of the outcome table where this subscore should be shown. Used for cases where scores
18
18
  # differ in subscores too much to be shown as one table. By default, all scores end up in the `:main` table.
19
- attribute :outcome_table, Types::Symbol.default(:main).meta(omittable: true)
19
+ # When outcome_table is explicitly nil, the value should not be shown in outcome tables
20
+ attribute :outcome_table, Types::Symbol.optional.default(:main)
20
21
  end
21
22
  end
22
23
  end
@@ -11,7 +11,7 @@ module Quby
11
11
  if block # defined in block for tests
12
12
  questionnaire = DSL.build(key, path: path, &block)
13
13
  else # sourcecode given as string
14
- tempfile = Tempfile.new(key)
14
+ tempfile = Tempfile.new([key, '.rb'])
15
15
  questionnaire = Entities::Questionnaire.new(key, last_update: last_update)
16
16
  Thread.current["quby-questionnaire-loading"] = Quby::Compiler::DSL::QuestionnaireBuilder.new(questionnaire, lookup_tables: lookup_tables)
17
17
 
@@ -63,7 +63,7 @@ module Quby
63
63
  end
64
64
 
65
65
  def validate(key:, sourcecode:)
66
- definition = Entities::Definition.new(key: key, sourcecode: sourcecode, path: "validating '#{key}'")
66
+ definition = Entities::Definition.new(key: key, sourcecode: sourcecode, path: "validating '#{key}'", lookup_tables: lookup_tables)
67
67
  definition.valid?
68
68
  definition
69
69
  end
@@ -84,19 +84,56 @@ module Quby
84
84
  # <subscore_key:Symbol>: <subscore.label:String> # headers for each subscore key for all tables.
85
85
 
86
86
  def outcome_tables_schema
87
+ if questionnaire.outcome_tables.present?
88
+ outcome_tables_from_definition
89
+ else
90
+ outcome_tables_from_score_schemas
91
+ end
92
+ end
93
+
94
+ def outcome_tables_from_definition
95
+ # hash of tables, with the score keys (rows) and subscore keys (columns) used for each
96
+ tables = {}
97
+ # hash of `subscore_key: subscore_label` pairs used in tables
98
+ headers = {}
99
+
100
+ questionnaire.outcome_tables.each do |table|
101
+ tables[table.key] = {name: table.name,
102
+ default_collapsed: table.default_collapsed,
103
+ score_keys: table.score_keys,
104
+ subscore_keys: table.subscore_keys}.compact
105
+
106
+ table.subscore_keys.each do |subscore_key|
107
+ table.score_keys.find do |score_key|
108
+ subschema = questionnaire.score_schemas[score_key].subscore_schemas.find do |subschema|
109
+ subschema.key == subscore_key
110
+ end
111
+ headers[subscore_key] = subschema&.label
112
+ end
113
+ end
114
+ end
115
+
116
+ {
117
+ headers: headers,
118
+ tables: tables,
119
+ }
120
+ end
121
+
122
+ def outcome_tables_from_score_schemas
87
123
  # hash of tables, with the score keys (rows) and subscore keys (columns) used for each
88
124
  tables = Hash.new{ |hash, key| hash[key] = {score_keys: Set.new, subscore_keys: Set.new } }
89
125
  # hash of `subscore_key: subscore_label` pairs used in tables
90
126
  headers = {}
91
-
127
+
92
128
  questionnaire.score_schemas.values.each do |schema|
93
129
  schema.subscore_schemas.each do |subschema|
130
+ next if subschema.outcome_table.blank?
94
131
  tables[subschema.outcome_table][:subscore_keys] << subschema.key
95
132
  tables[subschema.outcome_table][:score_keys] << schema.key
96
133
  headers[subschema.key] = subschema.label
97
134
  end
98
135
  end
99
-
136
+
100
137
  {
101
138
  headers: headers,
102
139
  tables: tables,
@@ -25,12 +25,11 @@ module Quby
25
25
  validate_outcome_tables(questionnaire)
26
26
  validate_markdown_fields(questionnaire) if questionnaire.validate_html
27
27
  validate_raw_content_items(questionnaire) if questionnaire.validate_html
28
- validate_scores(questionnaire)
29
28
  # Some compilation errors are Exceptions (pure syntax errors) and some StandardErrors (NameErrors)
30
29
  rescue Exception => exception # rubocop:disable Lint/RescueException
31
30
  definition.errors.add(:sourcecode, {message: "Questionnaire error: #{definition.key}\n" \
32
31
  "#{exception.message}",
33
- backtrace: exception.backtrace[0..5].join("<br/>")})
32
+ backtrace: exception.backtrace[0..20]})
34
33
  end
35
34
 
36
35
  def validate_fields(questionnaire)
@@ -81,6 +80,13 @@ module Quby
81
80
  fail "Score #{score.key} does not have a score schema" unless score_schema
82
81
  fail "Score label langer dan 100 tekens (geeft problemen oru accare)\n #{score_schema.label}" if score_schema.label&.length > 100
83
82
  end
83
+
84
+ export_keys = questionnaire.score_schemas.flat_map { |_key, score_schema|
85
+ score_schema.subscore_schemas.map(&:export_key)
86
+ }
87
+
88
+ duplicate_export_keys = export_keys.tally.select { |key, count| count > 1 }.keys
89
+ fail "Score export keys not unique, duplicates: #{duplicate_export_keys}" if duplicate_export_keys.present?
84
90
  end
85
91
 
86
92
  def validate_question_options(questionnaire, question)
@@ -241,8 +247,6 @@ scores_schema tables to the resulting seed."
241
247
 
242
248
  def validate_score_label_present(score)
243
249
  fail "Score #{score.key} label must be passed in as an option." unless score.label.present?
244
-
245
-
246
250
  end
247
251
 
248
252
  def validate_subquestion_absence_in_select(question)
@@ -292,19 +292,19 @@ module Quby
292
292
 
293
293
  def strip_question_number_slashes(quests)
294
294
  quests.transform_values! do |value|
295
- value.gsub /^(\s*\d+)\\/, '\1'
295
+ value&.gsub /^(\s*\d+)\\/, '\1'
296
296
  end
297
297
  end
298
298
 
299
299
  def process_scores
300
300
  scores_from_schemas
301
301
  end
302
-
302
+
303
303
  def scores_from_schemas
304
304
  score_headers = [] # headers outcome (humanized name for subscores)
305
305
  score_keys = [] # headers data-export (not all of it, just the score_subscore part, shortened)
306
306
  score_labels = [] # score names outcome (humanized name for score as a whole)
307
-
307
+
308
308
  questionnaire.score_schemas.values.each do |score_schema|
309
309
  score_labels << score_schema.label
310
310
  score_keys << score_schema.subscore_schemas.map do |subschema|
@@ -318,18 +318,18 @@ module Quby
318
318
  hash
319
319
  end
320
320
  end
321
-
321
+
322
322
  headers = score_schema.subscore_schemas.map(&:label)
323
323
  score_headers += headers - score_headers
324
324
  end
325
-
325
+
326
326
  {
327
327
  headers: score_headers,
328
328
  keys: score_keys,
329
329
  labels: score_labels
330
330
  }
331
331
  end
332
-
332
+
333
333
  class ShortenKeysUniq
334
334
  def initialize
335
335
  @seen_results = []
@@ -1,5 +1,5 @@
1
1
  module Quby
2
2
  module Compiler
3
- VERSION = "0.3.1"
3
+ VERSION = "0.3.6"
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.3.1
4
+ version: 0.3.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marten Veldthuis
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-02-24 00:00:00.000000000 Z
11
+ date: 2021-07-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -271,7 +271,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
271
271
  - !ruby/object:Gem::Version
272
272
  version: '0'
273
273
  requirements: []
274
- rubygems_version: 3.1.2
274
+ rubygems_version: 3.1.4
275
275
  signing_key:
276
276
  specification_version: 4
277
277
  summary: Quby::Compiler compiles a DSL for questionnaires to JSON