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
Related
I want to extend Enumerable methods to my class, so that I can call them directly on my class, and not on class's instance variable i.e. X.objects
class X
include Enumerable
extend Forwardable
#objects ||= []
# tried this, but it doesn't work
# def_delegator self, :count, #objects.count
#def_delegators self, #objects
class << self
attr_accessor :objects
end
def initialize(name)
#name = name
end
def save
self.class.objects << self
end
def self.show
objects
end
end
y = X.new('a')
y.save
z = X.new('b')
z.save
n = X.new('c')
n.save
q = X.new('d')
q.save
p X.show
#p X.count # I want this to work.
I assume you're trying to define delegator on the class level
You may try to define it like this:
class << self
extend Forwardable
attr_accessor :objects
def_delegator :objects, :count
end
I mean, the only difference between your code and this code is extending by Forwardable inside class << self
Hi I am trying to create a helper for mass defining ruby methods as private class methods. In general one can define a method as a private class method by using private_class_method key work. But I would like to create a helper in the following style:
class Person
define_private_class_methods do
def method_one
end
def method_two
end
end
end
The way I planned to dynamically define this is in the following way, which is not at all working:
class Object
def self.define_private_class_methods &block
instance_eval do
private
&block
end
end
end
any ideas where I might be going wrong?
$ cat /tmp/a.rb
class Object
def self.define_private_class_methods &cb
existing = methods(false)
instance_eval &cb
(methods(false) - existing).each { |m| singleton_class.send :private, m }
end
end
class Person
define_private_class_methods do
def method_one
puts "¡Yay!"
end
end
end
Person.send(:method_one)
Person.public_send(:method_one)
$ ruby /tmp/a.rb
¡Yay!
/tmp/a.rb:18:in `public_send': private method `method_one'
called for Person:Class (NoMethodError)
Did you mean? method
from /tmp/a.rb:18:in `<main>'
Please note, that it’s hard to understand, what you are trying to achieve and possibly there is better, cleaner and more robust way to achieve this functionality.
Similar, yet different (and semantically more correct IMHO) to #mudasobwa's answer:
class Class
def define_private_class_methods(&definition)
class_methods_prior = methods
singleton_class.class_eval(&definition)
(methods - class_methods_prior).each do |method_name|
private_class_method method_name
end
end
end
class Person
define_private_class_methods do
def method_one
1
end
end
end
Person.method_one # !> NoMethodError: private method `method_one' called for Person:Class
Person.send :method_one # => 1
Note: It will not change the accessibility of a class method that you are currently overwriting.
You could define the methods in an anonymous module by passing the block to Module.new, make each instance method in the module private and extend your class with the module:
class Class
def define_private_class_methods(&block)
mod = Module.new(&block)
mod.instance_methods.each { |m| mod.send(:private, m) }
extend(mod)
end
end
This has the desired result:
class Person
define_private_class_methods do
def method_one
123
end
end
end
Person.send(:method_one)
#=> 123
Person.method_one
#=> private method `method_one' called for Person:Class (NoMethodError)
... and as a bonus, it also gives you a super method: (probably of little use)
class Person
def self.method_one
super * 2
end
end
Person.method_one
#=> 456
Of course, you don't have to use extend, you could just as well define the methods manually:
class Class
def define_private_class_methods(&block)
mod = Module.new(&block)
mod.instance_methods.each do |m|
define_singleton_method(m, mod.instance_method(m))
private_class_method(m)
end
end
end
The essential component is the anonymous module, so you have a (temporary) container to define the methods in.
Given the simple example here:
class Base
#tag = nil
def self.tag(v = nil)
return #tag unless v
#tag = v
end
end
class A < Base
tag :A
end
class B < Base
tag :B
end
class C < Base; end
puts "A: #{A.tag}"
puts "B: #{B.tag}"
puts "A: #{A.tag}"
puts "C: #{C.tag}"
which works as expected
A: A
B: B
A: A
C:
I want to create a module that base will extend to give the same functionality but with all the tag information specified by the class. Eg.
module Tester
def add_ident(v); ....; end
end
class Base
extend Tester
add_ident :tag
end
I've found i can do it with a straight eval, so:
def add_ident(v)
v = v.to_s
eval "def self.#{v}(t = nil); return ##{v} unless t; ##{v} = t; end"
end
but i really dislike using eval string in any language.
Is there a way that i can get this functionality without using eval? I've gone through every combination of define_method and instance_variable_get/set i can think of and i can't get it to work.
Ruby 1.9 without Rails.
You want to define a dynamic method on the singleton class of the class you're extending. The singleton class of a class can be accessed with expression like this: class << self; self end. To open the scope of a class's class, you can use class_eval. Putting all this together, you can write:
module Identification
def add_identifier(identifier)
(class << self; self end).class_eval do
define_method(identifier) do |*args|
value = args.first
if value
instance_variable_set("##{identifier}", value)
else
instance_variable_get("##{identifier}")
end
end
end
end
end
class A
extend Identification
add_identifier :tag
end
If you're using recent versions of Ruby, this approach can be replaced with Module#define_singleton_method:
module Identification
def add_identifier(identifier)
define_singleton_method(identifier) do |value = nil|
if value
instance_variable_set("##{identifier}", value)
else
instance_variable_get("##{identifier}")
end
end
end
end
I don't believe you want to use self.class.send(:define_method), as shown in another answer here; this has the unintended side effect of adding the dynamic method to all child classes of self.class, which in the case of A in my example is Class.
module Tester
def add_ident(var)
self.class.send(:define_method, var) do |val=nil|
return instance_variable_get("##{var}") unless val
instance_variable_set "##{var}", val
end
end
end
My favourite ruby book Metaprogramming Ruby solved these questions like the following way:
module AddIdent
def self.included(base)
base.extend ClassMethods # hook method
end
module ClassMethods
def add_ident(tag)
define_method "#{tag}=" do |value=nil|
instance_variable_set("##{tag}", value)
end
define_method tag do
instance_variable_get "##{tag}"
end
end
end
end
# And use it like this
class Base
include AddIdent
add_ident :tag
end
Bah isn't it always the way that once you get frustrated enough to post you then find the answer :)
The trick seems to be in (class << self; self; end) to give you the class instance without destroying the local scope. Referencing: How do I use define_method to create class methods?
def add_ident(v)
var_name = ('#' + v.to_s).to_sym
(class << self; self; end).send(:define_method, v) do |t = nil|
return instance_variable_get(var_name) unless t
instance_variable_set(var_name, t)
end
end
I'll accept better answers if them come along though.
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 make a method similar to attr_reader but I can't seem to get the instance of the class that the method gets called in.
class Module
def modifiable_reader(*symbols)
# Right here is where it returns Klass instead of #<Klass:0x1df25e0 #readable="this">
mod = self
variables = symbols.collect { |sym| ("#" << sym.to_s).to_sym }
attr_reader *symbols
(class << ModifyMethods; self; end).instance_eval do
define_method(*symbols) do
mod.instance_variable_get(*variables)
end
end
end
end
class Object
module ModifyMethods; end
def modify(&block)
ModifyMethods.instance_eval(&block)
end
end
class Klass
modifiable_reader :readable
def initialize
#readable = "this"
end
end
my_klass = Klass.new
my_klass.modify do
puts "Readable: " << readable.to_s
end
I'm not sure what it is you're trying to do.
If it helps, the spell for attr_reader is something like this:
#!/usr/bin/ruby1.8
module Kernel
def my_attr_reader(symbol)
eval <<-EOS
def #{symbol}
##{symbol}
end
EOS
end
end
class Foo
my_attr_reader :foo
def initialize
#foo = 'foo'
end
end
p Foo.new.foo # => "foo"
What I can understand from your code is that you want to have the modify block to respond to the instance methods of Klass, that's as simple as:
class Klass
attr_reader :modifiable
alias_method :modify, :instance_eval
def initialize(m)
#modifiable = m
end
end
Klass.new('john').modify do
puts 'Readable %s' % modifiable
end
About this tidbit of code:
def modifiable_reader(*symbols)
# Right here is where it returns Klass instead of #<Klass:0x1df25e0 #readable="this">
mod = self
...
Probably this can give you a hint of what is going on:
Class.superclass # => Module
Klass.instance_of?(Class) # => true
Klass = Class.new do
def hello
'hello'
end
end
Klass.new.hello # => 'hello'
When you are adding methods to the Module class, you are also adding methods to the Class class, which will add an instance method to instances of Class (in this case your class Klass), at the end this means you are adding class methods on your Klass class