2

Our app uses the send method to call functions on our objects. Unfortunately, some times the string passed to send may not be a legit method name in Ruby. Does anyone know of a regexp that would allow us to check this?

And by legit, I mean a method name that doesn't start with "?", etc. I don't care whether the object responds to the method, because we use method_missing in this case, and we actually want it to be used, which would only happen for methods for which the object doesn't respond.

Technically, I'm looking for a regexp which does this :

Ruby identifiers are consist of alphabets, decimal digits, and the underscore character, and begin with a alphabets(including underscore). There are no restrictions on the lengths of Ruby identifiers.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Julien Genestoux
  • 31,046
  • 20
  • 66
  • 93
  • I thought there was a question in SO about a regex to match a variable name, which'd be somewhat relevant to this question, but I can't find it. – Andrew Grimm Dec 07 '10 at 22:30
  • If you arrived here looking for a regex that you could use to roughly search for where a method is being used, you could use (ripgrep example): `rg '(^|[^_a-zA-Z0-9:])bunny([^_a-zA-Z0-9:]|$)'` <-- this is pretty liberal, but Ruby allows a lot of edge cases – edmqkk Sep 01 '22 at 20:57

4 Answers4

16

You can take advantage of the fact that Symbol#inspect quotes the name when it is not a valid identifier. A valid symbol becomes ":hello!" when inspected, but an invalid one becomes ":\"invalid!?\"". This handles exotic symbols like :foo=, :[]=, and any other valid method name.

Adding the additional requirement that @-names are valid identifiers but not valid method names gives us this:

class Symbol
  def method_name?
    return /[@$"]/ !~ inspect
  end
end
Josh Lee
  • 171,072
  • 38
  • 269
  • 275
  • 1
    Wow, very elegant :) I'd +1 you if I hadn't used up my votes today. – Phrogz Dec 07 '10 at 21:15
  • I like it! I'll +1 you as I haven't used up mine just yet :) – Lee Jarvis Dec 07 '10 at 21:24
  • This is the best way to handle the situation. Rely on Ruby to use its heuristics rather than try to recreate them on your own, then sense when there's a failure/mismatch. I think it should be part of Object instead of Symbol though, but Symbol will do. – the Tin Man Dec 08 '10 at 00:12
  • You can make the above solution a bit shorter/nicer by replacing the line `return (not /[@"]/ =~ inspect)` with `/[@"]/ !~ inspect` – hawx Jul 01 '11 at 15:30
  • Don't forget about globals: /[@$"]/ would be safer. – Conrad Irwin Jul 28 '11 at 19:56
5

What if it is a legit method name, but the method doesn't actually exist on the object you're attempting to send it to?

Either way, you should check that the object responds to a method before attempting to invoke it, no matter if the string is a legit method name of not. You can do this by using the Object#respond_to? method.

For example:

obj = Person.new
method = "set_name"

if obj.respond_to?(method)
  obj.send(method, "foo")
end

If you want to make sure a string is a legit method name you'd want to use regular expressions.. something like /^([a-zA-Z_]+|\[\])[\?!=]?$/ should work for general methods. Regardless my point still stands for making sure this object responds to a method name

Lee Jarvis
  • 16,031
  • 4
  • 38
  • 40
  • A legit method name is a method that is recognized by Ruby as a method name... for example `?echo` isn't a legit method name. – Julien Genestoux Dec 07 '10 at 16:14
  • 1
    Right, am I'm saying you should be checking that the object responds to the method before invoking it. Did you try it before downvoting? – Lee Jarvis Dec 07 '10 at 16:16
  • Hum. no, I don't want to check whether the object responds, because basically n our case, the object responds to _all_ methods. We use method_missing. – Julien Genestoux Dec 07 '10 at 16:33
  • And, yes, I asked for a regex. I'm sorry you felt offended, but your response wasn't answering the question at all when you initially wrote it, and it still doesn't, because I'm looking for a regexp that matches all possible ruby method names. For example, it doesn't match `hello1` which is a legit method name. – Julien Genestoux Dec 07 '10 at 16:34
  • 1
    Julien, you edited your message with the regex tag after I had posted. The whole point of using `method_missing` is so you don't have to check if the method name is a legit method name or not. I think you need to show more code so people can understand what you're trying to do. I assure you just having the regexp isn't much help if the rest of the logic is wrong – Lee Jarvis Dec 07 '10 at 16:39
  • Sorry, look at the revision history, regex was here from the begining and even as a tag! – Julien Genestoux Dec 07 '10 at 21:28
1

Ruby method identifiers allowed in source code can be matched by this:

# Yes, upper-case first letters are legal
/\A(?:[a-z_]\w*[?!=]?|\[\]=?|<<|>>|\*\*|[!~+\*\/%&^|-]|[<>]=?|<=>|={2,3}|![=~]|=~)\z/i 

However, note that methods may be defined that don't match this pattern:

class Foo
  define_method("!dumb~name"){ puts "yup" }
 end
 Foo.new.send('!dumb~name')
 #=> yup

Edit: Updated to add all the Operator Expressions.

Phrogz
  • 296,393
  • 112
  • 651
  • 745
0

I'm not so sure regexp is the way to go here but here's a try

def could_be_method_name?(m)
   match = /^([a-zA-Z_]([\w]*[\w!?=]$){0,1})/.match(m)
   return match == nil ? false : match[1].length==m.length
end

Ruby method names must start with a letter or an underscore. They can only contain alphanumeric characters but the last character is allowed to also be !, ?, or =.

Jonas Elfström
  • 30,834
  • 6
  • 70
  • 106
  • this wont match "[]", "[]=", "<<", etc or uppercase method names (which are valid, just not conventional) – Lee Jarvis Dec 07 '10 at 16:47
  • @JonaElfström Instead of testing the lengths, you should anchor your regex at the start and end of the input: `/\A...\z/`. (While it is unlikely that there would be a newline in this context, note that `^` is only start of line, not start of input.) – Phrogz Dec 07 '10 at 16:57