[Rubycocoa-devel 845] Aha!

Zurück zum Archiv-Index

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




More information about the Rubycocoa-devel mailing list
Zurück zum Archiv-Index