Class: SmilyCli::CLI
- Inherits:
-
BaseCommand
- Object
- Thor
- BaseCommand
- SmilyCli::CLI
- Defined in:
- lib/smily_cli/cli.rb
Overview
The top-level smily command. It wires the reusable resource CRUD commands
(one registered subcommand per Registry entry), the auth/config
subcommand groups, and a handful of core commands (api, resources,
whoami, completion, version). Business logic lives in the library
classes; this class only parses input and dispatches.
Constant Summary collapse
- HOISTABLE_VALUE_FLAGS =
Single-value global flags that are safe to move past the subcommand.
%w[ --profile --token --base-url --account-id --max-pages --retry -o --output --mcp-token --mcp-url ].freeze
- HOISTABLE_BOOL_FLAGS =
Value-less global flags.
%w[-v --verbose -q --quiet --no-color].freeze
Class Method Summary collapse
-
.command_names ⇒ Array<String>
Every top-level command word, so normalization can tell a real flag value from a subcommand the user meant to run.
-
.normalize_argv(argv) ⇒ Array<String>
Thor's registered subcommands can't see class options that appear before the subcommand word, so
smily -o json rentals listwould drop-o. - .start(given_args = ARGV, config = {}) ⇒ Object
-
.takes_value?(token) ⇒ Boolean
A token is a flag's value only when it's present and not a command word the user more likely meant to run (a forgotten flag value).
Instance Method Summary collapse
- #api(method, path) ⇒ Object
- #completion(shell) ⇒ Object
- #resources(filter = nil) ⇒ Object
- #version ⇒ Object
- #whoami ⇒ Object
Methods inherited from BaseCommand
Class Method Details
.command_names ⇒ Array<String>
Every top-level command word, so normalization can tell a real flag value from a subcommand the user meant to run.
31 32 33 34 35 |
# File 'lib/smily_cli/cli.rb', line 31 def self.command_names @command_names ||= ( Registry.commands + %w[api auth config mcp resources whoami completion version help] ).freeze end |
.normalize_argv(argv) ⇒ Array<String>
Thor's registered subcommands can't see class options that appear before
the subcommand word, so smily -o json rentals list would drop -o. We
normalize by moving any leading global flags to the end of the args, where
every command re-declares them. (Array-valued flags like --fields are
left alone and must follow the subcommand.)
A leading value flag with no attached =value whose next token is missing
or is itself a command word is a forgotten value (smily --token rentals list). We fail with a clear UsageError rather than swallow the
subcommand into the flag — which otherwise yields a baffling "command not
found" or a request fired with a bogus value. Use --flag=value when a
value legitimately collides with a command name (e.g. a profile named like
a resource).
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 |
# File 'lib/smily_cli/cli.rb', line 54 def self.normalize_argv(argv) leading = [] rest = argv.dup until rest.empty? token = rest.first break if token == "--" name = token.split("=", 2).first if HOISTABLE_VALUE_FLAGS.include?(name) leading << rest.shift next if token.include?("=") # value already attached value = rest.first unless takes_value?(value) raise UsageError, "Missing value for #{name}. Pass it as `#{name} <VALUE>`, or as " \ "`#{name}=<VALUE>` if the value is also a command name." end leading << rest.shift elsif HOISTABLE_BOOL_FLAGS.include?(token) leading << rest.shift else break end end leading.empty? || rest.empty? ? argv : rest + leading end |
.start(given_args = ARGV, config = {}) ⇒ Object
91 92 93 |
# File 'lib/smily_cli/cli.rb', line 91 def self.start(given_args = ARGV, config = {}) super(normalize_argv(Array(given_args)), config) end |
.takes_value?(token) ⇒ Boolean
A token is a flag's value only when it's present and not a command word the user more likely meant to run (a forgotten flag value).
87 88 89 |
# File 'lib/smily_cli/cli.rb', line 87 def self.takes_value?(token) !token.nil? && !command_names.include?(token) end |
Instance Method Details
#api(method, path) ⇒ Object
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/smily_cli/cli.rb', line 129 def api(method, path) verb = method.to_s.downcase unless %w[get post put patch delete head].include?(verb) raise UsageError, "Unknown HTTP method #{method.inspect}. Use get, post, put, patch or delete." end query = QueryOptions.build(fields: context.fields, query: ["query"] || []) if ["paginate"] result = client.list(path, query: query, all: true) render_api(result, records: true) else body = ["data"] ? parse_data(["data"]) : nil result = client.request(verb, path, query: query, body: body) render_api(result, records: false) end end |
#completion(shell) ⇒ Object
168 169 170 |
# File 'lib/smily_cli/cli.rb', line 168 def completion(shell) emit(Completion.script(shell)) end |
#resources(filter = nil) ⇒ Object
148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/smily_cli/cli.rb', line 148 def resources(filter = nil) selected = Registry.all.select { |r| filter.nil? || r.command.include?(filter) } if context.output_format == "table" render_resources_table(selected) else payload = selected.map do |r| { "command" => r.command, "path" => r.path, "group" => r.group, "description" => r.description, "readonly" => r.readonly } end render_result(Result.new(records: payload, single: false)) end end |
#version ⇒ Object
173 174 175 |
# File 'lib/smily_cli/cli.rb', line 173 def version emit("smily #{SmilyCli::VERSION} (ruby #{RUBY_VERSION}, bookingsync-api #{BookingSync::API::VERSION})") end |
#whoami ⇒ Object
163 164 165 |
# File 'lib/smily_cli/cli.rb', line 163 def whoami render_result(client.me) end |