diff --git a/spec/cli_generator_spec.cr b/spec/cli_generator_spec.cr deleted file mode 100644 index a8a50d0..0000000 --- a/spec/cli_generator_spec.cr +++ /dev/null @@ -1,9 +0,0 @@ -require "./spec_helper" - -describe CliGenerator do - # TODO: Write tests - - it "works" do - false.should eq(true) - end -end diff --git a/spec/command_spec.cr b/spec/command_spec.cr new file mode 100644 index 0000000..5cb1e14 --- /dev/null +++ b/spec/command_spec.cr @@ -0,0 +1,131 @@ +require "spec" +require "../src/command" + +@[CliGenerator::CommandInfo(description: "Test")] +class CommandSubclass < CliGenerator::Command + + define_argument(testvar, + type: Int32, + long: "--testvar TESTVAR", + description: "Does things" + ) + + define_argument(testvar_the_return, + type: Int32, + def_getter: true, + long: "--testvar_the_return TESTVAR", + description: "Does things" + ) +end + +EXAMPLES = [ + "Have test" +] + +@[CliGenerator::SubCommand(description: "test", examples: ::EXAMPLES)] +def CommandSubclass.do_thing + puts "HI" +end + +@[CliGenerator::CommandPreRun] +def CommandSubclass.check_things + puts "I was run" +end + +## Need to make sure EVERYTHING is generated before testing +macro finished +describe CliGenerator::Command do + + describe "subclassing" do + + describe "Creates the arguments" do + it "has testvar" do + {{CommandSubclass.class.has_method?(:testvar)}}.should be_true + end + + it "has testvar2 getter" do + {{CommandSubclass.class.has_method?(:get_testvar_the_return)}}.should be_true + end + + end + + it "has Header" do + CommandSubclass::HEADER.empty?.should be_false + end + + it "has examples" do + CommandSubclass::HEADER.includes?("Examples").should be_true + end + + {% pre_runs = CommandSubclass.class.methods.select(&.annotation(::CliGenerator::CommandPreRun)) %} + {% run = CommandSubclass.class.methods.find{|m| m.name.stringify == "run"}.stringify %} + {% if pre_runs.size > 0 %} + describe "when defining pre-run methods" do + {% for pre_run in pre_runs %} + it "generates {{pre_run.name}}" do + {{run}}.includes?({{pre_run.name.stringify}}).should be_true + end + {% end %} + end + {% end %} + + {% subcommands = CommandSubclass.class.methods.select(&.annotation(::CliGenerator::SubCommand)) %} + {% unless subcommands.empty? %} + describe "should have subcommands" do + {% for subcommand in subcommands %} + it "has {{subcommand.name}}" do + {{CommandSubclass.class.has_method?(subcommand.name.symbolize)}}.should be_true + end + {% end %} + end + {% end %} + {% arguments = CommandSubclass.class.methods.select(&.annotation(::CliGenerator::CommandArgument)) %} + {% unless arguments.empty? %} + describe "should have arguments" do + {% for argument in arguments %} + it "has {{argument.name}}" do + {{CommandSubclass.class.has_method?(argument.name.symbolize)}}.should be_true + end + {% end %} + end + {% end %} + + {% if [arguments, subcommands].any?{|i| i.size >= 0 } %} + describe "should have generated OptionParser parser.on" do + {{ make_parser = CommandSubclass.class.methods.find{|m| m.name.stringify == "make_parser"}.stringify}} + {% unless subcommands.empty? %} + describe "for subcommands" do + {% for command in subcommands %} + it "has {{command.name}}" do + {{make_parser}}.includes?("on(" + {{command.name.stringify.stringify}}).should be_true + end + {% end %} + end + {% end %} + {% unless arguments.empty? %} + describe "for arguments" do + {% for argument in arguments %} + {% anno = argument.annotation(::CliGenerator::CommandArgument) %} + it "has " + {{anno[:long].stringify}} do + {{make_parser}}.includes?({{anno[:long]}}).should be_true + end + {% end %} + end + {% end %} + end + {% end %} + + describe "Should have generated macro class methods" do + it "has generated run" do + {{CommandSubclass.class.has_method?(:run)}}.should be_true + end + + it "has generated make_parser" do + {{CommandSubclass.class.has_method?(:make_parser)}}.should be_true + end + end + + end + +end +end diff --git a/spec/spec_helper.cr b/spec/spec_helper.cr deleted file mode 100644 index 0611842..0000000 --- a/spec/spec_helper.cr +++ /dev/null @@ -1,2 +0,0 @@ -require "spec" -require "../src/cli_generator" diff --git a/src/cli_generator.cr b/src/cli_generator.cr index 13fce6f..e20f5a1 100644 --- a/src/cli_generator.cr +++ b/src/cli_generator.cr @@ -3,6 +3,25 @@ require "option_parser" module CliGenerator VERSION = "0.1.0" + record AdditionalDefaultFlag, + short : String?, + long : String?, + description : String, + work : Proc(Nil) + + private ADDITIONAL_DEFAULT_FLAGS = [] of AdditionalDefaultFlag + + def self.add_default_flag(short : String? = nil, long : String? = nil, description : String? = nil, &work : -> ) + raise "ERROR : add_default_flag : You must provide a description" unless description + raise "ERROR : add_default_flag : You must provide at least long or short" unless [short, long].any? + ADDITIONAL_DEFAULT_FLAGS << AdditionalDefaultFlag.new( + short: short, + long: long, + description: description, + work: work + ) + end + annotation DefaultFlag end @@ -40,6 +59,11 @@ module CliGenerator end end } + + ADDITIONAL_DEFAULT_FLAGS.each do |flag| + args = [flag.short, flag.long, flag.description].reject(&.nil?) + {{parser}}.on(*args) &flag.work + end end macro define_root_parser diff --git a/src/command.cr b/src/command.cr index 14fd518..3c7e2d9 100644 --- a/src/command.cr +++ b/src/command.cr @@ -33,23 +33,22 @@ module CliGenerator end macro define_actions - ACTIONS : Array(String) = {{@type.class.methods.select(&.annotation(::CliGenerator::SubCommand)).map(&.name.stringify)}} + ACTIONS = {{@type.class.methods.select(&.annotation(::CliGenerator::SubCommand)).map(&.name.stringify)}} of String end macro define_header {% name = @type.name.split("::").last.downcase.id %} {% info_annos = @type.class.methods.select(&.annotation(::CliGenerator::SubCommand)).map(&.annotation(::CliGenerator::SubCommand)) %} + {% examples = [] of StringLiteral %} + {% info_annos.select(&.[](:examples)).map(&.[](:examples).resolve).each(&.each{|example| examples << example}) %} HEADER = [ "#{PROGRAM_NAME} {{name}} [[flags]]", "", - {% unless info_annos.empty? %} + {% unless examples.empty? %} "Examples:".colorize(:green), "-------------------------------------------------------------------------------".colorize(:blue), - {% for anno in info_annos %} - {% examples = anno[:examples].resolve %} - {% for example in examples %} - {{example}}, - {% end %} + {% for example in examples %} + {{example}}, {% end %} {% end %} ].join("\n") @@ -79,7 +78,7 @@ module CliGenerator end {% end %} - @[CommandArgument(type: {{type}}, short: {{short}}, long: {{long}}, description: {{description}})] + @[::CliGenerator::CommandArgument(type: {{type}}, short: {{short}}, long: {{long}}, description: {{description}})] def self.{{arg_name}}(var : String) : Nil {% if check %} ## If the check exists go ahead and call it diff --git a/src/command/parser.cr b/src/command/parser.cr index 2e790c0..32a91be 100644 --- a/src/command/parser.cr +++ b/src/command/parser.cr @@ -1,12 +1,14 @@ module CliGenerator::Parser macro extended + {% verbatim do %} macro define_parser def self.make_parser(parent_parser : OptionParser) : OptionParser {% puts "#{@type.name} OptionParser is being generated" %} {% name = @type.name.split("::").last %} {% var = name.downcase %} {% info = @type.annotation(::CliGenerator::CommandInfo) %} + {% raise "ERROR : No CommandInfo annotation provided to #{@type.name}" unless info %} subparser = OptionParser.new do |parser| parser.banner = {{@type.name}}::HEADER {% subcommands = @type.class.methods.select(&.annotation(::CliGenerator::SubCommand)) %} @@ -46,6 +48,7 @@ module CliGenerator::Parser } end end + {% end %} end end