determining when inside 'class << self'

A

Ara.T.Howard

i like a method like this

def inside_metaclass?
# to be implemented
end

class C
class << self
p inside_metaclass? #=> true
end

p inside_metaclass? #=> false
end

p inside_metaclass? #=> false

class << []
p inside_metaclass? #=> true
end

can this be done?

cheers.

-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| renunciation is not getting rid of the things of this world, but accepting
| that they pass away. --aitken roshi
===============================================================================
 
A

Assaph Mehr

Ara.T.Howard said:
i like a method like this

def inside_metaclass?
# to be implemented
end

class C
class << self
p inside_metaclass? #=> true
end

p inside_metaclass? #=> false
end

p inside_metaclass? #=> false

class << []
p inside_metaclass? #=> true
end

can this be done?


Would you settle for testing if you're inside an anonymous class?
Anonymous classes have names that look like:
#<Class:...>

So you can define your method as:

def inside_metaclass?
Integer === (self.to_s =~ /^#<Class:/)
end

Which will evaluate to true for anonymous classes. Since (I believe)
all meta-classes are anonymous that can give you a pretty good guess
(or at least has the same result as your sample).

Out of curiosity I've run:
ObjectSpace.each_object { |o| p [o, o.class] if o.to_s =~ /^#<Class:/ }

Which returned ~30 such objects (anonymous classes, unless I'm
mistaken). So unless you're opening an anonymous class, you should be
OK (and I'm not even sure you can reopen an anonymous class).


HTH,
Assaph
 
M

Mark Hubbart

Ara.T.Howard said:
i like a method like this

def inside_metaclass?
# to be implemented
end

class C
class << self
p inside_metaclass? #=> true
end

p inside_metaclass? #=> false
end

p inside_metaclass? #=> false

class << []
p inside_metaclass? #=> true
end

can this be done?

Would you settle for testing if you're inside an anonymous class?
Anonymous classes have names that look like:
#<Class:...>

So you can define your method as:

def inside_metaclass?
Integer === (self.to_s =~ /^#<Class:/)
end

Which will evaluate to true for anonymous classes. Since (I believe)
all meta-classes are anonymous that can give you a pretty good guess
(or at least has the same result as your sample).

To expand on that... metaclasses have a peculiar inspect value. This
code is breakable, but probably only if you try to:

class Class
def metaclass?
id = inspect[/\A\#<Class:\#<.+?\:0x(.+?)>>\Z/, 1]
ObjectSpace._id2ref(id.to_i(16)/2) if id
end
end

The metaclass? method will return nil if the receiver is not a
metaclass, or it's instance if it *is* a metaclass. Use it like this:

class << (a="test")
p self.metaclass? #==> prints "test"
end

class Foo
p self.metaclass? #==> prints nil
end

There's probably a safer way to do this using ruby/dl, or evil.rb. I
don't know, though.

cheers,
Mark
Out of curiosity I've run:
ObjectSpace.each_object { |o| p [o, o.class] if o.to_s =~ /^#<Class:/ }

Which returned ~30 such objects (anonymous classes, unless I'm
mistaken). So unless you're opening an anonymous class, you should be
OK (and I'm not even sure you can reopen an anonymous class).

HTH,
Assaph
 
G

George Ogata

Ara.T.Howard said:
i like a method like this

def inside_metaclass?
# to be implemented
end

class C
class << self
p inside_metaclass? #=> true
end

p inside_metaclass? #=> false
end

p inside_metaclass? #=> false

class << []
p inside_metaclass? #=> true
end

can this be done?

Is this cheating?:

#include "ruby.h"

VALUE metaclass_p(VALUE self) {
if (FL_TEST(self, FL_SINGLETON))
return Qtrue;
else
return Qfalse;
}

void Init_test(void) {
rb_define_method(rb_cClass, "metaclass?", metaclass_p, 0);
}
 
F

Florian Groß

Mark said:
To expand on that... metaclasses have a peculiar inspect value. This
code is breakable, but probably only if you try to:

[...]

There's probably a safer way to do this using ruby/dl, or evil.rb. I
don't know, though.

I'm not sure if you consider this safer, but while you can subclass
anonymous and normal classes you can not do so for idioclasses so this
ought to work:

class Class
def idioclass?()
Class.new(self)
return true
rescue TypeError
return false
end
end

Of course somebody /could/ raise a TypeError in the inherited hook, but
that is quite unlikely to happen.

evil-ruby can of course check the actual RTYPE of the class which is
unbreakable, but that is a dependency you might not want to have.
 
A

Ara.T.Howard

Would you settle for testing if you're inside an anonymous class? Anonymous
classes have names that look like: #<Class:...>

So you can define your method as:

def inside_metaclass?
Integer === (self.to_s =~ /^#<Class:/)
end

Which will evaluate to true for anonymous classes. Since (I believe) all
meta-classes are anonymous that can give you a pretty good guess (or at
least has the same result as your sample).

Out of curiosity I've run:
ObjectSpace.each_object { |o| p [o, o.class] if o.to_s =~ /^#<Class:/ }

Which returned ~30 such objects (anonymous classes, unless I'm mistaken). So
unless you're opening an anonymous class, you should be OK (and I'm not even
sure you can reopen an anonymous class).

hmm. that will probably work. what i want is something for my attributes
module that does

class C
attribute 'foobar' #=> defines instance method

class_attribute 'foobar' #=> defines class instance method

class << self
attribute 'foobar' #=> defines class instance method
end
end

eg. i want the metacode generater 'attribute' to be context sensitive as in

def attribute name
if inside_metaclass? and self.is_a? Class
class_attribute name
else
...
end
end

the above should work but may give unforseen problems... any spring to mind?

cheers.

-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| renunciation is not getting rid of the things of this world, but accepting
| that they pass away. --aitken roshi
===============================================================================
 
R

Robert Klemme

Ara.T.Howard said:
i like a method like this

def inside_metaclass?
# to be implemented
end

class C
class << self
p inside_metaclass? #=> true
end

p inside_metaclass? #=> false
end

p inside_metaclass? #=> false

class << []
p inside_metaclass? #=> true
end

can this be done?

Yes, like this:

class Object
def inside_metaclass?() false end
end

class Class
def inside_metaclass?
begin
self.allocate
false
rescue TypeError => e
/virtual class/i =~ e.to_s and true
end
end
end

irb(main):058:0> class <<Object.new; p inside_metaclass? end
true
=> nil
irb(main):059:0> p inside_metaclass?
false
=> nil
irb(main):060:0> class String; p inside_metaclass? end
false
=> nil

Cheers

robert
 
F

Florian Groß

Lionel said:
class << []
p inside_metaclass? #=> true
end

Sorry for the newbie question, but what does this mean?

class << obj enters the idioclass of an object which is a class that
contains method that will only be defined for that particular object.

When

obj = "Pacman -> (<"

then

class << obj
def reverse() ">) <- Pacman" end
end

is the same as

def obj.reverse() ">) <- Pacman" end

So we provide a custom implementation of reverse() for a single method
only -- you can also use this for adding completely new functionality.

So why is the class << obj syntax necessary at all?

To apply other class abilities to a single object. You might want to
create an accessor for only one single object:

obj = Array.new
class << obj
attr_accessor :creator
end

obj.creator # => nil
obj.creator = ENV["username"]
obj.creator # => "flgr" (your result may vary ;))
obj << "foo"
obj << "bar"
obj # => ["foo", "bar"]

But even after that code:

ary = Array.new
ary.creator # raises NoMethodError

Oh, and p obj just outputs an object's state for debugging. (It is the
same as doing puts obj.inspect)
 
A

Ara.T.Howard

class Object
def inside_metaclass?() false end
end

class Class
def inside_metaclass?
begin
self.allocate
false
rescue TypeError => e
/virtual class/i =~ e.to_s and true
end
end
end

irb(main):058:0> class <<Object.new; p inside_metaclass? end
true
=> nil
irb(main):059:0> p inside_metaclass?
false
=> nil
irb(main):060:0> class String; p inside_metaclass? end
false
=> nil

Cheers

robert

hmm. i like this more than the pure matching version - which seemed a bit
fragile.

seems like i could do

harp:~ > cat a.rb
class Object
def inside_metaclass?
begin
allocate()
false
rescue NoMethodError
false
rescue TypeError => e
%r/virtual class/i =~ e.to_s and true
end
end
end

obj = Object::new
klass = Class::new

p inside_metaclass? #=> false

class << obj
p inside_metaclass? #=> true
end

class << klass
p inside_metaclass? #=> true
end

class << self
p inside_metaclass? #=> true
end


harp:~ > ruby a.rb
false
true
true
true

too.

now - any opinions on whether this violates POLS or not:


#
# my attributes module - hoping to become a collection of better 'attr' like
# metamethods...
#
require 'attributes'

class C

#
# define a class attr => used like C::foo
#
class_attribute 'foo'

#
# define an instance attr => C::new.bar
#
attribute 'bar'

class << self
#
# define a class attr => used like C::foobar
#
# note that this would be the SAME as using class_attribute 'foobar'
# outside of metaclass
#
attribute 'foobar'
end

end

so, you see, Object::attribute would be context sensitive to being inside a
metaclass or not - if inside a Class.metaclass it's defining a class
attribute, else an instance attribute.

thoughts?

-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| renunciation is not getting rid of the things of this world, but accepting
| that they pass away. --aitken roshi
===============================================================================
 
M

Mark Hubbart

Lionel said:
class << []
p inside_metaclass? #=> true
end

Sorry for the newbie question, but what does this mean?

class << obj enters the idioclass of an object which is a class that
contains method that will only be defined for that particular object.

Florian: Great explanation.

Lionel: If 'class << []' itself seems a little odd to you, read on...

The class << obj notation can be used in ways that you might not
expect. Since ruby syntax is not as ridged as some other languages,
any expression can take the place of 'obj' in that notation. For
example, if you want to create a temporary/mock object of some sort:

def clear
class << (a = Object.new)
def inspect() `clear` end
end
end

(this example was pulled right out of my irbrc)

As you can see, 'a' was assigned to, and then the resulting object was
opened for modification. So this code:

class << []
# do stuff
end

... just creates an anonymous array and opens it's metaclass (or
idioclass or singleton class or virtual class, whatever). However, the
array is never assigned to a variable, so it will probably be garbage
collected pretty soon. You will probably never see this exact thing in
someone's code, but it is possible.

cheers,
Mark
 
T

Trans

Have not tested thouroughly but...

class Object
def idioclass? ; false ; end
end

class Class
alias :newnew :new
def new( *args )
class << self
def idioclass? ; !super ; end
end
newnew( *args )
end
end

# test

class A
def initialize
p idioclass? #=> false
class << self
p idioclass? #=> true
end
end
end

a = A.new

produces

false
true

T.
 
L

Lionel Thiry

Mark Hubbart a écrit :
Lionel Thiry wrote:

class << []
p inside_metaclass? #=> true
end

Sorry for the newbie question, but what does this mean?

class << obj enters the idioclass of an object which is a class that
contains method that will only be defined for that particular object.


Florian: Great explanation.

Yeah, great. :)

But I already knew about the singleton methods and their container object, the
said idioclass. But I didn't know well all the subtleties of 'class << obj',
hence my initial question.
Lionel: If 'class << []' itself seems a little odd to you, read on...

Ok, I read, I read! :)
The class << obj notation can be used in ways that you might not
expect. Since ruby syntax is not as ridged as some other languages,
any expression can take the place of 'obj' in that notation. For
example, if you want to create a temporary/mock object of some sort:

def clear
class << (a = Object.new)
def inspect() `clear` end
end
end

(this example was pulled right out of my irbrc)

As you can see, 'a' was assigned to, and then the resulting object was
opened for modification. So this code:

class << []
# do stuff
end

.. just creates an anonymous array and opens it's metaclass (or
idioclass or singleton class or virtual class, whatever). However, the
array is never assigned to a variable, so it will probably be garbage
collected pretty soon. You will probably never see this exact thing in
someone's code, but it is possible.

This probably explains why it was so odd to me, I've mainly met the 'class <<
self' version.

Thanks a lot to you all! :)
 
D

Dave Burt

now - any opinions on whether this violates POLS or not:

#
# my attributes module - hoping to become a collection of better 'attr'
like
# metamethods...
#
require 'attributes'
class C
# define a class attr => used like C::foo
class_attribute 'foo'
# define an instance attr => C::new.bar
attribute 'bar'
class << self
# define a class attr => used like C::foobar
attribute 'foobar'
end
end

The way it works is analogous to "def attribute_name", which is
unsurprising. I like it.

I also like the setter capability of the getter methods (setting attributes
in a block passed to the constructor looks very clean), and I'm sure I'll be
using your module a in future.

Cheers,
Dave
 
M

Mark Hubbart

i like a method like this

def inside_metaclass?
# to be implemented
end

While experimenting with this, I discovered something interesting: it
is possible to instantiate a metaclass. By duping a metaclass, you
regain the ability to instantiate it, apparently without losing any
functionality.

name = "Jack Smith"
def name.reverse
split.reverse.join ", "
end
name.reverse #==> "Smith, Jack"

# dup name's metaclass
nameclass = class << name; self; end.dup

name2 = nameclass.new "John Doe"
name2.reverse #==> "Doe, John"

Any thoughts? Am I strange for finding this a bit surprising?

cheers,
Mark
 
G

George Ogata

Mark Hubbart said:
While experimenting with this, I discovered something interesting: it
is possible to instantiate a metaclass. By duping a metaclass, you
regain the ability to instantiate it, apparently without losing any
functionality.

name = "Jack Smith"
def name.reverse
split.reverse.join ", "
end
name.reverse #==> "Smith, Jack"

# dup name's metaclass
nameclass = class << name; self; end.dup

name2 = nameclass.new "John Doe"
name2.reverse #==> "Doe, John"

Any thoughts? Am I strange for finding this a bit surprising?

Well, duping a metaclass doesn't really return a metaclass. That is,
it is no longer a thing tied to one object; it's just a free-roaming
class like any other. (I won't comment on whether or not that's
"surprising" -- *wanting* to #dup a singleton class is surprising
enough for me. ;-)

What you may have noticed is that #dup-ing a class doesn't copy it's
singleton-class-ness, but #clone-ing does. Replace your `dup' with
`clone', and you'll get an error, since you can't instantiate the
clone:

irb(main):001:0> s = ''
=> ""
irb(main):002:0> (class << s; self; end).clone.new
TypeError: can't create instance of singleton class
from (irb):2:in `new'
from (irb):2

The weirdness here is that the clone is a singleton class (as implied
by the error message), but can have no instance!
 
A

Ara.T.Howard

While experimenting with this, I discovered something interesting: it
is possible to instantiate a metaclass. By duping a metaclass, you
regain the ability to instantiate it, apparently without losing any
functionality.

name = "Jack Smith"
def name.reverse
split.reverse.join ", "
end
name.reverse #==> "Smith, Jack"

# dup name's metaclass
nameclass = class << name; self; end.dup

name2 = nameclass.new "John Doe"
name2.reverse #==> "Doe, John"

Any thoughts? Am I strange for finding this a bit surprising?

i dunno - but that's cool.

-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| renunciation is not getting rid of the things of this world, but accepting
| that they pass away. --aitken roshi
===============================================================================
 
D

David A. Black

Hi --

While experimenting with this, I discovered something interesting: it
is possible to instantiate a metaclass. By duping a metaclass, you
regain the ability to instantiate it, apparently without losing any
functionality.

name = "Jack Smith"
def name.reverse
split.reverse.join ", "
end
name.reverse #==> "Smith, Jack"

# dup name's metaclass
nameclass = class << name; self; end.dup

name2 = nameclass.new "John Doe"
name2.reverse #==> "Doe, John"

Any thoughts? Am I strange for finding this a bit surprising?

I find it pretty startling. I've often wondered whether there was a
way to do essentially this. I'd concluded there wasn't. Maybe, at
the time, there wasn't....

I subjected it to one further test, to make sure that the
name2.reverse is actually a different method (since if two objects
were sharing a singleton method, that would have to be considered a
bug):

class << name
def reverse
"hi"
end
end

name.reverse # hi
name2.reverse # Doe, John
"hello".reverse # olleh <= just to be totally sure :)

Hmmmm, it's still daytime in Paris, so we may yet learn of some
limitation or reason to avoid it... :)


David
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

Forum statistics

Threads
473,780
Messages
2,569,611
Members
45,268
Latest member
AshliMacin

Latest Threads

Top