Forum: Ruby mixin puzzle

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
polypus (Guest)
on 2006-05-04 01:59
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?
unknown (Guest)
on 2006-05-04 02:17
(Received via mailing list)
On Thu, 4 May 2006, polypus wrote:

> end
>
> class X
>  extend M
> end
>
> would it possible for
>
> x = X.new
>
> to somehow call foo as an instance method of x?

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
polypus (Guest)
on 2006-05-04 02:27
> class X
>    include M
> end
>

thanks, but you modified class X, which was my provision that you should
not do.
Daniel H. (Guest)
on 2006-05-04 02:35
(Received via mailing list)
On May 3, 2006, at 11:59 PM, polypus wrote:

> would it possible for
>
> x = X.new

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

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

-- Daniel
polypus (Guest)
on 2006-05-04 02:37
> -- Daniel


thanks, close but no cigar. notice that i want X.new to call foo. i
don't think that it's possible.
polypus (Guest)
on 2006-05-04 02:40
the solution should read exactly as i wrote it, the only modifications
allowed are to the body of the two modules.
Mike H. (Guest)
on 2006-05-04 03:38
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
Mauricio F. (Guest)
on 2006-05-04 04:10
(Received via mailing list)
On Thu, May 04, 2006 at 06:59:59AM +0900, polypus wrote:
> end
>
> class X
>   extend M
> end
>
> would it possible for
>
> x = X.new
>
> to somehow call foo as an instance method of x?

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?
Mike H. (Guest)
on 2006-05-04 04:26
I had no idea that extend has an append_features-style callback, you
learn something every day.  Thanks Mauricio.
unknown (Guest)
on 2006-05-04 06:45
(Received via mailing list)
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.

sorry.  i assumed since you said

   class X
     extend M
   end

that you didn't really mean it ;-)

-a
unknown (Guest)
on 2006-05-04 06:57
(Received via mailing list)
On Thu, 4 May 2006, Mike H. wrote:

> 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:

>
> 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
polypus (Guest)
on 2006-05-04 08:30
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.
polypus (Guest)
on 2006-05-05 06:14
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
Logan C. (Guest)
on 2006-05-05 06:24
(Received via mailing list)
On May 4, 2006, at 10:14 PM, polypus wrote:

> print something like #<X:0xb7e188a4>
>   ...
>
> --
> Posted via http://www.ruby-forum.com/.
>

module M
   class ::X
     include N
   end
end
polypus (Guest)
on 2006-05-05 06:31
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
Logan C. (Guest)
on 2006-05-05 06:43
(Received via mailing list)
On May 4, 2006, at 10:31 PM, polypus wrote:

> also you did not solve rule 3
>
> --
> Posted via http://www.ruby-forum.com/.
>

eh It was a joke answer anyway :-p
unknown (Guest)
on 2006-05-05 06:50
(Received via mailing list)
On Fri, 5 May 2006, polypus wrote:

> 4) you may not use class_eval, module_eval, or use the extend_object
> end
>
> class X
>  extend M
> end
>
> X.new
>
> yes there is a solution, good luck

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
polypus (Guest)
on 2006-05-05 06:56
hallelujah!

wasn't that hard for you was it?
wondering whether it wouldn't be a good one for rubyquiz though.
Logan C. (Guest)
on 2006-05-05 06:59
(Received via mailing list)
On May 4, 2006, at 10:31 PM, polypus wrote:

> 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.
test (Guest)
on 2006-05-05 07:39
test
This topic is locked and can not be replied to.