为何Module#define_method会无视Ruby方法命名规则?
define_method allow almost any string as a method name, ignoring Ruby's method naming rules? Great question! Let's break this down clearly:
The core difference: Syntax parsing vs runtime API
When you use the def keyword, you're working within Ruby's syntax parsing stage. The Ruby parser enforces strict rules for method names here—they have to be valid identifiers (start with a letter/underscore, followed by letters, numbers, or underscores; no special characters like . allowed). That's why this throws a syntax error immediately:
def the.dot :dot end # Syntax error, unexpected '.'
The parser sees the as a variable, then .dot as an attempt to call a dot method on it—it never recognizes the.dot as a single method name.
On the other hand, define_method is a runtime API. It skips the syntax parsing checks entirely, and directly modifies the method table of the current class/module. Ruby's method tables are essentially hash maps where keys are symbols—and Ruby lets you create symbols from almost any string (even those with dots, spaces, or other special characters).
Example walkthrough
When you run this code:
define_method('the.dot') { :dot } #=> :"the.dot" method('the.dot') #=> #<Method: main.the.dot>
You're telling Ruby to add a new entry to the method table: the symbol :"the.dot" maps to your block. No syntax parser is involved here—you're just manipulating the runtime state directly.
Why regular calls fail, but runtime dispatch works
When you try the.dot, you're back to the syntax parser. It interprets the as an object and .dot as a call to a dot method on that object—hence the NameError because the isn't defined.
But methods like public_send or method(name).call are runtime calls that take a symbol/string as the method name. They bypass the syntax parser and look directly in the method table for the matching symbol:
method('the.dot').call #=> :dot public_send('the.dot') #=> :dot
Quick summary
defis bound by Ruby's syntax rules because it's processed during parsing.define_methodoperates at runtime, directly interacting with Ruby's method table, which allows any valid symbol (derived from almost any string) as a method name.- Calling these "unconventional" methods requires runtime dispatch (like
send) since the regular dot-syntax can't parse their names.
内容的提问来源于stack exchange,提问作者Andrey Deineko

