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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 521eac399843d8fe002c1017b24b0cf72644f179460d3a599a1b9bf9d0949d7e
4
- data.tar.gz: d245d65f31c08f15c0f9bad5d4e13e3d7cb1785936772ab6a83887cd4fdf4564
3
+ metadata.gz: '07113181085c0df0c8f4bc34aca661d4c29fb9bbc309012ace52b36e8bdf9ead'
4
+ data.tar.gz: 773d7de6a820fd1702e4e618c595237e2293c74dca6cc83f3ea57ba9eaaa18db
5
5
  SHA512:
6
- metadata.gz: 286af4633230ddd40c39e40c2ec92f810bd47cc533c1d578dc42178179552518bfc057f992beef340cc4a34592c603d0ddc6d10eef1df928ce83be383cd9857c
7
- data.tar.gz: 82e587aac72882cb1b3e317d20d2dc37c54ed8e927fc39ffa9d28dd871d77b05c6d2fc64ad13dc5e3fe7c903b6f5312dfa0b34e9e46391e66788435286c909e9
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` 指定時だけ引数なしで明示的に初期化(初期化引数の CLI 受け渡しは未対応なので、必要なら pre-script や自前ファクトリで注入) |
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` simply instantiates without passing CLI args (use pre-scripts or your own factories if you need injected dependencies) |
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) { JSON.parse(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) {
@@ -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 Instantiate the class/module before invoking CLI commands
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 '--new', '-n'
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
 
@@ -34,6 +34,7 @@ module Rubycli
34
34
  def reset!
35
35
  @metadata_cache.clear
36
36
  @comment_extractor.reset!
37
+ @parser.reset_type_dictionary_cache! if @parser.respond_to?(:reset_type_dictionary_cache!)
37
38
  end
38
39
 
39
40
  private
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rubycli
4
- VERSION = '0.1.6'
4
+ VERSION = '0.1.7'
5
5
  end
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
- target.new
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
- runner_target = new ? instantiate_target(target) : target
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 ? " (#{defined_constants.size} total)" : ''
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.6
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-11 00:00:00.000000000 Z
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