|
| 1 | +--- |
| 2 | +title: Meta Methodology |
| 3 | +date: 2015-05-25 |
| 4 | +tags: core, meta |
| 5 | +--- |
| 6 | + |
| 7 | +Ruby **clutters** its objects a lot with methods for metaprogramming other methods: |
| 8 | + |
| 9 | +ARTICLE |
| 10 | + |
| 11 | + Class.instance_methods.grep /method/ |
| 12 | + => [:instance_methods, :public_instance_methods, |
| 13 | + :protected_instance_methods, :private_instance_methods, |
| 14 | + :method_defined?, :public_method_defined?, |
| 15 | + :private_method_defined?, :protected_method_defined?, |
| 16 | + :public_class_method, :private_class_method, :instance_method, |
| 17 | + :public_instance_method, :methods, :singleton_methods, |
| 18 | + :protected_methods, :private_methods, :public_methods, |
| 19 | + :method, :public_method, :singleton_method, |
| 20 | + :define_singleton_method] |
| 21 | + |
| 22 | + Class.private_instance_methods.grep /method/ |
| 23 | + => [:method_added, :method_removed, :method_undefined, |
| 24 | + :remove_method, :undef_method, :alias_method, :define_method, |
| 25 | + :instance_methods_for, :__method__, :singleton_method_added, |
| 26 | + :singleton_method_removed, :singleton_method_undefined, |
| 27 | + :method_missing] |
| 28 | + |
| 29 | +It's so many methods, because working with methods is a multi-dimensional problem: |
| 30 | + |
| 31 | +- Where are the modules, in the current instance or if a class/module, in its instances? |
| 32 | +- Or in its [singleton class](http://www.devalot.com/articles/2008/09/ruby-singleton)? |
| 33 | +- What about the method visibility? |
| 34 | +- Should the inheritance chain be considered? |
| 35 | + |
| 36 | +Let's put everything in some order: |
| 37 | + |
| 38 | +## Method Lists |
| 39 | + |
| 40 | +Methods returning method lists always take a boolean argument, which will prevent inheritance if set to `false` |
| 41 | + |
| 42 | +Method | From | Target | Visibility |
| 43 | +------------------------------------------------------------------------------------------------------------------|-----------|----------------------|------------------- |
| 44 | +[Object#singleton_methods](http://ruby-doc.org/core-2.2.2/Object.html#method-i-singleton_methods) | instance | singleton | public + protected |
| 45 | +[Object#methods](http://ruby-doc.org/core-2.2.2/Object.html#method-i-methods) | instance | instance + singleton | public + protected |
| 46 | +[Object#public_methods](http://ruby-doc.org/core-2.2.2/Object.html#method-i-public_methods) | instance | instance + singleton | public |
| 47 | +[Object#protected_methods](http://ruby-doc.org/core-2.2.2/Object.html#method-i-protected_methods) | instance | instance + singleton | protected |
| 48 | +[Object#private_methods](http://ruby-doc.org/core-2.2.2/Object.html#method-i-private_methods) | instance | instance + singleton | private |
| 49 | +[Module#instance_methods](http://ruby-doc.org/core-2.2.2/Module.html#method-i-instance_methods) | class | instance | public + protected |
| 50 | +[Module#public_instance_methods](http://ruby-doc.org/core-2.2.2/Module.html#method-i-public_instance_methods) | class | instance | public |
| 51 | +[Module#proected_instance_methods](http://ruby-doc.org/core-2.2.2/Module.html#method-i-proected_instance_methods) | class | instance | proected |
| 52 | +[Module#private_instance_methods](http://ruby-doc.org/core-2.2.2/Module.html#method-i-private_instance_methods) | class | instance | private |
| 53 | +{:.table-38-15-20-X} |
| 54 | + |
| 55 | +- There is no API for getting a list of private singleton methods |
| 56 | + |
| 57 | +## Method Defined? Checks |
| 58 | + |
| 59 | +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: |
| 60 | + |
| 61 | +Method | From | Target | Visibilitiy |
| 62 | +--------------------------------------------------------------------------------------------------------------------|-------|----------|------------ |
| 63 | +[Module#method_defined?](http://ruby-doc.org/core-2.2.2/Module.html#method-i-method_defined-3F) | class | instance | all |
| 64 | +[Module#public_method_defined?](http://ruby-doc.org/core-2.2.2/Module.html#method-i-public_method_defined-3F) | class | instance | public |
| 65 | +[Module#protected_method_defined?](http://ruby-doc.org/core-2.2.2/Module.html#method-i-protected_method_defined-3F) | class | instance | protected |
| 66 | +[Module#private_method_defined?](http://ruby-doc.org/core-2.2.2/Module.html#method-i-private_method_defined-3F) | class | instance | private |
| 67 | +{:.table-38-15-20-X} |
| 68 | + |
| 69 | +- This is also the best way to get the visibility of a method |
| 70 | +- There is no direct way to check for singleton methods |
| 71 | + |
| 72 | +## Method Getters |
| 73 | + |
| 74 | +These methods will return method objects for further metaprogramming action: |
| 75 | + |
| 76 | +Method | From | Target | Visibility | Returns |
| 77 | +------------------------------------------------------------------------------------------------------------|-----------|----------------------|------------|-------- |
| 78 | +[Object#singleton_method](http://ruby-doc.org/core-2.2.2/Object.html#method-i-singleton_method) | instance | singleton | all | [Method](http://ruby-doc.org/core-2.2.2/Method.html) |
| 79 | +[Object#method](http://ruby-doc.org/core-2.2.2/Object.html#method-i-method) | instance | instance + singleton | all | [Method](http://ruby-doc.org/core-2.2.2/Method.html) |
| 80 | +[Object#public_method](http://ruby-doc.org/core-2.2.2/Object.html#method-i-public_method) | instance | instance + singleton | public | [Method](http://ruby-doc.org/core-2.2.2/Method.html) |
| 81 | +[Module#instance_method](http://ruby-doc.org/core-2.2.2/Module.html#method-i-instance_method) | class | instance | all | [UnboundMethod](http://ruby-doc.org/core-2.2.2/UnboundMethod.html) |
| 82 | +[Module#public_instance_method](http://ruby-doc.org/core-2.2.2/Module.html#method-i-public_instance_method) | class | instance | public | [UnboundMethod](http://ruby-doc.org/core-2.2.2/UnboundMethod.html) |
| 83 | +{:.table-34-15-20-14-X} |
| 84 | + |
| 85 | +- There are no methods to explicitely get private methods |
| 86 | + |
| 87 | +## Method Manipulation |
| 88 | + |
| 89 | +These methods will actually modify your objects: |
| 90 | + |
| 91 | +Method | From | Target | Visibility |
| 92 | +--------------------------------------------------------------------------------------------------------------|-----------|-----------|----------- |
| 93 | +[Object#define_singleton_method](http://ruby-doc.org/core-2.2.2/Object.html#method-i-define_singleton_method) | instance | singleton | public |
| 94 | +[Module#define_method](http://ruby-doc.org/core-2.2.2/Module.html#method-i-define_method) (private) | class | instance | public (see notes) |
| 95 | +[Module#remove_method](http://ruby-doc.org/core-2.2.2/Module.html#method-i-remove_method) (private) | class | instance | - |
| 96 | +[Module#undef_method](http://ruby-doc.org/core-2.2.2/Module.html#method-i-undef_method) (private) | class | instance | - |
| 97 | +[Module#alias_method](http://ruby-doc.org/core-2.2.2/Module.html#method-i-alias_method) (private) | class | instance | same |
| 98 | +{:.table-38-15-20-X} |
| 99 | + |
| 100 | +- No direct way to define a non-public method, but `define_method` respects visibility modifiers |
| 101 | +- No direct way to define a non-public singleton method |
| 102 | +- `remove_method` only deletes the method from the current module, while `undef_method` also deletes it from all ancestors |
| 103 | + |
| 104 | +## Method Hooks |
| 105 | + |
| 106 | +Hook methods can be defined and will be called by the Ruby interpreter when the respective event happens: |
| 107 | + |
| 108 | +Method | From | Target |
| 109 | +------------------------------------------------------------------------------------------------------------------------------|----------|------- |
| 110 | +[BasicObject#singleton_method_added](http://ruby-doc.org/core-2.2.2/BasicObject.html#method-i-singleton_method_added) | instance | singleton |
| 111 | +[BasicObject#singleton_method_undefined](http://ruby-doc.org/core-2.2.2/BasicObject.html#method-i-singleton_method_undefined) | instance | singleton |
| 112 | +[BasicObject#singleton_method_removed](http://ruby-doc.org/core-2.2.2/BasicObject.html#method-i-singleton_method_removed) | instance | singleton |
| 113 | +[Module#method_added](http://ruby-doc.org/core-2.2.2/Module.html#method-i-method_added) | class | instance |
| 114 | +[Module#method_undefined](http://ruby-doc.org/core-2.2.2/Module.html#method-i-method_undefined) | class | instance |
| 115 | +[Module#method_removed](http://ruby-doc.org/core-2.2.2/Module.html#method-i-method_removed) | class | instance |
| 116 | +[BasicObject#method_missing](http://ruby-doc.org/core-2.2.2/BasicObject.html#method-i-method_missing) | class | instance |
| 117 | +{:.table-38-15-20-X} |
| 118 | + |
| 119 | +- As long as you haven't defined a hook, Ruby considers it as an empty private method |
| 120 | + |
| 121 | +## Method Visibility Modifiers |
| 122 | + |
| 123 | +Besides `public`, `protected`, and `private`, there are two additional methods with the sole purpose of changing a method's visibility: |
| 124 | + |
| 125 | +Method | From | Target | Description |
| 126 | +--------------------------------------------------------------------------------------------------------|-------|-----------|------------ |
| 127 | +[Module#public_class_method](http://ruby-doc.org/core-2.2.2/Module.html#method-i-public_class_method) | class | singleton | Makes a class's singleton method public |
| 128 | +[Module#private_class_method](http://ruby-doc.org/core-2.2.2/Module.html#method-i-private_class_method) | class | singleton | Makes a class's singleton method private |
| 129 | +{:.table-34-12-12-X} |
| 130 | + |
| 131 | +## Current Method Name |
| 132 | + |
| 133 | +There are two underscore-wrapped methods that return the current method's name: |
| 134 | + |
| 135 | +Method | From | Returns |
| 136 | +--------------------------------------------------------------------------------------------------|----------|-------- |
| 137 | +[Kernel#\_\_method\_\_](http://ruby-doc.org/core-2.2.2/Kernel.html#method-i-__method__) (private) | anywhere | Original method name |
| 138 | +[Kernel#\_\_callee\_\_](http://ruby-doc.org/core-2.2.2/Kernel.html#method-i-__callee__) (private) | anywhere | Aliased method name |
| 139 | +{:.table-35-20-X} |
| 140 | + |
| 141 | +- Also see [Kernel#caller](http://ruby-doc.org/core-2.2.2/Kernel.html#method-i-caller) and [Kernel#caller_locations](http://ruby-doc.org/core-2.2.2/Kernel.html#method-i-caller_locations) |
| 142 | + |
| 143 | +## A Better API for Metaprogramming Methods? |
| 144 | + |
| 145 | +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](https://github.com/rubyworks/instance/). 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? |
0 commit comments