From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on starla X-Spam-Level: X-Spam-Status: No, score=0.1 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_BL_SPAMCOP_NET,SPF_HELO_PASS, SPF_PASS autolearn=no autolearn_force=no version=3.4.6 Received: from nue.mailmanlists.eu (nue.mailmanlists.eu [94.130.110.93]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by dcvr.yhbt.net (Postfix) with ESMTPS id 46C721F4CC for ; Wed, 8 Jan 2025 01:50:51 +0000 (UTC) Authentication-Results: dcvr.yhbt.net; dkim=pass (1024-bit key; unprotected) header.d=ml.ruby-lang.org header.i=@ml.ruby-lang.org header.a=rsa-sha256 header.s=mail header.b=ENuRthRS; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=ruby-lang.org header.i=@ruby-lang.org header.a=rsa-sha256 header.s=s1 header.b=cGttoz97; dkim-atps=neutral DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ml.ruby-lang.org; s=mail; t=1736301019; bh=iMX2CQzMFCWsbyPVs0ULH/ZSxikBK8SVa+YS01N/UEI=; h=Date:References:To:Reply-To:Subject:List-Id:List-Archive: List-Help:List-Owner:List-Post:List-Subscribe:List-Unsubscribe: From:Cc:From; b=ENuRthRScBm0/S4jkjdbFC+M9EwNtyQs64/+sDZq8CrL97rf16bn+45cxuXFDo4kd zDYB8OtdNjRW8yfzKddAywb4WxV2/zrqV4h8DM+zyeUFoPPgX+5/wHvvYlqiPUNgar ayJDxR/p1Fx1JKRVaG6CHbvbPJZ3U9sx52B2gY6w= Received: from nue.mailmanlists.eu (localhost [IPv6:::1]) by nue.mailmanlists.eu (Postfix) with ESMTP id 689AD46993 for ; Wed, 8 Jan 2025 01:50:19 +0000 (UTC) Authentication-Results: nue.mailmanlists.eu; dkim=pass (2048-bit key; unprotected) header.d=ruby-lang.org header.i=@ruby-lang.org header.a=rsa-sha256 header.s=s1 header.b=cGttoz97; dkim-atps=neutral Received: from s.wrqvtzvf.outbound-mail.sendgrid.net (s.wrqvtzvf.outbound-mail.sendgrid.net [149.72.126.143]) by nue.mailmanlists.eu (Postfix) with ESMTPS id EDD4846956 for ; Wed, 8 Jan 2025 01:50:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ruby-lang.org; h=from:references:subject:mime-version:content-type: content-transfer-encoding:list-id:to:cc:content-type:from:subject:to; s=s1; bh=z11gLDvkk5Ir6MFXfqJjM90jc+GAf+GchEblVqxPi28=; b=cGttoz97RLixIy9UAU6d0nr7kHAeo1N2I0GTd5iu2T6T/ah1zeHGKX3T6Ngo9NS15rTT 9396PAdJ2rxFThw3HihVNk063gkKHPFnSvRsqCOBqtsOhCkYbjoprj6VlmROr3hHaU75TS S8yr7P7Yo0hILq0tlxFmfKoVi/dAKFMxE+WKjBsnsoPrMuQB8Nz3mTOzASmFYsW4zX06PW MS+oVdRm9w+/sCWLZnmU3bnryxQ+kF69B7nA/1e79gOELjagV+aXhXrxvdiQBjrEAiiimJ QM9USpmT7hLArmCsDzd5aWABzK2U+hx5G7GvCWcN+THsxbwBsenxtzKdZawizhvQ== Received: by recvd-84b546689d-94c5v with SMTP id recvd-84b546689d-94c5v-1-677DD9D4-4 2025-01-08 01:50:12.140745079 +0000 UTC m=+4681724.282886126 Received: from herokuapp.com (unknown) by geopod-ismtpd-25 (SG) with ESMTP id I86bA2JhQ-OFg4K4heShxA for ; Wed, 08 Jan 2025 01:50:12.048 +0000 (UTC) Date: Wed, 08 Jan 2025 01:50:12 +0000 (UTC) Message-ID: References: Mime-Version: 1.0 X-Redmine-Project: ruby-master X-Redmine-Issue-Tracker: Feature X-Redmine-Issue-Id: 21015 X-Redmine-Issue-Author: sampersand2 X-Redmine-Issue-Priority: Normal X-Redmine-Sender: sampersand2 X-Mailer: Redmine X-Redmine-Host: bugs.ruby-lang.org X-Redmine-Site: Ruby Issue Tracking System X-Auto-Response-Suppress: All Auto-Submitted: auto-generated X-Redmine-MailingListIntegration-Message-Ids: 97221 X-SG-EID: =?us-ascii?Q?u001=2EaRyJ3f5qZB1SavJGSkl5hD9+7zdUjXU8OVuvzuaY5pXJ1G=2FJRzAky2CY6?= =?us-ascii?Q?fUyZQgLO9o45KDebiY6BQvvF0yWbVCJzbNWb5v6?= =?us-ascii?Q?Z7GlxiroQX8jOc02K1R=2FmYLX+lFU=2F3GctZQXWs9?= =?us-ascii?Q?xEJ=2FFEIyW5bb5M6PlIj5xZVn31IZ76wu0kYYZQz?= =?us-ascii?Q?D1j8s5rCCw0oFOCMi+VO3cmpTKudT4gGT2V4FQ+?= =?us-ascii?Q?2JkmxDJ=2F1x=2FbFB3FVQso9KiKyv2rtq0NMHJ+ZRi?= =?us-ascii?Q?f8bav907CWAtMQbWsZ=2FtEuZjEQ=3D=3D?= To: ruby-core@ml.ruby-lang.org X-Entity-ID: u001.I8uzylDtAfgbeCOeLBYDww== Message-ID-Hash: RTL4FOARO7V5L6CN6EAT3G53O6XTYBCU X-Message-ID-Hash: RTL4FOARO7V5L6CN6EAT3G53O6XTYBCU X-MailFrom: bounces+313651-b711-ruby-core=ml.ruby-lang.org@em5188.ruby-lang.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.9 Precedence: list Reply-To: Ruby developers Subject: [ruby-core:120545] [Ruby master Feature#21015] Add in a `-g` flag, like `-s` but with a few more quality of life features List-Id: Ruby developers Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: "sampersand2 (Sam Westerman) via ruby-core" Cc: "sampersand2 (Sam Westerman)" Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Issue #21015 has been reported by sampersand2 (Sam Westerman). ---------------------------------------- Feature #21015: Add in a `-g` flag, like `-s` but with a few more quality of life features https://bugs.ruby-lang.org/issues/21015 * Author: sampersand2 (Sam Westerman) * Status: Open ---------------------------------------- Add in a `-g` flag, like `-s` but with a few more QOL features ## TL;DR ([PR](https://github.com/ruby/ruby/pull/12526).) Ruby's `-s` flag has quite a few shortfalls that make it difficult to use when writing scripts . A new `-g` flag will fix it: ```shell # Support `-abc` as a shorthand for `-a -b -c` ruby -ge'p [$a, $b, $c]' -- -abc #=> [1, 1, 1] # Support `-vvv` as a shorthand for `-v=3` ruby -ge'p $v' -- -vvv #=> 3 # Support things other than strings: ruby -ge'p [$n, $f]' -- -n90 -f=false #=> [90, false] # long-forms don't have a leading `_` ruby -ge'p $foo_bar' -- --foo-bar #=> true ``` I propose a `-g` flag to fix it. # Background: What is `-s` Ruby's `-s` flag is an extremely helpful feature when writing short little scripts to set config options; it automatically processes `ARGV` and removes leading "flags" for you, assigning them to global variables. ```ruby #!/bin/ruby -s # echo, ruby-style! If `-n` is given, no newline is printed print ARGV.join(' '), ($n ? "" : "\n") ``` While `-s` is significantly less powerful than `OptionParse`, or even via just parsing options yourselves, it's **incredibly** useful when writing simple little scripts where the option parsing code would be longer than the script itself. ## Problem 1 (The big one): No support for chained short-form flags The biggest problem with `-s` is that it doesn't let you concatenate short-form flags together: conventionally, `./myprogram -x -y -z` should be the same as `./myprogram -xyz`. However, if `./myprogram` were a ruby program using `-s`, you'd end up with the global variable `$xyz`. Short scripts that use `-s` to parse options thus need to add the following code to handle all different permutations of `-x`, `-y`, and `-z`: ```ruby # Handle all permutations of `-xyz` $xyz || $xzy || $yxz || $yzx || $zxy || $zyx and $x=$y=$z=true # Handle only two options given $xy || $yx and $x=$y=true $xz || $zx and $x=$z=true $yz || $zy and $y=$z=true ``` Four flags becomes even worse: ```ruby $wxyz || $wxzy || $wyxz || $wyzx || \ $wzxy || $wzyx || $xwyz || $xwzy || \ $xywz || $xyzw || $xzwy || $xzyw || \ $ywxz || $ywzx || $yxwz || $yxzw || \ $yzwx || $yzxw || $zwxy || $zwyx || \ $zxwy || $zxyw || $zywx || $zyxw and $w=$x=$y=$z=true $wxy || $wyx || $xwy || $xyw || $ywx || $yxw and $w=$x=$y=true $wxz || $wzx || $xwz || $xzw || $zwx || $zxw and $w=$x=$z=true $wyz || $wzy || $ywz || $yzw || $zwy || $zyw and $w=$y=$z=true $xyz || $xzy || $yxz || $yzx || $zxy || $zyx and $x=$y=$z=true $wx || $xw and $w=$x=true $wy || $yw and $w=$y=true $wz || $zw and $w=$z=true $xy || $yx and $x=$y=true $xz || $zx and $x=$z=true $yz || $zy and $y=$z=true ``` This is a huge problem for simple little scripts, as they're forced into an uncomfortable choice: 1. Use `OptionParser`, which is very much overkill for tiny scripts 2. Break from unix standards and require passing short-forms individually (i.e. `./program.rb -x -y -z`) 3. Do the cumbersome permutation checks as shown above 4. Give up on flags all together and use environment variables. None of these options are great, *especially* because Ruby's all about programmer happiness and none of these options spark joy. ## Problem 2: All values are `Strings` Less important than the short-form issue is that all values provided to flags become Strings (e.g. `ruby -se'p $foo' -- -foo=30` yields `"30"`). While this can be solved by `$foo = $foo&.to_i` somewhere early on, it's verbose: ```ruby #!ruby -s # Very simple benchmarking program $log_level = $log_level&.to_i $amount = $amount&.to_i $timeout = $timeout&.to_i $precision = $precision&.to_i $amount.times do |iter| start = Time.now $log_level > 1 and puts "[iteration: #{iter}] running: #{ARGV}" pipe = IO.popen ARGV unless select [pipe], nil, nil, $timeout warn "took too long!" next end printf "%0.#{$precision}f", Time.now - start end ``` Moreover, there's no way to create a falsey flag other than just omitting it, which can make interacting with `-s` scripts somewhat irritating: ```ruby # Have to use `*[... ? ... : nil].compact`, otherwise # we'd end up passing an empty string/nil as an arg system("./myprogram.rb", *[enable_foo ? "--foo" : nil].compact) ``` A better solution would be to allow for `--foo=false` or `--foo=true`, and then parse the `false`/`true`. ## Problem 3 (Minor): Long-form options have a leading `_` Long-form options, such as `--help`, have a leading `_` appended to them (to disambiguate them from `-help`). While it can be worked around (either `alias $help $_help` or just using `$_help` directly), it's irritating enough that I've been known to just force end-users to use `-help`. This diverges from the common "long-form options should have two dashes in front of them," further making ruby scripts with `-s` a bit awkward to use ## Problem 4 (Minor): Repeated flags aren't supported Sometimes it's useful to know how many times a flag was repeated, such as `-vvv` to enable "very very verbose mode": Using `-s` you must do ```ruby is_verbose = $v ? 1 : ($vv ? 2 : ($vvv ? 3 : ($vvvv ? 4 : ...))) ``` While I've yet to see a use-case for repeating a flag beyond three or four times, having to manually enumerate everything out is, again, a bit of a pain. # Solution: Add a new `-g` flag, which supports all this I propose the addition of a new command-line flag, `-g`, which adds in a new form of argument parsing that solves all these issues. [PR](https://github.com/ruby/ruby/pull/12526) ## Overview: ```shell ruby -ge'p [$a, $b, $c]' -- -abc #=> [1, 1, 1] ruby -ge'p $d' -- -d90 #=> 90 ruby -ge'p [$d, $e]' -- -d90e=foo #=> [90, "foo"] ruby -ge'p [$hi, $foo_bar]' -- --hi --foo-bar=false #=> [true, false] ruby -ge'p $x' -- -xxx #=> 3 # Putting it together now, lol. ruby -g -e'p [$a, $b, $c, $d, $e, $world]' -- -abbcd90e=hello --world=false # => [true, 2, true, 90, "hello", false]``` ``` # Open Questions ## How should supplying both `-g` and `-s` work? This is the first flag that'd directly conflict with another command-line flag, so what should be done when both `-g` and `-s` are given (such as `ruby -gs -e'p $x' -- -x=9`). Here's the options as I see them: 1. Use to last supplied flag (in the example `-s`). This'd act like how incompatible flags work in other utilities 2. Always use `-g`, as it can be considered a sort-of a "super set" of `-s`. 3. Emit a warning, and then do either `1.` or `2.` 4. Emit an error, and then do either `1.` or `2.` I'm personally partial to emitting a warning on `-W2`, and then using the last supplied flag, however I could be convinced to any of the options ## Should `-x` do `$x = true` or `$x = 1` I personally'd like `-x` to be `true`, just like `-s`. However, this conflicts with `-xx` yielding `2`, as `log if $x > 1` wouldn't work. Since `1` is truthy, I've defaulted `-x` to `1`, but I could be convinced to remove it (and even remove the entire `-xx` thing if there was a good argument.) ## Why introduce a new flag? Why name it `-g`? I think adding in a new flag makes sense: `-g` is a modification of `-s`, so it naturally should be a new flag. (Attempting to shoehorn these options into `-s` would be a nightmare.) I briefly considered using `-ss` as the flag name, to make it clear that it was a modification of `-s`, however that'd be the first two-character short flag and I didn't want to introduce that. (And, it'd be pretty ironic as a large portion of the impetus behind `-g` is to parse short flags, which `-ss` isn't :-P.) As for `-g` specifically, I considered `-o` for "options" (nixed because most utilities use it for "output," and I didn't want the cognitive overload), and `-f` for "flags" (nixed because some utilities use it to mean "file," and ruby might use it in the future.) I picked `-g` because it's short for "globals" or "getflags". # Alternatives (TODO) ## Continue using `-s` (TODO) not great, as shown above # Prior art (TODO) Perl has `-s`, and it works like Ruby's `-s`. IDK of any other languages which even attempt this. -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/