Created
December 6, 2012 04:00
-
-
Save elgalu/4221679 to your computer and use it in GitHub Desktop.
My quiz on: Metaprogramming Ruby: Program Like the Ruby Pros by Paolo Perrotta
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
- Returns? | |
module Module | |
def const_missing(name) | |
"sorry i don't have #{name} yet" | |
end | |
end | |
Untitled #=> ? | |
Monkeys #=> ? | |
=> | |
You get `TypeError: Module is not a module` before reaching Untitled code | |
Because you must use `class Module` to open the class | |
- How to? | |
cad = 'stuff' | |
class << cad | |
def uno() 'uno' end | |
def self.dos() 'dos' end | |
end | |
??? #=> 'uno' | |
??? #=> 'dos' | |
=> | |
cad.uno #=> 'uno' | |
cad.singleton_class.dos #=> 'dos' | |
- Diffs bt `undef` `undef_method` `remove_method` | |
=> | |
undef | |
is a keyword | |
accepts method names or symbols or strings | |
available for any object | |
can not be overridden | |
undef_method | |
defined for Module | |
accepts symbols or strings | |
prevent any calls to the method affecting the ancestor chain | |
remove_method | |
same as undef_method but only removes it from current self | |
allowing the method to be found on the ancestors if defined | |
- Show the file and line number where some method is defined | |
=> | |
method(:irb_exit).source_location # 1.9 | |
#=> ["c:/.../1.9.1/irb/extend-command.rb", 22] | |
- Returns? | |
def leo(uno, dos=2, *params) | |
'hola' | |
end | |
method(:leo).parameters #=> ? | |
=> | |
[[:req, :uno], [:opt, :dos], [:rest, :params]] | |
- Diff bt is_a? and instance_of? | |
=> | |
obj.instance_of?(klass) | |
returns true if obj is a instance of klass and klass only | |
obj.is_a?(klass) or obj.kind_of?(klass) | |
same and also true if obj is an instance of one of klass ascendants | |
- Returns? | |
module Mod | |
def tres() end | |
end | |
class B | |
def B.cuatro() end | |
end | |
a = B.new | |
def a.uno() end | |
class << a | |
include Mod | |
def dos() | |
end | |
end | |
B.singleton_methods #=> ? | |
a.singleton_methods(false) #=> ? | |
a.singleton_methods #=> ? | |
=> | |
# singleton_methods(all=true) | |
# Public and protected. all: include methods in modules included in obj | |
B.singleton_methods #=> [:cuatro] | |
a.singleton_methods(false) #=> [:uno, :dos] | |
a.singleton_methods #=> [:uno, :dos, :tres] | |
- Returns? | |
cad1, cad2 = 'abc', 'leo' | |
cad1.instance_eval { def rev; reverse; end } | |
cad1.rev #=> ? | |
cad2.rev #=> ? | |
=> | |
cad1.rev #=> "cba" | |
cad2.rev #=> NoMethodError | |
# instance_eval makes current self cad1 and current class cad1.singleton_class | |
# Given `def` works on current class and creates instance methods | |
# And given the instance methods of cad1 singleton class are cad1 methods | |
# Then cad1.rev is possible | |
- Returns? | |
Math.singleton_class #=> ? | |
Mike = Module.new | |
Mike.singleton_class #=> ? | |
=> | |
Math.singleton_class #=> #<Class:Math> | |
Mike.singleton_class #=> #<Class:Mike> | |
- Returns? | |
class Module | |
def self.const_missing(name) | |
"sorry i don't have #{name} yet" | |
end | |
end | |
Untitled #=> ? | |
Monkeys #=> ? | |
=> | |
Untitled #=> NameError: uninitialized constant Untitled | |
Monkeys #=> NameError: uninitialized constant Monkeys | |
# In this case your const_missing method is never executed | |
# because `self.` is not meant to be used here. | |
# Untitled & Monkey are instances of Module so re-define const_missing as a Module instance method | |
- Returns? | |
class A | |
def self.mattr; self; end | |
mattr | |
end #=> ? | |
class B < A | |
mattr | |
end #=> ? | |
=> | |
class A ... end #=> A | |
class B ... end #=> B | |
# remember B is not an instance of A, is an instance of Class | |
# that inherites from A | |
- Where is extend() defined? describe what it does. | |
=> | |
Object#extend(*mods) <public> | |
Adds to obj the instance methods from each module | |
It is a shortcut for opening the singleton class and using `include` there | |
- Returns? | |
z = Object.new | |
class << z | |
instance_eval do | |
def foo() "test" end | |
end | |
end | |
z.foo #=> ? | |
=> | |
NoMethodError: undefined method `foo` | |
z.singleton_methods #=> [] | |
`foo` is defined as an singleton method of the singleton class of `z` | |
z.singleton_class.singleton_methods #=> [:foo] | |
z.singleton_class.foo #=> "test" | |
- By whom can protected methods be called? | |
=> | |
# Protected methods can only be called if the the caller of the method | |
# (the receiver) is of the same class (or subclass) of that method. | |
- Which are the 2/3/4 most common ways to generate methods dynamically? | |
=> | |
def meth() "hello" end | |
Class#define_method(name, *params) {proc} | |
Class#define_method(name, method) | |
# The method parameter can be a Proc, a Method or an UnboundMethod object. | |
thing.class.send(:define_method, name) do |*args| {proc} | |
class_eval { def meth; "hello"; end } | |
- What the `ancestors` method does? Where is defined? | |
=> | |
Module#ancestors returns all the modules where methods called will be searched for, | |
in that order [Module1, Module2, ..., Object, Kernel, BasicObject] | |
starting first place with the module//class in question | |
- Given: | |
module Mod | |
def mono() "works" end | |
end | |
class Foo | |
include Mod | |
end | |
class Bar | |
extend Mod | |
end | |
# Returns? | |
Foo.new.mono #=> ? | |
Foo.mono #=> ? | |
Bar.new.mono #=> ? | |
Bar.mono #=> ? | |
=> | |
Foo.new.mono #=> "works" | |
Foo.mono #=> NoMethodError `mono` for Foo:Class | |
Bar.new.mono #=> NoMethodError `mono` for #<Bar:0x33> | |
Bar.mono #=> "works" | |
- Get the list of class variables and the class instance variables. | |
Where are the methods defined? | |
How the result look in 1.8 vs 1.9? | |
=> | |
Object#instance_variables | |
Module#class_variables | |
sample_output #=> [:@ivar] / [:@@cvar] | |
Diff bt 1.8 and 1.9 is that 1.8 returns strings instead of symbols | |
- What techniques can be used to call private methods? | |
=> | |
c = "stuff" | |
c.upcase #=> "STUFF" | |
c.singleton_class.send :private, :upcase | |
=> #<Class:#<String:0x2a1a250>> | |
c.upcase #=> NoMethodError: private method `upcase` | |
# Besides `send` these also works: | |
c.instance_eval { upcase } | |
c.instance_eval "upcase" | |
- What are the things that can change the value of self? | |
=> | |
# An explicit reciever | |
`class` keyword | |
`def` keyword | |
Object#instance_eval | |
Module#class_eval | |
- Is it true that ruby creates a singleton class to store the method in this example? | |
class Foo | |
def self.meth | |
"things" | |
end | |
end | |
=> | |
Yes, class methods are actually stored as instance methods of the class singleton | |
- Modules can be used in many ways or purposes, describe them. | |
=> | |
+ As namespace constants organization for classes or global methods | |
+ As collections of methods to be mixed in later using include//extend | |
+ As scope separators for security reasons | |
- Returns? | |
arr = %w(c b a) | |
module Sor | |
def sor | |
self.sort | |
end | |
end | |
# What's the diff bt | |
arr.extend Sor | |
# vs | |
class << arr | |
include Sor | |
end | |
=> | |
No diff, Object#extend is actually a shortcut for doing | |
an include() within the singleton of the object in question | |
This technique is called a Class Extension | |
- Diff bt class_eval and instance_eval | |
=> | |
class_eval only works for Class types and | |
when `def` is evaluated in this context, instance methods are created | |
instance_eval works for any object and | |
when used on a Class//Module `def` defines class methods | |
- Returns? | |
class Module | |
def const_missing(name) | |
if name.to_s =~ /^U/ | |
"starts with U" | |
else | |
super(name) | |
end | |
end | |
end | |
Untitled #=> ? | |
Monkeys #=> ? | |
=> | |
Untitled #=> "starts with U" | |
Monkeys #=> NoMethodError: super: no superclass method `const_missing' for Object:Class | |
- Returns? | |
class Object | |
def meth() self end | |
end | |
Object.meth #=> ? | |
Module.meth #=> ? | |
Class.meth #=> ? | |
(A = Class.new).meth #=> ? | |
A.new.meth #=> ? | |
=> | |
Object.meth #=> Object | |
Module.meth #=> Module | |
Class.meth #=> Class | |
(A = Class.new).meth #=> A | |
A.new.meth #=> #<A:0x01> | |
- Describe `super` behaviour when called without arguments | |
=> | |
If called without arguments it invokes the ancestor method | |
with the arguments received by the current method. | |
Modifications made locally to the params will be sent. | |
To explicitly invoke the ancestor method without any arguments use super() | |
- How to delete a variable or constant? | |
=> | |
private Object::remove_const(*syms) | |
public Object#remove_instance_variable(*syms) | |
# Samples: | |
Object.send(:remove_const, :Foo) | |
remove_instance_variable :@name | |
var = nil # all you can do about scope variables | |
- Returns? | |
method(:test) #=> ? | |
Kernel.method(:test) #=> ? | |
=> | |
method(:test) #=> #<Method: Object(Kernel)#test> | |
Kernel.method(:test) #=> #<Method: Kernel.test> | |
- Define Object#tap to explain what it does | |
=> | |
# tap is used to split a call chain specially for debugging purposes | |
# or to get better error messages in cause of failure | |
class Object | |
def tap | |
yield self | |
self | |
end unless Object.respond_to?(tap) | |
end | |
- Returns? | |
class << Class | |
def mattr | |
self | |
end | |
end | |
Class.mattr #=> ? | |
class Test1 | |
mattr | |
end #=> ? | |
=> | |
Class.mattr #=> Class | |
class Test1; mattr; end #=> NameError | |
# cause mattr() is a class method of Class, not an instance method of Class | |
- Non existing globals returns? | |
$asfdasfd #=> ? | |
=> | |
nil | |
- Returns? | |
"cad".instance_methods | |
=> | |
NoMethodError: undefined `instance_methods` for "cad":String | |
Because is Module#instance_methods and only mods and classes can have instance methods | |
While objects like "cad" have public_methods() or its alias methods() | |
- Returns? | |
def meth | |
x = 1 | |
yield 2 | |
end | |
meth {|y| x + y} #=? ? | |
=> | |
NameError cause it will look for `x` where the Proc was defined | |
not where the Proc is called (within meth) | |
The block creates a closure where is defined, not where is yielded | |
class Module | |
def meth() self end | |
end | |
Object.meth #=> ? | |
Module.meth #=> ? | |
Class.meth #=> ? | |
(A = Class.new).meth #=> ? | |
A.new.meth #=> ? | |
=> | |
Object.meth #=> Object | |
Module.meth #=> Module | |
Class.meth #=> Class | |
(A = Class.new).meth #=> A | |
A.new.meth #=> NoMethodError cause meth is available for classes only | |
- Returns? | |
Module.new | |
=> | |
#<Module#0x01> | |
- Give a sample of a memoized accessor | |
=> | |
def account | |
@account ||= Account.find(@data[:account_id]) | |
end | |
- Find a better way to write this: | |
def foo | |
begin | |
do_it | |
rescue | |
handle_it | |
end | |
end | |
=> | |
# Every method in Ruby is implicitly a begin block | |
def foo | |
do_it | |
rescue | |
handle_it | |
end | |
- What is TOPLEVEL_BINDING ? | |
=> | |
Is a Ruby constant of the top-level scope bindings | |
eval "self", TOPLEVEL_BINDING #=> main | |
- How can you tell which classes or modules where added after doing some require? | |
=> | |
# With Module.constants you can get the ones defined in the system | |
before = Module.constants.dup | |
require 'securerandom' | |
newones = Module.constants - before | |
#=> [:OpenSSL, :Digest, :StringIO, :Fcntl, :SecureRandom] | |
- Returns? | |
class Class | |
def meth() self end | |
end | |
Object.meth #=> Object | |
Module.meth #=> Module | |
Class.meth #=> Class | |
(A = Class.new).meth #=> A | |
A.new.meth #=> NoMethodError | |
- Implement Symbol#to_proc and also: | |
:inspect.to_proc.call(self) #=> ? | |
class Fixnum | |
def printmyself() | |
print "#{self} " | |
end | |
end | |
3.times &(:printmyself.to_proc) #=> ? | |
[1, 2, 5].inject(0) {|memo, obj| memo + obj } #=> ? | |
# Improve that last one using Symbol#to_proc | |
=> | |
# Is a proc that takes an argument and calls the method (named the symbol) on the argument | |
:inspect.to_proc.call(self) #=> "main" , i.e. self.inspect #=> "main" | |
class Symbol | |
def to_proc | |
Proc.new {|x| x.send(self) } | |
end | |
end | |
# Remember that with `&` on a proc you can convert the proc into a block: | |
3.times &(:printmyself.to_proc) #=> prints: 0 1 2 returns: 3 | |
# Note the & operator will auto convert that obj to a Proc by calling to_proc | |
3.times &(:printmyself) | |
# Samea as | |
3.times &:printmyself | |
# And thanks to all that you can do: | |
%w(el ga lu).map(&:upcase) #=> ["EL", "GA", "LU"] | |
# Symbol#to_proc also supports blocks with more than 1 argument, e.g. |memo, obj| | |
[1, 2, 5].inject(0, &:+) #=> 8 | |
- Returns? | |
class MyClass | |
attr_accessor :my_attr | |
def initialize_attributes | |
my_attr = 10 | |
end | |
end | |
obj = MyClass.new | |
obj.initialize_attributes | |
obj.my_attr #=> ? | |
=> | |
returns nil because my_attr looks like local-variable assignment | |
instead use self.my_attr or @my_attr | |
- Improve this code using a Nil Guard | |
class C | |
def initialize | |
@a = [] | |
end | |
def elements | |
@a | |
end | |
end | |
=> | |
def elements | |
@a ||= [] | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment