Skip to content

Instantly share code, notes, and snippets.

@cpowell
Created May 27, 2011 19:36
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save cpowell/995979 to your computer and use it in GitHub Desktop.
Save cpowell/995979 to your computer and use it in GitHub Desktop.
A singleton class to manage a MacRuby application's data storage requirements.
#
# Datastore.rb
# A singleton class to manage a MacRuby application's data storage requirements.
#
# Chris Powell, cpowell@prylis.com, http://cbpowell.wordpress.com
#
# This work is licensed under a Creative Commons Attribution 3.0 Unported License.
# http://creativecommons.org/licenses/by/3.0/
#
# For usage and discussion, see http://cbpowell.wordpress.com/category/macruby/
#
class Datastore
# Required initialization of class variables
@@managedObjectModel = nil
@@managedObjectContext = nil
@@persistentStoreCoordinator = nil
# Just to make sure no one instantiates me :)
private_class_method :new
# Returns the support folder for the application, used to store the Core Data
# store file. This code uses a folder named "YOUR_APP_NAME" for
# the content, either in the NSApplicationSupportDirectory location or (if the
# former cannot be found), the system's temporary directory.
#
def self.applicationSupportFolder
paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, true)
basePath = (paths.count > 0) ? paths[0] : NSTemporaryDirectory()
return basePath.stringByAppendingPathComponent("YOUR_APP_NAME")
end
# The Managed Object Model (or MOM) keeps track of objects and their relationships.
# It doesn't care about actual persistency which is the MOC's job.
#
# We designed our model by editing a xcdatamodeld file. When we compile our app, this is
# compiled into a mom file and stored into a folder with the momd extension
# (as in mom deployment directory). This is how the application knows about the data
# structures and relationships to use.
#
# Returns the managed object model for the application, creating if necessary.
def self.mom
unless @@managedObjectModel
model_url = NSBundle.mainBundle.URLForResource("YOUR_APP_NAME", withExtension:"momd")
@@managedObjectModel = NSManagedObjectModel.alloc.initWithContentsOfURL(model_url)
end
@@managedObjectModel
end
# The managed object context acts as a bridge between the MOM and the actual persistence mechanism.
#
# You can think of a managed object context as an intelligent scratch pad.
# When you fetch objects from a persistent store, you bring temporary copies
# onto the scratch pad where they form an object graph (or a collection of
# object graphs). You can then modify those objects however you like. Unless
# you actually save those changes, however, the persistent store remains unaltered.
#
# Returns the managed object context (MOC) for the application (which is already
# bound to the persistent store coordinator for the application.)
#
def self.moc
unless @@managedObjectContext
coordinator = self.psc
unless coordinator
dict = {
NSLocalizedDescriptionKey => "Failed to initialize the store",
NSLocalizedFailureReasonErrorKey => "There was an error building up the data file."
}
error = NSError.errorWithDomain("YOUR_ERROR_DOMAIN", code:9999, userInfo:dict)
NSApplication.sharedApplication.presentError(error)
return nil
end
@@managedObjectContext = NSManagedObjectContext.alloc.init
@@managedObjectContext.setPersistentStoreCoordinator(coordinator)
end
@@managedObjectContext
end
# A PSC is the actual persistence mechanism, and is connected to a MOC.
# When you fetch objects, the MOC asks the coordinator to return those objects that match
# the fetch request.
#
# Returns the persistent store coordinator (PSC) for the application. This implementation
# creates and returns a coordinator, having added the store for the application to it.
# (The directory for the store is created, if necessary.)
#
def self.psc
unless @@persistentStoreCoordinator
error = Pointer.new_with_type('@')
fileManager = NSFileManager.defaultManager
applicationSupportFolder = self.applicationSupportFolder
unless fileManager.fileExistsAtPath(applicationSupportFolder, isDirectory:nil)
fileManager.createDirectoryAtPath(applicationSupportFolder, attributes:nil)
end
url = NSURL.fileURLWithPath(applicationSupportFolder.stringByAppendingPathComponent("YOUR_APP_NAME.sql"))
@@persistentStoreCoordinator = NSPersistentStoreCoordinator.alloc.initWithManagedObjectModel(self.mom)
# Could be NSBinaryStoreType, NSXMLStoreType or NSSQLiteStoreType
unless @@persistentStoreCoordinator.addPersistentStoreWithType(NSSQLiteStoreType,
configuration:nil,
URL:url,
options:nil,
error:error)
NSApplication.sharedApplication.presentError(error[0])
end
end
@@persistentStoreCoordinator
end
# Retrieve objects from the database.
# Credit to Matt Gallagher of 'Cocoa with Love' for inspiring this: http://cocoawithlove.com/2008/03/core-data-one-line-fetch.html
#
# * *Args* :
# - +entity_name+ -> the Core Data entity to retrieve, e.g. "Person"
# - +withLimit+ -> a limit on the query results, nil if no limit desired
# - +withOrder+ -> a string or NSSortDescriptor object to order the results, nil if none required
# - +withPredicate+ -> a string or NSPredicate object to constrain the results, nil if none required
# * *Returns* :
# - an array of objects
# * *Raises* :
# - +RuntimeError+ -> if there is a fetch error
#
def self.find_by_entity_name(entity_name, withLimit:limit, withOrder:string_or_sort_descriptor, withPredicate:string_or_predicate, *args)
request = NSFetchRequest.new
request.entity = self.mom.entitiesByName[entity_name.to_sym]
if string_or_predicate
if string_or_predicate.is_a?(NSString)
pred = NSPredicate.predicateWithFormat(string_or_predicate, argumentArray:args)
elsif string_or_predicate.is_a?(NSPredicate)
pred = string_or_predicate
end
end
if string_or_sort_descriptor
if string_or_sort_descriptor.is_a?(NSString)
order_by = NSSortDescriptor.alloc.initWithKey(string_or_sort_descriptor, ascending:true)
elsif string_or_sort_descriptor.is_a?(NSSortDescriptor)
order_by = string_or_sort_descriptor
end
end
request.setPredicate(pred) if pred
request.setFetchLimit(limit) if limit
request.sortDescriptors = [order_by] if order_by
fetch_error = Pointer.new_with_type('@')
results = self.moc.executeFetchRequest(request, error:fetch_error)
if ((fetch_error[0] != nil) || (results == nil))
msg = (fetch_error[0].localizedDescription ? fetch_error[0].localizedDescription : "Unknown")
raise "Error fetching entity #{entity_name} because #{msg}"
end
return results
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment