rubycli 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/README.ja.md +19 -2
- data/README.md +19 -2
- data/lib/rubycli/argument_parser.rb +60 -1
- data/lib/rubycli/command_line.rb +25 -3
- data/lib/rubycli/documentation/metadata_parser.rb +7 -1
- data/lib/rubycli/documentation_registry.rb +1 -0
- data/lib/rubycli/version.rb +1 -1
- data/lib/rubycli.rb +61 -5
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '07113181085c0df0c8f4bc34aca661d4c29fb9bbc309012ace52b36e8bdf9ead'
|
|
4
|
+
data.tar.gz: 773d7de6a820fd1702e4e618c595237e2293c74dca6cc83f3ea57ba9eaaa18db
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dadd19c4c2081e6fd7f8d6e40750c3cbc727a2c7f0452a5eb81b6feb80ce878e50fac111589ea87415d7656db4d288591bf7352fd4ee80031998e5835e84e732
|
|
7
|
+
data.tar.gz: 0aef161e5d6fd30fb475de5afc908ecc94f1b5d4d1c96c0d735399c90b615e865e91286fa100008804d846d22e37874528b192dcbcea74b52484e45ef56273b6
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
# Changelog
|
|
4
|
+
|
|
5
|
+
## [0.1.7] - 2025-11-12
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- `--new` now optionally accepts constructor arguments inline (e.g., `--new=[...]`); YAML/JSON-like literals are safely parsed, and `--json-args` / `--eval-args` / `--eval-lax` still apply. Arrays become positional args, hashes become keyword args.
|
|
9
|
+
- Positional argument coercion now runs through the same type-conversion pipeline as options/`--new`, including comment-driven array/element coercion and strict-mode validation; new tests cover arrays, hashes, booleans, and `--new` with JSON/eval modes.
|
|
10
|
+
- Added `examples/new_mode_runner.rb` to showcase `--new` with constructor args, eval/JSON modes, and pre-script initialization for instance-only classes.
|
|
11
|
+
- Strengthened tests for `--eval-lax` success/fallback with `--new` and end-to-end positional Hash coercion to guard against regressions.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- `--new` arguments now flow through the same type-coercion pipeline as regular CLI arguments (including comment-driven type hints) before being passed to `initialize`.
|
|
15
|
+
|
|
3
16
|
## [0.1.6] - 2025-11-11
|
|
4
17
|
|
|
5
18
|
### Changed
|
data/README.ja.md
CHANGED
|
@@ -156,7 +156,9 @@ Rubycli は「ファイル名を CamelCase にした定数」を公開対象だ
|
|
|
156
156
|
|
|
157
157
|
大規模なコードベースでも安全側を保ちながら、どうしても自動選択したいときだけ 1 フラグで切り替えられます。
|
|
158
158
|
|
|
159
|
-
> **インスタンスメソッド専用のクラスについて** – 公開メソッドがインスタンス側(`def greet` など)にしか無い場合は、`--new` を付けて事前にインスタンス化しないと CLI から呼び出せません。クラスメソッドを 1 つ用意するか、`--new` を明示して実行してください。`--new` を付ければ `rubycli --help` でもインスタンスメソッドが一覧に現れ、`rubycli --check --new` でコメントの lint
|
|
159
|
+
> **インスタンスメソッド専用のクラスについて** – 公開メソッドがインスタンス側(`def greet` など)にしか無い場合は、`--new` を付けて事前にインスタンス化しないと CLI から呼び出せません。クラスメソッドを 1 つ用意するか、`--new` を明示して実行してください。`--new` を付ければ `rubycli --help` でもインスタンスメソッドが一覧に現れ、`rubycli --check --new` でコメントの lint も実行できます。初期化時に引数が必要なら `--new=VALUE` のように続けて指定できます(通常の引数と同様に YAML/JSON ライクな安全パースに加え、`--json-args` / `--eval-args` / `--eval-lax` も適用可能)。`initialize` に書いたコメントも通常の CLI メソッドと同様に型変換に反映されます。
|
|
160
|
+
|
|
161
|
+
> 補足: `--new 1` のようにスペース区切りで 1 つだけ値を渡すと、後続トークンがパス扱いされやすいため `--new=VALUE` のように `=` 付きで指定するのが確実です。
|
|
160
162
|
|
|
161
163
|
## 開発方針
|
|
162
164
|
|
|
@@ -172,6 +174,21 @@ Rubycli は「ファイル名を CamelCase にした定数」を公開対象だ
|
|
|
172
174
|
- 引数はデフォルトで安全なリテラルとして解釈し、必要に応じて厳格 JSON モードや Ruby eval モードを切り替え可能
|
|
173
175
|
- `--pre-script`(エイリアス: `--init`)で任意の Ruby コードを評価し、その結果オブジェクトを公開
|
|
174
176
|
- `--check` でコメント整合性を lint、`--strict` で入力値をドキュメント通りに強制する二段構えのガード
|
|
177
|
+
- `examples/new_mode_runner.rb` ではインスタンス専用クラスを `--new=VALUE` で初期化し、eval/JSON モードや pre-script を組み合わせる例を示しています。
|
|
178
|
+
|
|
179
|
+
### サンプル / 付属例
|
|
180
|
+
|
|
181
|
+
- `examples/hello_app.rb` / `examples/hello_app_with_docs.rb`: 最小のモジュール関数とドキュメント付きの版
|
|
182
|
+
- `examples/typed_arguments_demo.rb`: 標準ライブラリ型 (Date/Time/BigDecimal/Pathname) の coercion
|
|
183
|
+
- `examples/strict_choices_demo.rb`: リテラル列挙と `--strict` の組み合わせ
|
|
184
|
+
- `examples/new_mode_runner.rb`: インスタンス専用クラスを `--new=VALUE` で初期化し、eval/JSON/pre-script を組み合わせる例
|
|
185
|
+
|
|
186
|
+
#### サンプルコマンド
|
|
187
|
+
|
|
188
|
+
- `rubycli examples/new_mode_runner.rb run --new='["a","b","c"]' --mode reverse`
|
|
189
|
+
- `rubycli --json-args --new='["x","y"]' examples/new_mode_runner.rb run --mode summary --options '{"source":"json"}'`
|
|
190
|
+
- `rubycli --eval-args --new='["x","y"]' examples/new_mode_runner.rb run --mode summary --options '{tags: [:a, :b]}'`
|
|
191
|
+
- `rubycli --pre-script 'NewModeRunner.new(%w[a b c], options: {from: :pre})' examples/new_mode_runner.rb run --mode summary`
|
|
175
192
|
|
|
176
193
|
> 補足: `--strict` はコメントに書かれた型/許可値をそのまま信頼して検証するため、コメントが誤記だと実行時には検出できません。CI では必ず `rubycli --check` を走らせ、`--strict` は「 lint を通過したドキュメントを本番で厳密に守る」用途に使ってください。
|
|
177
194
|
|
|
@@ -185,7 +202,7 @@ Rubycli は「ファイル名を CamelCase にした定数」を公開対象だ
|
|
|
185
202
|
| 機能 | Python Fire | Rubycli |
|
|
186
203
|
| ---- | ----------- | -------- |
|
|
187
204
|
| 属性の辿り方 | オブジェクトを辿ってプロパティ/属性を自動公開 | 対象オブジェクトの公開メソッドをそのまま公開(暗黙の辿りは無し) |
|
|
188
|
-
| クラス初期化 | `__init__` 引数を CLI で自動受け取りインスタンス化 | `--new`
|
|
205
|
+
| クラス初期化 | `__init__` 引数を CLI で自動受け取りインスタンス化 | `--new` 指定時だけ初期化(コンストラクタ引数は `--new=VALUE` で渡せる。YAML/JSON らしいリテラルは安全にパース、`--json-args` / `--eval-args` / `--eval-lax` も適用可能。より複雑なら pre-script や自前ファクトリを利用) |
|
|
189
206
|
| インタラクティブシェル | コマンド未指定時に Fire REPL を提供 | インタラクティブモード無し。コマンド実行専用 |
|
|
190
207
|
| 情報源 | 反射で引数・プロパティを解析 | ライブなメソッド定義を基点にしつつコメントをヘルプへ反映 |
|
|
191
208
|
| 辞書/配列 | dict/list を自動でサブコマンド化 | クラス/モジュールのメソッドに特化(辞書自動展開なし) |
|
data/README.md
CHANGED
|
@@ -163,7 +163,9 @@ Rubycli assumes that the file name (CamelCased) matches the class or module you
|
|
|
163
163
|
|
|
164
164
|
This keeps large projects safe by default but still provides a one-flag escape hatch when you prefer the fully automatic behaviour.
|
|
165
165
|
|
|
166
|
-
> **Instance-only classes** – If a class only defines public *instance* methods (for example, it exposes functionality via `def greet` on the instance), you must run Rubycli with `--new` so the class is instantiated before commands are resolved. Otherwise Rubycli cannot see any CLI-callable methods. Add at least one public class method when you do not want to rely on `--new`. Passing `--new` also makes those instance methods appear in `rubycli --help` output and allows `rubycli --check --new` to lint their documentation.
|
|
166
|
+
> **Instance-only classes** – If a class only defines public *instance* methods (for example, it exposes functionality via `def greet` on the instance), you must run Rubycli with `--new` so the class is instantiated before commands are resolved. Otherwise Rubycli cannot see any CLI-callable methods. Add at least one public class method when you do not want to rely on `--new`. Passing `--new` also makes those instance methods appear in `rubycli --help` output and allows `rubycli --check --new` to lint their documentation. When your constructor needs arguments, pass them inline with `--new=VALUE` (safe YAML/JSON-like parsing by default; `--json-args` for strict JSON, `--eval-args` / `--eval-lax` for Ruby literals). Any comments on `initialize` are respected for type coercion just like regular CLI methods.
|
|
167
|
+
|
|
168
|
+
> Hint: Single values should be passed as `--new=value` so they aren’t mistaken for the next path/command. Space-separated single tokens like `--new 1` may be treated as the following path unless they look obviously structured.
|
|
167
169
|
|
|
168
170
|
## Project Philosophy
|
|
169
171
|
|
|
@@ -179,6 +181,14 @@ This keeps large projects safe by default but still provides a one-flag escape h
|
|
|
179
181
|
- Safe literal parsing out of the box (arrays / hashes / booleans) with opt-in strict JSON and Ruby eval modes
|
|
180
182
|
- Optional pre-script hook (`--pre-script` / `--init`) to evaluate Ruby and expose the resulting object
|
|
181
183
|
- Dedicated CLI flags for quality gates: `--check` lints documentation/comments without running commands, and `--strict` treats documented types/choices as hard requirements
|
|
184
|
+
- Example `examples/new_mode_runner.rb` demonstrates instance-only classes with `--new=VALUE` constructor arguments, eval/JSON modes, and a pre-script initialization pattern.
|
|
185
|
+
|
|
186
|
+
### Examples
|
|
187
|
+
|
|
188
|
+
- `examples/hello_app.rb` / `examples/hello_app_with_docs.rb`: minimal module-function variants, with and without docs
|
|
189
|
+
- `examples/typed_arguments_demo.rb`: stdlib type coercions (Date/Time/BigDecimal/Pathname)
|
|
190
|
+
- `examples/strict_choices_demo.rb`: literal enumerations and `--strict`
|
|
191
|
+
- `examples/new_mode_runner.rb`: instance-only class initialized via `--new=VALUE` with eval/JSON/pre-script combinations
|
|
182
192
|
|
|
183
193
|
> Tip: `--strict` trusts whatever types/choices your comments spell out—if the annotations are misspelled, runtime enforcement has nothing reliable to compare against. Keep `rubycli --check` in CI so documentation typos are caught before production runs that rely on `--strict`.
|
|
184
194
|
|
|
@@ -192,11 +202,18 @@ This keeps large projects safe by default but still provides a one-flag escape h
|
|
|
192
202
|
| Capability | Python Fire | Rubycli |
|
|
193
203
|
| ---------- | ----------- | -------- |
|
|
194
204
|
| Attribute traversal | Recursively exposes attributes/properties on demand | Exposes public methods defined on the target; no implicit traversal |
|
|
195
|
-
| Constructor handling | Automatically prompts for `__init__` args when instantiating classes | `--new`
|
|
205
|
+
| Constructor handling | Automatically prompts for `__init__` args when instantiating classes | `--new` instantiates and accepts constructor arguments via `--new=VALUE` (safe YAML/JSON-like parsing by default; `--json-args` for strict JSON, `--eval-args` / `--eval-lax` for Ruby literals). Use pre-scripts or your own factories for more complex wiring. |
|
|
196
206
|
| Interactive shell | Offers Fire-specific REPL when invoked without command | No interactive shell mode; strictly command execution |
|
|
197
207
|
| Input discovery | Pure reflection, no doc comments required | Doc comments drive option names, placeholders, and validation |
|
|
198
208
|
| Data structures | Dictionaries / lists become subcommands by default | Focused on class or module methods; no automatic dict/list expansion |
|
|
199
209
|
|
|
210
|
+
#### Example commands
|
|
211
|
+
|
|
212
|
+
- `rubycli examples/new_mode_runner.rb run --new='["a","b","c"]' --mode reverse`
|
|
213
|
+
- `rubycli --json-args --new='["x","y"]' examples/new_mode_runner.rb run --mode summary --options '{"source":"json"}'`
|
|
214
|
+
- `rubycli --eval-args --new='["x","y"]' examples/new_mode_runner.rb run --mode summary --options '{tags: [:a, :b]}'`
|
|
215
|
+
- `rubycli --pre-script 'NewModeRunner.new(%w[a b c], options: {from: :pre})' examples/new_mode_runner.rb run --mode summary`
|
|
216
|
+
|
|
200
217
|
## Installation
|
|
201
218
|
|
|
202
219
|
Rubycli is published on RubyGems.
|
|
@@ -59,6 +59,7 @@ module Rubycli
|
|
|
59
59
|
end
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
+
pos_args = convert_positional_arguments(pos_args, method, metadata)
|
|
62
63
|
debug_log "Final parsed - pos_args: #{pos_args.inspect}, kw_args: #{kw_args.inspect}"
|
|
63
64
|
[pos_args, kw_args]
|
|
64
65
|
end
|
|
@@ -186,6 +187,60 @@ module Rubycli
|
|
|
186
187
|
[key, value]
|
|
187
188
|
end
|
|
188
189
|
|
|
190
|
+
def convert_positional_arguments(pos_args, method, metadata)
|
|
191
|
+
return pos_args unless method
|
|
192
|
+
|
|
193
|
+
positional_map = metadata[:positionals_map] || {}
|
|
194
|
+
return pos_args if positional_map.empty?
|
|
195
|
+
|
|
196
|
+
converted = pos_args.dup
|
|
197
|
+
method.parameters.each_with_index do |(type, name), index|
|
|
198
|
+
next unless %i[req opt].include?(type)
|
|
199
|
+
definition = positional_map[name]
|
|
200
|
+
next unless definition
|
|
201
|
+
next if index >= converted.size
|
|
202
|
+
|
|
203
|
+
converter = converter_for_definition(definition)
|
|
204
|
+
next unless converter
|
|
205
|
+
|
|
206
|
+
begin
|
|
207
|
+
converted[index] = converter.call(converted[index])
|
|
208
|
+
rescue StandardError => e
|
|
209
|
+
label = definition.label || definition.placeholder || name.to_s.upcase
|
|
210
|
+
raise ArgumentError, "Value '#{converted[index]}' for #{label} is invalid: #{e.message}"
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
converted
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def converter_for_definition(definition)
|
|
218
|
+
types = Array(definition.types).compact
|
|
219
|
+
return nil if types.empty?
|
|
220
|
+
|
|
221
|
+
types.each do |type|
|
|
222
|
+
normalized = type.to_s.strip
|
|
223
|
+
next if normalized.empty?
|
|
224
|
+
|
|
225
|
+
if normalized.start_with?('Array<') && normalized.end_with?('>')
|
|
226
|
+
inner = normalized[6..-2].strip
|
|
227
|
+
element_converter = converter_for_single_type(inner)
|
|
228
|
+
return ->(value) { TypeUtils.parse_list(value).map { |item| element_converter ? element_converter.call(item) : item } }
|
|
229
|
+
elsif normalized.end_with?('[]')
|
|
230
|
+
inner = normalized[0..-3]
|
|
231
|
+
element_converter = converter_for_single_type(inner)
|
|
232
|
+
return ->(value) { TypeUtils.parse_list(value).map { |item| element_converter ? element_converter.call(item) : item } }
|
|
233
|
+
elsif normalized.casecmp('Array').zero?
|
|
234
|
+
return ->(value) { TypeUtils.parse_list(value) }
|
|
235
|
+
else
|
|
236
|
+
single = converter_for_single_type(normalized)
|
|
237
|
+
return single if single
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
nil
|
|
242
|
+
end
|
|
243
|
+
|
|
189
244
|
def resolve_keyword_parameter(cli_key, kw_param_names)
|
|
190
245
|
exact_match = kw_param_names.find { |name| name == cli_key }
|
|
191
246
|
return exact_match if exact_match
|
|
@@ -528,7 +583,11 @@ module Rubycli
|
|
|
528
583
|
require 'time'
|
|
529
584
|
->(value) { Time.parse(value) }
|
|
530
585
|
when 'JSON', 'Hash'
|
|
531
|
-
->(value) {
|
|
586
|
+
->(value) {
|
|
587
|
+
return value if value.is_a?(Hash)
|
|
588
|
+
|
|
589
|
+
JSON.parse(value)
|
|
590
|
+
}
|
|
532
591
|
when 'Pathname'
|
|
533
592
|
require 'pathname'
|
|
534
593
|
->(value) {
|
data/lib/rubycli/command_line.rb
CHANGED
|
@@ -3,15 +3,16 @@
|
|
|
3
3
|
module Rubycli
|
|
4
4
|
module CommandLine
|
|
5
5
|
USAGE = <<~USAGE
|
|
6
|
-
Usage: rubycli [--new|-n] [--pre-script=<src>] [--json-args|-j | --eval-args|-e | --eval-lax|-E] [--strict] [--check|-c] <target-path> [<class-or-module>] [-- <cli-args>...]
|
|
6
|
+
Usage: rubycli [--new|-n[=<value>]] [--pre-script=<src>] [--json-args|-j | --eval-args|-e | --eval-lax|-E] [--strict] [--check|-c] <target-path> [<class-or-module>] [-- <cli-args>...]
|
|
7
7
|
|
|
8
8
|
Examples:
|
|
9
9
|
rubycli scripts/sample_runner.rb echo --message hello
|
|
10
10
|
rubycli scripts/sample_runner.rb AlternateRunner greet --name Ruby
|
|
11
11
|
rubycli --new lib/akiya_fetcher.rb fetch_simplified_html https://example.com
|
|
12
|
+
rubycli --json-args --new='["a","b"]' examples/new_mode_runner.rb run --mode summary --options '{"source":"json"}'
|
|
12
13
|
|
|
13
14
|
Options:
|
|
14
|
-
--new, -n
|
|
15
|
+
--new, -n [<value>] Instantiate the class/module before invoking CLI commands; optional constructor arguments can follow (array/hash recommended; respects --json-args/--eval-args/--eval-lax)
|
|
15
16
|
--pre-script=<src> Evaluate Ruby code and use its result as the exposed target (--init alias; also accepts space-separated form)
|
|
16
17
|
--json-args, -j Parse all following arguments strictly as JSON (no YAML literals)
|
|
17
18
|
--eval-args, -e Evaluate following arguments as Ruby code
|
|
@@ -40,6 +41,7 @@ module Rubycli
|
|
|
40
41
|
end
|
|
41
42
|
|
|
42
43
|
new_flag = false
|
|
44
|
+
new_args_expr = nil
|
|
43
45
|
json_mode = false
|
|
44
46
|
eval_mode = false
|
|
45
47
|
eval_lax_mode = false
|
|
@@ -55,9 +57,23 @@ module Rubycli
|
|
|
55
57
|
when '-h', '--help', 'help'
|
|
56
58
|
$stdout.puts(USAGE)
|
|
57
59
|
return 0
|
|
58
|
-
when
|
|
60
|
+
when /\A--new(?:=(.+))?\z/
|
|
59
61
|
new_flag = true
|
|
62
|
+
new_args_expr = Regexp.last_match(1)
|
|
60
63
|
args.shift
|
|
64
|
+
if new_args_expr.nil?
|
|
65
|
+
possible = args.first
|
|
66
|
+
if possible && !possible.start_with?('-') && likely_new_args_value?(possible)
|
|
67
|
+
new_args_expr = args.shift
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
when '-n'
|
|
71
|
+
new_flag = true
|
|
72
|
+
args.shift
|
|
73
|
+
possible = args.first
|
|
74
|
+
if possible && !possible.start_with?('-') && likely_new_args_value?(possible)
|
|
75
|
+
new_args_expr = args.shift
|
|
76
|
+
end
|
|
61
77
|
when /\A--pre-script=(.+)\z/, /\A--init=(.+)\z/
|
|
62
78
|
label = Regexp.last_match(0).start_with?('--pre-script') ? '--pre-script' : '--init'
|
|
63
79
|
expr = Regexp.last_match(1)
|
|
@@ -141,6 +157,7 @@ module Rubycli
|
|
|
141
157
|
target_path,
|
|
142
158
|
class_or_module,
|
|
143
159
|
new: new_flag,
|
|
160
|
+
new_args: new_args_expr,
|
|
144
161
|
pre_scripts: pre_script_sources,
|
|
145
162
|
constant_mode: constant_mode
|
|
146
163
|
)
|
|
@@ -151,6 +168,7 @@ module Rubycli
|
|
|
151
168
|
class_or_module,
|
|
152
169
|
args,
|
|
153
170
|
new: new_flag,
|
|
171
|
+
new_args: new_args_expr,
|
|
154
172
|
json: json_mode,
|
|
155
173
|
eval_args: eval_mode,
|
|
156
174
|
eval_lax: eval_lax_mode,
|
|
@@ -166,5 +184,9 @@ module Rubycli
|
|
|
166
184
|
warn "[ERROR] #{e.message}"
|
|
167
185
|
1
|
|
168
186
|
end
|
|
187
|
+
|
|
188
|
+
def likely_new_args_value?(token)
|
|
189
|
+
token.include?(',') || token.start_with?('[', '{')
|
|
190
|
+
end
|
|
169
191
|
end
|
|
170
192
|
end
|
|
@@ -873,6 +873,8 @@ module Rubycli
|
|
|
873
873
|
end
|
|
874
874
|
|
|
875
875
|
def type_dictionary
|
|
876
|
+
return @type_dictionary if defined?(@type_dictionary) && @type_dictionary
|
|
877
|
+
|
|
876
878
|
builtins = (INLINE_TYPE_HINTS + %w[NilClass Fixnum Decimal Struct]).uniq
|
|
877
879
|
constant_names = []
|
|
878
880
|
begin
|
|
@@ -888,7 +890,11 @@ module Rubycli
|
|
|
888
890
|
constant_names = Object.constants.map(&:to_s)
|
|
889
891
|
end
|
|
890
892
|
|
|
891
|
-
(builtins + constant_names).map(&:to_s).map(&:strip).reject(&:empty?).uniq
|
|
893
|
+
@type_dictionary = (builtins + constant_names).map(&:to_s).map(&:strip).reject(&:empty?).uniq
|
|
894
|
+
end
|
|
895
|
+
|
|
896
|
+
def reset_type_dictionary_cache!
|
|
897
|
+
@type_dictionary = nil
|
|
892
898
|
end
|
|
893
899
|
|
|
894
900
|
|
data/lib/rubycli/version.rb
CHANGED
data/lib/rubycli.rb
CHANGED
|
@@ -209,6 +209,7 @@ module Rubycli
|
|
|
209
209
|
class_name = nil,
|
|
210
210
|
cli_args = nil,
|
|
211
211
|
new: false,
|
|
212
|
+
new_args: nil,
|
|
212
213
|
json: false,
|
|
213
214
|
eval_args: false,
|
|
214
215
|
eval_lax: false,
|
|
@@ -224,6 +225,10 @@ module Rubycli
|
|
|
224
225
|
target_path,
|
|
225
226
|
class_name,
|
|
226
227
|
new: new,
|
|
228
|
+
new_args: new_args,
|
|
229
|
+
json_mode: json,
|
|
230
|
+
eval_mode: eval_args,
|
|
231
|
+
eval_lax: eval_lax,
|
|
227
232
|
pre_scripts: pre_scripts,
|
|
228
233
|
constant_mode: constant_mode
|
|
229
234
|
)
|
|
@@ -243,8 +248,12 @@ module Rubycli
|
|
|
243
248
|
target_path,
|
|
244
249
|
class_name = nil,
|
|
245
250
|
new: false,
|
|
251
|
+
new_args: nil,
|
|
246
252
|
pre_scripts: [],
|
|
247
|
-
constant_mode: nil
|
|
253
|
+
constant_mode: nil,
|
|
254
|
+
json_mode: false,
|
|
255
|
+
eval_mode: false,
|
|
256
|
+
eval_lax: false
|
|
248
257
|
)
|
|
249
258
|
raise ArgumentError, 'target_path must be specified' if target_path.nil? || target_path.empty?
|
|
250
259
|
previous_doc_check = Rubycli.environment.doc_check_mode?
|
|
@@ -255,6 +264,10 @@ module Rubycli
|
|
|
255
264
|
target_path,
|
|
256
265
|
class_name,
|
|
257
266
|
new: new,
|
|
267
|
+
new_args: new_args,
|
|
268
|
+
json_mode: json_mode,
|
|
269
|
+
eval_mode: eval_mode,
|
|
270
|
+
eval_lax: eval_lax,
|
|
258
271
|
pre_scripts: pre_scripts,
|
|
259
272
|
constant_mode: constant_mode
|
|
260
273
|
)
|
|
@@ -348,10 +361,18 @@ module Rubycli
|
|
|
348
361
|
raise Error.new(message), cause: nil
|
|
349
362
|
end
|
|
350
363
|
|
|
351
|
-
def instantiate_target(target)
|
|
364
|
+
def instantiate_target(target, initializer_args = nil)
|
|
365
|
+
positional_args, keyword_args = Array(initializer_args || [[], {}])
|
|
366
|
+
positional_args ||= []
|
|
367
|
+
keyword_args ||= {}
|
|
368
|
+
|
|
352
369
|
case target
|
|
353
370
|
when Class
|
|
354
|
-
|
|
371
|
+
if keyword_args.empty?
|
|
372
|
+
target.new(*positional_args)
|
|
373
|
+
else
|
|
374
|
+
target.new(*positional_args, **keyword_args)
|
|
375
|
+
end
|
|
355
376
|
when Module
|
|
356
377
|
Object.new.extend(target)
|
|
357
378
|
else
|
|
@@ -373,10 +394,43 @@ module Rubycli
|
|
|
373
394
|
end
|
|
374
395
|
end
|
|
375
396
|
|
|
397
|
+
def parse_initializer_arguments(raw_value, target, json_mode:, eval_mode:, eval_lax:)
|
|
398
|
+
return [[], {}] if raw_value.nil?
|
|
399
|
+
raise Rubycli::ArgumentError, '--json-args cannot be combined with --eval-args or --eval-lax' if json_mode && eval_mode
|
|
400
|
+
|
|
401
|
+
initializer_method = initializer_method_for(target)
|
|
402
|
+
tokens = Array(raw_value)
|
|
403
|
+
|
|
404
|
+
positional_args = []
|
|
405
|
+
keyword_args = {}
|
|
406
|
+
|
|
407
|
+
Rubycli.argument_mode_controller.with_json_mode(json_mode) do
|
|
408
|
+
Rubycli.argument_mode_controller.with_eval_mode(eval_mode, lax: eval_lax) do
|
|
409
|
+
positional_args, keyword_args = Rubycli.argument_parser.parse(tokens.dup, initializer_method)
|
|
410
|
+
Rubycli.apply_argument_coercions(positional_args, keyword_args)
|
|
411
|
+
Rubycli.argument_parser.validate_inputs(initializer_method, positional_args, keyword_args)
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
[positional_args || [], keyword_args || {}]
|
|
416
|
+
rescue Rubycli::ArgumentError => e
|
|
417
|
+
raise Runner::Error, "Failed to parse --new arguments: #{e.message}"
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
def initializer_method_for(target)
|
|
421
|
+
return nil unless target.is_a?(Class)
|
|
422
|
+
|
|
423
|
+
target.instance_method(:initialize) rescue nil
|
|
424
|
+
end
|
|
425
|
+
|
|
376
426
|
def prepare_runner_target(
|
|
377
427
|
target_path,
|
|
378
428
|
class_name,
|
|
379
429
|
new: false,
|
|
430
|
+
new_args: nil,
|
|
431
|
+
json_mode: false,
|
|
432
|
+
eval_mode: false,
|
|
433
|
+
eval_lax: false,
|
|
380
434
|
pre_scripts: [],
|
|
381
435
|
constant_mode: nil
|
|
382
436
|
)
|
|
@@ -403,7 +457,9 @@ module Rubycli
|
|
|
403
457
|
)
|
|
404
458
|
end
|
|
405
459
|
|
|
406
|
-
|
|
460
|
+
initializer_args = new ? parse_initializer_arguments(new_args, target, json_mode: json_mode, eval_mode: eval_mode, eval_lax: eval_lax) : nil
|
|
461
|
+
|
|
462
|
+
runner_target = new ? instantiate_target(target, initializer_args) : target
|
|
407
463
|
runner_target = apply_pre_scripts(pre_scripts, target, runner_target)
|
|
408
464
|
[runner_target, full_path]
|
|
409
465
|
end
|
|
@@ -536,7 +592,7 @@ module Rubycli
|
|
|
536
592
|
|
|
537
593
|
if defined_constants && !defined_constants.empty?
|
|
538
594
|
sample = defined_constants.first(5)
|
|
539
|
-
suffix = defined_constants.size > sample.size ? "
|
|
595
|
+
suffix = defined_constants.size > sample.size ? " ... (#{defined_constants.size} total)" : ''
|
|
540
596
|
lines << "Constants found in this file: #{sample.join(', ')}#{suffix}"
|
|
541
597
|
else
|
|
542
598
|
lines << 'Rubycli could not detect any publicly exposable constants in this file.'
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rubycli
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- inakaegg
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2025-11-
|
|
10
|
+
date: 2025-11-16 00:00:00.000000000 Z
|
|
11
11
|
dependencies: []
|
|
12
12
|
description: Rubycli turns plain Ruby classes and modules into command-line interfaces
|
|
13
13
|
by reading their documentation comments, inspired by Python Fire but tailored for
|