Crystal: using block with method_missing

Created on 28 Nov 2016  路  4Comments  路  Source: crystal-lang/crystal

i create some dsl like this:

class Bla
  def add(method, &block : -> U) forall U
    p "added #{method} #{block}"
  end

  def some1(&block)
    add("some1", &block)
  end

  def some2(&block)
    add("some2", &block)
  end
end

bla = Bla.new

bla.some1 { 1 }
bla.some2 { 2 }

"added some1 #"
"added some2 #"

This is ok, but i don't want to define methods some1, some2, ...
How i can do it with method_missing?

class Bla
  def add(method, &block : -> U) forall U
    p "added #{method} #{block}"
  end

  macro method_missing(call)
    add({{call.name.stringify}}, &{{call.block}})
  end
end

bla = Bla.new

bla.some1 { 1 }
bla.some2 { 2 }
Error in line 6: macro didn't expand to a valid program, it expanded to:

================================================================================
--------------------------------------------------------------------------------
   1.     add("some1", &do
   2.   yield
   3. end)
   4.   
--------------------------------------------------------------------------------
Syntax error in expanded macro: method_missing:1: expecting token ')', not 'NEWLINE'

    add("some1", &do
                    ^
feature compiler

Most helpful comment

It seems there's no way to do that right now. To fix this I'll try this: method_missing's generated content can also be a def. If so, it's used as the method to generate. In that way you could specify the block argument. I'll probably send a PR and link this.

All 4 comments

It seems there's no way to do that right now. To fix this I'll try this: method_missing's generated content can also be a def. If so, it's used as the method to generate. In that way you could specify the block argument. I'll probably send a PR and link this.

@kostya The solution I have in mind in this (I'll soon send a PR):

class Bla
  def add(method, &block : -> U) forall U
    p "added #{method} #{block}"
  end

  macro method_missing(call)
    def {{call.name}}(&block : -> U) forall U
      add({{call.name.stringify}}, &block)
    end
  end
end

bla = Bla.new

bla.some1 { 1 }
bla.some2 { 2 }

You can see method_missing is generating a method definition, so you can specify the captured block, if any, and its types.

I'm curious about your use case. In your DSL all methods capture the block? What is your real program?

my real program is trying to create stubs like rr: https://github.com/rr/rr

@kostya Cool! In any case I think #3610 is general enough to worth merging it, regardless of the use case (I was just curious)

Was this page helpful?
0 / 5 - 0 ratings