views:

138

answers:

3

What I'm trying to find out is whether there is some sort of equivalence to what I see in Groovy as ExpandoMetaClasses. I've been reading about Open Classes but I can't quite see what level of scoping Ruby allows of the class modifications.

Borrowing an example from the blog above, in Groovy, I could modify Java's String class and add a method to it like so:

String.metaClass.shout = {->
  return delegate.toUpperCase()
}

println "Hello MetaProgramming".shout()

// output
// HELLO METAPROGRAMMING

And I think that Ruby would have you redefine the class and possibly alias it (please help clarify my misunderstandings at this point):

class String
  def foo
    "foo"
  end
end 

puts "".foo # prints "foo"

In Groovy, there are ways to scope the redefinition of core Java library methods to single instances or to a group of instances using Categories, which feel similar to what I would define as mixins in Ruby.

What are the ways to scope open classes to specific instances or to subsets of modules?

If I were to install a gem that had redefined some core class, would only that module be affected, or would any .rb file I include that gem with be affected with it?

Apologies in advance for making some possible assumptions on both Ruby and Groovy, I'm new to both but have been trying to find equivalence between the two.

+2  A: 

Ruby's classes are never "closed". So when you say:

class String
  def omg!
    self.replace "OMG"
  end
end

You are defining the omg! method on the String class. Unlike in Groovy, which requires the usage of a special metaclass concept, Ruby classes are always open, period.

If you wanted to modify a particular set of Strings, you could do this:

module Magic
  def presto
    puts "OMG A HAT!"
  end
end

class Array
  include Magic
end

x = "Hello".extend(Magic)
puts x        #=> Hello
x.presto      #=> OMG A HAT!
[].presto     #=> OMG A HAT!

def x.really?
  true
end

x.really?     #=> true

Effectively, a module is a collection of methods that can be added to a class or specific instances.

So you can either open a class directly or add new methods to a class using a module. You can also open an instance directly or add new methods to an instance using a module. That's because a class is just an instance of Class ;) Pretty nifty!

Yehuda Katz
+1  A: 

In addition to what Yehuda said, instances in Ruby also have metaclasses (technically called "singleton classes"), accessed with class <<whatever. For example, to redo Yehuda's Magic example with a singleton class:

x = "Hello"
class <<x
  include Magic
  def magical?
    true
  end
end
x.presto                   #=> OMG A HAT!
x.magical?                 #=> true
"Something else".magical?  #=> NoMethodError
Chuck
A: 

There's no scoping on modifications to classes. As soon as a class is modified, the modified class is accessible to all later requires and following code.

rampion