special super call for module class-level inclusion

T

Trans

Since I've come upon the issue again, I reckon, it's as good time as
any to put this in the air.

The issue, again, is that module class methods are not
"inherited" (for lack of a better word). In the past I've argued for
this, but it has been consistently argued down due to the extraneous
methods it would add to classes. However, it occurs to me that the
core problem that I continually face for the lack of this, it simply
that I can't call super in a class level method and expect it to move
through included module methods. The reason this is such a problem, it
that it makes it very difficult to create an "inheriting variable".
This is the reason Facets contains inheritor.rb, but even that is
limited to only simple cases.

To make this clear, here is a simple example of the idea:

class X
include M
def self.foo
[ 'Xfoo' ]
end
end

module M
def self.foo
[ 'Mfoo' ] + super
end
end

class Y < X
include M
def self.foo
[ 'Yfoo' ] + super
end
end

Y.foo #=> [ 'Yfoo', 'Xfoo' ]

As you can see there is no 'Mfoo' in the list.

If your're wondering how this is useful, consider any use-case where
meta-information needs to associated to a method. Just as methods are
inherited so too their meta-information. Possible uses are annotations
and pre, post, around wraps.

So the thought I had today is that rather then full inclusion at the
class-level, that we might simply have an alternate #super call that
would traverse the include modules class methods as well as classes. I
realize that might be tricky to implement, but it would satisfy the
need without all the unwanted baggage.

T.
 
D

Daniel DeLorme

Trans said:
The issue, again, is that module class methods are not
"inherited" (for lack of a better word). In the past I've argued for
this, but it has been consistently argued down due to the extraneous
methods it would add to classes. However, it occurs to me that the

IMHO the reason for this problem is that modules serve double duty as
both mixins (instance methods) and namespaces (class methods).
core problem that I continually face for the lack of this, it simply
that I can't call super in a class level method and expect it to move
through included module methods. The reason this is such a problem, it
that it makes it very difficult to create an "inheriting variable".
This is the reason Facets contains inheritor.rb, but even that is
limited to only simple cases.

The problem piqued my interest so despite my best attempts at
self-control I wound up writing code for it :-/

class Module
attr_accessor :class_methods_module
def class_methods(&block)
self.class_methods_module ||= begin
mod = Module.new
core = (class << self; self; end)
prev = core.method:)included)
core.send:)define_method, :included) do |into_class|
prev.call(into_class)
into_class.extend(mod)
end
mod
end
class_methods_module.class_eval(&block)
end
end

class Class
def class_methods(&block)
instance_eval(&block)
end
end

module M
def a;"Ma";end
class_methods do
def b;"Mb";end
end
end

class C
include M
def a;"Ca"+super;end
class_methods do
def b;"Cb"+super;end
end
end

puts C.new.a #=> CaMa
puts C.b #=> CbMb
 
T

Trans

The problem piqued my interest so despite my best attempts at
self-control I wound up writing code for it :-/

class Module
attr_accessor :class_methods_module
def class_methods(&block)
self.class_methods_module ||= begin
mod = Module.new
core = (class << self; self; end)
prev = core.method:)included)
core.send:)define_method, :included) do |into_class|
prev.call(into_class)
into_class.extend(mod)
end
mod
end
class_methods_module.class_eval(&block)
end
end

class Class
def class_methods(&block)
instance_eval(&block)
end
end

Nice work! That's not the easiest piece of meta-code to write. But has
been touched on before. Facets has had a version of this for a while
now, used extensively by Nitro/Og, written primarily by Daniel
Schierbeck. It looks like this:

class Module


alias_method :append_features_without_class_extension, :append_features

def class_extension(&block)
@class_extension ||= Module.new do
def self.append_features(mod)
append_features_without_class_extension(mod)
end
end
@class_extension.module_eval(&block) if block_given?
@class_extension
end

private :class_extension

def append_features(mod)
append_features_without_class_extension(mod)
mod.extend(class_extension)
if mod.instance_of? Module
mod.__send__:)class_extension).__send__:)include,
class_extension)
end
end

end

class Class
undef_method :class_extension
end

Your definition of Class's method is an interesting idea and may make
a good adjustment Facets' undef_method.

On the downside, this approach doesn't RDoc well --actually it doesn't
RDOc at all. Also, many people seem put off by the general aesthetic
of it too.

As of late I've been leaning toward the idea of the old ClassMethods
module solution, but modifying #include (or a similar new method) to
handle the extension rather than using the included callback.
(Personally, I'd also prefer a better name than ClassMethods, but
that's of minor consequence).

T.
 

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

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,567
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top