Class variables in class_eval

M

Mark Somerville

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
 
R

Robert Dober

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
 
M

Mark Somerville

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
-------------------------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
 
J

John N. Joyner

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.
 
J

John Joyner

John said:
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
 
M

Morton Goldberg

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.

If you suspect this, test your hypothesis with the Benchmark library.
Apologies! I had meant to start a new thread.

Regards, Morton
 
G

Gregory Brown

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.
 
R

Robert Dober

And to round up the picture and to keep David happy ;)

--------------------- 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
 
G

Gregory Brown

And to round up the picture and to keep David happy ;)

--------------------- 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.

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.
 

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

Members online

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top