16. December, 2004
16. December, 2004
in by Michael Neumann
I recently came across this thread on Slashdot, as it was mentioned on ruby-talk mailinglist. Someone has written a 15 lines P2P program in Python. I tried to port it to Ruby, but either it was too late this night (3am), or the Python’s list comprehensions confused me, so I got stucked with this little piece of code:
require 'openssl'
require 'xmlrpc/client'

def hmac(key, msg) OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new('md5'), key, msg) end

passwd = "abc"
url = "http://127.0.0.1:3001"

p XMLRPC::Client.new2(url).call("f", hmac(passwd, url), 0, [])

It should query a tinyp2p.py server started with:

python tinyp2p.py abc server 127.0.0.1 3001

But it failed for some unknown reason. So I stopped at this point.

Today, Florian Gross posted a 6 lines P2P program in Ruby which is shown below:

# Server: ruby p2p.rb password server server-uri merge-servers
# Sample: ruby p2p.rb foobar server druby://localhost:1337 druby://foo.bar:1337
# Client: ruby p2p.rb password client server-uri download-pattern
# Sample: ruby p2p.rb foobar client druby://localhost:1337 *.rb
require'drb';F,D,C,P,M,U,*O=File,Class,Dir,*ARGV;def s(p)F.split(p[/[^|].*/])[-1
]end;def c(u);DRbObject.new((),u)end;def x(u)[P,u].hash;end;M=="client"&&c(U).f(
x(U)).each{|n|p,c=x(n),c(n);(c.f(p,O[0],0).map{|f|s f}-D["*"]).each{|f|F.open(f,
"w"){|o|o<<c.f(p,f,1)}}}||(DRb.start_service U,C.new{def f(c,a=[],t=2)c==x(U)&&(
t==0&&D[s(a)]||t==1&&F.read(s(a))||p(a))end;def y()(p(U)+p).each{|u|c(u).f(x(u),
p(U))rescue()};self;end;private;def p(x=[]);O.push(*x).uniq!;O;end}.new.y;sleep)

I think I would understand this code better than the Python code, but I will not try it ;-)

17. August, 2004
17. August, 2004
in by Michael Neumann
In {Part I}[PythonDecorators] of the article, I hacked the Ruby interpreter and modified one single line to implement Python Decorators in Ruby. Now I’ll show some more examples in both Python and Ruby.

The examples are taken from the article Decorate this.

Wrapping

Let me show the Python example first.

def wrapwith(obj):
    def decorator(f):
        def _wrapper(*args, **kwargs):
          print "##", obj
          return f(*args, **kwargs)
        return _wrapper
    return decorator

@wrapwith(42)
def f(x): return x*2

# let's use it
print f(4)

Now the same example in Ruby (with my single-line patch applied):

def wrapwith(obj, method_id)
  old_method = method(method_id)
  new_method = proc {|*args|
    print "##", obj, "\n"
    old_method.call(*args)
  }
  self.class.send(:define_method, method_id, new_method)
end

wrapwith 42,
def f(x) x*2 end

# let's use it
print f(4)

For me, the Python example is harder to understand, as three nested function definitions are involved. Now, the Ruby example is just fine, but in Ruby 2.0 we can even do better:

def f(x) x*2 end

def wrap:f(x)
  print "##", 42, "\n"
  super # calls "f"
end

print f(4)

For all examples, the output will be:

## 42
8

Prototype-based OO

Below, I only show how to apply prototype-based OO with Decorators in Python, not how to implement it. For the complete Python example see Decorate this.

class Foo:
    def __init__(self):
        self.x = 42

foo = Foo()

@addto(foo)
def print_x(self):
    print self.x

foo.print_x()   # => 42

The same in Ruby:

class Foo
  def initialize
    @x = 42
  end
end

foo = Foo.new

def foo.print_x
  print @x
end

foo.print_x     # => 42

Note that the Ruby example is complete whereas in the Python example the implementation of @addto is missing. But what if you want to add a whole bunch of methods to an object? In Ruby it’s as easy:

foo = Foo.new

class << foo
  def m1
    1
  end

  def m2
    2
  end
end

foo.m1   # => 1
foo.m2   # => 2
16. August, 2004
16. August, 2004
in by Michael Neumann
I read about Python Decorators on Lambda-the-Ultimate. They will be part of Python 2.4. But, was it really worth to add special syntax for something that can be done without problems in a different way? Isn’t Python breaking it’s own principle "There’s only one way to do it"? Will Python become a second Perl? Which so-called "feature" will be added next? Don’t get me wrong, Python is a valuable language!

The Ruby language instead is pretty constant since it’s very beginnings (version 1.0 of December 1996). There are no visible changes in the language (but in the library!) except the introduction of class-variables in 1.4 (or was it 1.6?).

Decorators in Python

Instead of:

def f(...)
  ...
f = synchronize(f)

you can now write:

@synchronize
def f(...)
  ...

The part after the at-sign is simply a function (synchronize is our case).

Decorators in Ruby

def f
  ...
end
synchronize :f

That’s pretty the same as the first Python example shown above. But can we simulate the second Python example in Ruby? Yes we can! We could do this in pure Ruby, but that would be a bit more advanced. Therefore I’ll show you how to do it with a minor change to the Ruby interpreter. Minor means that the change does not break any existing code and that only one line needs to be changed:

--- eval.c    2 Aug 2004 08:52:53 -0000  1.686
+++ eval.c    16 Aug 2004 12:52:28 -0000
@@ -3701,7 +3701,7 @@
                rb_add_method(rb_singleton_class(ruby_class),
                              node->nd_mid, defn, NOEX_PUBLIC);
            }
-         result = Qnil;
+         result = ID2SYM(node->nd_mid);
        }
        break;

With this patch applied, a method definition will return the method name as symbol. Let’s try this:

x =
def f
end
p x # => :f

Now we can "simulate" the second Python example in Ruby:

synchronize def f
  ...
end

# or with the decorator on a separate line

synchronize \
def f
  ...
end

Note that the backslash after the synchronize in the second part of the example is neccessary! This tells Ruby that the argument is on the next line.

And we can even cascade "decorators":

private memoize synchronize \
def f
  ...
end

Of course this only works if each involved "decorator" method passes the method-id through.