Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.ruby > #2915 > unrolled thread

looking for an "inversion" pattern

Started byFearless Fool <r@alum.mit.edu>
First post2011-04-15 00:16 -0500
Last post2011-04-16 23:40 +0200
Articles 13 — 6 participants

Back to article view | Back to comp.lang.ruby


Contents

  looking for an "inversion" pattern Fearless Fool <r@alum.mit.edu> - 2011-04-15 00:16 -0500
    Re: looking for an "inversion" pattern Fearless Fool <r@alum.mit.edu> - 2011-04-15 01:27 -0500
      Re: looking for an "inversion" pattern Robert Klemme <shortcutter@googlemail.com> - 2011-04-15 04:24 -0500
        Re: looking for an "inversion" pattern Fearless Fool <r@alum.mit.edu> - 2011-04-15 10:40 -0500
          Re: looking for an "inversion" pattern Robert Klemme <shortcutter@googlemail.com> - 2011-04-16 15:29 +0200
    Re: looking for an "inversion" pattern Kevin Mahler <kevin.mahler@yahoo.com> - 2011-04-15 02:14 -0500
      Re: looking for an "inversion" pattern Fearless Fool <r@alum.mit.edu> - 2011-04-15 02:43 -0500
        Re: looking for an "inversion" pattern Kevin Mahler <kevin.mahler@yahoo.com> - 2011-04-15 08:44 -0500
      Re: looking for an "inversion" pattern 7stud -- <bbxx789_05ss@yahoo.com> - 2011-04-16 13:20 -0500
        Re: looking for an "inversion" pattern 7stud -- <bbxx789_05ss@yahoo.com> - 2011-04-16 13:43 -0500
    Re: looking for an "inversion" pattern Jesús Gabriel y Galán <jgabrielygalan@gmail.com> - 2011-04-15 02:45 -0500
    Re: looking for an "inversion" pattern Brian Candler <b.candler@pobox.com> - 2011-04-16 16:14 -0500
      Re: looking for an "inversion" pattern Robert Klemme <shortcutter@googlemail.com> - 2011-04-16 23:40 +0200

#2915 — looking for an "inversion" pattern

FromFearless Fool <r@alum.mit.edu>
Date2011-04-15 00:16 -0500
Subjectlooking for an "inversion" pattern
Message-ID<d0c168880ff1c65dd84fc6d70778ddb2@ruby-forum.com>
I'm sure there's a clean way to do this in Ruby, but I haven't figured
it out.

I'd like to create a method +foo+ that transforms:

   my_obj.foo.some_method(*args)

to

  MyClass.some_method(my_obj, *args)

Maybe I'm overthinking this.  I'll keep working on this, but in the
meantime, I'm open to suggestions.

Thanks!

-- ff

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

[toc] | [next] | [standalone]


#2917

FromFearless Fool <r@alum.mit.edu>
Date2011-04-15 01:27 -0500
Message-ID<7c9986702d2a6acd3748e6cca228d8da@ruby-forum.com>
In reply to#2915
Fearless Fool wrote in post #992929:
> Maybe I'm overthinking this...

Meh.  I s'pose I can substitute an underscore for a period:

  my_obj.foo_some_method(*args)

.. whereupon this become a simple mixin on my_obj's class:

  def foo_some_method(*args) {
    MyClass.some_method(self, *args)
  }

The only potential disadvantage is that I need to write one of these for 
each method.  The advantages include likely to run much faster (than 
constructing lambdas or invoking method_missing processing) and possibly 
easier to understand and maintain than what I was originally trying to 
do.

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

[toc] | [prev] | [next] | [standalone]


#2927

FromRobert Klemme <shortcutter@googlemail.com>
Date2011-04-15 04:24 -0500
Message-ID<BANLkTikFjzEuhMevuXJ6ZGSHA6h=MNTnjA@mail.gmail.com>
In reply to#2917
On Fri, Apr 15, 2011 at 8:27 AM, Fearless Fool <r@alum.mit.edu> wrote:
> Fearless Fool wrote in post #992929:
>> Maybe I'm overthinking this...
>
> Meh.  I s'pose I can substitute an underscore for a period:
>
>  my_obj.foo_some_method(*args)
>
> ... whereupon this become a simple mixin on my_obj's class:
>
>  def foo_some_method(*args) {
>    MyClass.some_method(self, *args)
>  }
>
> The only potential disadvantage is that I need to write one of these for
> each method.  The advantages include likely to run much faster (than
> constructing lambdas or invoking method_missing processing) and possibly
> easier to understand and maintain than what I was originally trying to
> do.

I am sorry, I still do not understand your motivation to have a class
method which receives an instance as first argument.  Isn't this
conceptually exactly what an instance method is for?  Why do you need
this?

If you need this as general pattern you could always do

class X
  def method_missing(m, *a, &b)
    self.class.send(m, self, *a, &b)
  end

  def self.foo(obj, str)
    printf "%p: %s\n", obj, str
  end
end

X.new.foo 123

But I would seriously question the wisdom of this. :-)

Other than that I would follow Jesus's approach to make #foo create a
proxy instance.  Here's another way to do it which would also allow
method chaining on the proxy:

class Proxy < BasicObject
  def initialize(orig, delegate)
    @orig = orig
    @delegate = delegate
  end

  def method_missing(m, *a, &b)
    res = @delegate.send(m, @orig, *a, &b)
    @orig.equal?(res) ? self : res
  end
end

class Object
  # create proxy for delegation to given
  # instance
  def proxy(delegate)
    Proxy.new(self, delegate)
  end
end

class YourClass
  def proxy
    super(MyClass)
  end
end

Kind regards

robert

-- 
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

[toc] | [prev] | [next] | [standalone]


#2949

FromFearless Fool <r@alum.mit.edu>
Date2011-04-15 10:40 -0500
Message-ID<9781d8dfa4b6039362d09f98e7b7036e@ruby-forum.com>
In reply to#2927
Thanks everybody!!

@Jesús:

I like your suggestion.  It is simple and does what I'm looking for.

@Robert K:
> I am sorry, I still do not understand your motivation to have a class
> method which receives an instance as first argument.

My motivation is that I was writing a lot of code like this:

module HasXattr
    def xattr_reference(key);  Xattr.xattr_reference(self, key); end
    def xattr_store(key, value); Xattr.xattr_store(self, key, value); 
end
    def xattr_has_key?(key); Xattr.xattr_has_key?(self, key); end
    def xattr_delete(key); Xattr.xattr_delete(self, key); end
    ...
end

.. and I thought to myself: this is Ruby.  There must be a better way. 
I guess I should have shown this concrete example earlier in the thread.

@Kevin:
> One of the problems with Ruby is that the syntax changes depending on
> the compile-time or run-time context, which is a totally arbitrary
> distinction.

As I said, I came from a scheme background where everything was 
syntactically simple and life was simple.   Ah, for those days.  I guess 
I should have said "that's mighty fine Ruby-fu", since your 
understanding of Ruby far outstrips mine.

Again, thanks all...

- ff

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

[toc] | [prev] | [next] | [standalone]


#3005

FromRobert Klemme <shortcutter@googlemail.com>
Date2011-04-16 15:29 +0200
Message-ID<90tjukFgaiU1@mid.individual.net>
In reply to#2949
On 15.04.2011 17:40, Fearless Fool wrote:
> Thanks everybody!!

You're welcome!

>> I am sorry, I still do not understand your motivation to have a class
>> method which receives an instance as first argument.
>
> My motivation is that I was writing a lot of code like this:
>
> module HasXattr
>      def xattr_reference(key);  Xattr.xattr_reference(self, key); end
>      def xattr_store(key, value); Xattr.xattr_store(self, key, value);
> end
>      def xattr_has_key?(key); Xattr.xattr_has_key?(self, key); end
>      def xattr_delete(key); Xattr.xattr_delete(self, key); end
>      ...
> end
>
> .. and I thought to myself: this is Ruby.  There must be a better way.
> I guess I should have shown this concrete example earlier in the thread.

With my code you could do

module HasXattr
   def xattr # foo in your lingo
     proxy(Xattr)
   end
end

or directly use it in your class

module HasXattr
   def xattr # foo in your lingo
     proxy(Xattr)
   end
end

Then you can do

obj.xattr.has_key? "k"

or

obj.xattr.tap |x|
   x.store "k", "v" unless x.has_key?
end

Btw, what is Xattr?  Where does it come from and what does it do?  From 
the looks it seems to be some external storage for attributes associated 
with your instance which you do not want to store in the instance itself 
(for whatever reasons).  What do you need that for?

Kind regards

	robert

-- 
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

[toc] | [prev] | [next] | [standalone]


#2921

FromKevin Mahler <kevin.mahler@yahoo.com>
Date2011-04-15 02:14 -0500
Message-ID<fe2a1539e193df32600e0c6768313e7e@ruby-forum.com>
In reply to#2915
Fearless Fool wrote in post #992929:
> I'd like to create a method +foo+ that transforms:
>
>    my_obj.foo.some_method(*args)
>
> to
>
>   MyClass.some_method(my_obj, *args)

  class MyClass
    def self.some_method(obj, *args)
      puts "MyClass.some_method:"
      puts "obj: #{obj.inspect}"
      puts "args: #{args.inspect}"
    end
  end

  def define_foo(obj, clazz, method)
    obj.singleton_class.class_eval do
      define_method :foo do
        Class.new do
          define_method method do |*args|
            clazz.send(method, obj, *args)
          end
        end.new
      end
    end
  end

  my_obj = "my_obj thing"
  args = [1,2,3]
  define_foo(my_obj, MyClass, :some_method)
  my_obj.foo.some_method(*args)

  # =>
  # MyClass.some_method:
  # obj: "my_obj thing"
  # args: [1, 2, 3]

Returning facade or proxy objects like this can be an elegant solution 
to certain problems. It's rather high on the abstraction ladder, though, 
and without knowing the context I would wonder if more direct solutions 
are possible.

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

[toc] | [prev] | [next] | [standalone]


#2922

FromFearless Fool <r@alum.mit.edu>
Date2011-04-15 02:43 -0500
Message-ID<8168e82bf280948bb58750b7b5e9632e@ruby-forum.com>
In reply to#2921
@Kevin:

Kevin Mahler wrote in post #992945:
>   ...
>   def define_foo(obj, clazz, method)
>     obj.singleton_class.class_eval do
>       define_method :foo do
>         Class.new do
>           define_method method do |*args|
>             clazz.send(method, obj, *args)
>           end
>         end.new
>       end
>     end
>   end

That's some mighty fine code-fu -- back in the day I had no problem with 
metaprogramming constructs in scheme, but I'm not yet at ease with the 
syntax and class structure of Ruby.

> Returning facade or proxy objects like this can be an elegant solution
> to certain problems. It's rather high on the abstraction ladder, though,
> and without knowing the context I would wonder if more direct solutions
> are possible.

Well, yes -- as per my previous post.  Since you must call define_foo() 
for each object & method you want to cover, I don't see a lot of 
advantage to the approach.  But it does show off Ruby's function 
defining tricks nicely.

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

[toc] | [prev] | [next] | [standalone]


#2942

FromKevin Mahler <kevin.mahler@yahoo.com>
Date2011-04-15 08:44 -0500
Message-ID<7955c9f83df5347bbe5fc000bc9e93f6@ruby-forum.com>
In reply to#2922
Fearless Fool wrote in post #992947:
>>   ...
>>   def define_foo(obj, clazz, method)
>>     obj.singleton_class.class_eval do
>>       define_method :foo do
>>         Class.new do
>>           define_method method do |*args|
>>             clazz.send(method, obj, *args)
>>           end
>>         end.new
>>       end
>>     end
>>   end
>
>That's some mighty fine code-fu...

Um, no it's not. Inspecting from top to bottom: (a) open the singleton
class, (b) define a method, (c) create an instance of a new class (d)
define a method. This has nothing to do with code-fu.

One of the problems with Ruby is that the syntax changes depending on
the compile-time or run-time context, which is a totally arbitrary
distinction. It's a disservice to programmers because it makes them
say "code-fu" when presented with the run-time counterparts of
familiar, mundane compile-time constructs.

Don't get me started about using eval to "fix" that.

>Since you must call define_foo() for each object & method you want to
>cover, I don't see a lot of advantage to the approach.

You can automate it as much as you like, including removing the need
for define_foo(). Perhaps you took my code too literally.

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

[toc] | [prev] | [next] | [standalone]


#3018

From7stud -- <bbxx789_05ss@yahoo.com>
Date2011-04-16 13:20 -0500
Message-ID<ae7c406b849430b336b88ff7712ea683@ruby-forum.com>
In reply to#2921
Kevin Mahler wrote in post #992945:
> Fearless Fool wrote in post #992929:
>> I'd like to create a method +foo+ that transforms:
>>
>>    my_obj.foo.some_method(*args)
>>
>> to
>>
>>   MyClass.some_method(my_obj, *args)
>
>   class MyClass
>     def self.some_method(obj, *args)
>       puts "MyClass.some_method:"
>       puts "obj: #{obj.inspect}"
>       puts "args: #{args.inspect}"
>     end
>   end
>
>   def define_foo(obj, clazz, method)
>     obj.singleton_class.class_eval do
>       define_method :foo do
>         Class.new do
>           define_method method do |*args|
>             clazz.send(method, obj, *args)
>           end
>         end.new
>       end
>     end
>   end
>
>   my_obj = "my_obj thing"
>   args = [1,2,3]
>   define_foo(my_obj, MyClass, :some_method)
>   my_obj.foo.some_method(*args)
>
>   # =>
>   # MyClass.some_method:
>   # obj: "my_obj thing"
>   # args: [1, 2, 3]
>
> Returning facade or proxy objects like this can be an elegant solution
> to certain problems. It's rather high on the abstraction ladder, though,
> and without knowing the context I would wonder if more direct solutions
> are possible.

I don't know what's conceptually easier to understand, but in ruby there 
is no limit to how high you can stack singleton methods, e.g.:

obj.foo
obj.foo.some_method

If obj.foo() returns obj's singleton class, then some_method() is being 
called as a class method of the singleton class.  That means some_method 
is a method inside the singleton class's singleton class:

obj's class
   ^
   |
   |
singleton2: some_method()
   ^
   |
   |
singleton1: foo() -> returns singleton1
   ^
   |
   |
  obj


obj.foo.some_method

And you can add as many singleton classes to the method lookup path as 
you want.  For instance, if you have this call:

obj.foo.some_method.do_stuff

and obj.foo.some_method() returns singleton2, then do_stuff() is being 
called as a class method of singleton2, i.e. do_stuff() is a method in a 
parent class, singleton3, above singleton2.

So in the original example at the top of the post, if the 
creating-a-new-anonymous-class-with-a-method-named-some_method-and-returning-an-instance-of-that-class 
is too hard to understand, you could do this:

class MyClass
    def self.some_method(obj, *args)
      puts "MyClass.some_method:"
      puts "obj: #{obj.inspect}"
      puts "args: #{args.inspect}"
    end
  end

  def define_foo(obj, clazz, method)
    obj.singleton_class.class_eval do
      singleton = self

      define_method :foo do
        #Class.new do
          #define_method method do |*args|
            #clazz.send(method, obj, *args)
          #end
        #end.new


        #In here, self is equal to the obj that
        #will eventually call foo()--not obj.singleton_class.
        #Hence, the need to do singleton = self above.

        singleton.singleton_class.class_eval do
          define_method method do |*args|
            clazz.send(method, obj, *args)
          end
        end

        singleton #return the singleton class
      end

    end

  end

  my_obj = "my_obj thing"
  args = [1,2,3]
  define_foo(my_obj, MyClass, :some_method)
  my_obj.foo.some_method(*args)

--output:--
MyClass.some_method:
obj: "my_obj thing"
args: [1, 2, 3]

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

[toc] | [prev] | [next] | [standalone]


#3022

From7stud -- <bbxx789_05ss@yahoo.com>
Date2011-04-16 13:43 -0500
Message-ID<56fa7979e91b731d6aa10e9f26926e09@ruby-forum.com>
In reply to#3018
7stud -- wrote in post #993237:
>

I guess since obj *is* visible in all the nested blocks, its not 
strictly necessary to do the singleton = self trick--you can always get 
the singleton class from obj using obj.singleton_class:

  class MyClass
    def self.some_method(obj, *args)
      puts "MyClass.some_method:"
      puts "obj: #{obj.inspect}"
      puts "args: #{args.inspect}"
    end
  end

  def define_foo(obj, clazz, method)

    obj.singleton_class.class_eval do
      define_method :foo do
        obj.singleton_class.singleton_class.class_eval do
          define_method method do |*args|
            clazz.send(method, obj, *args)
          end
        end

        obj.singleton_class
      end

    end

  end

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

[toc] | [prev] | [next] | [standalone]


#2923

FromJesús Gabriel y Galán <jgabrielygalan@gmail.com>
Date2011-04-15 02:45 -0500
Message-ID<BANLkTin7haFE5RJKAKRVsto0ohsmAFEsyw@mail.gmail.com>
In reply to#2915
On Fri, Apr 15, 2011 at 7:16 AM, Fearless Fool <r@alum.mit.edu> wrote:
> I'm sure there's a clean way to do this in Ruby, but I haven't figured
> it out.
>
> I'd like to create a method +foo+ that transforms:
>
>   my_obj.foo.some_method(*args)
>
> to
>
>  MyClass.some_method(my_obj, *args)
>
> Maybe I'm overthinking this.  I'll keep working on this, but in the
> meantime, I'm open to suggestions.

I would create a method foo that returns a proxy that remembers which
was the original receiver. This proxy can have a method_missing
implementation that calls MyClass.xxxx passing the original receiver
and all params. For example:

ruby-1.8.7-p334 :003 > class Proxy
ruby-1.8.7-p334 :004?>   def initialize target
ruby-1.8.7-p334 :005?>     @target = target
ruby-1.8.7-p334 :006?>     end
ruby-1.8.7-p334 :007?>   def method_missing meth, *args, &blk
ruby-1.8.7-p334 :008?>     MyClass.send(meth,@target,*args,&blk)
ruby-1.8.7-p334 :009?>     end
ruby-1.8.7-p334 :010?>   end
 => nil
ruby-1.8.7-p334 :011 > class Something
ruby-1.8.7-p334 :012?>   def foo
ruby-1.8.7-p334 :013?>     Proxy.new self
ruby-1.8.7-p334 :014?>     end
ruby-1.8.7-p334 :015?>   end
 => nil
ruby-1.8.7-p334 :016 > class MyClass
ruby-1.8.7-p334 :017?>   def self.test1 obj, *args
ruby-1.8.7-p334 :018?>     p [obj,*args]
ruby-1.8.7-p334 :019?>     end
ruby-1.8.7-p334 :020?>   end
 => nil
ruby-1.8.7-p334 :021 > my_obj = Something.new
 => #<Something:0xb72c8514>
ruby-1.8.7-p334 :022 > my_obj.foo.test1 1,2,3
[#<Something:0xb72c8514>, 1, 2, 3]

I don't know if method foo is part of your class, or maybe you can
build a method and extend the required objects with it.

Jesus.

[toc] | [prev] | [next] | [standalone]


#3029

FromBrian Candler <b.candler@pobox.com>
Date2011-04-16 16:14 -0500
Message-ID<c9351f67cbcb09e73dec51721abdc873@ruby-forum.com>
In reply to#2915
Fearless Fool wrote in post #992929:
> I'm sure there's a clean way to do this in Ruby, but I haven't figured
> it out.
>
> I'd like to create a method +foo+ that transforms:
>
>    my_obj.foo.some_method(*args)
>
> to
>
>   MyClass.some_method(my_obj, *args)

Not sure, but would bound or unbound methods help you?

bound_method = my_obj.method(:foo)
bound_method.call(*args)

unbound_method = my_obj.class.instance_method(:foo)
unbound_method.bind(my_obj).call(*args)

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

[toc] | [prev] | [next] | [standalone]


#3031

FromRobert Klemme <shortcutter@googlemail.com>
Date2011-04-16 23:40 +0200
Message-ID<90ugngF3ieU1@mid.individual.net>
In reply to#3029
On 16.04.2011 23:14, Brian Candler wrote:
> Fearless Fool wrote in post #992929:
>> I'm sure there's a clean way to do this in Ruby, but I haven't figured
>> it out.
>>
>> I'd like to create a method +foo+ that transforms:
>>
>>     my_obj.foo.some_method(*args)
>>
>> to
>>
>>    MyClass.some_method(my_obj, *args)
>
> Not sure, but would bound or unbound methods help you?
>
> bound_method = my_obj.method(:foo)
> bound_method.call(*args)
>
> unbound_method = my_obj.class.instance_method(:foo)
> unbound_method.bind(my_obj).call(*args)

You can only bind to instances of the original type.  You cannot use 
#unbind and #bind to let a method be called on a different type.  But 
this would be necessary for the OP's requirement to be fulfilled.

irb(main):001:0> class Foo
irb(main):002:1> def self.bar;123;end
irb(main):003:1> end
=> nil
irb(main):004:0> m=Foo.method(:bar).unbind
=> #<UnboundMethod: #<Class:Foo>#bar>
irb(main):005:0> f=Foo.new
=> #<Foo:0x105a24a4>
irb(main):006:0> m.bind(f)
TypeError: singleton method called for a different object
         from (irb):6:in `bind'
         from (irb):6
         from /usr/local/bin/irb19:12:in `<main>'
irb(main):007:0> m.bind(Foo)
=> #<Method: Foo.bar>
irb(main):008:0> m.bind(Foo).call
=> 123

Kind regards

	robert

-- 
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.ruby


csiph-web