From: Rupert B. <rup...@li...> - 2005-11-03 21:31:25
|
Hi, I am using today's latest CVS version of RubyCocoa, and trying coredata and kvc_depends_on : great ! I am trying to "translate" the NSPersistentDocumentTutorial into Ruby : http://developer.apple.com/documentation/Cocoa/Conceptual/ NSPersistentDocumentTutorial I have got to chapter 3 (creating the custome Employee class) : this works with Employee.rb, I have even got fullNameAndID working, dynamically computed from firstName and lastName, and displayed in the GUI. However, I do not seem to be able to connect firstName in the GUI with the model in Employee.rb; ditto for lastName. This is my Employee class : class Employee < OSX::NSManagedObject include OSX attr_accessor :firstName attr_accessor :lastName kvc_accessor :lastName, :firstName kvc_depends_on([ :lastName, :firstName], :fullNameAndID) def initialize @firstName = "MyFirst" @lastName = "MyLast" end def fullNameAndID return OSX::NSString.stringWithString(@lastName + ", " + @firstName) end end What am I missing ? Is there something not obvious which should be done, considering that this tutorial uses bindings (of course) ad core data (including a data model with entities) ? Thanks for any help, Rup |
From: Jonathan P. <jp...@dc...> - 2005-11-03 23:13:48
|
On 3 Nov 2005, at 21:31, Rupert Barrow wrote: > I have got to chapter 3 (creating the custome Employee class) : > this works with Employee.rb, I have even got fullNameAndID working, > dynamically computed from firstName and lastName, and displayed in > the GUI. > However, I do not seem to be able to connect firstName in the GUI > with the model in Employee.rb; ditto for lastName. This is my > Employee class : I'm not entirely sure from your description what the problem you're having is. However, I do notice a problem with your code that may be related: > class Employee < OSX::NSManagedObject > include OSX > > attr_accessor :firstName > attr_accessor :lastName > > kvc_accessor :lastName, :firstName > kvc_depends_on([ :lastName, :firstName], :fullNameAndID) > > def initialize > @firstName = "MyFirst" > @lastName = "MyLast" > end > [Let me say first that I've not used Core Data yet, so this is my understanding from glancing over the documentation. Please correct any misunderstanding!] Since you're using Core Data, all the data members of the class are already managed for you. Therefore, you should not define firstName and lastName as accessors, nor should you use ruby @firstName/ @lastName variables. Instead, just access firstName etc as if it were already defined as an accessor (or perhaps you need to use setValue:forKey: and valueForKey:). RubyCocoa will forward the call through to Objective C, where Core Data will handle the request for you. Hope that is of some help, Jonathan |
From: kimura w. <ki...@us...> - 2005-11-04 11:21:42
|
Hi, I'm trying this tutorial. Thu, 3 Nov 2005 23:13:03 +0000, Jonathan Paisley wrote: >On 3 Nov 2005, at 21:31, Rupert Barrow wrote: > >Since you're using Core Data, all the data members of the class are >already managed for you. Therefore, you should not define firstName >and lastName as accessors, nor should you use ruby @firstName/ >@lastName variables. > >Instead, just access firstName etc as if it were already defined as >an accessor (or perhaps you need to use setValue:forKey: and >valueForKey:). RubyCocoa will forward the call through to Objective >C, where Core Data will handle the request for you. > It's right. To access attributes of an Employee, we have to use setValue:forKey and valueForKey:. for example: ------------ def fullNameAndID return OSX::NSString.stringWithString(valueForKey("lastName").to_s + ", " + valueForKey("firstName").to_s) end ------------ I felt that was not smart. I defined a method "kvc_wrapper". This method defines accessors wrapping "valueForKey:" and "setValue:forKey:". # tell me a better name of "kvc_wrapper", please. my Employee.rb: --------------- require 'osx/cocoa' require 'osx/coredata' # add kvc_wrapper module OSX module NSKVCBehaviorAttachment # accessor for keys defined in Cocoa def kvc_wrapper(*keys) keys.each do |key| class_eval <<-EOE_KVC_WRAPPER,__FILE__,__LINE__+1 def #{key} valueForKey("#{key}") end def #{key}=(val) setValue_forKey(val, "#{key}") end EOE_KVC_WRAPPER end end end end # Employee class class Employee < OSX::NSManagedObject kvc_depends_on [:lastName, :firstName], :fullNameAndID # define wrappers kvc_wrapper :lastName, :firstName, :employeeID def fullNameAndID sprintf('%s, %s (%d)', lastName.to_s, firstName.to_s, employeeID) end end --------------- I think that is possible to define wrappers automatically from NSManagedObjectModel#entities -> NSEntityDescription, but I do not know how to implement. -- kimura wataru |
From: kimura w. <ki...@us...> - 2005-11-07 14:34:13
|
Hi, I added some changes to CVS. This changes enables automated wrapping of Key-Value Coding accessors. When you define a subclass of NSManagedObject, RubyCocoa defines wrappers to attributes and relationships from CoreData models. my Employee.rb at chapter 3. ------------- require 'osx/cocoa' require 'osx/coredata' class Employee < OSX::NSManagedObject kvc_depends_on [:lastName, :firstName], :fullNameAndID @@temp_id = 0 def fullNameAndID sprintf('%s, %s (%d)', lastName.to_s, firstName.to_s, employeeID) end ns_overrides :awakeFromInsert def awakeFromInsert @@temp_id += 1 self.employeeID = OSX::NSNumber.numberWithInt(@@temp_id) end end ------------- A new module function OSX::CoreData.define_wrapper() is core of the feature. Fri, 04 Nov 2005 20:21:09 +0900, kimura wataru wrote: >Hi, > >I'm trying this tutorial. > >I think that is possible to define wrappers automatically from >NSManagedObjectModel#entities -> NSEntityDescription, but I do not >know how to implement. > -- kimura wataru |
From: Rupert B. <rup...@fr...> - 2005-11-04 20:54:19
|
OK, here is the final results of the experiment. With this code ... class Employee < OSX::NSManagedObject include OSX =09 @@lastID =3D 123 kvc_accessor :lastName, :firstName, :employeeID kvc_depends_on([ :lastName, :firstName, :employeeID ], = :fullNameAndID) def initialize rbSetValue_forKey(NSNumber.numberWithInt(@@lastID), = "employeeID") puts "*** = rbSetValue_forKey(NSNumber.numberWithInt(@@lastID), =20 \"employeeID\") : #{rbValueForKey("employeeID")} ***" end def fullNameAndID puts "*** self =3D #{self} ***" puts "*** rbValueForKey(\"employeeID\") =3D = #{rbValueForKey=20 ("employeeID")} ***" end end ... I get the following output ... *** rbSetValue_forKey(NSNumber.numberWithInt(@@lastID), =20 "employeeID") : 123 *** *** self =3D <Employee: 0x557eb0> (entity: Employee; id: 0x55a950 <x-=20 coredata:///Employee/t31E6B1EE-8E9F-4E1A-BA1E-47CA62FEBF3D> ; data: { department =3D nil; directReports =3D (); employeeID =3D nil; firstName =3D First; lastName =3D Last; manager =3D nil; salary =3D 0; }) *** *** rbValueForKey("employeeID") =3D 123 *** I am setting the employeeID correctly (through setValue), but the =20 value is not 'arriving' in the coredata object. However, it is not =20 lost, because I re-read from in another method (fullNameAndID) with =20 the valueForKey method. Any ideas ? Rup Le 4 nov. 05 =C3=A0 21:32, Rupert Barrow a =C3=A9crit : > Jonathan, > > Thanks for answering quickly. > > I removed definition of the @firstName and @lastName attributes, =20 > and it worked.. > > After studying your answer, I noticed the following : > ' kvc_accessor :lastName, :firstName' > creates these methods : > firstName > firstName=3D > lastName > lastName=3D > > If I comment out this code, the methods disappear. When I dump =20 > Employee, I see the "attributes" : > *** Employee =3D <Employee: 0x3ff5b0> (entity: Employee; id: 0x53f370 =20= > <x-coredata:///Employee/tFE462E87-F74A-4826-8487-7C16DBECD891> ; =20 > data: { > department =3D nil; > directReports =3D (); > employeeID =3D nil; > firstName =3D First; > lastName =3D Last; > manager =3D nil; > salary =3D 0; > }) *** > but when I try to access them, the methods are obviously undefined : > 2005-11-04 21:25:56.696 DepartmentAndEmployees[789] Exception =20 > raised during posting of notification. Ignored. exception: =20 > undefined method `lastName' for class `Employee' > > So : > - kvc_depends_on declares and uses one set of accessors > - CoreData relies on another access/update method : through =20 > setValue:forKey: and valueForKey:, as you mentioned. > > Would it not be nice to 'merge' them both, so as not to have to =20 > handle to access/update methods for the same data ? > > Rup > > > Le 4 nov. 05 =C3=A0 00:13, Jonathan Paisley a =C3=A9crit : > >> On 3 Nov 2005, at 21:31, Rupert Barrow wrote: >> >>> class Employee < OSX::NSManagedObject >>> include OSX >>> >>> attr_accessor :firstName >>> attr_accessor :lastName >>> >>> kvc_accessor :lastName, :firstName >>> kvc_depends_on([ :lastName, :firstName], :fullNameAndID) >>> >>> def initialize >>> @firstName =3D "MyFirst" >>> @lastName =3D "MyLast" >>> end >>> >> >> [Let me say first that I've not used Core Data yet, so this is my =20 >> understanding from glancing over the documentation. Please correct =20= >> any misunderstanding!] >> >> Since you're using Core Data, all the data members of the class =20 >> are already managed for you. Therefore, you should not define =20 >> firstName and lastName as accessors, nor should you use ruby =20 >> @firstName/@lastName variables. > |
From: Jonathan P. <jp...@dc...> - 2005-11-04 21:05:18
|
On 4 Nov 2005, at 20:53, Rupert BARROW wrote: > > kvc_accessor :lastName, :firstName, :employeeID > kvc_depends_on > ([ :lastName, :firstName, :employeeID ], :fullNameAndID) > > def initialize > rbSetValue_forKey(NSNumber.numberWithInt(@@lastID), > "employeeID") > > I am setting the employeeID correctly (through setValue), but the > value is not 'arriving' in the coredata object. However, it is not > lost, because I re-read from in another method (fullNameAndID) with > the valueForKey method. You're calling rbSetValue... rather than setValue... The rbSetValue method ends up just calling the ruby employeeID= method, which has the effect of setting the instance variable in the ruby class (look at the definition in oc_import.rb to understand more). Try replacing kvc_accessor with kvc_wrapper, as defined by Kimura Wataru in an email earlier today in this thread. This declares the ruby-level accessors in terms of setValue:forKey: and valueForKey: which should hopefully solve the problem. i.e.: kvc_wrapper :lastName, :firstName, :employeeID kvc_depends_on ([ :lastName, :firstName, :employeeID ], :fullNameAndID) def initialize employeeID = @@lastID ... end Also, since this is a Cocoa class, I'm not sure that the initialize method works - I think you have to override init and define it like in a Cocoa class. I'll try and come up with an example later if you think this is a problem. Hope that helps. Jonathan |
From: Rupert B. <rup...@fr...> - 2005-11-04 22:58:59
|
Le 4 nov. 05 =C3=A0 22:04, Jonathan Paisley a =C3=A9crit : > On 4 Nov 2005, at 20:53, Rupert BARROW wrote: > > Try replacing kvc_accessor with kvc_wrapper, as defined by Kimura =20 > Wataru in an email earlier today in this thread. Sorry, I'm on a 'digest' mail subscription to the list, so I missed =20 your post, Kimura-San. > This declares the ruby-level accessors in terms of setValue:forKey: =20= > and valueForKey: which should hopefully solve the problem. > > i.e.: > > kvc_wrapper :lastName, :firstName, :employeeID > kvc_depends_on=20 > ([ :lastName, :firstName, :employeeID ], :fullNameAndID) > > def initialize > employeeID =3D @@lastID > ... > end Great, thanks to both of you, things are now working, but not =20 *exactly* as you mention. In my 'awakeFromInsert' method, why does employeeID =3D 123 # not self.employeeID puts employeeID puts self reply '123' for employeeID, but NOT update self : <Employee: 0x520d00> (entity: Employee; id: 0x523ed0 <x-coredata:///=20 Employee/tFC895DBA-B5E8-4764-B296-32FA106D280E> ; data: { department =3D nil; directReports =3D (); employeeID =3D nil; # not updated firstName =3D First; lastName =3D Last; manager =3D nil; salary =3D 0; }) when self.employeeID =3D 123 # using self.employeeID puts employeeID puts self replies '123' for employeeID AND updates CORRECTLY self ? <Employee: 0x520d00> (entity: Employee; id: 0x523ed0 <x-coredata:///=20 Employee/tFC895DBA-B5E8-4764-B296-32FA106D280E> ; data: { department =3D nil; directReports =3D (); employeeID =3D 123; # updated correctly firstName =3D First; lastName =3D Last; manager =3D nil; salary =3D 0; }) Thanks for your help. Being rather newbie on Cocoa/CoreData and =20 RubyCocoa, I do not know whether I am using incorrectly the former or =20= the latter. Cheers, Rup= |
From: Jonathan P. <jp...@dc...> - 2005-11-04 23:23:15
|
> employeeID = 123 # not self.employeeID > puts employeeID > puts self > self.employeeID = 123 # using self.employeeID > puts employeeID > puts self Ah, my fault for getting this wrong in my earlier message. Ruby assumes 'employeeID = 123' is a local variable assignment. Hence, the 'puts employeeID' simply retrieves the local variable assigned on the preceding line. CoreData or RubyCocoa are never involved. In your second example, the first line directly calls the setter (self.employeeID=) and the second line indirectly calls the getter (self.employeeID) since no local variable with that name (employeeID) has been defined yet. Hope that makes sense. Glad to hear that things are beginning to work. Jonathan |
From: Rupert B. <rup...@fr...> - 2005-11-04 23:30:57
|
Le 5 nov. 05 =C3=A0 00:22, Jonathan Paisley a =C3=A9crit : >> employeeID =3D 123 # not self.employeeID >> puts employeeID >> puts self > >> self.employeeID =3D 123 # using self.employeeID >> puts employeeID >> puts self > > Ah, my fault for getting this wrong in my earlier message. > > Ruby assumes 'employeeID =3D 123' is a local variable assignment. Of course ! Gee, I'm getting tired. I'm moving on to chapter 4 ... Thanks again, Rup |