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
  • 155,156
  • 41
  • 207
  • 295
Julien Genestoux
  • 28,578
  • 19
  • 64
  • 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

4 Answers4

15

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
  • 161,055
  • 37
  • 262
  • 269
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
  • 15,553
  • 4
  • 37
  • 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
  • 1
    +1 to offset stupid downvoting – ryeguy Dec 07 '10 at 16:19
  • @ryeguy Thanks @Julien I've updated the example regardless – Lee Jarvis Dec 07 '10 at 16:20
  • 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
  • 284,740
  • 104
  • 634
  • 722
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
  • 29,906
  • 6
  • 69
  • 104
  • this wont match "[]", "[]=", "< – 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