define_method: dynamically create method using blocks - within some specific scope
class_eval (aka module_eval): Use when you want into a class but you can't reopen the class either because you don't kn\
ow the class name yet, or because you don't want to change scope
instance_eval: opens an object and executes code in the object's scope
Kernal#eval(string): evaluates its argument (which is usually a string of code)
Object#send(:method_name, args) (e.g. my_thing.send(:method_name, args)): Calls method_name on object, passing in the a\
rguments, args.
method_missing(name, args, &block): dynamically creates a method when you call a method that does not exist in this obj\
ect or its inheritance tree.
instance_variable_set: sets an instance variable dynamically (use this when you don't know the name of the instance var\
iable until run time.
instance_variable_get: returns an instance variable dynamically (use this when you don't know the name of the instance \
variable until run time.
------------
def self.inherited(base) ...... end : when base inherits from self, run this method
def self.included(base) ..... end : runs the code in this method when self is included into another object
def self.include(*modules) ...... end : run this code when you include the modules on the list; REMEMBER to call super.
object.include(ModuleName): add ModuleName's methods as the object's instance methods.
object.extend(ModuleName): add ModuleName's methods that go into object's eigenclass.
Same as: class << self
include ModuleName
end