2

I was reading on cells gem when I suddenly got curious on its implementation of unnamed method in its documentation.

Particularly the code below:

CommentCell.(@comment).()

and

cell(:comment, @song).(:index)

where it uses .(arguments) without a method name.

I am certain the answer is just somewhere in the source code itself. But briefly looking at Cell::ViewModel doesn't immediately help me, so I was just wondering first if anyone knows how to do this before I thoroughly inspect the source code, and hopefully satisfies my curiosity.

I can see some uses of this in the app that I will be making soon.

Jay-Ar Polidario
  • 6,463
  • 14
  • 28

2 Answers2

4

.() looks like a method invocation missing the method name. This is syntactic-sugar that invokes the call method. It can be used with any object that defines a call method and works with Proc or lambda.

class Test
  def call
    puts "test"
  end
end

t = Test.new
t.()
# => "test"
lambda {puts "test"}.()
Proc.new {puts "test"}.()

But there are other possible solutions to react on .(). You could override method_missing or set an alias.

class Test
  def test
    puts "test"
  end

  alias call test;
end

t = Test.new
t.()
# => "test"
AlexN
  • 1,613
  • 8
  • 21
  • To just safely handle only noname method name, does it need to be something like the following? `def method_missing(m, *args, &block); if m==''; DO_SOMETHING; end` – Jay-Ar Polidario Jun 21 '16 at 11:31
  • Tried your answer and it worked. I see... so it does not need to be `method_missing` and seems like `call` is a special or reserved method name. Thanks :) – Jay-Ar Polidario Jun 21 '16 at 11:37
  • No, `call` is not a special or reserved method name. It's a method just like any other. It's just that `foo.()` gets translated to `foo.call()` in exactly the same manner that `foo[bar, baz] = quux` gets translated to `foo.[]=(bar, baz, quux)` or `+foo` gets translated to `foo.+@()` or `not foo` gets translated to `foo.!()`. – Jörg W Mittag Jun 21 '16 at 11:59
  • @JörgWMittag ohh i see. that makes sense now. – Jay-Ar Polidario Jun 21 '16 at 13:09
1

If you want to figure out what method call a particular piece of syntax translates to, you can try that out yourself:

class << foo = BasicObject.new
  def method_missing(meth, *args)
    $>.puts "`foo.#{meth}(#{args.inspect[1...-1]})`"
  end

  BasicObject.instance_methods.each(&method(:undef_method))
end

print '`foo.(1, 2, 3)` gets translated to '
foo.(1, 2, 3)
# `foo.(1, 2, 3)` gets translated to `foo.call(1, 2, 3)`

print '`not foo` gets translated to '
not foo
# `not foo` gets translated to `foo.!()`

print '`+foo` gets translated to '
+foo
# `+foo` gets translated to `foo.+@()`

print '`~foo` gets translated to '
~foo
# `~foo` gets translated to `foo.~()`

print '`foo[1, 2, 3] = 4` gets translated to '
foo[1, 2, 3] = 4
`foo[1, 2, 3] = 4` gets translated to `foo.[]=(1, 2, 3, 4)`

And so on …

As you can see, foo.(bar, baz) gets translated to foo.call(bar, baz).

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653