Eloy Duran
eloy.****@gmail*****
Tue Jun 24 00:33:21 JST 2008
Hi, I know this will probably be a waste of energy since most time is spend on MacRuby, but here goes anyways. I have used kvc_accessors etc on several apps in the past and it has always not felt 100% right to me, but I was too lazy to take a look at it. Most of the times if I needed to do stuff with the variables I override #rbSetValue_forKey This has always managed me to be able to write working code, but during testing it breaks horribly. Today I was cleaning up some code and wanted to have all my tests run in one go so I get nice output of the total tests passed/failed. So what happens? The following is a small sample from endless recursion: /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `_kvc_internal_host=' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `send' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `_kvc_internal_host=' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `send' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `_kvc_internal_host=' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `send' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `_kvc_internal_host=' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `send' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `_kvc_internal_host=' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `send' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `_kvc_internal_host=' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `send' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `_kvc_internal_host=' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `send' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `_kvc_internal_host=' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `send' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `_kvc_internal_host=' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `send' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `_kvc_internal_host=' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `send' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `_kvc_internal_host=' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `send' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `_kvc_internal_host=' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `send' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `_kvc_internal_host=' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `send' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `_kvc_internal_host=' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `send' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `_kvc_internal_host=' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `send' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `_kvc_internal_host=' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `send' /Library/Frameworks/RubyCocoa.framework/Resources/ruby/osx/objc/ oc_import.rb:510:in `host=' I took a quick look at oc_import.rb, this is the method: If oc_import.rb:501 is patched like so it works: def kvc_writer(*args) args.flatten.each do |key| setter = key.to_s + '=' attr_writer(key) unless method_defined?(setter) alias_method kvc_internal_setter(key), setter self.class_eval <<-EOE_KVC_WRITER,__FILE__,__LINE__+1 def #{kvc_setter_wrapper(key)}(value) willChangeValueForKey('#{key.to_s}') send('#{kvc_internal_setter(key)}', value) didChangeValueForKey('#{key.to_s}') end EOE_KVC_WRITER alias_method setter, kvc_setter_wrapper(key) end end If oc_import.rb:501 is patched like so it works: def kvc_writer(*args) args.flatten.each do |key| setter = key.to_s + '=' #attr_writer(key) unless method_defined?(setter) # <= unnecessary? #alias_method kvc_internal_setter(key), setter # <= unnecessary? self.class_eval <<-EOE_KVC_WRITER,__FILE__,__LINE__+1 def #{kvc_setter_wrapper(key)}(value) willChangeValueForKey('#{key.to_s}') #send('#{kvc_internal_setter(key)}', value) # <= unnecessary? @#{key} = value # <= added! didChangeValueForKey('#{key.to_s}') end EOE_KVC_WRITER alias_method setter, kvc_setter_wrapper(key) end end Ok so I just quickly hacked some things to see what actually goes wrong and the problem is really some recursive setter stuff. I understand why one might have added this, but I *think* it's a case of YAGNI. The real problem with this is that there are no tests at all that contain "kvc" and I couldn't find any sample code that overrode the setter methods... :( So even if I would like to clean this up, I'd have no idea what I would break. I have code at http://github.com/alloy/passengerpane/tree/master which exhibits the problem. If you run the "rake test" task you will see where it goes wrong. The "rake test_normal" task is what I have to use now to circumvent these problems. Cheers, Eloy