Pierce T. Wetter III
pierc****@twinf*****
Sat Apr 14 05:20:54 JST 2007
Still using RubyCocoa with CoreData. Turns out that define_wrapper thing is optional, the code to call it was in AppDelegate, put there by the Ruby Core Data template. So you have to comment that out first if you want custom accessors. The problem being that it redefines things _after_ the class code has loaded. I'm still having to define my own valueForKey and setValue_forKey, because as I pointed out earlier, NSManagedObject normally relies on the standard kvc mechanism being able to find accessors if they exist, otherwise NSManagedObject falls through to valueForUndefinedKey. Since the ObjC side doesn't seem to know about the accessors on the ruby side that doesn't work. I'm guessing that the ruby stuff uses a proxy or something and traps forwardInvocation or something? Since I'm generating the code via a template off the data model anyways it would be really easy for me to tweak the template so that all the accessors get registered right after being declared. So is there anyway to register a ruby accessor with the objective-C side sort of as a way to "preload" the forward mechanism? Meanwhile, the whole define_wrapper thing could be made more foolproof if you changed: # This moved there as osx/coredata is now deprecated. module CoreData # define wrappers from NSManagedObjectModel def define_wrapper(model) unless model.isKindOfClass? OSX::NSManagedObjectModel raise RuntimeError, "invalid class: #{model.class}" end model.entities.to_a.each do |ent| klassname = ent.managedObjectClassName.to_s next if klassname == 'NSManagedObject' next unless Object.const_defined?(klassname) attrs = ent.attributesByName.allKeys.to_a.collect {|key| key.to_s} rels = ent.relationshipsByName.allKeys.to_a.collect {|key| key.to_s} klass = Object.const_get(klassname) klass.instance_eval <<-EOE_AUTOWRAP,__FILE__,__LINE__+1 kvc_wrapper attrs kvc_wrapper_reader rels EOE_AUTOWRAP end end module_function :define_wrapper end to this: # This moved there as osx/coredata is now deprecated. module CoreData # define wrappers from NSManagedObjectModel def define_wrapper(model) unless model.isKindOfClass? OSX::NSManagedObjectModel raise RuntimeError, "invalid class: #{model.class}" end model.entities.to_a.each do |ent| klassname = ent.managedObjectClassName.to_s next if klassname == 'NSManagedObject' next unless Object.const_defined?(klassname) attrs = ent.attributesByName.allKeys.to_a.collect {|key| key.to_s} rels = ent.relationshipsByName.allKeys.to_a.collect {|key| key.to_s} klass = Object.const_get(klassname) attrs= attrs.reject!{|key| klass.method_defined? key} rels = rels.reject!{|key| klass.method_defined? key} klass.instance_eval <<-EOE_AUTOWRAP,__FILE__,__LINE__+1 kvc_wrapper attrs kvc_wrapper_reader rels EOE_AUTOWRAP end end module_function :define_wrapper end end The idea being that the: attrs= attrs.reject!{|key| klass.method_defined? key} rels = rels.reject!{|key| klass.method_defined? key} Will won't redefine any code if a read accessor already exists for the class. Though I suppose this leaves a hole if they define a write accessor but NOT a read accessor. That change would be slightly more complex because then the eval loop would have to change to check for each and define each separately. Just a thought. Pierce