Ruby clutters its objects a lot with methods for metaprogramming other methods:
Class.instance_methods.grep /method/
=> [:instance_methods, :public_instance_methods,
:protected_instance_methods, :private_instance_methods,
:method_defined?, :public_method_defined?,
:private_method_defined?, :protected_method_defined?,
:public_class_method, :private_class_method, :instance_method,
:public_instance_method, :methods, :singleton_methods,
:protected_methods, :private_methods, :public_methods,
:method, :public_method, :singleton_method,
:define_singleton_method]
Class.private_instance_methods.grep /method/
=> [:method_added, :method_removed, :method_undefined,
:remove_method, :undef_method, :alias_method, :define_method,
:instance_methods_for, :__method__, :singleton_method_added,
:singleton_method_removed, :singleton_method_undefined,
:method_missing]
It's so many methods, because working with methods is a multi-dimensional problem:
- Where are the modules, in the current instance or if a class/module, in its instances?
- Or in its singleton class?
- What about the method visibility?
- Should the inheritance chain be considered?
Let's put everything in some order:
Method Lists
Methods returning method lists always take a boolean argument, which will prevent inheritance if set to false
¹
Method | From | Target | Visibility |
---|---|---|---|
Object#singleton_methods | any | singleton | public + protected |
Object#methods | any | self + singleton | public + protected |
Object#public_methods | any | self + singleton | public |
Object#protected_methods | any | self + singleton | protected |
Object#private_methods | any | self + singleton | private |
Module#instance_methods | class | instances | public + protected |
Module#public_instance_methods | class | instances | public |
Module#protected_instance_methods | class | instances | protected |
Module#private_instance_methods | class | instances | private |
- There is no API for getting a list of private singleton methods
- ¹ A special case is the
methods
method: Iffalse
is given as argument, it will switch target to singleton only
Method Defined? Checks
Instead of listing all methods and checking if the resulting array contains a specific method, you can also directly check if a method is defined.
Since Ruby 2.6, you can also pass in a boolean as second argument which will ignore the inheritance chain (similar like it is with the method listings above).
Method | From | Target | Visibility |
---|---|---|---|
Module#method_defined? | class | instances | all |
Module#public_method_defined? | class | instances | public |
Module#protected_method_defined? | class | instances | protected |
Module#private_method_defined? | class | instances | private |
- This is also the best way to get the visibility of a method
- There is no direct way to check for singleton methods
Method Getters
These methods will return method objects for further metaprogramming action:
Method | From | Target | Visibility | Returns |
---|---|---|---|---|
Object#singleton_method | any | singleton | all | Method |
Object#method | any | self + singleton | all | Method |
Object#public_method | any | self + singleton | public | Method |
Module#instance_method | class | instances | all | UnboundMethod |
Module#public_instance_method | class | instances | public | UnboundMethod |
- There are no methods to explicitly get private methods
Method Manipulation
These methods will actually modify your objects:
Method | From | Target | Visibility |
---|---|---|---|
Object#define_singleton_method | any | singleton | public |
Module#define_method (private) | class | instances | public (see notes) |
Module#remove_method (private) | class | instances | - |
Module#undef_method (private) | class | instances | - |
Module#alias_method (private) | class | instances | same |
- No direct way to define a non-public method, but
define_method
respects visibility modifiers - No direct way to define a non-public singleton method
remove_method
only deletes the method from the current module, whileundef_method
also deletes it from all ancestors
Method Hooks
Hook methods can be defined and will be called by the Ruby interpreter when the respective event happens:
Method | From | Target |
---|---|---|
BasicObject#singleton_method_added | any | singleton |
BasicObject#singleton_method_undefined | any | singleton |
BasicObject#singleton_method_removed | any | singleton |
Module#method_added | class | instances |
Module#method_undefined | class | instances |
Module#method_removed | class | instances |
BasicObject#method_missing | class | instances |
- As long as you haven't defined a hook, Ruby considers it as an empty private method
Method Visibility Modifiers
Besides public
, protected
, and private
, there are two additional methods with the sole purpose of changing a method's visibility:
Method | From | Target | Description |
---|---|---|---|
Module#public_class_method | class | singleton | Makes a class's singleton method public |
Module#private_class_method | class | singleton | Makes a class's singleton method private |
Current Method Name
There are two underscore-wrapped methods that return the current method's name:
Method | From | Returns |
---|---|---|
Kernel#__method__ (private) | any | Original method name |
Kernel#__callee__ (private) | any | Aliased method name |
- Also see Kernel#caller and Kernel#caller_locations
A Better API for Metaprogramming Methods?
Metaprogramming in Ruby has evolved over time, but it might be a good idea to clean it up a little - A good example of how to clean up one of Ruby's other metaprogramming APIs is the instance gem. It gives you a neat API for working with an object's state, like setting instance variables. Someone feels like building a similar gem for Ruby's Method APIs? 2019 Update: I gave it a try! You can see the results here: object_shadow
More Idiosyncratic Ruby
- Please Comment on GitHub
- Next Article: File Encoding Magic
- Previous Article: Goto Fail