#!/usr/bin/env ruby # # Command Line utilities for Jeckyl # # Author:: Robert Sharp # Copyright:: Copyright (c) 2013 Robert Sharp # License:: Open Software Licence v3.0 # # This software is licensed for use under the Open Software Licence v. 3.0 # The terms of this licence can be found at http://www.opensource.org/licenses/osl-3.0.php # and in the file copyright.txt. Under the terms of this licence, all derivative works # must themselves be licensed under the Open Software Licence v. 3.0 # # #require 'rubygems' # not required in ruby 1.9.3 + require 'optplus' require 'jeckyl' require 'jeckyl/errors' class JeckylCli < Optplus::Parser usage "action [params] [options]" description "Useful utilities for Jeckyl configuration files.", "To find out more, type 'jeckyl readme'" # before_all def options(opts) opts.on('-C', '--class Index', Integer, 'select a class by index') do |i| set_option :klass_index, i end opts.on('-k', '--concat', 'concat all classes, starting with the oldest') do set_option :konkat end end # before_actions describe "readme", "display the readme file for the gem" def readme gem_spec = Gem::Specification.find_by_name('jeckyl') readme_path = File.join(gem_spec.gem_dir, 'README.md') File.open(readme_path) do |rfile| rfile.each_line do |rline| puts rline end end end describe "list", "list all the config classes involved in a given class" def list cfile = next_argument_or_error("missing config class") require cfile classes = Jeckyl::Options.descendants display_classes(classes) rescue LoadError puts "Error: could not load #{cfile}".red.bold end help :list, "Show a list of this config class and its ancestors", "This is useful when you have create a config class from a parent class", "in order to include the parent classes' parameters.", "The list is indexed and the index can be used in other actions,", "include 'jeckyl config'." describe "config", "generate config file for the given config class" def config cfile = next_argument_or_error("You must provide a config class") require cfile # catch LoadError if cfile does not load my_class_index = get_option(:klass_index) || 0 my_class_index -= 1 if my_class_index > 0 konkat = get_option :konkat classes = Jeckyl::Options.descendants if !konkat && classes[my_class_index].nil? then $stderr.puts "Index #{my_class_index + 1} is invalid. Please select a valid index".red.bold display_classes(classes) return false end $stderr.puts "There are #{classes.length} sets of configs, use 'jeckyl list' to list them or -k to generate them all".yellow if classes.length > 1 my_classes = [] if konkat then classes.each_index {|i| my_classes.unshift i} else my_classes << my_class_index end my_classes.each do |ci| puts "#" puts "# Configuration Options for: #{classes[ci].name}" puts "#" puts classes[ci].generate_config(true) end unless konkat || classes.length == 1 puts "" puts "# There are also parameters in:" classes.each do |klass| puts "# #{klass.name}"unless klass == classes[my_class_index] end end rescue LoadError if cfile.nil? then puts "Error: you need to specify a configration file".red.bold else puts "Error: could not load #{cfile}".red.bold end end help :config, "Outputs a default config file for the given class.", "Use this to create a default file that you can then tailor for", "specific settings. If there is more than one class involved use", "-k to create an output with all the config parameters included.", "Output is to stdout and can easily be redirected to a file as needed." describe "klass", "generate a simple class template" def klass name = next_argument_or_error("missing class name") names = name.split("::") mod_name = names[0] class_name = names[1] || 'Config' parent = next_argument_or('Jeckyl::Config') puts < #{name} Parameter Descriptions # class #{class_name} < #{parent} def configure_a_parameter(val) default "default" comment "Comment line", "more comments" a_type_of(String) end end end EOTXT end help :klass, " jeckyl klass [}]", "", "Generate a simple class file as a template for defining your", "own parameters. You can also specify a class to inherit if you want as", "an additional parameter. Otherwise the parent will default to", "Jeckyl::Config. Alternatives include Jellog::Config and JerbilService::Config.", "Note the class name can be compound and will be split into module and class.", "For example: 'Module::Class'." describe "check", "check the given config file is valid for the given class" def check(klass_file, conf_file) klass_file = next_argument_or_error("missing class name") conf_file = next_argument_or_error("missing config file") require klass_file # catch LoadError if cfile does not load my_class_index = 0 # assume there is only one classes = Jeckyl::Options.descendants classes[my_class_index].check_config(conf_file) rescue LoadError puts "Error: could not load #{cfile}".red end help :check, "Use the given class (which must be requirable) and check", "that the given config file is OK or show errors if not.", "Useful way to check that a config file works before using it in earnest." describe "markdown", "display the comments from a class in markdown format" def markdown cfile = next_argument_or_error("missing class") require cfile # catch LoadError if cfile does not load my_class_index = get_option(:klass_index) || 0 my_class_index -= 1 if my_class_index > 0 konkat = get_option :konkat classes = Jeckyl::Options.descendants if !konkat && classes[my_class_index].nil? then $stderr.puts "Index #{my_class_index + 1} is invalid. Please select a valid index".red.bold display_classes(classes) return false end $stderr.puts "There are #{classes.length} sets of configs, use 'jeckyl list' to list them or -k to generate them all".yellow if classes.length > 1 my_classes = [] if konkat then classes.each_index {|i| my_classes << i} else my_classes << my_class_index end first = true sublevel = 2 my_classes.each do |ci| my_class = classes[ci] my_configurator = my_class.new(nil, :local=>true) if first then puts "# #{my_class.name} Parameters" puts "" puts "The following parameters are defined in {#{my_class.name}} and should be used" puts "in a configuration file. A default config file can be generated using:" puts "" puts " jeckyl config #{cfile}" else puts "## Additional Parameters from #{my_class.name}" puts "" puts "The following additional parameters are defined in #{my_class.name}, which" puts "is an ancestor of this config class. See separate documentation for more details." sublevel = 3 end first = false puts "" puts "#" * sublevel + " Parameters" puts "" my_configurator.comments.each do |param, comment| puts " * **#{param}**" puts " " comment.each {|line| puts " #{line}"} puts "" if my_configurator.defaults.has_key?(param) then puts " Default: #{my_configurator.defaults[param].inspect}" else puts " No default set" end puts "" end end unless konkat || classes.length == 1 puts "" puts "## See Also" puts "" puts "There are also parameters in:" puts "" classes.each do |klass| puts " * #{klass.name}"unless klass == classes[my_class_index] end end rescue LoadError puts "Error: could not load #{cfile}".red end help :markdown, "Similar to config but instead of creating the config file itself", "this creates a markdown file that can be used in rdoc to describe the config", "parameters." def display_classes(classes) $stderr.puts "The following classes are available:".green count = 1 classes.each do |klass| $stderr.puts " #{count}. #{klass.name}" count += 1 end end end JeckylCli.run!