The method_missing macro expand to ..., however, it should only expand to a single def

Hello.

Following is the spec output for one of my shards, i consider it can describe my issue quite directly.

Showing last frame. Use --error-trace for full trace.

In spec/hashr_spec.cr:22:11

 22 | value.foo.should eq h
            ^--
Error: wrong method_missing expansion

The method_missing macro expanded to:

    1 |     def foo
    2 |       value = @obj["foo"]
    3 | 
    4 |       Hashr.new(value)
    5 |     end
    6 | 
    7 |     def foo=(value)
    8 |       @obj["foo"] = value
    9 |     end
   10 |   

However, it should only expand to a single def

so, method_missing can only create a single method, right?

If i want to create two method at one-off i method missing macro, how can i do that?

Thanks.

Can you create a minimal example of what your code currently does versus what you want it to do? It’s not entirely clear from just looking at this error.

Please check this.

Ahh, well good question. I tried just using two method_missing calls and got:

The method_missing macro expanded to:

   1 |     def blah=(value)
   2 |       pp value
   3 |     end
   4 |   

However, the generated method won't be found by the original call invocation

So, guess you just can’t? :person_shrugging:.

Related: Remove method_missing macro hook · Issue #4701 · crystal-lang/crystal · GitHub

You can create each def by itself casing on the usage.

class A
  @hash = Hash(String, Int32).new

  macro method_missing(call)
    {% if call.name.ends_with? "=" %}
      def {{call.name}}(value)
        @hash[{{call.name.gsub(/=/, "").stringify}}] = value
      end
    {% else %}
      def {{call.name}}
        @hash[{{call.name.stringify}}]
      end
    {% end %}
  end
end

a = A.new
a.value = 5
p a.value # => 5

EDIT: quick hack, please add the corresponding checks to make sure it won’t blow out in your face!

2 Likes

Not work for me when test on 1.9.2, raise following error when a.phone = “xxx”, :grinning:

There was a problem expanding macro 'method_missing'

Called macro defined in src/hashr.cr:19:3

 19 | macro method_missing(key)

Which expanded to:

 > 1 |     
 > 2 |       def phone = _arg0(value)
 > 3 |         @obj["phone"] = value
 > 4 |       end
 > 5 |     
 > 6 |   
Error: unexpected token: "="


Well, given the error I would assume your code is not entirely faithful to what I posted, but I can’t help much without further context :slight_smile: You can print/debug the macro code with something like {% p! call.name %} to know what you’re getting.

Oops, my fault, i am not try your code directly, instead of, i follow your’s ideas, change code directly on my shard, and run spec, it failed because i misunderstood the call.name with call.id, some part of my code use call.id instead, which cause issue.

It works now like a charm! i release v0.3 hashr just now, it can also be used as a models object now.

thank you very much!

1 Like