Mixin puzzle

given the below, if you were not allowed to modify class X

module N
  def foo
  end
end

module M
  # ...
end

class X
  extend M
end

would it possible for

x = X.new

to somehow call foo as an instance method of x?

···

--
Posted via http://www.ruby-forum.com/.

harp:~ > cat a.rb
module N
   def foo() p 42 end
end
module M
  include N
end

class X
   include M
end

x = X.new
x.foo

harp:~ > ruby a.rb
42

-a

···

On Thu, 4 May 2006, polypus wrote:

given the below, if you were not allowed to modify class X

module N
def foo
end
end

module M
# ...
end

class X
extend M
end

would it possible for

x = X.new

to somehow call foo as an instance method of x?

--
be kind whenever possible... it is always possible.
- h.h. the 14th dali lama

module N
   def foo() "foo" end
end
class X
end

x = X.new
class << x
   include N
end
x.foo # => "foo"

-- Daniel

···

On May 3, 2006, at 11:59 PM, polypus wrote:

given the below, if you were not allowed to modify class X

module N
  def foo
  end
end
class X
  extend M
end

would it possible for

x = X.new

module N
  class << self; attr_accessor :bleh end # just to prove #foo was run
  self.bleh = "#foo not executed"
  def foo
    N.bleh = "#foo was executed!!"
  end
end

module M
  def self.extend_object(o)
    o.class_eval do
      include N
      def self.new(*a,&b)
        r = super(*a, &b) # explicit -> less stuff to remember
  r.foo # is this what you meant?? weird.
        # why won't X.new.foo do?
  r
      end
    end
  end
end

# ===== unmodified =>
class X
  extend M
end

x = X.new # => #<X:0xb7e2f034>
# <=====
RUBY_VERSION # => "1.8.4"
N.bleh # => "#foo was executed!!"

Now, care to explain the intended use of this?
Or is it just a pointless exercise?

···

On Thu, May 04, 2006 at 06:59:59AM +0900, polypus wrote:

given the below, if you were not allowed to modify class X

module N
  def foo
  end
end

module M
  # ...
end

class X
  extend M
end

would it possible for

x = X.new

to somehow call foo as an instance method of x?

--
Mauricio Fernandez - http://eigenclass.org - singular Ruby

class X
   include M
end

thanks, but you modified class X, which was my provision that you should
not do.

···

--
Posted via http://www.ruby-forum.com/\.

-- Daniel

thanks, close but no cigar. notice that i want X.new to call foo. i
don't think that it's possible.

···

--
Posted via http://www.ruby-forum.com/\.

I had no idea that extend has an append_features-style callback, you
learn something every day. Thanks Mauricio.

···

--
Posted via http://www.ruby-forum.com/.

the solution should read exactly as i wrote it, the only modifications
allowed are to the body of the two modules.

···

--
Posted via http://www.ruby-forum.com/.

sorry. i assumed since you said

   class X
     extend M
   end

that you didn't really mean it :wink:

-a

···

On Thu, 4 May 2006, polypus wrote:

class X
   include M
end

thanks, but you modified class X, which was my provision that you should
not do.

--
be kind whenever possible... it is always possible.
- h.h. the 14th dali lama

I had no idea that extend has an append_features-style callback, you
learn something every day. Thanks Mauricio.

i think you should elaborate on what you really want, what mauricio posted,
though cool, definitely modifies the class:

   harp:~ > cat a.rb
   module N
     class << self; attr_accessor :bleh end # just to prove #foo was run
     self.bleh = "#foo not executed"
     def foo
       N.bleh = "#foo was executed!!"
     end
   end
   module M
     def self.extend_object(o)
       o.class_eval do
         include N
         def self.new(*a,&b)
           r = super(*a, &b) # explicit -> less stuff to remember
           r.foo # is this what you meant?? weird.
                 # why won't X.new.foo do?
           r
         end
       end
     end
   end
   # ===== unmodified =>
   class X
     p ancestors
     extend M
     p ancestors
   end

   harp:~ > ruby a.rb
   [X, Object, Kernel]
   [X, N, Object, Kernel]

what, exactly do you mean when you say the class must not be modified? both
the solution i posted and this one modify the class in exactly the same way :
by including a module in the class.

what you originally posted:

given the below, if you were not allowed to modify class X

module N
  def foo
  end
end

module M
  # ...
end

class X
  extend M
end

would it possible for

x = X.new

to somehow call foo as an instance method of x?

iff you really do not modify the class X, can be accomplished thus:

   harp:~ > cat a.rb
   module N
     def foo() p 42 end
   end
   module M
     include N
   end
   class X; end
   x = X.new
   x.extend M
   x.foo

   harp:~ > ruby a.rb
   42

any other way to be able to call x.foo will, in fact, modify the class X. but
maybe that's not what you really want?

regards.

-a

···

On Thu, 4 May 2006, Mike Harris wrote:
--
be kind whenever possible... it is always possible.
- h.h. the 14th dali lama

Obviously I modified more than the 2 modules. This is the best you're
gonna do, cause I don't think extend has anything that can act as a
"callback," ala append_features

class Object
  alias_method :old_extend,:extend
  def extend(mod)
    include(mod) if self == X
    old_extend(mod)
  end
end

module N
  def foo
    "N#foo"
  end
end

module M
  include N
end

class X
  extend M
end

puts X.new.foo # N#foo

···

--
Posted via http://www.ruby-forum.com/.

thanks a lot everybody, you have helped me simplify my library
interface.
mauricio you solved it. i knew about the extend callback and am already
in fact using it in the project from whence the very point-full exercise
was distilled, but i hadn't thought of your solution, so thanks. unknown
(ara?) you're right my definition of 'not modify' was not clear, i just
meant do not modify my code listing, while modifying class in memory was
fine. i'll be releasing my project as a gem and will announce it on list
when i do so mauricio you'll see your answer in action.

to illustrate my point, now the user of my lib can go

class X
  extend M
end

instead of

class X
  extend M
  def initialize
    init_lib
  end
end

it's not really a huge deal, but hey it's one less thing for the user to
need to know/worry about. and i've always wondered if it was possible.

···

--
Posted via http://www.ruby-forum.com/.

ok for those who like pointless exercises here is a harder version.

rules:

1) you may replace the elipses with any amount of code but may not alter
any other part of the code listing
2) when the program is run it must return, i.e. X.new must evaluate to
an instance of class X
3) foo must be called as an instance method of an instance of X and
print something like #<X:0xb7e188a4>
4) you may not use class_eval, module_eval, or use the extend_object
callback

module N
  def foo
    puts self
  end
end

module M
  ...
end

class X
  extend M
end

X.new

yes there is a solution, good luck

···

--
Posted via http://www.ruby-forum.com/.

module M
   class ::X
     include N
   end
end

···

On May 4, 2006, at 10:14 PM, polypus wrote:

ok for those who like pointless exercises here is a harder version.

rules:

1) you may replace the elipses with any amount of code but may not alter
any other part of the code listing
2) when the program is run it must return, i.e. X.new must evaluate to
an instance of class X
3) foo must be called as an instance method of an instance of X and
print something like #<X:0xb7e188a4>
4) you may not use class_eval, module_eval, or use the extend_object
callback

module N
  def foo
    puts self
  end
end

module M
  ...
end

class X
  extend M
end

X.new

yes there is a solution, good luck

--
Posted via http://www.ruby-forum.com/\.

harp:~ > cat a.rb
module N
  def foo() puts self end
end

module M
   def new(*a, &b)
     obj = super
     obj.extend N
     obj.foo
     obj
   end
end

class X
  extend M
end

X.new

harp:~ > ruby a.rb
#<X:0xb75d196c>

-a

···

On Fri, 5 May 2006, polypus wrote:

ok for those who like pointless exercises here is a harder version.

rules:

1) you may replace the elipses with any amount of code but may not alter
any other part of the code listing
2) when the program is run it must return, i.e. X.new must evaluate to
an instance of class X
3) foo must be called as an instance method of an instance of X and
print something like #<X:0xb7e188a4>
4) you may not use class_eval, module_eval, or use the extend_object
callback

module N
def foo
   puts self
end
end

module M
...
end

class X
extend M
end

X.new

yes there is a solution, good luck

--
be kind whenever possible... it is always possible.
- h.h. the 14th dali lama

ok i guess i need a rule 5, you may not mention class X in your code

module M
   class ::X
     include N
   end
end

also you did not solve rule 3

···

--
Posted via http://www.ruby-forum.com/\.

hallelujah!

wasn't that hard for you was it?
wondering whether it wouldn't be a good one for rubyquiz though.

···

--
Posted via http://www.ruby-forum.com/.

eh It was a joke answer anyway :-p

···

On May 4, 2006, at 10:31 PM, polypus wrote:

ok i guess i need a rule 5, you may not mention class X in your code

module M
   class ::X
     include N
   end
end

also you did not solve rule 3

--
Posted via http://www.ruby-forum.com/\.

Here's some more whacky code:

% cat whackextend.rb
module M
    $old_extend = Object.instance_method(:extend)
    class ::Object
      def extend(other)
       if other.name == "M"
          eval %Q{
           class #{self}
             include N
             def initialize
               foo
             end
           end
           }
       else
         $old_extend.bind(self).call(other)
       end
     end
   end
end

I'm pretty sure it follows all your rules.

···

On May 4, 2006, at 10:31 PM, polypus wrote:

ok i guess i need a rule 5, you may not mention class X in your code

module M
   class ::X
     include N
   end
end

also you did not solve rule 3

--
Posted via http://www.ruby-forum.com/\.