Class: Courseware
- Inherits:
-
Object
- Object
- Courseware
- Defined in:
- lib/courseware.rb,
lib/courseware/help.rb,
lib/courseware/utils.rb,
lib/courseware/version.rb
Defined Under Namespace
Classes: Cache, Composer, Dummy, Generator, Manager, Printer, Repository
Constant Summary collapse
- VERSION =
'0.5.4'
Class Method Summary collapse
- .bailout?(message) ⇒ Boolean
- .choose(message, options, default = nil) ⇒ Object
- .choose_variant ⇒ Object
- .confirm(message, default = true) ⇒ Object
- .dialog(header, body = nil, width = 80) ⇒ Object
- .get_component(initial) ⇒ Object
- .grep(match, filename) ⇒ Object
- .help ⇒ Object
- .increment(version, type = :point) ⇒ Object
-
.parse_showoff(filename) ⇒ Object
TODO: I’m not happy with this being here, but I don’t see a better place for it just now.
- .question(message, default = nil, required = false) ⇒ Object
Instance Method Summary collapse
- #compose(subject) ⇒ Object
- #debug ⇒ Object
- #generate(subject) ⇒ Object
-
#initialize(config, configfile) ⇒ Courseware
constructor
A new instance of Courseware.
- #options(opts) ⇒ Object
- #package(subject) ⇒ Object
- #print(subject) ⇒ Object
- #release(subject) ⇒ Object
- #validate(subject) ⇒ Object
- #wordcount(subject) ⇒ Object
Constructor Details
#initialize(config, configfile) ⇒ Courseware
Returns a new instance of Courseware.
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# 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 |
Class Method Details
.bailout?(message) ⇒ Boolean
38 39 40 41 42 43 44 45 46 |
# File 'lib/courseware/utils.rb', line 38 def self.bailout?() print "#{} Continue? [Y/n]: " unless [ 'y', 'yes', '' ].include? STDIN.gets.strip.downcase if block_given? yield end raise "User cancelled" end end |
.choose(message, options, default = nil) ⇒ Object
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/courseware/utils.rb', line 62 def self.choose(, , default = nil) body = "" .each_with_index { |item, index| body << "\n[#{index}] #{item}" } dialog(, 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....size).include? ans end ans end |
.choose_variant ⇒ Object
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/courseware/utils.rb', line 84 def self.choose_variant variants = Dir.glob('*.json') maxlen = variants.max { |x,y| x.size <=> y.size }.size - 4 # accomodate for the extension we're stripping return 'showoff.json' if variants == ['showoff.json'] # Ensures that the default `showoff.json` is listed first variants.unshift "showoff.json" variants.uniq! = 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:", , 0) variants[idx] end |
.confirm(message, default = true) ⇒ Object
25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/courseware/utils.rb', line 25 def self.confirm(, default=true) return default unless STDIN.tty? if default print "#{} [Y/n]: " answers = [ 'y', 'yes', '' ] else print "#{} [N/y]: " answers = [ 'y', 'yes' ] end answers.include? STDIN.gets.strip.downcase end |
.dialog(header, body = nil, width = 80) ⇒ Object
48 49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/courseware/utils.rb', line 48 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) ⇒ Object
133 134 135 136 137 138 139 |
# File 'lib/courseware/utils.rb', line 133 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) ⇒ Object
141 142 143 |
# File 'lib/courseware/utils.rb', line 141 def self.grep(match, filename) File.read(filename) =~ Regexp.new(match) end |
.help ⇒ Object
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# 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. Case sensitive. Note: This validator will validate all course variants. missing: Lists all slides that are missing. Note that this does not check for missing image files yet. Case sensitive. lint: Runs a markdown linter on each slide file, using our own style definition. * 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) ⇒ Object
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/courseware/utils.rb', line 145 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 |
.parse_showoff(filename) ⇒ Object
TODO: I’m not happy with this being here, but I don’t see a better place for it just now
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/courseware/utils.rb', line 107 def self.parse_showoff(filename) showoff = JSON.parse(File.read(filename)) sections = showoff['sections'].map do |entry, section| next entry if entry.is_a? String and section.nil? if entry.is_a? Hash file = entry['include'] else file = section end unless file puts "Malformed entry: #{entry.inspect} - #{section.inspect}" next nil end path = File.dirname(file) data = JSON.parse(File.read(file)) data.map { |source| "#{path}/#{source}" } end.flatten.compact showoff['sections'] = sections return showoff end |
.question(message, default = nil, required = false) ⇒ Object
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# File 'lib/courseware/utils.rb', line 5 def self.question(, default=nil, required=false) unless STDIN.tty? raise "The question '#{}' requires an answer and cannot be run non-interactively." if required return default end loop do if default print "#{} [#{default}] " else print "#{} " end answer = STDIN.gets.strip next if required and answer.empty? return answer.empty? ? default : answer end end |
Instance Method Details
#compose(subject) ⇒ Object
151 152 153 |
# File 'lib/courseware.rb', line 151 def compose(subject) @composer.build(subject) end |
#debug ⇒ Object
159 160 161 162 |
# File 'lib/courseware.rb', line 159 def debug require 'pry' binding.pry end |
#generate(subject) ⇒ Object
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# 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. 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) ⇒ Object
28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/courseware.rb', line 28 def (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) ⇒ Object
155 156 157 |
# File 'lib/courseware.rb', line 155 def package(subject) @composer.package(subject) end |
#print(subject) ⇒ Object
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# 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) ⇒ Object
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
# 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) ⇒ Object
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# 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) ⇒ Object
71 72 73 |
# File 'lib/courseware.rb', line 71 def wordcount(subject) @manager.wordcount(subject) end |