From: janosch-x Date: 2022-05-23T10:17:54+00:00 Subject: [ruby-core:108656] [Ruby master Feature#18788] Support passing Regexp options as String to Regexp.new Issue #18788 has been updated by janosch-x (Janosch M��ller). @nobu Thank you for the explanation regarding symbols! > we'll need migration period to warn anything other than inter and valid string I think the optional third argument should be also deprecated as described in #18797. Otherwise we might still want to allow `nil` as "stopgap" value for the second argument. > Regexp.new(code, eval("//#{options}").options) This feels too unsafe for some of the cases mentioned above. > Regexp.new("(?#{options}:#{code})") This is clever and will work for most of the use cases mentioned above. Just a few minor downsides: 1. The group options syntax isn't so well-known and universally understood. 2. A few use cases might need to preserve notation (e.g. codemod). 3. The result may become verbose in case of recursion or nesting of Regexps. ---------------------------------------- Feature #18788: Support passing Regexp options as String to Regexp.new https://bugs.ruby-lang.org/issues/18788#change-97699 * Author: janosch-x (Janosch M��ller) * Status: Open * Priority: Normal ---------------------------------------- ## Current situation `Regexp.new` takes an integer as second argument which needs to be ORed together from multiple constants: ``` Regexp.new('foo', Regexp::IGNORECASE | Regexp::MULTILINE | Regexp::EXTENDED) # => /foo/imx ``` Any other non-nil value is treated as `i` flag: ``` Regexp.new('foo', Object.new) # => /foo/i ``` ## Suggestion `Regexp.new` should support passing the regexp flags not only as an Integer, but also as a String or Symbol, like so: ``` Regexp.new('foo', 'i') # => /foo/i Regexp.new('foo', :i) # => /foo/i Regexp.new('foo', 'imx') # => /foo/imx Regexp.new('foo', :imx) # => /foo/imx # edge cases Regexp.new('foo', 'iii') # => /foo/i Regexp.new('foo', :iii) # => /foo/i Regexp.new('foo', '') # => /foo/ Regexp.new('foo', :'') # => /foo/ # unsupported flags could be ignored - # or raise an ArgumentError to reveal changed behavior? Regexp.new('foo', 'jmq') # => /foo/m Regexp.new('foo', :jmq) # => /foo/m Regexp.new('foo', '-m') # => /foo/m Regexp.new('foo', :'-m') # => /foo/m ``` ## Reasons 1. The constants are a bit cumbersome to use, particularly when building the regexp from variable data: ``` def make_regexp(regexp_body, opt_string) opt_int = 0 opt_int |= Regexp::IGNORECASE if opt_string.include?('i') opt_int |= Regexp::MULTILINE if opt_string.include?('m') opt_int |= Regexp::EXTENDED if opt_string.include?('x') Regexp.new(regexp_body, opt_int) end ``` 2. Passing a String or Symbol is already silently accepted, and people might get the wrong impression that it works: ``` Regexp.new('foo', 'i') # => /foo/i Regexp.new('foo', :i) # => /foo/i ``` ... but it doesn't really work: ``` Regexp.new('foo', 'x') # => /foo/i Regexp.new('foo', :x) # => /foo/i ``` ## Backwards compatibility This change would not be fully backwards compatible. Code that relies on the second argument being either a String/Symbol or nil to decide whether the Regexp should be case insensitive would break (unless the String or Symbol contains "i"). I can't come up with a scenario where one would write such code, though - except maybe code golfing? -- https://bugs.ruby-lang.org/ Unsubscribe: