2

I've recently returned to a Rails side project that's been sitting on the shelf for some time and am in the process of upgrading the various components, including Ruby and Rails. The app runs on Heroku, recently upgraded from cedar-14 to the heroku-16 stack. I've upgraded from Rails 4.1.0 to 4.2.10 (need to improve test coverage before pushing to Rails 5), and from Ruby 2.1.0 to 2.3.4-p301, and things are mostly working well. Unfortunately I'm seeing some regex behavior that I can't figure out. I'm referencing ruby-doc.org for the expected behavior.

The content is unimportant, but as an example, say I'm trying to parse a URL and capture the protocol, which I've always done with the following code:

m = /(https?)/.match("https://www.example.com")
=> #<MatchData "https" 1:"https"> 

m[1]
=> "https"

This still works as expected when I run the code above in the Console, both locally on my dev machine and on Heroku. But in the application itself, both locally and on Heroku, the first statement now returns the string "https" instead of the MatchData object, leading to the following:

m = /(https?)/.match("https://www.example.com")
=> "https" 

m[1]
=> "t"

In my searching and review of the documentation, I can't find any scenarios where a string is returned instead of a MatchData object.

Of course, I could just use the string, but that gives me pause for obvious reasons since I can't reconcile it with the docs. Has anyone seen this before? Maybe a bigger question is - can you suggest configs or other factors that might cause me to see different results in Rails C (expected behavior) vs. Rails S (unexpected behavior) on such straightforward code (doesn't touch database, etc.)?

Thanks so much for your help.

Bart
  • 1,341
  • 1
  • 9
  • 8
  • What would something like this return in both envs =>> puts "#{("https://www.yahoo.com"[/(http?s)/])}".class . Try yours with print class. – z atef Jun 17 '18 at 02:39
  • 3
    Is there a gem installed that's redefining that method? (["How to find where a method is defined at runtime?"](https://stackoverflow.com/questions/175655/how-to-find-where-a-method-is-defined-at-runtime), so like maybe `//.method(:match).source_location`) – Simple Lime Jun 17 '18 at 02:42
  • It might be useful to know how you know what it's returning in the application. Debugger with breakpoints? Logging the value? It's at least possible that the issue is there. Otherwise, I agree with @SimpleLime that trying to find if the method is being redefined would be the next step. – Max Jun 17 '18 at 02:48
  • 2
    Are you sure there's no hidden string interpolation (or `to_s` calls) in your app? – mu is too short Jun 17 '18 at 04:10
  • @Max So far I've used logging to check - will definitely give SimpleLime's suggestion a shot and report back – Bart Jun 17 '18 at 04:11
  • When I run your code on https://wandbox.org using different Ruby versions (1.8.7-2.5.*) it is working fine. – wp78de Jun 17 '18 at 05:24
  • @Bart what's the _exact_ code you have in your application? The one that results in the unexpected behavior. – Stefan Jun 18 '18 at 05:56

1 Answers1

0

Check to see if the match method has been redefined. For example, in a clean unmodified irb console, I would expect:

regex = /(https?)/
url_str = "https://www.example.com"

p regex.match(url_str)
p url_str.match(regex)
# => #<MatchData "https" 1:"https">

# should return nil, meaning it's defined in C and not in ruby code
p regex.method(:match).source_location

# reproduces your situation
class Regexp
  alias_method :old_match, :match

  def match(str)
    old_match(str).to_s
  end
end

p regex.match(url_str)
p url_str.match(regex)
# => "https"

# this should reveal if it was changed in a way similar to above
p regex.method(:match).source_location
Kache
  • 15,647
  • 12
  • 51
  • 79