Forum: Ruby Need advice- would like to implement a delayed-load proxy class

Posted by Gary Weaver (garysweaver)
on 2012-11-29 04:19
I'd like to have const_missing return a class that would do the
following:

1. Have the same class name as the class it should have loaded but
without loading the class. Let's call this "proxy".

That part is easy, as I've done similar in my constance gem. Here is
where it starts to get more difficult:

2. Implement method missing such that when "proxy" is called it in turn
calls another class that unloads the proxy class and then loads the
originally requested file.

Implementing method missing is easy, and removing a constant not hard
either, but how can an unloaded class return something from method
missing when it is gone? That wouldn't work.

So then you might say "of course not, just use method missing and return
from the proxy", but if possible I don't want to have the "proxy" class
act as a wrapper or delegate all calls because that is additional
overhead. Maybe it would be ok and transparent and I could mess with the
caller stack to make it appear as if the proxy were not even there, but
I think eventually the magic go-between wrapper would be a problem.

This actually isn't an academic question- I was trying to think of a
solution to the problem of people using names of classes rather than the
constants in class bodies where associations, etc. are defined in Rails
(note: this is not a Rails question, but yes that is where I'd like to
use it). By returning a flyweight proxy class that loads and calls the
real class as needed, they wouldn't need to use strings instead of
classnames because const_missing would return something that didn't get
into some sort of massive autoloading disaster that the community is
afraid of.

Sorry if this just sounds idiotic.
Posted by Gary Weaver (garysweaver)
on 2012-11-29 11:56
> That part is easy, as I've done similar in my constance gem.

Sorry, I meant the classmeta gem: 
https://github.com/garysweaver/classmeta

Been thinking more about this, and although I still think it is a 
somewhat horrid idea, I thought of some tweaks:

class FlyweightProxy < BasicObject
  def initialize(const_name)
    @@name = const_name
  end

  def method_missing(meth, *args, &block)
    remove_class_variable(:@@name)
    # really load class or get instance, and then call method on it
    SomeAutoloader.get(@@name).send(meth, *args, &block)
  end
end

# this is not to be taken literally. for example, in classmeta I 
overrode the method called in Rails autoloading via alias_method'ing and 
redefining load_missing_constant, which I might have just been able to 
do via defining load_missing_constant and calling super instead, but 
don't remember if I tried that: 
https://github.com/garysweaver/classmeta/blob/mast...

module ModuleConstMissing
  def const_missing(const_name)
    FlyweightProxy.new(const_name)
  end
end

Module.class_eval { include ModuleConstMissing }

But, I'm not sure if that would work (need to test).

Sorry if this is way too poorly thought out. Just looking for ideas, 
mostly.

Gary Weaver wrote in post #1086999:
> I'd like to have const_missing return a class that would do the
> following:
>
> 1. Have the same class name as the class it should have loaded but
> without loading the class. Let's call this "proxy".
>
> That part is easy, as I've done similar in my constance gem. Here is
> where it starts to get more difficult:
>
> 2. Implement method missing such that when "proxy" is called it in turn
> calls another class that unloads the proxy class and then loads the
> originally requested file.
>
> Implementing method missing is easy, and removing a constant not hard
> either, but how can an unloaded class return something from method
> missing when it is gone? That wouldn't work.
>
> So then you might say "of course not, just use method missing and return
> from the proxy", but if possible I don't want to have the "proxy" class
> act as a wrapper or delegate all calls because that is additional
> overhead. Maybe it would be ok and transparent and I could mess with the
> caller stack to make it appear as if the proxy were not even there, but
> I think eventually the magic go-between wrapper would be a problem.
>
> This actually isn't an academic question- I was trying to think of a
> solution to the problem of people using names of classes rather than the
> constants in class bodies where associations, etc. are defined in Rails
> (note: this is not a Rails question, but yes that is where I'd like to
> use it). By returning a flyweight proxy class that loads and calls the
> real class as needed, they wouldn't need to use strings instead of
> classnames because const_missing would return something that didn't get
> into some sort of massive autoloading disaster that the community is
> afraid of.
>
> Sorry if this just sounds idiotic.
Posted by Gary Weaver (garysweaver)
on 2012-11-29 11:59
Ha! Oops, wrong order, I meant:

   def method_missing(meth, *args, &block)
     # really load class or get instance, and then call method on it
     c = SomeAutoloader.get(@@name).send(meth, *args, &block)
     remove_class_variable(:@@name)
     c
   end
Posted by Gary Weaver (garysweaver)
on 2012-11-29 15:15
> class FlyweightProxy < BasicObject

Flyweight is a misnomer since is not implementing flyweight pattern. 
LazyLoadingProxy may be a better name.
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.