Why does greeting become a method of the created subclass?
The short answer is: because that's how Struct.new works.
Why would it work that way? Imagine if it didn't. greeting would be added to all Structs and that would be bad.
It's passed to the block as an argument.
This is the "how". The "block" is really you defining a function and passing it into new. The code above is syntax sugar for...
func = Proc.new do |new_class|
p "The new subclass is #{new_class}"
def greeting
"Hello #{name} at #{address}"
end
end
Customer = Struct.new('Customer', :name, :address, &func)
See The Ultimate Guide to Blocks, Procs & Lambdas for more.
Struct.new runs the proc, passing in the name of the new class, but it runs it in a special way. It runs it as if it were the class Struct::Customer. It does this (probably) by calling class_eval to run a proc as if it were defined inside another class.
We can do the same to demonstrate.
class Foo
# Defined inside the Foo class, farewell is a method of Foo.
def farewell
"goodbye"
end
end
# This proc is defined outside the Foo class...
func = Proc.new do
def greeting
"hello"
end
end
# ...but we can run it as if it were.
Foo.class_exec(&func)
dave = Foo.new
p dave.greeting
Many Ruby libraries use this trick, and ones like it.
The block passed to each is also a proc, but each just runs it normally passing in each element of the Enumerable.
However, what you're written is rather odd. This...
[1,2,5].each { |x|
public
def m1 a
a + self
end
}[0].m1 3
Is really this.
nums = [1,2,5].each { |x|
public
def m1(a)
a + self
end
}
nums[0].m1(3)
Which is really this.
[1,2,5].each { |x|
public
def m1(a)
a + self
end
}
1.m1(3)
each returns what it iterated over, so it returns [1,2,5]. nums[0] is 1. So we're calling m1 on an Integer. Why would m1 be defined on an Integer?
It isn't! It's actually defined on Object. Why?
Everything is an object in Ruby, there always has to be a self. When outside a class in Ruby, self is main which is an Object. m1 is a method of Object.
p self # main
p self.class # Object
Integer inherits from Object so it can call m1. We can see this using method. Everything is an object, including Methods.
p self.method(:m1) #<Method: Object#m1>
p 1.method(:m1) #<Method: Integer(Object)#m1>
That says m1 is defined on Object and Integer inherits it from Object.
But why is it private? Any method defined like this is defined on Object. (Almost) everything inherits from Object. If it were public, methods in a script would be polluting other classes. So they're private. This is a special case only for main.
Defining m1 in each is a red herring. each is just running the code but doing nothing special. You can just define it in main (ie. Object) and get the same effect.
public
def m1(a)
p a + self
end
p self.method(:m1)
p 1.method(:m1)
publicI get: ``` (file): eval:1:in<main>': private methodm1' called for 1:Integer (NoMethodError) ```eachgets the elements iterated over.selfin the block is also bound to that subclass.