🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Thread View: pl.comp.lang.ruby
8 messages
8 total messages Started by Jaroslaw Zabiello Thu, 07 Sep 2006 10:43
Ruby i dekorator
#183
Author: Jaroslaw Zabiello
Date: Thu, 07 Sep 2006 10:43
6 lines
907 bytes
Jak w Ruby zaimplementować pythonowy dekorator? A może jest jakaś metoda
która wyciąga całe ciało funkcji? Chcę coś dodać przed nim i po nim..
 
-- 
Jarosław Zabiełło
http://blog.zabiello.com
Re: Ruby i dekorator
#184
Author: Marcin Mielżyński
Date: Thu, 07 Sep 2006 11:42
24 lines
1360 bytes
Jaroslaw Zabiello wrote:
> Jak w Ruby zaimplementować pythonowy dekorator? A może jest jakaś metoda
> która wyciąga całe ciało funkcji? Chcę coś dodać przed nim i po nim..
>  

można skorzystać np z method_added alias i define_method

domyślam się że chodzi o coś takiego w pythonie: ?

def around(fun):
     def x(self):
         print "pre"
         fun(self)
         print "post"
     return x

class A:
     @around
     def m(self):
         print "!"

A().m()

lopex
Re: Ruby i dekorator
#185
Author: Jaroslaw Zabiello
Date: Thu, 07 Sep 2006 12:36
36 lines
1886 bytes
Dnia Thu, 07 Sep 2006 13:42:42 +0200, Marcin Mielżyński napisał(a):

>> Jak w Ruby zaimplementować pythonowy dekorator? A może jest jakaś metoda
>> która wyciąga całe ciało funkcji? Chcę coś dodać przed nim i po nim..
>>  
> 
> można skorzystać np z method_added alias i define_method

method_added() wyłapuje fakt dodania metody, więc po co mi to? alias nie
potrzebuję, bo chcę zachować tą samą nazwę. A define_method tworzy a nie
odczytuje ciało metody.

> domyślam się że chodzi o coś takiego w pythonie: ?
> 
> def around(fun):
>      def x(self):
>          print "pre"
>          fun(self)
>          print "post"
>      return x
> 
> class A:
>      @around
>      def m(self):
>          print "!"
> 
> A().m()

Tak. W RoR znalazlem blad i chcialem sobie poprawić (kurcze, ze dwa bite
dni straciłem szukając błędu). (http://dev.rubyonrails.org/ticket/6073)
Zanim jednak dodadza (o ile dodadza) to do kodu RoR, wole wykorzystac
otwarte klasy i to sobie dodac w kontrolerze

-- 
Jarosław Zabiełło
http://blog.zabiello.com
Re: Ruby i dekorator
#186
Author: Marcin Mielżyński
Date: Thu, 07 Sep 2006 12:59
59 lines
2191 bytes
Jaroslaw Zabiello wrote:
> Dnia Thu, 07 Sep 2006 13:42:42 +0200, Marcin Mielżyński napisał(a):

> method_added() wyłapuje fakt dodania metody, więc po co mi to? alias nie
> potrzebuję, bo chcę zachować tą samą nazwę. A define_method tworzy a nie
> odczytuje ciało metody.

method_added będzie bardzo przydatne dla zbliżenia do pythonowej wersji,
method_alias i define_method - koniecznie - no chyba chyba że przez 
undef_method.

podstawy w bardzo prosty sposób można zrobić tak (dwie wersje):

class Class

     def around meth,pre,post
         alias_method :"old_#{meth}",meth.to_sym
         define_method meth do
             pre.call
             send :"old_#{meth}"
             post.call
         end
     end

     def around2 meth
         alias_method :"old_#{meth}",meth.to_sym
         define_method meth do
             yield method(:"old_#{meth}").to_proc
         end
     end

end

class A
     def meth
         p "meth"
     end

     def meth2
         p "meth2"
     end
end

A.around :meth, proc{p "pre"},proc{p "post"}
A.new.meth

A.around2 :meth2 do |m|
     p "pre2"
     m.call
     p "post2"
end

A.new.meth2


łatwo przerobić na dużo wygodniejsze użycie...


lopex
Re: Ruby i dekorator
#187
Author: Jaroslaw Zabiello
Date: Thu, 07 Sep 2006 16:35
139 lines
4798 bytes
Dnia Thu, 07 Sep 2006 14:59:22 +0200, Marcin Mielżyński napisał(a):

>> method_added() wyłapuje fakt dodania metody, więc po co mi to? alias nie
>> potrzebuję, bo chcę zachować tą samą nazwę. A define_method tworzy a nie
>> odczytuje ciało metody.
> 
> method_added będzie bardzo przydatne dla zbliżenia do pythonowej wersji,
> method_alias i define_method - koniecznie - no chyba chyba że przez 
> undef_method.
> 
> podstawy w bardzo prosty sposób można zrobić tak (dwie wersje):
> 
> class Class
> 
>      def around meth,pre,post
>          alias_method :"old_#{meth}",meth.to_sym
>          define_method meth do
>              pre.call
>              send :"old_#{meth}"
>              post.call
>          end
>      end
> 
>      def around2 meth
>          alias_method :"old_#{meth}",meth.to_sym
>          define_method meth do
>              yield method(:"old_#{meth}").to_proc
>          end
>      end
> 
> end
> 
> class A
>      def meth
>          p "meth"
>      end
> 
>      def meth2
>          p "meth2"
>      end
> end
> 
> A.around :meth, proc{p "pre"},proc{p "post"}
> A.new.meth
> 
> A.around2 :meth2 do |m|
>      p "pre2"
>      m.call
>      p "post2"
> end
> 
> A.new.meth2
> 
> 
> łatwo przerobić na dużo wygodniejsze użycie...

Pogubiłem się trochę w tym kodzie. Chodzi mi o odpowiednik w Ruby czegoś
takiego jak tu w Pythonie:

>>> headers = {'Content-Type': 'text/html'}
>>> class Base:
... 	@classmethod
... 	def hello(cls, msg):
... 	    headers['Content-Type'] = 'application/xml'
... 	    return msg
... 
>>> Base.hello('JA')
'JA'
>>> headers
{'Content-Type': 'application/xml'}
>>> 

Jak widać metoda hello() zmieniła słownik headers. Moim celem jest tak
zmienić klasę Base aby tego NIE zrobiła. Np. tak:

>>> headers = {'Content-Type': 'text/html'}
>>> x = Base.hello
>>> class Base:
... 	@classmethod
... 	def hello(cls, msg):
... 	    old = headers['Content-Type']
... 	    result = x(msg)
... 	    if old != headers['Content-Type']:
... 	        headers['Content-Type'] = old
... 	    return result
... 
>>> Base.hello('JA')
'JA'
>>> headers
{'Content-Type': 'text/html'}

W Pythonie wyssałem cała metodę za pomocą prostego przypisania. Następnie
otworzyłem tą klasę, dodałem warunki chroniące i wywołałem z powrotem. W
tym ujęciu nie jest w ogóle ważne co sie dzieje w metodzie hello(). Z mego
pkt. widzenia może być "czarną skrzynką". Wiem tylko tyle, że modyfikowała
mi jeden klucz w zewn. słownik i przed tym chciałem się zabezpieczyć.

Wracając do Rubiego i do konretnego problemu jaki spotkałem w RoR. Chcę
poprawić metodę render_to_string() bo ona gdzieś modyfikuje nagłówek HTTP.
Pierwotnie definicja wygląda tak:

module ActionController
  class Base
    def render_to_string(options = nil, &block)
      result = render(options, &block)
      erase_render_results
      forget_variables_added_to_assigns
      reset_variables_added_to_assigns
      result
    end
  end
end 

Nie interesuje mnie co sie dzieje w środku. Chcę tylko dodać zabezpieczenie
przed zmianą klucza Content-Type w @headers. Zrobiłem to trochę siłowo,
kopiując wszystko jak leci i dodając swój warunek:

module ActionController
  class Base
    def render_to_string(options = nil, &block)
      content_type = @headers['Content-Type'] 
      result = render(options, &block)
      erase_render_results
      forget_variables_added_to_assigns
      reset_variables_added_to_assigns
      if content_type != @headers['Content-Type'] 
        @headers['Content-Type'] = content_type
      end
      result
    end
  end
end 

Interesowało mnie jednak, czy mógłbym to zrobić bardziej elegancko, bez
kopiowania środka metody. Tak jak w Pythonie.

-- 
Jarosław Zabiełło
http://blog.zabiello.com
Re: Ruby i dekorator
#188
Author: Marcin Mielżyński
Date: Thu, 07 Sep 2006 17:36
33 lines
1537 bytes
to chyba najprościej tak:

class Base

     def render_to_string(options = nil, &block)
         # oryginalna czarna skrzynka
     end

end


# powtórne otwarcie klasy

class Base
     alias :orig_render_to_string render_to_string

     def render_to_string(*args, &blk)

         result = orig_render_to_string(*args,&blk)
         # ...
         result
     end
end

A.new.render_to_string


myślałem że chodzi o bardziej 'generyczne' rozwiązanie ;)

W 2.0 będą kombinatory :pre :post i :around


lopex
Re: Ruby i dekorator
#189
Author: Marcin Mielżyński
Date: Thu, 07 Sep 2006 17:37
6 lines
1095 bytes
Marcin Mielżyński wrote:
Oops:

> 
> Base.new.render_to_string
> 
Re: Ruby i dekorator
#190
Author: Przemysław Kowalczyk
Date: Wed, 13 Sep 2006 21:22
11 lines
1183 bytes
On Thu, 07 Sep 2006 12:43:28 +0200, Jaroslaw Zabiello  
<hipertracker@gmail.com> wrote:

> Jak w Ruby zaimplementować pythonowy dekorator? A może jest jakaś metoda
> która wyciąga całe ciało funkcji? Chcę coś dodać przed nim i po nim..

moze to pomoze:  
http://swik.net/Ruby/Code+Snippets:+ruby/Partial+Mock+Object+or+mocking+an+instance+of+an+object./cimp

pzdr
	szeryf
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads