Forum: Ruby Modules, instance methods and class methods

Posted by Vincent Fourmond (Guest)
on 2006-09-27 00:22
(Received via mailing list)
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
Posted by unknown (Guest)
on 2006-09-27 01:46
(Received via mailing list)
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
Posted by Trans (Guest)
on 2006-09-27 04:40
(Received via mailing list)
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
Posted by Vincent Fourmond (Guest)
on 2006-09-27 11:10
(Received via mailing list)
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
Posted by unknown (Guest)
on 2006-09-27 12:16
(Received via mailing list)
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
Posted by Trans (Guest)
on 2006-09-27 15:26
(Received via mailing list)
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
No account? Register here.