object_rework: working MVP with runtime CommandNode tree

- BaseCommandNode (abstract) / CommandNode(T) split for heterogeneous tree
- Flag(T) with compile-time type branching, Parsable/Coercable modules
- Arg double-process invariant enforcement
- App.generate macro builds CommandNode tree from Command subclasses
- Command#initialize generated via macro finished, populates ivars from handler
- Reserved -h/--help enforcement in Flag#check!
- MatchType::Help added, SubCommand dispatch fixed
- design.adoc: added Planned Features (markdown docs, bash completion)
- Makefile: tabs, .DEFAULT_GOAL, doc_show target

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-08-09 22:49:55 -05:00
parent f5571cde73
commit 6f31a5d27c
9 changed files with 130 additions and 29 deletions
+11 -4
View File
@@ -1,14 +1,21 @@
module CliGen
class Command
macro argument(variable, short, long, description, validation = nil)
macro argument(variable, long, description, short = nil, validation = nil, on_match = nil)
{% raise "ERROR : CliGen::Command.argument : First argument (#{variable}) must be a TypeDeclaration (ex: '<var> : <type> [= val]')" unless variable.is_a? TypeDeclaration %}
{% name = variable.name %}
{% name = variable.var %}
{% type = variable.type %}
{% raise "ERROR : CliGen::Command.argument(#{name}) : Provided short must be a string" unless short.is_a? StringLiteral || string == nil %}
{% if short %}
{% raise "ERROR : CliGen::Command.argument(#{name}) : Provided short must be a string" unless short.is_a? StringLiteral %}
{% end %}
{% raise "ERROR : CliGen::Command.argument(#{name}) : Provided long must be a string" unless long.is_a? StringLiteral || long == nil %}
{% raise "ERROR : CliGen::Command.argument(#{name}) : You must provide a short or long" unless long || short %}
{% raise "ERROR : CliGen::Command.argument(#{name}) : You must provide a description" unless description %}
{% raise "ERROR : CliGen::Command.argument(#{name}) : Provided description must be a String" unless description.is_a? StringLiteral %}
{% unless on_match.nil? %}
{% puts on_match %}
{% raise "ERROR : CliGen::Command.argument(#{name}) : Provided on_match must be a Proc" unless on_match.is_a? ProcLiteral %}
{% raise "ERROR : CliGen::Command.argument(#{name}) : Provided on_match return type must be a Bool" unless on_match.return_type == Nil %}
{% end %}
{% unless validation.nil? %}
{% raise "ERROR : CliGen::Command.argument(#{name}) : Provided validation must be a Proc" unless validation.is_a? ProcLiteral %}
{% raise "ERROR : CliGen::Command.argument(#{name}) : Provided validation return type must be a Bool" unless validation.return_type == Bool %}
@@ -23,7 +30,7 @@ module CliGen
@[CliGen::Argument(short: {{short}}, long: {{long}}, description: {{description}}, validation: {{validation}}, on_match: {{on_match}})]
@{{variable}}
def {{variable.name}}= (value : {{type}})
def {{variable.var}}= (value : {{type}})
{% unless validation.nil? %}
raise "ERROR : #{@type.name}##{@def.name} : Provided value #{value} is not passing validation" unless {{validation}}.call(value)
{% end %}