Skip to content

Instantly share code, notes, and snippets.

@jorgenpt
Created November 3, 2011 08:15
Show Gist options
  • Save jorgenpt/1336025 to your computer and use it in GitHub Desktop.
Save jorgenpt/1336025 to your computer and use it in GitHub Desktop.
Objective-C-esque calling convention in Ruby (or: Why I shouldn't be let near method_missing)
# See the bottom of the file for how this actually works.
module ObjC
def method_missing(method, *args)
ObjCCall.new(self, [], []).send(method, *args)
end
end
class ObjCCall
def initialize(obj, selector, args)
@obj = obj
@selector = selector
@args = args
end
def method_missing(method_sym, *args)
# This is for a more authentic Objective C experience.
#if @args.length > @selector.length
# raise ArgumentError, "You can only specify multiple arguments if it's the end of the method."
#end
method = method_sym.to_s
should_call = false
if method.end_with?('!')
method = method[0...-1]
should_call = true
end
ret = self.class.new(@obj, @selector + [method], @args + args)
ret = ret._send_call if should_call
ret
end
def _selector
@selector.join('_').to_sym
end
def _valid_call
@obj.respond_to?(_selector)
end
def +@; _send_call; end
def _send_call
if _valid_call
@obj.send(_selector, *@args)
else
raise NoMethodError, "undefined method `#{_selector}' for #{@obj.inspect}:#{@obj.class}"
end
end
end
if $0 == __FILE__
class Test
include ObjC
def do_thing_with_other_thing(thing1, thing2)
[thing1, thing2]
end
end
test = Test.new
puts "ObjC Call!"
puts (+test.do_thing('laugh').with_other_thing('face')).inspect
puts
puts "Alternate ObjC Call!"
puts test.do_thing('laugh').with_other_thing!('face').inspect
end
# See the bottom of the file for how this actually works.
require 'objc_calling'
module ObjCMagic
def method_missing(m, *args)
ObjCCallMagic.new(self, [], []).send(m, *args)
end
end
class ObjCCallMagic < ObjCCall
def method_missing(m, *args)
ret = super
if ret.kind_of?(ObjCCall) and @obj.respond_to?(ret._selector)
ret._send_call
else
ret
end
end
end
if $0 == __FILE__
class TestMagic
include ObjCMagic
def some_thing_with_other_thing(thing1, thing2)
[thing1, thing2]
end
end
puts "ObjC Call, Magic Edition!"
puts TestMagic.new.some_thing(1).with_other_thing(2).inspect
end
@numist
Copy link

numist commented Nov 3, 2011

This is why we can't have nice things!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment