Showing posts with label binding. Show all posts
Showing posts with label binding. Show all posts

Friday, January 18, 2013

Some undocumented differences between 1.8 and 1.9

Here I bring some differences among Ruby versions 1.8.7, 1.9.1 and 1.9.2 which I didn't found in other sites, maybe because they represent very rare use cases (or maybe because I didn't search enough). For these cases, version 1.9.3 behaves as 1.9.2. I was faced with them when trying to make Namebox compatible with Ruby 1.8.7 and 1.9.1, but after weeks of work I concluded that it doesn't worth.

Methods names' type


The method names for instance_methods will be String in 1.8.7 and Symbol since 1.9.1.
Since instance_method(method_name)don't care whether method_name is Symbol or String, this seems to be a innocuous difference, but if you code has something like Klass.instance_methods.include?("f") it will break when changing versions. This is also valid for methods and singleton_methods.

class A; def f; end; end

p A.instance_method(:f)         #=> #<Method:A#f>
p A.instance_method('f')        #=> #<Method:A#f>

p A.instance_methods(false)
#=> Ruby 1.8.7: ["f"]
#=> Ruby 1.9.1: [:f]

The superclass of the singleton class of a class


The superclass of the singleton class (also known as eigenclass) of a class X is the singleton class of the superclass of X. This follows the natural way of class methods lookup (not counting the extended modules). However, this works only since 1.9.1. In Ruby 1.8.7, the superclass of the singleton class of any class is the singleton class of the class Class:

class A; end
class B < A; end

# singleton class of B
SB = class << B; self; end

p SB        #=> #<Class:B>

p SB.superclass
#=> Ruby 1.8.7: #<Class:Class>
#=> Ruby 1.9.1: #<Class:A>

Binding class methods to a subclass


For instance methods, you can bind an unbound method to an object since that object is an instance of the method's class or subclass. For class methods, you can bind an unbound method to a subclass of the method's class (or to the class itself). This works since 1.9.2. Earlier versions raises TypeError if you try to bind a class method to a subclass:

class A
  def self.f
    "self is #{self}"
  end
end

class B < A; end

p A.method(:f).unbind.bind(B).call
#=> Ruby until 1.9.1: TypeError
#=> Ruby since 1.9.2: "self is B"

super from a module method after binding


In Ruby 1.8.7, if you bind an instance method of a module to an object (of a class which includes that module), and if that method has super, it will raise NoMethodError instead of looking for the super method. It will flow normally if that method is invoked without bind.

class A
  def f
    "Hello"
  end
end

module M
  def f
    super + " world!"
  end
end

class B < A
  include M
end

b = B.new
p b.f       #=> "Hello world!"

p M.instance_method(:f).bind(b).call
#=> Ruby 1.8.7: NoMethodError
#=> Ruby 1.9.1: "Hello world!"


Saturday, July 30, 2011

Caller binding

One of most useful feature not present in Ruby is to get the binding of the caller of current method, to do something with its local variables.

There is an implementation in the Extensions gem, but it must be the last method call in the method, and we must use the binding within a block.

There is another implementation here, but it depends on tracing along all the code execution, compromising the performance.

However, in this answer in StackOverflow, Taisuke Yamada implemented an version of ppp.rb (what is it?) which inspired me to implement my own version of a caller_binding method. Enjoy!

#!/usr/bin/ruby
#
# (c) Sony Fermino dos Santos, 2011
# License: Public domain
# Implementation: 2011-07-30
#
# Published at:
# http://rubychallenger.blogspot.com/2011/07/caller-binding.html
#
# Inspired on:
# http://stackoverflow.com/questions/1356749/can-you-eval-code-in-the-context-of-a-caller-in-ruby/6109886#6109886
#
# How to use:
# return unless bnd = caller_binding
# After that line, bnd will contain the binding of caller

require 'continuation' if RUBY_VERSION >= '1.9.0'

def caller_binding
  cc = nil     # must be present to work within lambda
  count = 0    # counter of returns

  set_trace_func lambda { |event, file, lineno, id, binding, klass|
    # First return gets to the caller of this method
    # (which already know its own binding).
    # Second return gets to the caller of the caller.
    # That's we want!
    if count == 2
      set_trace_func nil
      # Will return the binding to the callcc below.
      cc.call binding
    elsif event == "return"
      count += 1
    end
  }
  # First time it'll set the cc and return nil to the caller.
  # So it's important to the caller to return again
  # if it gets nil, then we get the second return.
  # Second time it'll return the binding.
  return callcc { |cont| cc = cont }
end

# Example of use:

def var_dump *vars
  return unless bnd = caller_binding
  vars.each do |s|
    value = eval s.to_s, bnd
    puts "#{s} = #{value.inspect}"
  end
end

def test
  a = 1
  s = "hello"
  var_dump :s, :a
end

test

Sunday, April 3, 2011

Swapping variables

Today I was learning about binding, and the example showed in that page is how to use binding to swap variables.

The text ended up with
swap(ref{:a}, ref{:b})

However, I show here a less verbose (and simpler) swap (without binding, references, etc.):

swap = lambda do |x, y|
  eval "#{x}, #{y} = #{y}, #{x}"
end

a = 'hihaha'
b = 33

swap[:a, :b]

puts a         #=> 33
puts b         #=> 'hihaha'

I guess the former example is still valid as didatic text about binding.