1# Copyright (c) 2006-2008, The RubyCocoa Project. 2# Copyright (c) 2001-2006, FUJIMOTO Hisakuni. 3# All Rights Reserved. 4# 5# RubyCocoa is free software, covered under either the Ruby's license or the 6# LGPL. See the COPYRIGHT file for more information. 7 8module OSX 9 10 class << self 11 attr_accessor :relaxed_syntax 12 13 # Backward compatibility check; get C constants 14 def method_missing(mname, *args) 15 if args.length == 0 16 begin 17 ret = const_get(mname) 18 warn "#{caller[0]}: syntax 'OSX.#{mname}' to get the constant is deprecated and its use is discouraged, please use 'OSX::#{mname}' instead." 19 return ret 20 rescue 21 end 22 end 23 super 24 end 25 end 26 27 module OCObjWrapper 28 29 # A convenience method to dispatch a message to ObjC as symbol/value/... 30 # For example, [myObj doSomething:arg1 withObject:arg2] would translate as: 31 # myObj.objc_send(:doSomething, arg1, :withObject, arg2) 32 def objc_send(*args) 33 raise ArgumentError, "wrong number of arguments (#{args.length} for at least 1)" if args.empty? 34 mname = "" 35 margs = [] 36 args.each_with_index do |val, index| 37 if index % 2 == 0 then 38 mname << val.to_s << ':' 39 else 40 margs << val 41 end 42 end 43 mname.chomp!(':') if args.size == 1 44 return self.ocm_send(mname.to_sel, nil, false, *margs) 45 end 46 47 def method_missing(mname, *args) 48 m_name, m_args, as_predicate = analyze_missing(mname, args) 49 begin 50 result = self.ocm_send(m_name, mname, as_predicate, *m_args) 51 rescue OCMessageSendException => e 52 if self.private_methods.include?(mname.to_s) 53 raise NoMethodError, "private method `#{mname}' called for ##{self}" 54 else 55 raise e 56 end 57 end 58 end 59 60 def ocnil? 61 self.__ocid__ == 0 62 end 63 64 private 65 66 def analyze_missing(mname, args) 67 m_name = mname.to_s 68 m_args = args 69 70 # remove `oc_' prefix 71 m_name.sub!(/^oc_/, '') 72 73 # remove `?' suffix (to keep compatibility) 74 # explicit predicate if `?' suffix with OSX.relaxed_syntax 75 as_predicate = false 76 if m_name[-1] == ?? 77 m_name.chop! 78 as_predicate = OSX.relaxed_syntax 79 # convert foo? to isFoo 80 orig_sel = m_args.size > 0 ? m_name.sub(/[^_:]$/, '\0_') : m_name 81 unless ocm_responds?(orig_sel) 82 m_name = 'is' + m_name[0].chr.upcase + m_name[1..-1] 83 end 84 end 85 86 # convert foo= to setFoo 87 if m_name[-1] == ?= and m_name.index(?_, 1).nil? 88 m_name.chop! 89 m_name = 'set' + m_name[0].chr.upcase + m_name[1..-1] 90 end 91 92 # check call style 93 # as Objective-C: [self aaa: a0 Bbb: a1 Ccc: a2] 94 # as Ruby: self.aaa_Bbb_Ccc_ (a0, a1, a2) 95 # only if OSX.relaxed_syntax == true, check missing final underscore 96 # as Ruby: self.aaa_Bbb_Ccc (a0, a1, a2) 97 # other syntaxes are now deprecated 98 # as Ruby: self.aaa (a0, :Bbb, a1, :Ccc, a2) 99 # as Ruby: self.aaa (a0, :Bbb => a1, :Ccc => a2) 100 if OSX.relaxed_syntax 101 if (m_args.size == 2) && (not m_name.include?('_')) && m_args[1].is_a?(Hash) && (m_args[1].size >= 1) then 102 # Parse the inline Hash case. 103 mname = m_name.dup 104 args = [] 105 args << m_args[0] 106 if m_args[1].size == 1 107 m_args[1].each do |key, val| 108 mname << "_#{key}" 109 args << val 110 end 111 else 112 # FIXME: do some caching here 113 self.objc_methods.each do |sel| 114 selname, *selargs = sel.split(/:/) 115 if selname == m_name and selargs.all? { |selarg| m_args[1].has_key?(selarg.to_sym) } then 116 selargs.each do |selarg| 117 mname << "_#{selarg}" 118 args << m_args[1][selarg.to_sym] 119 end 120 break 121 end 122 end 123 end 124 m_name = "#{mname}_" 125 m_args = args 126 warn "#{caller[1]}: inline Hash dispatch syntax is deprecated and its use is discouraged, please use '#{m_name}(...)' or 'objc_send(...)' instead." 127 elsif (m_args.size >= 3) && ((m_args.size % 2) == 1) && (not m_name.include?('_')) && (m_args.values_at(*(1..m_args.size - 1).to_a.select { |i| i % 2 != 0 }).all? { |x| x.is_a?(Symbol) }) then 128 # Parse the symbol-value-symbol-value-... case. 129 mname = m_name.dup 130 args = [] 131 m_args.each_with_index do |val, index| 132 if (index % 2) == 0 then 133 args << val 134 else 135 mname << "_#{val.to_s}" 136 end 137 end 138 m_name = "#{mname}_" 139 warn "#{caller[1]}: symbol-value-... dispatch syntax is deprecated and its use is discouraged, please use '#{m_name}(...)' or 'objc_send(...)' instead." 140 m_args = args 141 else 142 m_name.sub!(/[^_:]$/, '\0_') if m_args.size > 0 143 end 144 end 145 return [ m_name, m_args, as_predicate ] 146 end 147 148 end 149 150end # module OSX 151