ruql 0.1.5 → 1.0.5

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
- SHA1:
3
- metadata.gz: 1a384710404460b95c945d6a1fb850c88531db8a
4
- data.tar.gz: 5c6af8b878133a20c3f7b0e4e38a184b4b0b7400
2
+ SHA256:
3
+ metadata.gz: ff79fa8465db54b4e2ce0e9ee62c891649b2066a5093c76747f7a3693cc8321e
4
+ data.tar.gz: 43f22ab810aebe22665a985d25db33a8a8d46cba38ff1f0d4cd6145de4bf0fce
5
5
  SHA512:
6
- metadata.gz: feb04ca43e5e83c6e6a97f042ab03b3692663a23f835783121edd68bc3ebba1b3ffe728ab682edf053055ef64222625cf8f056a93112ebc287314f475edb0f4a
7
- data.tar.gz: 0414a7abbd51a50703b290c2a912575b6fbaead7330cfbcede247950dd67e0e4d84a6f6e6d35120d26e8fdbcb37a6b103c75d512211e16b9db8eb2fcdc2b5736
6
+ metadata.gz: 7f2ec53ca0098f8c7fe2bf43557935351b73313469ce3610517eb51122bdecb5353509dedd84a05119b7f422c85510b47cc2fb062b17a34a51bd15e077deb4a3
7
+ data.tar.gz: 78913cb4c7913a01748cd9f6966bf0fbfed7886bc5c94e81a95bac7ec893f0e5469c2621a7f00124f18f57fd45e64fe8541f6e8d76a16316a75e7ff52fe88411
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+ .byebug_history
13
+ Gemfile.lock
14
+ TAGS
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.6.3
7
+ before_install: gem install bundler -v 1.17.3
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in ruql.gemspec
6
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,6 @@
1
+ Copyright (c) 2012 Strawberry Canyon LLC.
2
+
3
+ Contents of this repository are governed by the Creative Commons
4
+ Attribution - Noncommercial - ShareAlike 3.0 Unported license:
5
+
6
+ http://creativecommons.org/licenses/by-nc-sa/3.0/
@@ -0,0 +1,318 @@
1
+ [![CodeClimate](https://codeclimate.com/github/saasbook/ruql/badges/gpa.svg)](https://codeclimate.com/github/saasbook/ruql)
2
+ [![Coverage](https://codeclimate.com/github/saasbook/ruql/badges/coverage.svg)](https://codeclimate.com/github/saasbook/ruql/coverage)
3
+ [![Gem Version](https://badge.fury.io/rb/ruql.svg)](https://badge.fury.io/rb/ruql)
4
+
5
+ Ruby-based DSL for Authoring Quiz Questions
6
+ ===========================================
7
+
8
+ This is a simple gem that takes a set of questions (a "quiz") written in
9
+ RuQL ("Ruby quiz language" or "Ruby question language" - a DSL embedded
10
+ in Ruby), and produces one of several possible output formats.
11
+
12
+ Some types of questions or question elements that can be expressed in
13
+ RuQL cannot be expressed by some LMSs, and some LMS-specific question
14
+ features cannot be expressed in RuQL.
15
+
16
+ [Registered ESaaS instructors](http://www.saasbook.info/instructors) can access
17
+ [this repo](/saasbook/csw169a-quizzes) of questions, grouped by
18
+ chapter of the [ESaaS textbook](http://www.saasbook.info).
19
+
20
+
21
+ Installation
22
+ ============
23
+
24
+ `gem install ruql` to install this from RubyGems. It works with Ruby 1.9 or later.
25
+
26
+ You'll also need to install one or more formatters to produce quiz output.
27
+
28
+ Installation: Current Formatters
29
+ ================================
30
+
31
+ * [ruql-html](/saasbook/ruql-html): produces HTML 5 output using a default or user-supplied HTML
32
+ template
33
+ * [ruql-canvas](/saasbook/ruql-canvas): creates a quiz in Canvas LMS using its REST API
34
+ * [ruql-olx](/saasbook/ruql-olx): creates a quiz in edX's Open Learning XML
35
+
36
+
37
+ Running RuQL
38
+ ============
39
+
40
+ Create your `quiz.rb` file as described below, then run:
41
+
42
+ `ruql formatter-name quiz.rb`
43
+
44
+ Where `formatter-name` is from the list above, eg `html` for the
45
+ `ruql-html` formatter.
46
+
47
+ `ruql formatter-name -h` lists the options supported by that
48
+ formatter, and `ruql -h` shows RuQL's global options.
49
+
50
+
51
+ Creating Quiz Questions in RuQL
52
+ ===============================
53
+
54
+ RuQL supports a few different types of short-answer questions and can
55
+ output them in a variety of formats compatible with different Learning
56
+ Management Systems or in printable form.
57
+
58
+ RuQL is a DSL embedded in Ruby, so you can include expressions in
59
+ questions, for example, to generate simple variants of a question along
60
+ with its solution.
61
+
62
+ In addition to the basic parts of a question (prompt text, answers,
63
+ etc.), each question can also have an optional list of one or more
64
+ _tags_, and an optional _comment_. These attributes
65
+ are available to formatters, but most formatters ignore them.
66
+ They can be used to provide information about the
67
+ question's topic(s) or other info useful to a question-management app
68
+ such as [Course Question Bank](https://github.com/saasbook/coursequestionbank).
69
+
70
+ A question can also optionally have a _group_. Questions with the
71
+ same group name (within the scope of a single quiz) are grouped in
72
+ a "pool" from which a single question is selected at quiz generation time.
73
+ For "static" formatters such as HTML, RuQL will pick a random
74
+ question. For some LMSs, like Canvas, the questions are put into a
75
+ "quiz group" and any given student gets a randomly chosen one at runtime.
76
+
77
+ Preparing a quiz
78
+ ----------------
79
+
80
+ A quiz is an `.rb` file with a collection of questions:
81
+
82
+ quiz 'Example quiz name here' do
83
+ # (questions here)
84
+ end
85
+
86
+ You create a quiz by putting the quiz in its own file and
87
+ copying-and-pasting the questions you want into it. (Yes, that's ugly.
88
+ Soon, questions will have unique IDs and you'll be able to create a quiz
89
+ by reference.)
90
+
91
+ Multiple-choice questions with a single correct answer
92
+ ------------------------------------------------------
93
+
94
+ You can provide a generic `explanation` clause, and/or override it with
95
+ specific explanations to accompany right or wrong answers.
96
+ Choices are rendered in the order in which
97
+ they appear in the RuQL markup, but capable LMSs or formatters can be
98
+ told to randomize them. This example also shows the use of tags to
99
+ include metadata about a question's topic, and the use of groups to
100
+ place two questions into a group from which one will be chosen at
101
+ random for the quiz.
102
+
103
+ ```ruby
104
+ choice_answer do
105
+ tags 'US states', 'geography'
106
+ group 'states-1'
107
+ text "What is the largest US state?"
108
+ explanation "Not big enough." # for distractors without their own explanation
109
+ answer 'Alaska'
110
+ distractor 'Hawaii'
111
+ distractor 'Texas', :explanation => "That's pretty big, but think colder."
112
+ end
113
+ choice_answer do
114
+ tags 'US cities', 'geography'
115
+ group 'states-1'
116
+ text "What is the largest US city?"
117
+ answer 'New York'
118
+ distractor 'Los Angeles'
119
+ distractor 'Chicago'
120
+ end
121
+ ```
122
+
123
+ Specifying `:raw => true` allows HTML markup in the question to be
124
+ passed through unescaped, such as for `<pre>` or `<code>` blocks.
125
+
126
+
127
+ ```ruby
128
+ choice_answer :raw => true do
129
+ text %Q{What does the following code do:
130
+ <pre>
131
+ puts "Hello world!"
132
+ </pre>
133
+ }
134
+ distractor 'Throws an exception', :explanation => "Don't be an idiot."
135
+ answer 'Prints a friendly message'
136
+ end
137
+ ```
138
+
139
+ Multiple-choice "select all that apply" questions
140
+ -------------------------------------------------
141
+
142
+ These use the same syntax as single-choice questions, but multiple
143
+ `answer` clauses are allowed:
144
+
145
+ ```ruby
146
+ select_multiple do
147
+ text "Which are American political parties?"
148
+ answer "Democrats"
149
+ answer "Republicans"
150
+ answer "Greens", :explanation => "Yes, they're a party!"
151
+ distractor "Tories", :explanation => "They're British"
152
+ distractor "Social Democrats"
153
+ end
154
+ ```
155
+
156
+ True or false questions
157
+ -----------------------
158
+
159
+ Internally, true/false questions are treated as a special case of
160
+ multiple-choice questions with a single correct answer, but there's a
161
+ shortcut syntax for them.
162
+
163
+ ```ruby
164
+ truefalse 'The week has 7 days.', true
165
+ truefalse 'The earth is flat.', false, :explanation => 'No, just looks that way'
166
+ ```
167
+
168
+ Short-answer fill-in-the-blanks questions
169
+ -----------------------------------------
170
+
171
+ Put three or more hyphens in a row where you want the "blanks" to be,
172
+ and provide a string or regexp to check the answer; all regexps are
173
+ case-INSENSITIVE unless :case_sensitive => true is passed.
174
+
175
+ ```ruby
176
+ fill_in :points => 2 do
177
+ text 'The capital of California is ---.'
178
+ answer 'sacramento'
179
+ end
180
+ ```
181
+
182
+ Optional distractors can capture common incorrect answers. As with all
183
+ question types, an optional `:explanation` can accompany a correct
184
+ answer or a distractor; its usage varies with the LMS, but a typical use
185
+ is to display a hint if the wrong answer is given, or to display
186
+ explanatory text accompanying the correct answer.
187
+
188
+ ```ruby
189
+ fill_in do
190
+ text 'The visionary founder of Apple is ---'
191
+ answer /^ste(ve|phen)\s+jobs$/
192
+ distractor /^steve\s+wozniak/, :explanation => 'Almost, but not quite.'
193
+ end
194
+ ```
195
+
196
+ You can have multiple blanks per question and pass an array of regexps
197
+ or strings to check them. Passing `:order => false` means that the
198
+ order in which blanks are filled doesn't matter. The number of elements
199
+ in the array must exactly match the number of blanks.
200
+
201
+ ```ruby
202
+ fill_in do
203
+ text 'The --- brown fox jumped over the lazy ---'
204
+ answer [/fox/, /dog/], :explanation => 'This sentence contains all of the letters of the English Alphabet'
205
+ end
206
+
207
+ fill_in do
208
+ text 'The three stooges are ---, ---, and ---.'
209
+ answer %w(larry moe curly), :order => false
210
+ end
211
+ ```
212
+
213
+
214
+
215
+ Questions with one or more dropdown-menu choices
216
+ ------------------------------------------------
217
+
218
+ A question can consist of one or more dropdown menus and interstitial text (which is rendered verbatim without newlines,
219
+ so use `:raw => true` to embed `<br>` tags if you want breaks). A choice selector needs
220
+ two arguments: the 0-based index of the correct choice, and an array of strings of all the choices.
221
+ The `label` method provides interstitial text that separates the dropdowns.
222
+
223
+ ```ruby
224
+ dropdown do
225
+ text "Arguably the world's first theme park was"
226
+ choice 0, ['Disneyland', 'Mickey World', 'Teenage Mutant Ninja Turtles Park']
227
+ label "located in"
228
+ choice 2, ['Beijing, China', 'Paris, France', 'California, USA']
229
+ end
230
+ ```
231
+
232
+
233
+ Additional arguments and options
234
+ --------------------------------
235
+
236
+ The following arguments and options have different behavior (or no
237
+ effect on behavior) depending on what format questions are emitted in:
238
+
239
+ 1. All question types accept a `:name => 'something'` argument, which some
240
+ output generators use to create a displayable name for the question or
241
+ to identify it within a group of questions.
242
+
243
+ 2. The optional `tag` clause is followed by a string or array of strings,
244
+ and associates the given tag(s) with the question, in anticipation of
245
+ future tools that can use this information.
246
+
247
+ 3. The optional `comment` clause is followed by a string and allows a
248
+ free-text comment to be added to a question.
249
+
250
+ 4. The optional `group` clause, which takes a single string, names
251
+ a "pool" of questions of which this question is a part. Some formatters
252
+ such as the Canvas importer will turn this into a "quiz question group"
253
+ so that one question from the pool will be randomly chosen to show the
254
+ student. See each formatter's documentation for how/whether it uses this property.
255
+
256
+
257
+ Adding your own formatter
258
+ ========================
259
+
260
+ **This documentation is incomplete**
261
+
262
+ If you're creating the `foobar` formatter, the gem should
263
+ be named `ruql-foobar` and have the following directory
264
+ structure (heavily recommend using Bundler):
265
+
266
+ ```
267
+ ruql-foobar:
268
+ .
269
+ ├── CODE_OF_CONDUCT.md
270
+ ├── Gemfile
271
+ ├── README.md
272
+ ├── Rakefile
273
+ ├── bin
274
+ │   ├── console
275
+ │   └── setup
276
+ ├── lib
277
+ │   └── ruql
278
+ │   ├── foobar
279
+ │   │   ├── foobar.rb
280
+ │   │   └── version.rb
281
+ │   └── foobar.rb
282
+ ├── result.json
283
+ ├── ruql-foobar.gemspec
284
+ ├── spec
285
+ │   ├── ruql
286
+ │   │   └── foobar_spec.rb
287
+ │   └── spec_helper.rb
288
+ └── templates
289
+ └── quiz.json
290
+
291
+
292
+ ```
293
+
294
+ The main entry point should be in
295
+ `ruql-foobar/lib/ruql/foobar/foobar.rb` and include the following at a
296
+ minimum:
297
+
298
+ ```ruby
299
+ module Ruql
300
+ class Foobar
301
+ def initialize(quiz, options={})
302
+ # initialize yourself, given a Quiz object and command-line
303
+ # options in Getoptlong format
304
+ end
305
+ def self.allowed_options
306
+ opts = [['--foobar-option1', Getoptlong::REQUIRED_ARGUMENT],
307
+ ['--foobar-option2', Getoptlong::BOOLEAN_ARGUMENT]]
308
+ help_text = "Some text describing what the options do"
309
+ return [help_text, opts]
310
+ end
311
+ def render_quiz
312
+ # render your quiz however you like. See the descriptions of
313
+ # the Quiz class and various subclasses of Question and Answer.
314
+ end
315
+ end
316
+ end
317
+ ```
318
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "ruql"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/ruql CHANGED
@@ -1,121 +1,97 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- # require 'ruql'
3
+ require 'rubygems'
4
4
  require_relative '../lib/ruql'
5
- require 'getopt/long'
6
-
7
- def usage
8
- name = File.basename $0
9
- STDERR.puts <<eos
10
- Usage: #{name} filename.rb renderer [options]
11
- filename.rb contains questions expressed in RuQL
12
-
13
- renderer choices are:
14
- Html5 - HTML 5 suitable for Web display/printing
15
- HtmlForm - HTML forms
16
- EdXml - XML for OpenEdX platform in-course questions
17
- AutoQCM - LaTeX for use with AutoQCM (http://home.gna.org/auto-qcm)
18
- JSON - [partially implemented] JSON format
19
- Qualtrics -[partially implemented] Qualtrics survey (txt) format
5
+ require 'getoptlong'
20
6
 
7
+ module Ruql
8
+ class Runner
9
+
10
+ def initialize
11
+ @renderer = nil
12
+ @options = {}
13
+ @filename = nil
14
+ @template = nil
15
+ @solutions = nil
16
+ @help = ''
17
+ @additional_options = [[]]
18
+ end
19
+
20
+ def version
21
+ puts Ruql::VERSION
22
+ exit true
23
+ end
24
+
25
+ def load_renderer
26
+ renderer = ARGV.shift
27
+ @help = ''
28
+ @additional_options = []
29
+ if renderer == 'stats'
30
+ @renderer = Ruql::Stats
31
+ elsif renderer == 'json'
32
+ @renderer = Ruql::Json
33
+ else
34
+ begin
35
+ require "ruql/#{renderer}"
36
+ @renderer = Object.const_get("Ruql::" + renderer.gsub(/(?:_|^)(\w)/){$1.upcase})
37
+ @help,@additional_options = @renderer.allowed_options
38
+ rescue LoadError => e
39
+ raise Ruql::OptionsError.new("Formatter '#{renderer}' requires the gem ruql-#{renderer} to be installed")
40
+ end
41
+ end
42
+ end
43
+
44
+ def run
45
+ usage if ARGV.length < 1 || ARGV[0] =~ /^--?[Hh]/
46
+ version if ARGV[0] =~ /^-v/
47
+ load_renderer
48
+
49
+ options = [
50
+ ['-h', '--help', GetoptLong::NO_ARGUMENT],
51
+ ['-S', '--solutions', GetoptLong::NO_ARGUMENT],
52
+ ['-V', '--verbose', GetoptLong::NO_ARGUMENT]
53
+ ]
54
+ opts = GetoptLong.new(*(options + @additional_options))
55
+
56
+ opts.each do |opt,arg|
57
+ usage if (opt == '--help' || opt == '-H')
58
+ # anything else gets passed to renderer
59
+ @options[opt] = arg
60
+ end
61
+
62
+ @filename = ARGV.pop || usage
63
+ raise "Cannot read #{@filename}" unless File.readable? @filename
64
+
65
+ ## Quiz.set_yaml_file opts.delete("y") || opts.delete("yaml")
66
+ Quiz.set_options(@options)
67
+ Quiz.instance_eval "#{IO.read(@filename)}"
68
+ Quiz.quizzes.each { |quiz| puts quiz.render_with(@renderer, @options) }
69
+ end
70
+
71
+ def usage
72
+ name = File.basename $0
73
+ STDERR.puts <<eos
74
+ Usage: #{name} <formatter> [options] filename.rb
75
+
76
+ filename.rb contains questions expressed in RuQL. Formatters are packaged as separate gems
77
+ named ruql-*, for example, formatter 'html' is provided by the gem ruql-html, which must
78
+ be installed. See #{Gem.loaded_specs['ruql'].homepage} for available formatters or to add your own.
79
+
80
+ The special formatter 'stats' will just show stats for the input file without generating output.
21
81
  Global options:
22
- -r <file>, --report=<file>
23
- Write to <file> a 4-line report of #points, #questions, and first and last
24
- question#. Especially useful in conjunction with the -a/--start option.
25
- -a <first>, --start=<first>
26
- Sets the starting ordinal value (1=first) for question numbering. Default 1.
27
- If a filename is given, looks for a line of the form "last NN" and
28
- starts at the number following NN.
29
- -l <loglevel>, --log=<loglevel>
30
- In increasing verbosity, they are 'error' (nonfatal), 'warn', 'info',
31
- 'debug'; default is 'warn'
32
- -p <points>, --points-threshold <points>
33
- If a question's point value is more than <points> (default: 0), include
34
- the string "[N points]" at the beginning of the question text. Make this a
35
- large number to avoid any points info being printed.
36
- -P <points-string>, --points-string <points-string>
37
- String for formatting the points description prepended to the question text,
38
- with %d as a placeholder for the point value and %s as a placeholder for
39
- pluralizing the word 'points' or whatever. Default is "[%d point%s]".
40
- -R, --no-randomize
41
- Present the answer choices in the order they appear in the RuQL file(s),
42
- even for questions that have :randomize => true.
43
-
44
- The EdXML renderer supports these options:
45
- -n <name>, --name=<name>
46
- Only render the question(s) that have :name => 'name'.
47
- NOTE: Some markup that is legal in RuQL questions will break the EdX parser.
48
- Manually check your questions as you enter them into EdX. Code seems to
49
- be particularly troublesome.
50
- NOTE: The 'points' and 'randomize' attributes of questions are not honored by
51
- some systems.
52
-
53
- -y <file.yml>, --yaml=<file.yml>
54
- Render open-assessment questions using info in given Yaml file.
55
-
56
- The HTML5 and HTML Forms renderers supports these options:
57
- -o <type>, --list-type=<type>
58
- Sets the type of HTML list to use for answer choices: 'u' for unordered
59
- (<ul>) or 'o' for ordered (<ol>). Default is 'o'.
60
- -j <src>, --js=<src>
61
- embed <src> for JavaScript
62
- -t <file.html.erb>, --template=<file.html.erb>
63
- Use file.html.erb as HTML template rather than generating our own file.
64
- file.html.erb should have <%= yield %> where questions should go.
65
- The default template in the templates/ directory of RuQL will be used
66
- if this option is not given.
67
- The following local variables will be replaced with their values in
68
- the template:
69
- <%= quiz.title %> - the quiz title
70
- <%= quiz.num_questions %> - total number of questions
71
- <%= quiz.points %> - total number of points for whole quiz
72
- -s, --solutions
73
- generate solutions (showing correct answers and explanations)
74
- NOTE: If there is more than one quiz (collection of questions) in the file,
75
- a complete <html>...</html> block is produced in the output for EACH quiz.
76
-
77
- The AutoQCM renderer supports these options:
78
- -t <file.tex.erb>, --template=<file.tex.erb>
79
- MANDATORY: Use file.tex.erb as LaTeX/AutoQCM template.
80
- The file should have <%= yield %> where questions should go.
81
- See the description of template under HTML5 renderer for variable
82
- substitutions that can occur in the quiz body.
83
-
84
- The JSON renderer currently supports no options
85
-
86
- The Qualtrics renderer supports these options:
87
- -t <file.txt.erb>, --template=<file.txt.erb>
88
- The file should have <%= yield %> where questions should go. Since this just creates survey questions, grading information is ignored. Currently supports the same type of questions as the AutoQCM renderer.
82
+ -S, --solutions
83
+ Generate a version of the output with solutions included (not supported by all formatters)
84
+ -V, --verbose
85
+ Show verbose output for debugging
86
+
87
+ #{@help}
89
88
 
90
89
  eos
91
- exit
92
- end
93
-
94
- def main
95
- filename = ARGV.shift
96
- raise "Cannot read #{filename}" unless File.readable? filename
97
- renderer = ARGV.shift
98
- raise "Unknown renderer '#{renderer}'" unless Quiz.get_renderer(renderer)
99
-
100
- opts = Getopt::Long.getopts(
101
- ['-v', '--version', Getopt::BOOLEAN],
102
- ['-o', '--list-type', Getopt::REQUIRED],
103
- ['-r', '--report', Getopt::REQUIRED],
104
- ['-a', '--start', Getopt::REQUIRED],
105
- ['-c', '--css', Getopt::REQUIRED],
106
- ['-j', '--js', Getopt::REQUIRED],
107
- ['-t', '--template', Getopt::REQUIRED],
108
- ['-s', '--solutions', Getopt::BOOLEAN],
109
- ['-n', '--name', Getopt::REQUIRED],
110
- ['-l', '--log', Getopt::REQUIRED],
111
- ['-P', '--points-string', Getopt::REQUIRED],
112
- ['-p', '--points-threshold', Getopt::REQUIRED],
113
- ['-R', '--no-randomize', Getopt::BOOLEAN],
114
- ['-y', '--yaml', Getopt::REQUIRED]
115
- )
116
- Quiz.instance_eval "#{IO.read(filename)}"
117
- Quiz.quizzes.each { |quiz| puts quiz.render_with(renderer, opts) }
118
- end
119
-
120
- usage if ARGV.length < 2
121
- main
90
+ exit false
91
+ end
92
+ end
93
+ end
94
+
95
+ Ruql::Runner.new.run
96
+
97
+