class Courseware
Constants
- VERSION
Public Class Methods
bailout?(message, required=false) { || ... }
click to toggle source
# File lib/courseware/utils.rb, line 38 def self.bailout?(message, required=false) if required print "#{message} Continue? [y/N]: " options = ['y', 'yes'] else print "#{message} Continue? [Y/n]: " options = [ 'y', 'yes', ''] end unless options.include? STDIN.gets.strip.downcase if block_given? yield end raise "User cancelled" end end
choose(message, options, default = nil)
click to toggle source
# File lib/courseware/utils.rb, line 68 def self.choose(message, options, default = nil) body = "" options.each_with_index { |item, index| body << "\n[#{index}] #{item}" } dialog(message, body) ans = nil loop do if default print "Choose an option by typing its number [#{default}]: " ans = STDIN.gets.strip ans = (ans == "") ? default : Integer(ans) rescue nil else print "Choose an option by typing its number: " ans = Integer(STDIN.gets.strip) rescue nil end break if (0...options.size).include? ans end ans end
choose_variant()
click to toggle source
# File lib/courseware/utils.rb, line 90 def self.choose_variant variants = Dir.glob('*.json') return :none if variants.empty? return 'showoff.json' if variants == ['showoff.json'] maxlen = variants.max { |x,y| x.size <=> y.size }.size - 4 # accomodate for the extension we're stripping # Ensures that the default `showoff.json` is listed first variants.unshift "showoff.json" variants.uniq! options = variants.map do |variant| data = JSON.parse(File.read(variant)) name = (variant == 'showoff.json') ? 'default' : File.basename(variant, '.json') desc = data['description'] "%#{maxlen}s: %s" % [name, desc] end idx = Courseware.choose("This course has several variants available:", options, 0) variants[idx] end
confirm(message, default=true)
click to toggle source
# File lib/courseware/utils.rb, line 25 def self.confirm(message, default=true) return default unless STDIN.tty? if default print "#{message} [Y/n]: " answers = [ 'y', 'yes', '' ] else print "#{message} [N/y]: " answers = [ 'y', 'yes' ] end answers.include? STDIN.gets.strip.downcase end
dialog(header, body=nil, width=80)
click to toggle source
# File lib/courseware/utils.rb, line 54 def self.dialog(header, body=nil, width=80) width -= 6 puts '################################################################################' puts "## #{header[0..width].center(width)} ##" if body puts '##----------------------------------------------------------------------------##' body.wrap(width).split("\n").each do |line| printf "## %-#{width}s ##\n", line end end puts '################################################################################' end
get_component(initial)
click to toggle source
# File lib/courseware/utils.rb, line 119 def self.get_component(initial) puts 'The component ID for this course can be found at:' puts ' * https://tickets.puppetlabs.com/browse/COURSES/?selectedTab=com.atlassian.jira.jira-projects-plugin:components-panel' puts # grab the number ending the response--either the ID from the URL or the whole string question('Please enter the component ID or copy & paste in its URL:', initial)[/(\d*)$/] end
grep(match, filename)
click to toggle source
# File lib/courseware/utils.rb, line 127 def self.grep(match, filename) File.read(filename) =~ Regexp.new(match) end
help()
click to toggle source
# File lib/courseware/help.rb, line 2 def self.help IO.popen("less", "w") do |less| less.puts <<-EOF.gsub(/^ {6}/, '') Courseware Manager SYNOPSIS courseware [-c CONFIG] [-d] <verb> [subject] [subject] ... DESCRIPTION Manage the development lifecycle of Puppet courseware. This tool is not required for presenting the material or for contributing minor updates. The following verbs are recognized: * print Render course material as PDF files. This verb accepts one or more of the following arguments, where the default is all. Arguments (optional): handouts: Generate handout notes exercises: Generate the lab exercise manual solutions: Generate the solution guide guide: Generate the instructor guide * watermark Render watermarked PDF files. Accepts same arguements as the `print` verb. * generate or update Build new or update certain kinds of configuration. By default, this will update the stylesheet. Arguments (optional): skeleton <name>: Build a new course directory named <name> and generate required metadata for a Showoff presentation. config: Write current configuration to a `courseware.yaml` file. styles: Generate or update the stylesheet for the current version. links: Ensure that all required symlinks are in place. metadata: Interactively generate or update the `showoff.json` file. * validate Runs validation checks on the presentation. Defaults to running all the checks. Validators: obsolete: Lists all unreferenced images and slides. This reference checks all slides and all CSS stylesheets. This validation is case sensitive and should be run from the toplevel courseware root directory. missing: Lists all slides and images that are missing. This validation is case sensitive and should be run from within an individual course directory. lint: Runs a markdown linter on each slide file, using our own style definition. This should be run within a course directory. * release [type] Orchestrate a courseware release. Defaults to `point`. All instructors are expected to deliver the most current point release, except in extraordinary cases. We follow Semver, as closely as it can be adapted to classroom usage. Instructors can trust that updates with high potential to cause classroom disruptions will never make it into a point release. http://semver.org Release types: major: This is a major release with "breaking" changes, such as a major product update, or significant classroom workflow changes. This is not necessarily tied to product releases. Instructors should expect to spend significant time studying the new material thoroughly. minor: This indicates a significant change in content. Instructors should take extra time to review updates in minor releases. The release cadence is roughly once a quarter, give or take. point: Release early and release often. Changes made in the regular maintenance cycle will typically fit into this category. notes: Display release notes since last release and copy to clipboard. * wordcount [type] Display a simple wordcount of one or more content types. Arguments (optional): handouts: Counts words in the student handout guide exercises: Counts words in the lab exercise manual solutions: Counts words in the solution guide * compose [comma,separated,list,of,tags] Generates a variant of the complete course, using tags defined in `showoff.json`. The practical effect of this action is to generate a new presentation `.json` file, which can be displayed directly by passing the `-f` flag to Showoff, or by choosing a variant in the classroom `rake present` task. * package [variant.json] Package up a standalone form of a given variant of the presentation. You can pass in a `variant.json` file, or choose from a menu. Tarballs will be saved into the `build` directory and you can optionally retain the full working directory. * help You're looking at it. EOF end end
increment(version, type=:point)
click to toggle source
# File lib/courseware/utils.rb, line 131 def self.increment(version, type=:point) major, minor, point = version.split('.') case type when :major major.sub!(/^v/, '') # chop off the v if needed "v#{major.to_i + 1}.0.0" when :minor "#{major}.#{minor.to_i + 1}.0" when :point "#{major}.#{minor}.#{point.to_i + 1}" end end
new(config, configfile)
click to toggle source
# File lib/courseware.rb, line 10 def initialize(config, configfile) @config = config @configfile = configfile @cache = Courseware::Cache.new(config) @generator = Courseware::Generator.new(config) @composer = Courseware::Composer.new(config) if Courseware::Repository.repository? @repository = Courseware::Repository.new(config) @manager = Courseware::Manager.new(config, @repository) else require 'courseware/dummy' @repository = Courseware::Dummy.new(config) @manager = Courseware::Dummy.new(config) $logger.debug "Running outside a valid git repository." end end
parse_showoff(filename)
click to toggle source
TODO: we could use some validation here
# File lib/courseware/utils.rb, line 115 def self.parse_showoff(filename) JSON.parse(File.read(filename)) end
question(message, default=nil, required=false)
click to toggle source
# File lib/courseware/utils.rb, line 5 def self.question(message, default=nil, required=false) unless STDIN.tty? raise "The question '#{message}' requires an answer and cannot be run non-interactively." if required return default end loop do if default print "#{message} [#{default}] " else print "#{message} " end answer = STDIN.gets.strip next if required and answer.empty? return answer.empty? ? default : answer end end
Public Instance Methods
compose(subject)
click to toggle source
# File lib/courseware.rb, line 151 def compose(subject) @composer.build(subject) end
debug()
click to toggle source
# File lib/courseware.rb, line 159 def debug require 'pry' binding.pry end
generate(subject)
click to toggle source
# File lib/courseware.rb, line 75 def generate(subject) $logger.debug "Generating #{subject}" if subject.first == :skeleton subject.shift subject.each do |course| @generator.skeleton course.to_s end else subject.each do |item| case item when :config @generator.saveconfig @configfile when :styles course = @manager.coursename prefix = @manager.prefix @generator.styles(course, @repository.current(prefix)) when :links @generator.links when :metadata @generator.metadata when :rakefile @generator.rakefile when :shared @generator.shared else $logger.error "I don't know how to generate #{item}!" end end end end
options(opts)
click to toggle source
# File lib/courseware.rb, line 28 def options(opts) raise ArgumentError, "One or two arguments expected, not #{opts.inspect}" unless opts.size.between?(1,2) if opts.include? :section section = opts[:section] setting, value = opts.reject {|key, value| key == :section }.first @config[section][setting] = value else setting, value = opts.first @config[setting] = value end end
package(subject)
click to toggle source
# File lib/courseware.rb, line 155 def package(subject) @composer.package(subject) end
print(subject)
click to toggle source
# File lib/courseware.rb, line 40 def print(subject) $logger.debug "Printing #{subject}" #TODO: This should not be duplicated! opts = { :course => @manager.coursename, :prefix => @manager.prefix, :version => @repository.current(@manager.prefix), } Courseware::Printer.new(@config, opts) do |printer| subject.each do |item| case item when :handouts printer.handouts when :exercises printer.exercises when :solutions printer.solutions when :guide printer.guide else $logger.error "The #{item} document type does not exist." end end end end
release(subject)
click to toggle source
# File lib/courseware.rb, line 135 def release(subject) case subject when :major, :minor, :point $logger.debug "Creating a #{subject} release." @manager.release subject when :notes $logger.debug "Generating release notes." @manager.releasenotes else $logger.error "I don't know how to do that yet!" end end
validate(subject)
click to toggle source
# File lib/courseware.rb, line 113 def validate(subject) $logger.debug "Validating #{subject}" subject.each do |item| case item when :obsolete @manager.obsolete when :missing @manager.missing when :lint @manager.lint else $logger.error "I don't know how to do that yet!" end end $logger.warn "Found #{@manager.errors} errors and #{@manager.warnings} warnings." exit @manager.errors + @manager.warnings end
wordcount(subject)
click to toggle source
# File lib/courseware.rb, line 71 def wordcount(subject) @manager.wordcount(subject) end