Dynamic define_method on class creation per module namespace

Here's a wee challenge for Rubyists at large. Consider:

  module M

    class A
      def initialize ; puts "A" ; end
    end

    def a
      A.new
    end

    class B
      def initialize ; puts "B" ; end
    end

    def b
      B.new
    end

    # etc

  end

I would like to create a mixin of some sort that automatically creates the
module methods as needed. So,

  module M

    include ClassMethods

    class A
      def initialize ; puts "A" ; end
    end

    class B
      def initialize ; puts "B" ; end
    end

    # etc

  end

would give same effect as above. Possible?

Thanks,
T.

Sorry, slight correction. I intended those to be module methods (albeit it
probably doesn't matter much either way) So the example should be:

   module M

     class A
       def initialize ; puts "A" ; end
     end

     def self.a
       A.new
     end

     class B
       def initialize ; puts "B" ; end
     end

     def self.b
       B.new
     end

     # etc

   end

Thanks,
T.

Hi,

At Sun, 14 Nov 2004 16:22:41 +0900,
trans. (T. Onoma) wrote in [ruby-talk:120248]:

I would like to create a mixin of some sort that automatically creates the
module methods as needed. So,

One method is to use method_missing.

  module ClassMethods
    def method_missing(id, *args, &block)
      if /\A[a-z][A-Za-z_0-9]*\z/ =~ name = id.to_s
        classes =
        constants.each do |n|
          if name.casecmp(n).zero? and Class === (c = const_get(n))
            classes << c
          end
        end
        if classes.size == 1
          return classes.first.new(*args, &block)
        end
      end
      super
    end
  end

···

--
Nobu Nakada

I think you can hook into Class#inherited at the Object level, thusly:

class Object
    def self.inherited(child)
        for m in all_modules_that_have_included_ClassMethods
            if m.const_defined(child.name) && m.const_get(child.name)==child
                generated = "
                    def #{child.name.downcase}(*args, &b)
                        #{child.name).new(*args, &b)
                    end"
                m.module_eval generated
            end
        end
    end
end

I'm curious what you are trying to do with this. I am doing something
similar to allow easy creation of tree structures, but I need instance (not
module) methods to grab hold of the constructed objects, plus some 'typed'
attribute macros to know what to instantiate.

car 'A' {
    engine '8-cylinder' {
        valve { ...}
        valve { ...}
        valve { ...}
    }
}

I plan to allow some conveinent cross-tree references as well.

···

"trans. (T. Onoma)" <transami@runbox.com> wrote

  module M

    include ClassMethods

    class A
      def initialize ; puts "A" ; end
    end

    class B
      def initialize ; puts "B" ; end
    end

    # etc

  end

trans. (T. Onoma) schrieb:

Sorry, slight correction. I intended those to be module methods (albeit it probably doesn't matter much either way) So the example should be:

I don't see how you could cleanly implement this (disregarding
Nobu's missing method magic) with out a const_addded hook.
See [ruby-talk:44258]

/Christoph

I don't like using missing method usually, but I guess that's the only way to
currently do it.

Thanks,
T

···

On Sunday 14 November 2004 02:54 am, nobu.nokada@softhome.net wrote:

One method is to use method_missing.

  module ClassMethods
    def method_missing(id, *args, &block)
      if /\A[a-z][A-Za-z_0-9]*\z/ =~ name = id.to_s
        classes =
        constants.each do |n|
          if name.casecmp(n).zero? and Class === (c = const_get(n))
            classes << c
          end
        end
        if classes.size == 1
          return classes.first.new(*args, &block)
        end
      end
      super
    end
  end

I think you can hook into Class#inherited at the Object level, thusly:

class Object
    def self.inherited(child)
        for m in all_modules_that_have_included_ClassMethods
            if m.const_defined(child.name) &&
m.const_get(child.name)==child generated = "
                    def #{child.name.downcase}(*args, &b)
                        #{child.name).new(*args, &b)
                    end"
                m.module_eval generated
            end
        end
    end
end

Thanks, I may be able to work with that. If I can just get
all_modules_that_have_included_ClassMethods :slight_smile:

I'm curious what you are trying to do with this. I am doing something
similar to allow easy creation of tree structures, but I need instance (not
module) methods to grab hold of the constructed objects, plus some 'typed'
attribute macros to know what to instantiate.

car 'A' {
    engine '8-cylinder' {
        valve { ...}
        valve { ...}
        valve { ...}
    }
}

I plan to allow some conveinent cross-tree references as well.

I am doing something very similar with a markup parser (made-up example):

  tr = token_registry {
    block :literal, '"""', '"""'
    line :strong, '*', '*'
    line :italic, '_', '_'
  }

One of things that made the methods even better was not having to worry about
the namespace for the classes themselves --in other words, the classes Block
and Line themselves are, in a way, private.

T.

···

On Sunday 14 November 2004 04:08 pm, itsme213 wrote:

Ah yes, that would be perfect. One day, one day....

Thanks,
T.

P.S. I recall long ago someone mentioned a real quick way to access ruby-talk
messages (like the above ruby-talk:44258). Anyone know how?

···

On Sunday 14 November 2004 06:36 am, Christoph wrote:

trans. (T. Onoma) schrieb:
>Sorry, slight correction. I intended those to be module methods (albeit it
>probably doesn't matter much either way) So the example should be:

I don't see how you could cleanly implement this (disregarding
Nobu's missing method magic) with out a const_addded hook.
See [ruby-talk:44258]

Worst case:
ObjectSpace.each_object(Module) { |m|
m.included_modules.include?(ClassMethods) }

Better: hook into ClassMethods#included(module) to mark all modules that
include ClassMethods.

···

"trans. (T. Onoma)" <transami@runbox.com> wrote

Thanks, I may be able to work with that. If I can just get
all_modules_that_have_included_ClassMethods :slight_smile:

Hi,

At Mon, 15 Nov 2004 06:37:11 +0900,
trans. (T. Onoma) wrote in [ruby-talk:120320]:

I am doing something very similar with a markup parser (made-up example):

  tr = token_registry {
    block :literal, '"""', '"""'
    line :strong, '*', '*'
    line :italic, '_', '_'
  }

One of things that made the methods even better was not having to worry about
the namespace for the classes themselves --in other words, the classes Block
and Line themselves are, in a way, private.

What about this way? Though I don't think the name
class_method is proper...

module ClassMethods
  def class_method(name, superclass = ::Object, method = name.to_s.downcase,
                   &definition)
    c = Class.new(superclass, &definition)
    define_method(method, &c.method(:new))
    module_function method
    const_set(name, c)
  end
end

if $0 == __FILE__

  module M
    extend ClassMethods

    class_method(:A) do
      def initialize ; puts "A" ; end
    end

    class_method(:B) do
      def initialize ; puts "B" ; end
    end
  end

  p M.a, M.b
end

···

--
Nobu Nakada

trans. (T. Onoma) wrote:

P.S. I recall long ago someone mentioned a real quick way to access ruby-talk messages (like the above ruby-talk:44258). Anyone know how?

This used to work, IIRC, but doesn't now:

http://ruby-talk.org/12345

and going to http://ruby-talk.org/ gives the message:

The Ruby-talk.org domain is temporarily under maintenance. Please use http://blade.nagaokaut.ac.jp/ruby/ruby-talk/index.shtml\. Thanks, Ruby Central, Inc.

Anyway, it possible to configure many browsers to recognize

ruby-talk:12345

as a shortcut in the url bar for the url

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/12345

In konqueror, this feature is called Web Shortcuts.

OT: ironically message 12345 is from matz, reluctantly agreeing to remove the [ruby-talk:12345] tag that was automatically added to beginning of the subject line on messages from the ML. If you remember *that* discussion, you're getting to be an old timer here. (And if you were on the list, but don't remember the discussion you are really an old timer :wink:

Right, that's one way to do it. Although that's manualizing things to the
point that I think might be just as easy to go ahead and write out each
method.

Funny thing though, this really gives one pause in consideration of 'class'
possibly being an actual method that one could tap into.

T.

···

On Sunday 14 November 2004 07:22 pm, nobu.nokada@softhome.net wrote:

What about this way? Though I don't think the name
class_method is proper...

module ClassMethods
  def class_method(name, superclass = ::Object, method =
name.to_s.downcase, &definition)
    c = Class.new(superclass, &definition)
    define_method(method, &c.method(:new))
    module_function method
    const_set(name, c)
  end
end

if $0 == __FILE__

  module M
    extend ClassMethods

    class_method(:A) do
      def initialize ; puts "A" ; end
    end

    class_method(:B) do
      def initialize ; puts "B" ; end
    end
  end

  p M.a, M.b
end