- Create a new MacRuby Application
- Create a new empty file and call it AppDelegate.rb
- Paste the following code into that file:
# # AppDelegate.rb # MyGreatApp # # Created by Johannes Fahrenkrug on 10/17/08. # Copyright __MyCompanyName__ 2008 . All rights reserved. # class AppDelegate attr_writer :window # Returns the support folder for the application, used to store the Core Data # store file. This code uses a folder named "MyGreatApp" for # the content, either in the NSApplicationSupportDirectory location or (if the # former cannot be found), the system's temporary directory. def applicationSupportFolder paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, true) basePath = (paths.count > 0) ? paths[0] : NSTemporaryDirectory() return basePath.stringByAppendingPathComponent("MacObjTalks") end # # Creates and returns the managed object model for the application # by merging all of the models found in the application bundle. # def managedObjectModel if @managedObjectModel return @managedObjectModel end @managedObjectModel = NSManagedObjectModel.mergedModelFromBundles(nil) return @managedObjectModel end # # Returns the persistent store coordinator for the application. This # implementation will create and return a coordinator, having added the # store for the application to it. (The folder for the store is created, # if necessary.) # def persistentStoreCoordinator if @persistentStoreCoordinator return @persistentStoreCoordinator; end error = nil fileManager = NSFileManager.defaultManager applicationSupportFolder = self.applicationSupportFolder if !fileManager.fileExistsAtPath(applicationSupportFolder, isDirectory:nil) fileManager.createDirectoryAtPath(applicationSupportFolder, attributes:nil) end url = NSURL.fileURLWithPath(applicationSupportFolder.stringByAppendingPathComponent("MyData.xml")) @persistentStoreCoordinator = NSPersistentStoreCoordinator.alloc.initWithManagedObjectModel(self.managedObjectModel) if !@persistentStoreCoordinator.addPersistentStoreWithType(NSXMLStoreType, configuration:nil, URL:url, options:nil, error:error) NSApplication.sharedApplication.presentError(error) end return @persistentStoreCoordinator end # # Returns the managed object context for the application (which is already # bound to the persistent store coordinator for the application.) # def managedObjectContext return @managedObjectContext if @managedObjectContext coordinator = self.persistentStoreCoordinator if coordinator @managedObjectContext = NSManagedObjectContext.alloc.init @managedObjectContext.setPersistentStoreCoordinator(coordinator) end return @managedObjectContext end # # Returns the NSUndoManager for the application. In this case, the manager # returned is that of the managed object context for the application. # def windowWillReturnUndoManager(window) return self.managedObjectContext.undoManager end # # Performs the save action for the application, which is to send the save: # message to the application's managed object context. Any encountered errors # are presented to the user. # def saveAction(sender) error = nil; if !self.managedObjectContext.save(error) NSApplication.sharedApplication.presentError(error) end end # # Implementation of the applicationShouldTerminate: method, used here to # handle the saving of changes in the application managed object context # before the application terminates. # def applicationShouldTerminate(sender) error = nil reply = NSTerminateNow if self.managedObjectContext if (self.managedObjectContext.commitEditing) if (self.managedObjectContext.hasChanges and !self.managedObjectContext.save(error)) # This error handling simply presents error information in a panel with an # "Ok" button, which does not include any attempt at error recovery (meaning, # attempting to fix the error.) As a result, this implementation will # present the information to the user and then follow up with a panel asking # if the user wishes to "Quit Anyway", without saving the changes. # Typically, this process should be altered to include application-specific # recovery steps. errorResult = NSApplication.sharedApplication.presentError(error) if errorResult reply = NSTerminateCancel else alertReturn = NSRunAlertPanel(nil, "Could not save changes while quitting. Quit anyway?" , "Quit anyway", "Cancel", nil) if (alertReturn == NSAlertAlternateReturn) reply = NSTerminateCancel end end end else reply = NSTerminateCancel end end return reply end end
- Save it.
- Open MainMenu.nib
- Drag a new NSObject to the MainMenu.nib window:
- Open the inspector for the new object and open the next-to-last tab. Select "AppDelegate" from the Class popup list.
- Control-drag from the File's Owner to the App Delegate and connect it's delegate outlet.
- Control-drag from the App Delegate to the Window to connect it's window outlet.
- Save the changes to the nib.
- Add a Core Data data model (give it a name of your choice and just click Finish in the 3rd step of the wizard).
- Double-click on the new data model file to open it with the data model editor.
- Add an entity called "Skill" with a string attribute called "name" and an integer 16 attribute called "level".
- Save the data model.
- Back in Interface Builder, drag a Core Data Entity to the Window.
- In the wizard that comes up, select your Xcode Project, your data model and the "Skill" entity.
- In the next step, select the Master/Detail view and check all the options.
- In the next step, select both attributes and click "Finish".
- Your window should look like this:
- Disconnect the saveDocument action from the Save menu item by clicking the X on the left of "First Responder".
- Control-drag from the Save menu item to the App Delegate and connect it to the saveAction.
- Save the NIB.
- Click Build & Go.
- You should be able to add, remove, edit, and search for skills.
- Click File -> Save, close the app and restart it: your data should still be there.
- Commence frolicking and rejoicing.
Comments
Anonymous said...
Is there a rails fixture-esque feature for CoreData?
July 31, 2009 05:28 AMJohannes Fahrenkrug said...
Hi Anonymous (if that really IS your real name),
thank you for your friendly comment.
You are absolutely correct. I did this rather quickly, I basically went through the ObjC code line by line and "translated" it to Ruby. I neither optimized nor beautified it. But writing it the way you pointed out would have been much prettier and ruby-ish. Guilty as charged.
Btw, it should be "Very bad ruby style!!!", putting a whitespace before a punctuation mark is very bad writing style. ;-)
- Johannes
Anonymous said...
very bad ruby style !!!
def managedObjectModel
if @managedObjectModel
return @managedObjectModel
end
@managedObjectModel = NSManagedObjectModel.mergedModelFromBundles(nil)
return @managedObjectModel
end
it's
def managedObjectModel
@managedObjectModel ||= NSManagedObjectModel.mergedModelFromBundles(nil)
end
Johannes Fahrenkrug said...
Hey jamesu,
check out my update note on the top of the article!
Johannes Fahrenkrug said...
thank you for your comment, James! I know, the error = nil solution is less then optimal. I saw that they also do that in the RubyCocoa core data template, but that's no excuse. Not the value, but a reference has to be passed as the argument. If anyone has time to find out how to do that in macruby before me, please, by all means, leave a comment!
October 24, 2008 06:49 AMjamesu said...
Neat, was looking how to do this the other week.
Had an issue where i couldn't figure out what the "error" parameter should be for addPersistentStoreWithType. Looks like you have worked around this by using "nil"... interesting.
(I ended up just implementing this bit in Objective C. Yikes.)
Johannes Fahrenkrug said...
Not any that I know of, unfortunately. It should be easy enough to put something like that together in MacRuby, though.
July 31, 2009 06:20 AM