Permalink
Browse files

If we undef <=>, it solves a problem involving comparison. Please fig…

…ure out the root cause.
  • Loading branch information...
1 parent 94ed39d commit 0042f4166f783085eb909d69d542b5323d8af5d6 @wycats wycats committed Jun 8, 2010
Showing with 2 additions and 0 deletions.
  1. +2 −0 activesupport/lib/active_support/multibyte/chars.rb
@@ -51,6 +51,8 @@ def initialize(string) #:nodoc:
end
end
+ undef <=>
+
# Forward all undefined methods to the wrapped string.
def method_missing(method, *args, &block)
if method.to_s =~ /!$/

9 comments on commit 0042f41

Contributor

radar commented on 0042f41 Jun 8, 2010

Can confirm that this "fixes" #4772: http://github.com/rails/rails/commit/0042f4166f783085eb909d69d542b5323d8af5d6 but it would be nice to have a better understanding of why it fixes it.

doh, this "undef" broke my tests... the "<=>" method doesn't exist, so it can't undef it. (I'm on edge, ruby 1.9.1p378 (2010-01-10 revision 26273)).

It looks like "<=>" is defined further down the file, so maybe the undef is too called too early?

According to my tests (ruby 1.9.1p378 and rails beta 4), <=> doesn't exist, so the undef throws an error. Same problem as deanmao

boof replied Jun 9, 2010

The problem is, imho, caused because of the way compar.c calls the spaceship:

static VALUE
cmp_eq(VALUE *a)
{
    VALUE c = rb_funcall(a[0], cmp, 1, a[1]);
    ...

ruby-1.9.2-head >
  BrokenComparable = Module.new { def ==(other) self <=> other end }
  BrokenClass = Class.new { include BrokenComparable }
  BrokenClass.new == nil
SystemStackError: stack level too deep

The current implementation of the spaceship operator ...

static VALUE
rb_mod_cmp(VALUE mod, VALUE arg)
{
    *snip*
    cmp = rb_class_inherited_p(mod, arg);
    ...

... tries to lookup the method in the parent classes but fails because it's
actually implemented in the Kernel module:

ruby-1.9.2-head > Object.instance_methods(false)
 => [] 
ruby-1.9.2-head > Object.superclass.instance_methods(false)
 => [:==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__] 
ruby-1.9.2-head > Object.superclass.superclass.methods(false)
 => [] 
ruby-1.9.2-head > Object.superclass.superclass.class.instance_methods(false)
 => [:to_i, :to_f, :to_s, :to_a, :inspect, :&, :|, :^, :nil?, :to_r, :rationalize, :to_c] 
ruby-1.9.2-head > Object.superclass.superclass.class.superclass.instance_methods(false)
 => [] 
ruby-1.9.2-head > Object.superclass.superclass.class.superclass.superclass.instance_methods(false)
 => [:==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__] 
...

ruby-1.9.2-head > Kernel.instance_methods(false)
 => [:nil?, :===, :=~, :!~, :eql?, :hash, :<=>, :class, :singleton_class, :clone, :dup, :initialize_dup, :initialize_clone, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :to_s, :inspect, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :respond_to_missing?, :extend, :display, :method, :public_method, :define_singleton_method, :__id__, :object_id, :to_enum, :enum_for] 

But I'm neither familiar with C nor the ruby source so this is just a guess.

this actually seems to break functionality though, even if its allowing tests to pass. I start seeing an error because of this change: http://gist.github.com/431583

wait, so reading more from lighthouse and here again, is this a 1.9.2 fix?

boof replied Jun 9, 2010

Is there a problem to simply delegate it?

class ActiveSupport::Multibyte::Chars
  def <=>(other)
    @wrapped_string <=> other
  end
end

@boof well, the actual implementation is pretty much what you wrote, around line 102, but it only gets included if RUBY_VERSION < "1.9"

boof replied Jun 10, 2010

yepp, just delegating the method to the wrapped object should work around the buggy core implementation of Kernel#<=>.

Please sign in to comment.