I am confused by the following code from Poignant Guide:
# The guts of life force within Dwemthy's Array
class Creature
# Get a metaclass for this class
def self.metaclass; class << self; self; end; end
# Advanced metaprogramming code for nice, clean traits
def self.traits( *arr )
return #traits if arr.empty?
# 1. Set up accessors for each variable
attr_accessor( *arr )
# 2. Add a new class method to for each trait.
arr.each do |a|
metaclass.instance_eval do
define_method( a ) do |val|
#traits ||= {}
#traits[a] = val
end
end
end
# 3. For each monster, the `initialize' method
# should use the default number for each trait.
class_eval do
define_method( :initialize ) do
self.class.traits.each do |k,v|
instance_variable_set("##{k}", v)
end
end
end
end
# Creature attributes are read-only
traits :life, :strength, :charisma, :weapon
end
The above code is used to create a new class, as in the following:
class Dragon < Creature
life( 1340 ) # tough scales
strength( 451 ) # bristling veins
charisma( 1020 ) # toothy smile
weapon( 939 ) # fire breath
end
I need to study the basics of meta-programming more on my own, but for now I just want to know, where does the val block argument come from in define_method( a ) do |val|? It represents the point values assigned to each trait, but I don't understand how each of those numbers become a block argument.
Also, why is a passed in the parentheses to define_method, while val is passed in as a block argument?
I've read over this question on the subject of define_method arguments, but it doesn't address the reasons for passing arguments to define_method rather than to the block.
In the form
define_method(:foo){|x| ...}
:foo is the method name and x is the argument. They have different roles. It is the same as:
def foo(x)
...
end
Related
I would like to create an instance method that takes another instance method of its own class as a parameter, and then applies the passed method on the instance it's working on (known as self):
class MyClass
attr_reader :called_methods
def initialize
#called_methods = []
end
def my_first_method!
#called_methods << :my_first_method
self
end
def my_second_method!
#called_methods << :my_second_method
self
end
def my_strange_method!(secondary)
# Want to apply method whose name is given by secondary, to self
end
end
p MyClass.new.my_second_method!.my_strange_method!(:my_first_method!).called_methods
I suspect the unary & may be key, but all the web pages I can find on that operator involve calling methods on multiple objects, as when iterating over an Enumerable with #each or #map.
Use Object#public_send (or Object#send to apply protected/private method).
def my_strange_method!(secondary)
public_send(secondary)
self
end
p MyClass.new.
my_second_method!.
my_strange_method!(:my_first_method!).
called_methods
#⇒ [:my_second_method, :my_first_method]
There could be more defensive way to apply if and only the method is known:
def my_strange_method!(secondary)
raise ArgumentError.new("Unknown method #{secondary}") \
unless methods.include? secondary.to_s.to_sym
public_send(secondary)
end
p MyClass.new.
my_second_method!.
my_strange_method!(:my_first_method!).
called_methods
#⇒ [:my_second_method, :my_first_method]
p MyClass.new.
my_second_method!.
my_strange_method!(:inexisting_method!).
called_methods
#⇒ ArgumentError: Unknown method inexisting_method!
This is tagged with functional-programming so I'm going to offer a persistent (immutable) design -
class MyClass
attr_reader :called_methods
def initialize(m = [])
#called_methods = m
end
def my_first_method!
MyClass.new(called_methods + [ :first ])
end
def my_second_method!
MyClass.new(called_methods + [ :second ])
end
def my_strange_method!(secondary)
public_send secondary
end
end
MyClass.new.my_second_method!.my_strange_method!(:my_first_method!).called_methods
# [:second, :first]
I am reading why's poignant guide to ruby, and in chapter 6, he used this code:
class Creature
# Get a metaclass for this class
def self.metaclass; class << self; self; end; end
...
def self.traits( *arr )
# 2. Add a new class method to for each trait.
arr.each do |a|
metaclass.instance_eval do
define_method( a ) do |val|
#traits ||= {}
#traits[a] = val
end
end
end
end
end
Why is he calling instance_eval on metaclass of class Creature? Since instance_eval adds methods to metaclass, he can just do this:
def self.metaclass; self; end;
Or am I wrong? Are there any more elegant solutions to this?
The simpler way to write _why's code would be just
def self.traits( *arr )
# 2. Add a new class method to for each trait.
arr.each do |a|
metaclass.define_method(a) do |val|
#traits ||= {}
#traits[a] = val
end
end
end
The only problem here is that you get an error:
private method `define_method' called for metaclass (NoMethodError)
A private method can only be called with an implicit receiver i.e. the problem is the explicit metaclass. before the method call. But if we remove that, the implicit receiver (self) is Creature! So how do we change self to a different object? instance_eval:
metaclass.instance_eval do
define_method(a) do |val|
...
end
end
So it's really just a way of bypassing the fact that define_method is private. Another way to hack it would be to use send
metaclass.send(:define_method, a) do |val|
...
end
But these days all of that is totally unnecessary; you are allowed to define methods in the metaclass (AKA singleton class) without hacking around private methods:
def self.traits( *arr )
# 2. Add a new class method to for each trait.
arr.each do |a|
define_singleton_method(a) do |val|
#traits ||= {}
#traits[a] = val
end
end
end
If you do
def self.metaclass; self; end;
you will have a reference to Creature class. So, in that case, the methods will be defined not to the singleton class of object Creature but to the class Creature itself(to the list of instance methods of class Creature). The method
def self.metaclass; class << self; self; end; end
is a simple way to retrieve singleton class of object Creature in ruby < 1.9. In ruby 1.9+ was implemented method singleton_class which is shortcut of class << self. So that code can be simplified as:
class Creature
...
def self.traits( *arr )
# 2. Add a new class method to for each trait.
arr.each do |a|
singleton_class.instance_eval do
define_method( a ) do |val|
#traits ||= {}
#traits[a] = val
end
end
end
end
end
It's possible to create a Complex number in Ruby using
c = Complex.new(1,2)
but, it can be shortened to
c = Complex(1,2)
Is it possible to achieve the same functionality without having to define a function outside the class, like in the example below?
class Bits
def initialize(bits)
#bits = bits
end
end
def Bits(list) # I would like to define this function inside the class
Bits.new list
end
b = Bits([0,1])
I think Ruby should allow at least one of the proposed constructors below
class Bits
def initialize(bits)
#bits = bits
end
def self.Bits(list) # version 1
new list
end
def Bits(list) # version 2
new list
end
def Bits.Bits(list) # version 3
new list
end
end
Have this snippet:
def make_light_constructor(klass)
eval("def #{klass}(*args) #{klass}.new(*args) end")
end
Now you can do this:
class Test
make_light_constructor(Test)
def initialize(x,y)
print x + y
end
end
t = Test(5,3)
Yes, I know you're still defining a function outside a class - but it is only one function, and now any class you want can make use of its implementation rather than making one function per class.
c = Complex(1,2)
is actually calling a method on Kernel
Basically you can't - the () operator cannot be overriden in Ruby (Complex class is written in C).
You could achieve something similar using []:
class Bits
def self.[](list)
Bits.new list
end
end
Which would allow something like:
b = Bits[[1,2]]
If you pack your classes into some module you can use 2 methods:
self.included - called when you include Mod
self.extend - called when you extend Mod
I have created very basic method using self.included.
Cons: It is hard to write. You can say it is complex; It may not contain all features.
Pros: It looks exactly like Complex(2,3) (it uses () instead of [] as in https://stackoverflow.com/a/24351316/2597260 answer); You create just initialize, self.included create the rest.
module M1
# some random classes
class A; end
class B
def initialize list
#list = list
end
attr_accessor :list
end
class C
def initialize var1
#var1 = var1
end
attr_accessor :var1
end
Answer = 42
# called on `include module_name`
def self.included mod
# classes are constants (in normal cases)
constants.each do |cons|
class_eval do
# I don't like hard-coded `::M1`
klass = ::M1.const_get cons
if klass.class==Class
define_method cons do |*args, &block|
klass.new *args, &block
end
end
end
end
end
end
include M1
p A()
b = B([1,2,3])
p b.list
c = C 42
p c.var1
puts Answer()
# NoMethodError: undefined method `Answer' for main:Object
# thats good, because Answer is not a class!
Here's another hack that you could (but shouldn't) use, inspired by this blog post:
def method_missing(sym, *args, **kwargs, &blk)
Object.const_get(sym).new(*args, **kwargs, &blk)
end
This simply expects any unknown method name to be the name of a class and calls :new on the class.
With rudimentary error handling:
alias sys_method_missing method_missing
def method_missing(sym, *args, **kwargs, &blk)
cls = Object.const_get(sym) if Object.constants.include? sym
if cls.is_a?(Class) then cls.new(*args, **kwargs, &blk)
else sys_method_missing(sym, *args, **kwargs, &blk) end
end
If an unknown method name is the name of a class, this calls :new on the class. Otherwise, it delegates the call to the original implementation of method_missing().
Usage:
class Foo
end
foo = Foo()
p foo
Result:
#<Foo:0x00007f8fe0877180>
The following is a Ruby code snippet from Why's Poignant Guide to Ruby Chapter 6, where he attempts to demonstrate metaprogramming in Ruby:
# Get a metaclass for this class
def self.metaclass; class << self; self; end; end
I am not that well acquainted with Ruby, but is this what it would look like in expanded form?
def self.metaclass
def self.self
end
end
At least that's how I understand it. However, it still don't comprehend what this code does, exactly. What is its purpose?
Further on in the code, Why adds this:
arr.each do |a|
metaclass.instance_eval do
define_method( a ) do |val|
#traits ||= {}
#traits[a] = val
end
end
end
If I understand it correctly, this piece of code adds a new value to #traits with the given name and value. Is that correct?
Thanks for your help, here's the full source code that caused me trouble, for anyone who wants to see it:
# The guts of life force within Dwemthy's Array
class Creature
# Get a metaclass for this class
def self.metaclass; class << self; self; end; end
# Advanced metaprogramming code for nice, clean traits
def self.traits( *arr )
return #traits if arr.empty?
# 1. Set up accessors for each variable
attr_accessor *arr
# 2. Add a new class method to for each trait.
arr.each do |a|
metaclass.instance_eval do
define_method( a ) do |val|
#traits ||= {}
#traits[a] = val
end
end
end
# 3. For each monster, the `initialize' method
# should use the default number for each trait.
class_eval do
define_method( :initialize ) do
self.class.traits.each do |k,v|
instance_variable_set("##{k}", v)
end
end
end
end
# Creature attributes are read-only
traits :life, :strength, :charisma, :weapon
end
And in usage:
class Dragon < Creature
life( 1340 ) # tough scales
strength( 451 ) # bristling veins
charisma( 1020 ) # toothy smile
weapon( 939 ) # fire breath
end
class Foo
def self.bar # Create a method invoked by Foo.bar instead of Foo.new.bar
42 # the return value of this method (value of last expression)
end
end
class Foo
def self.jim # Another method on the class itself
class << self # Change the 'self' to be the metaclass of the current object
self # Evaluate the current 'self' as the 'return value' of
end # class<<self…end; and since this is the last expression in
end # the method, its value is the return value for the method
end
In short: what you are seeing defines a method named metaclass on the Creature class itself (not for instances). When you run this method, it finds the metaclass of Creature and returns that.
Read around the 'net for what the "metaclass" of an object is.
In expanded form it looks exactly the same:
def self.metaclass
class << self
self
end
end
Notice it's just returning self, which because it's evaluated within the context of the metaclass, which in effect what class << self does, self is the metaclass.
With the introduction of things like define_singleton_method, the number of cases where you need to have access to the metaclass directly are becoming very small.
It's all very complicated and seemingly a consequence of the "everything is an object" design principle.
I'm trying to optimize some code and I want to instead on checking a value on every method call just define the method to respond with the checking already pre-calculate, because this checking doesn't change on the whole live of the instance.
I decided to define different versions of the method for every instance created. More or less this way:
class TestingSingletonMethodsWithVariable
METHODS = %w(a b c d)
def initialize(favorite_method)
class << self
METHODS.each do |method_name|
if( favorite_method == method_name )
define_method method_name do
puts "#{method_name} its my favorite method"
end
else
define_method method_name do
puts "#{method_name} its not my favorite method"
end
end
end
end
end
end
t = TestingSingletonMethodsWithVariable.new('b')
t.a
t.b
t.c
t.d
# $ ruby test/testing_singleton_methods_with_variable.rb
# test/testing_singleton_methods_with_variable.rb:7:in `initialize': undefined local variable or method `favorite_method' for #<Class:#<TestingSingletonMethodsWithVariable:0x1001a77b8>> (NameError)
# from test/testing_singleton_methods_with_variable.rb:6:in `each'
# from test/testing_singleton_methods_with_variable.rb:6:in `initialize'
# from test/testing_singleton_methods_with_variable.rb:21:in `new'
# from test/testing_singleton_methods_with_variable.rb:21
What is happening is that something weird is happening with the variables: the variables declares out-side the class << self block are not visible for the variables inside.
Any one can explain me how can I do the behavior I'm looking for?
Thanks
In Ruby, only blocks can be closures, class bodies (as well as module and method bodies) cannot be closures. Or to put it another way: only blocks create a new nested lexical scope, all others (module bodies, class bodies, method bodies and script bodies) create new top-level scopes.
So, you will need a block. Normally, this would mean using some form of eval, but here you can just use define_singleton_method instead:
class TestingSingletonMethodsWithVariable
METHODS = %w(a b c d)
def initialize(favorite_method)
METHODS.each do |method_name|
if favorite_method == method_name
define_singleton_method method_name do
puts "#{method_name} its my favorite method"
end
else
define_singleton_method method_name do
puts "#{method_name} its not my favorite method"
end
end
end
end
end
t = TestingSingletonMethodsWithVariable.new('b')
t.a
t.b
t.c
t.d
Adding to Jörg's answer: define_singleton_method is Ruby 1.9+. If you want to run it in pre 1.9, the following works:
class Object
def metaclass
class << self; self; end
end
end
class TestingSingletonMethodsWithVariable
METHODS = %w(a b c d)
def initialize(favorite_method)
METHODS.each do |method_name|
if( favorite_method == method_name )
metaclass.send(:define_method, method_name, Proc.new do
puts "#{method_name} its my favorite method"
end)
else
metaclass.send(:define_method, method_name, Proc.new do
puts "#{method_name} its not my favorite method"
end)
end
end
end
end
t = TestingSingletonMethodsWithVariable.new('b')
t.a
t.b
t.c
t.d