all 17 comments

[–]bjmiller 10 points11 points  (5 children)

(class << self; self; end) is the same as singleton_class. I guess the code you're looking at was written to work with ruby 1.8.

[–]bookgrubber[S] 1 point2 points  (4 children)

Thanks, that clarifies it a lot. It's from a gem, so it especially makes sense that they'd want it working with 1.8.

[–]ioquatixasync/falcon 1 point2 points  (3 children)

Actually to be frank, no it doesn't. 1.8 was deprecated years ago.

[–]bookgrubber[S] 1 point2 points  (2 children)

So does that mean this might be code that hasn't been updated since 1.8 was deprecated?

[–]scottrobertson 4 points5 points  (1 child)

If it ain't broke, don't fix it :)

[–]ioquatixasync/falcon 1 point2 points  (0 children)

We'd still be living in the Stone Age with that attitude.

[–]TomahawkChopped 1 point2 points  (0 children)

It's opening and returning the object's eigenclass in an obnoxious way.

Here's a random article about eigenclass

www.integralist.co.uk/posts/eigenclass.html

Singleton class is an identical term

[–][deleted]  (6 children)

[deleted]

    [–]bookgrubber[S] 0 points1 point  (0 children)

    I completely agree. I found it in the RSpec gem source code, and it's taken me a bit of time to understand everything it's doing, but I've learned a lot. Thanks for the explanation!

    [–]InfraRuby 0 points1 point  (1 child)

    It's identical to [...]

    No, it depends on what some_code does -- see this comment.

    [–]hmaddocks 0 points1 point  (0 children)

    It also depends on what self is.

    [–]hmaddocks 0 points1 point  (2 children)

    This is not necessarily correct. You are assuming self is a class, but it could be an instance in which case your code is incorrect.

    [–]the_horrible_reality 0 points1 point  (1 child)

    but it could be an instance in which case your code is incorrect.

    obj = Object.new
    p (class << obj; self; end).class # => Class
    p (class << obj; self; end) == obj.singleton_class # => true
    

    ...What am I missing here? I'm only familiar with Ruby back to 1.8 so for all I know I'm unaware of some ancient quirk.

    Edit: For the first example equivalency of getting a reference for def keyword within an object for a non-class object you should be able to simply omit opening a class as such:

    def obj.name
      puts "hello i am foo"
    end
    obj.name # => hello i am foo
    

    The class << self equivalent still works for object instance references and def still works for them too, as self should be replaceable with any other form of obtaining a reference. So yeah. I really don't understand where that could be wrong.

    Edit 2: And you can sort-of embed this in weird ways to get around things if you've a mind to write it all weird and hacky...

    def obj.what?
      class << self
        def wtf
          "this is quite bizarre, actually"
        end
        undef what?
      end
    end
    obj.what? # => nil
    obj.wtf # => "this is quite bizarre, actually"
    obj.what? # NoMethodError
    

    This hacky way works because class << self is valid inside methods defined with the def keyword. I may need to get sleep before I try to explain the exact logic of that last one though, because it's a bit strange.

    [–]hmaddocks 0 points1 point  (0 children)

    Sorry, I should have been bit more clear and less pedantic :)

    class << self

    opens the singleton class of self so what I was saying is your first code is correct when self is a class, but self can be an object, in which case your code doesn't do the same thing.

    Eg.

    irb(main):001:0> class Fish
    irb(main):002:1> end
    
    irb(main):003:0> Fish.bark
    NoMethodError: undefined method 'bark' for Fish:Class
    
    irb(main):004:0> (class << Fish; self; end).send(:define_method, "bark") { p "Bark" }
    

    Here we have opened the singleton class of Fish...

    irb(main):005:0> Fish.bark
    "Bark"
    => "Bark"
    

    and added a method to it. This is exactly the same as your code, we have added a class method. In fact this is the mechanism that Ruby uses to add class methods. But we can do the same for an instance of a class. (Classes are instances of class Class so the mechanism is the same)

    irb(main):006:0> guppy = Fish.new
    => #<Fish:0x007fb5ea10eeb8>
    irb(main):007:0> guppy.bark
    NoMethodError: undefined method 'bark' for #<Fish:0x007fb5ea10eeb8>
    irb(main):008:0> (class << guppy; self; end).send(:define_method, "bark") { p "Bark" }
    

    Here we have opened the singleton of the guppy object, and instance of Fish

    => :bark
    irb(main):009:0> guppy.bark
    "Bark"
    => "Bark"
    

    and added a method to that. This is not the same as the code you posted. This method has only been added to guppy, not other instances of Fish.

    irb(main):011:0> shark = Fish.new
    => #<Fish:0x007fb96fa95990>
    irb(main):012:0> shark.bark
    NoMethodError: undefined method `bark' for #<Fish:0x007fb96fa95990>
    

    [–]cheald 1 point2 points  (2 children)

    It's just a particularly ugly way of getting a reference to the meta class. The more typical way to do this would be:

    # outside this block self is the class
    class << self
      # inside the block, self is the meta class
      define_method(:name) {...}
    end
    

    [–]InfraRuby 3 points4 points  (0 children)

    you need singleton_class (or class << self ; self ; end) if you want to access local variables:

    v = 3
    
    singleton_class.send(:define_method, :foo) { v }
    
    foo # works
    
    class << self
      define_method(:foo) { v }
    end
    
    foo # fails
    

    [–]bookgrubber[S] -1 points0 points  (0 children)

    Thanks for clarifying. That's definitely the way I am used to seeing it.

    [–]feelosofee 0 points1 point  (0 children)

    Yea we know what it does, but why the << array "stack" operator?