Class variables in class_eval

I'm confused, to say the least! I'm setting up caches on objects using a
class variable. It all works well until I try to extract it out to a module
to mixin to other objects:

module HostBasedCache
  def setup_cache(method, &method_proc)
    @@cache_method = method_proc if block_given?
    class_eval <<-METHOD
    def self.#{method}(param)
      #some stuff not relevant
      puts @@cache_method.call(param)
      #some stuff not relevant
    end
    METHOD
  end
end

class MyObject

extend HostBasedCache
setup_cache(:find_with_roles) do |id|
  User.find_by_id(id, :select => "users.id, roles.id", :include => [ :roles
])
end

end

Unfortunately, when calling find_with_roles I'm told that @@cache_method
isn't defined. Setting @@cache_method explicitly in MyObject (rather than the
setup_cache) call makes everything work. I must be missing some scoping
problem - can anyone explain this to me, please?

Thanks a lot,

Mark

I'm confused, to say the least! I'm setting up caches on objects using a
class variable.

My rule of thumb is, Do Not Use Class Variables, use Class Instance Variables

It all works well until I try to extract it out to a module

to mixin to other objects:

module HostBasedCache
  def setup_cache(method, &method_proc)
    @@cache_method = method_proc if block_given?
    class_eval <<-METHOD
    def self.#{method}(param)
      #some stuff not relevant
      puts @@cache_method.call(param)
      #some stuff not relevant
    end
    METHOD
  end
end

class MyObject

extend HostBasedCache
setup_cache(:find_with_roles) do |id|
  User.find_by_id(id, :select => "users.id, roles.id", :include => [ :roles
])
end

end

Unfortunately, when calling find_with_roles I'm told that @@cache_method
isn't defined. Setting @@cache_method explicitly in MyObject (rather than the
setup_cache) call makes everything work. I must be missing some scoping
problem - can anyone explain this to me, please?

Look at this slightly adapted code just using Class Instance Variables.
Does this solve your problem?

-------------------------8<----------------------------

module HostBasedCache

    def setup_cache(method, &method_proc)
      @cache_method = method_proc if block_given?
      class_eval <<-METHOD
        def self.#{method}(param)
        #some stuff not relevant
          puts @cache_method.call(param)
        #some stuff not relevant
        end
      METHOD
    end
end

class MyObject

  extend HostBasedCache
  setup_cache(:find_with_roles) do |id|
# User.find_by_id(id, :select => "users.id, roles.id", :include => [ :roles ])
    puts "Hi there " << id.to_s
  end

end

MyObject.find_with_roles( 42 )

-------------------------8<----------------------------
Cheers
Robert

···

On 6/19/07, Mark Somerville <ms@pccl.info> wrote:
--
You see things; and you say Why?
But I dream things that never were; and I say Why not?
-- George Bernard Shaw

Look at this slightly adapted code just using Class Instance Variables.
Does this solve your problem?

It does! I wasn't aware that there are class instance variables as well as
class variables.

Appreciated, thank you very much!

Mark

···

On Tue, Jun 19, 2007 at 09:05:38PM +0900, Robert Dober wrote:

-------------------------8<----------------------------

module HostBasedCache

   def setup_cache(method, &method_proc)
     @cache_method = method_proc if block_given?
     class_eval <<-METHOD
       def self.#{method}(param)
       #some stuff not relevant
         puts @cache_method.call(param)
       #some stuff not relevant
       end
     METHOD
   end
end

class MyObject

extend HostBasedCache
setup_cache(:find_with_roles) do |id|
# User.find_by_id(id, :select => "users.id, roles.id", :include => [ :roles
])
   puts "Hi there " << id.to_s
end

end

MyObject.find_with_roles( 42 )

-------------------------8<----------------------------
Cheers
Robert
--
You see things; and you say Why?
But I dream things that never were; and I say Why not?
-- George Bernard Shaw

Hi --

···

On Tue, 19 Jun 2007, Mark Somerville wrote:

On Tue, Jun 19, 2007 at 09:05:38PM +0900, Robert Dober wrote:

Look at this slightly adapted code just using Class Instance Variables.
Does this solve your problem?

It does! I wasn't aware that there are class instance variables as well as
class variables.

Anything that can be 'self' can have instance variables :slight_smile:

David

--
* Books:
   RAILS ROUTING (new! http://safari.awprofessional.com/9780321509246\)
   RUBY FOR RAILS (http://www.manning.com/black\)
* Ruby/Rails training
     & consulting: Ruby Power and Light, LLC (http://www.rubypal.com)

Is there a significant performance difference between these two ways to
call a method?
(a) Define the method in a module, then call it with
MODULE_NAME::METHOD_NAME
(b) Define the method in a class, then instantiate an object from the
class, then call the method with OBJECT_REFERENCE_VAR.METHOD_NAME

Purely for the convenience in naming methods, I'd love to do everything
in classes, but I can't help suspecting that performance will suffer
somewhat if I instantiate objects when not really necessary.

···

--
JJ

John N. Joyner wrote:

Is there a significant performance difference between these two ways to
call a method?
(a) Define the method in a module, then call it with
MODULE_NAME::METHOD_NAME
(b) Define the method in a class, then instantiate an object from the
class, then call the method with OBJECT_REFERENCE_VAR.METHOD_NAME

Purely for the convenience in naming methods, I'd love to do everything
in classes, but I can't help suspecting that performance will suffer
somewhat if I instantiate objects when not really necessary.

Apologies! I had meant to start a new thread.
- John

···

--
Posted via http://www.ruby-forum.com/\.

Is there a significant performance difference between these two ways to
call a method?
(a) Define the method in a module, then call it with
MODULE_NAME::METHOD_NAME

You don't need to use ::, that's mostly meant for constants and nested
class seperation.

Instead, do:

module MyModule

   module_function

  def whatever

  end

end

you can also of course pass specific method names to module_function.

This lets you do:

MyModule.whatever

(b) Define the method in a class, then instantiate an object from the
class, then call the method with OBJECT_REFERENCE_VAR.METHOD_NAME

You can also make class methods.

class A
  def self.whatever
  end
end

A.whatever

This is useful when an object is stateful but doesn't need to have
instances (or has behaviour at the class level and instance level)

Finally, to answer you question about performance, a big difference is
that modules *can't* be instantiated. But I doubt that they will be
much less heavyweight than a class that you don't create instances of,
or use just one instance. You'll want to use profile memory usage to
be sure.

···

On 6/22/07, John N. Joyner <jnj@thecoopercompany.com> wrote:

John N. Joyner wrote:

Is there a significant performance difference between these two ways to
call a method?
(a) Define the method in a module, then call it with
MODULE_NAME::METHOD_NAME
(b) Define the method in a class, then instantiate an object from the
class, then call the method with OBJECT_REFERENCE_VAR.METHOD_NAME

Purely for the convenience in naming methods, I'd love to do everything
in classes,

IMO that's not a good reason. The main reason for grouping methods into classes is to provide behavior to a set of objects where each object maintains it own individual state. Another is that classes support inheritance and modules don't. In particular, every class you define inherits great deal of commonly needed behavior from Object.

Also, I don't see why naming is more convenient in classes than in modules.

but I can't help suspecting that performance will suffer
somewhat if I instantiate objects when not really necessary.

If you suspect this, test your hypothesis with the Benchmark library.

Apologies! I had meant to start a new thread.

Regards, Morton

···

On Jun 23, 2007, at 10:28 AM, John Joyner wrote:

And to round up the picture and to keep David happy :wink:

--------------------- 8< ------------------
module M
   def my_fancy_method
   ...
...

class C
   extend M
...

C.my_fancy_method
--------------------- >8 -------------------

this might indeed be a useful pattern which is underused - citation David Black.

Cheers
Robert

···

On 6/23/07, Gregory Brown <gregory.t.brown@gmail.com> wrote:

--
You see things; and you say Why?
But I dream things that never were; and I say Why not?
-- George Bernard Shaw

sure. It's fairly OT from what was asked for though, but while we're here

I rarely find myself needing 'just' class methods, and I don't like
doing a seperate call to mix them in, so i'd typically do something
like

module M

  module ClassMethods
      def whatever; end
  end

  def self.included(base)
     base.extend(ClassMethods)
  end

end

class C
   include M
end

But if all you need is the class methods, and don't mind using extend,
what you suggested works fine.

···

On 6/23/07, Robert Dober <robert.dober@gmail.com> wrote:

On 6/23/07, Gregory Brown <gregory.t.brown@gmail.com> wrote:
And to round up the picture and to keep David happy :wink:

--------------------- 8< ------------------
module M
   def my_fancy_method
   ...
...

class C
   extend M
...

C.my_fancy_method
--------------------- >8 -------------------

this might indeed be a useful pattern which is underused - citation David Black.