Search code examples
ruby-on-railsrubymodel-view-controllerrubygemscomponents

"I'm getting such an error: #<NoMethodError: private method `<=>' called for nil>


I'm new to Ruby on Rails and I couldn't understand the reason. Can you help me? I'm new to Ruby on Rails and I couldn't understand the reason. What does '<=>' mean in Ruby on Rails? The problem is: I'm setting up a component structure using phlex in Rails dynamically. My product is full when initialized with prod_col, but when I do div(class: @comp.props['_class']) { @product.send(@comp.props['source']) }, I get an error."

def initialize(comp, product:)
          @comp = comp
          @product = product
        end

       
        def template
          if @comp._folder == 'prod_catalog'
            plain(@product.send(@comp.props['source']))
          else
            div(class: @comp.props['_class']) { @product.send(@comp.props['source']) }
          end
        end

I'm actually sending the column of the products in the database, and I want to get the unit name or unit names of the relevant product as a return. However, I encounter this error.


Solution

  • Most probably your issue is not really about the method <=> (yes, that is astonishingly a method call though) - but that you try to call it on the object nil. Which ruby version do you use? On 3.3.1 the method <=> is not private on nil NilClass. But if I remember correctly, that differed before.

    If you want to understand the method <=> you can check its source code. I like to start a pry console for something alike:

    [2] pry(main)> require 'pry-doc'
    => true
    [3] pry(main)> show-source nil.<=>
    
    From: object.c (C Method):
    Owner: Kernel
    Visibility: public
    Signature: <=>(arg1)
    Number of lines: 7
    
    static VALUE
    rb_obj_cmp(VALUE obj1, VALUE obj2)
    {
        if (rb_equal(obj1, obj2))
            return INT2FIX(0);
        return Qnil;
    }
    
    [4] pry(main)> nil <=> nil
    => 0
    [5] pry(main)> nil <=> 2
    => nil
    [6] pry(main)> nil <=> 'test'
    => nil
    [7] pry(main)> nil.<=> 'test'
    => nil
    
    

    Just to get a feeling, what this kind of is about. The method <=> normally is used to describe a comparision operator. That is expected to return 0 if both objects are equal. It returns -1 if first object is smaller and +1 if the argument (so the second object) is smaller. So just a small hint about it with some more code about it:

    [8] pry(main)> 1 <=> 2
    => -1
    [9] pry(main)> 2 <=> 1
    => 1
    [10] pry(main)> show-source 1.<=>
    
    From: numeric.c (C Method):
    Owner: Integer
    Visibility: public
    Signature: <=>(arg1)
    Number of lines: 13
    
    VALUE
    rb_int_cmp(VALUE x, VALUE y)
    {
        if (FIXNUM_P(x)) {
            return fix_cmp(x, y);
        }
        else if (RB_BIGNUM_TYPE_P(x)) {
            return rb_big_cmp(x, y);
        }
        else {
            rb_raise(rb_eNotImpError, "need to define `<=>' in %s", rb_obj_classname(x));
        }
    }
    

    I really hope, that your @comp does not include some user input. Else this @product.send request is highly dangerous.

    In the end I could not really answer about your real problem, since you missed to give some more context. But maybe this helps already about your question: "What does '<=>' mean in Ruby on Rails?"

    I hope I can give you a little more insights into the method <=> which is used behind the scenes i. e. if you sort an array, with following small playing around on console:

    [17] pry(main)> class Test
    [17] pry(main)*   def initialize(a,b)
    [17] pry(main)*     @a, @b = a, b
    [17] pry(main)*   end  
    [17] pry(main)*   def <=>(test2)
    [17] pry(main)*     return 0 if @a == test2.a && @b == test2.b
    [17] pry(main)*     return -1 if @a <= test2.a && @b <= test2.b
    [17] pry(main)*     return 1 if @a >= test2.a && @b >= test2.b
    [17] pry(main)*     nil # you could remove this line, since it's default, if all the guards before fail - I just wanted to make the default return value obvious here
    [17] pry(main)*   end  
    [17] pry(main)*   attr_reader :a, :b
    [17] pry(main)* end
    
    [18] pry(main)> [Test.new(6,7), Test.new(1,2), Test.new(4,5)].sort
    => [#<Test:0x00007f9741fb1c80 @a=1, @b=2>, #<Test:0x00007f9741fb1c30 @a=4, @b=5>, #<Test:0x00007f9741fb1cd0 @a=6, @b=7>]
    [19] pry(main)> [Test.new(1,2), Test.new(4,5), Test.new(1,7)].sort
    ArgumentError: comparison of Test with Test failed
    from (pry):36:in `sort'
    

    The second sort breaks, since Test.new(1,7) is not comparable to the other two instances in our <=> definition (returns nil then, instead of -1, 0 or 1).