Hello !
I have a rather tortured architecture where several classes should
share code for some class methods and for some instance methods. To fix
the ideas, I need that some class I define have:
def ThisClass.desc
return @desc
end
def desc
return self.class.desc
end
To do that, I define two modules
module BaseInclude
def desc
return self.class.desc
end
end
module BaseExtend
def desc
return @desc
end
end
and I include them with
class ThisClass
include BaseInclude
extend BaseExtend
end
That works perfectly fine. My question, then, is : is there a simpler
way ? I can't afford to have a shared ancestor for these classes.
Thanks !
Vince
on 2006-09-27 00:22
on 2006-09-27 01:46
Hi -- On Wed, 27 Sep 2006, Vincent Fourmond wrote: > > end > include BaseInclude > extend BaseExtend > end > > That works perfectly fine. My question, then, is : is there a simpler > way ? I can't afford to have a shared ancestor for these classes. Personally, I like your approach: you've grouped methods according to plan, and then carried out the plan. There are more elaborate ways, but this seems to work for you, and it's very clear. Just to point you to a couple of possible variations: You could make the module that's destined for the extend operation a nested module inside the other. In fact, you'll sometimes see this: module Something def a_method end module ClassMethods def some_class_level_method end end end followed by: class MyClass include Something extend Something::ClassMethods end (And you can even hook the extend operation into the include operation by defining an appropriate self.included hook method in Something, so that you don't have to have a separate extend call.) I use the name "ClassMethods" advisedly: it's a popular choice. It has the disadvantage of being inaccurate, in the sense that the methods inside it are instance methods. But as long as they're destined to be class methods (which, after all, are really instance methods of the singleton class of a class!), it might be a reasonable name. David
on 2006-09-27 04:40
Hi-- Vincent Fourmond wrote: > def desc > > extend BaseExtend > end > > That works perfectly fine. My question, then, is : is there a simpler > way ? I can't afford to have a shared ancestor for these classes. > > Thanks ! Not too long ago this was discussed with a length with Matz. The result was #class_extension. module Base def desc return self.class.desc end class_extension do def desc return @desc end end end class ThisClass include Base end An excellent pure Ruby implementation of this was developed by Daniel Schierbeck, It can be found it the Facets project (facets.rubyforge.org). To use: require 'facet/module/class_extension'. For convenience the code follows. It remains to be seen if Matz ultimately goes with this approach in Ruby 2.0. But as of yet I guess one could say it the most "official" technique available. (Not to say that others are prefectly valid of course) T. class Module alias_method :append_features_without_class_extension, :append_features # = class_extension # # Normally when including modules, class/module methods are not # extended. To achieve this behavior requires some clever # Ruby Karate. Instead class_extension provides an easy to use # and clean solution. Simply place the extending class methods # in a block of the special module method #class_extension. # # module Mix # def inst_meth # puts 'inst_meth' # end # # class_methods do # def class_meth # "Class Method!" # end # end # end # # class X # include Mix # end # # X.class_meth #=> "Class Method!" # 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
on 2006-09-27 11:10
Hello ! > return @desc > (facets.rubyforge.org). To use: require 'facet/module/class_extension'. > For convenience the code follows. Thanks ! > It remains to be seen if Matz ultimately goes with this approach in > Ruby 2.0. But as of yet I guess one could say it the most "official" > technique available. (Not to say that others are prefectly valid of > course) That would be cool if the syntax could be somewhat lighter: it somehow doesn't look right to me to have the code within a block. But that's just aesthetics... And that would be for ruby 2.0, if I understand right. Cheers ! Vince
on 2006-09-27 12:16
Hi -- On Wed, 27 Sep 2006, Vincent Fourmond wrote: >> >> >> course) > > That would be cool if the syntax could be somewhat lighter: it somehow > doesn't look right to me to have the code within a block. But that's > just aesthetics... And that would be for ruby 2.0, if I understand right. I agree; it seems odd to resort to a block when generally the inclusion/extension mechanism involves modules. Also, the terminology is a bit narrow; it should really be 'includer_extension' or something, since modules can be included by modules as well as classes. David
on 2006-09-27 15:26
dblack@wobblini.net wrote: > >> module Base > >> > > > inclusion/extension mechanism involves modules. Yea. I get the same vibe. There's no precedence for it so it doesn't seem very Rubyish. But at the very least I'm happy to have something that works well. Also it doesn't require any changes under the hood so I imagine it'll just be part of the standard library, nor core. Ideally though I still tend to favor an alternate to #include, eg #include_with_class_extension, or something (though that name is way too long). But a solution like that does require some changes to Ruby itself --it's not possible to implement in plain Ruby. > Also, the terminology > is a bit narrow; it should really be 'includer_extension' or > something, since modules can be included by modules as well as > classes. I think "class" is referring to the singleton, which is a class in etither case. T.
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
Log in with Google account | Log in with Yahoo account
No account? Register here.