<commit_before>//
//  DataStore
//  Galleon
//
//  Created by <NAME> on 11/8/2015.
//  Copyright © 2015 <NAME>. All rights reserved.
//

import Foundation
import ReactiveCocoa
import CoreData

/// Container manages a Core Data persistent store and coordinates between requested object contexts.
final public class Container: Base {
	public let url: NSURL
	public let model: NSManagedObjectModel
	public let modelConfiguration: String?

	public let mainContext: ObjectContext
	public let rootSavingContext: ObjectContext

	internal let persistentStoreCoordinator: NSPersistentStoreCoordinator

	private var _isSaving = MutableProperty<UInt>(0)
	private(set) public lazy var isSaving: AnyProperty<Bool> = { [unowned self] in
		return AnyProperty<Bool>(initialValue: false,
			producer: self._isSaving.producer
				.map { $0 != 0 }
				.skipRepeats())
	}()


	public init(url: NSURL, model: NSManagedObjectModel, modelConfiguration: String? = nil) {
		self.url = url
		self.model = model
		self.modelConfiguration = modelConfiguration

		let coordinator = NSPersistentStoreCoordinator(managedObjectModel: model)
		self.persistentStoreCoordinator = coordinator

		if url.pathExtension != "mdcontainer" {
			fatalError("You must open a MantleData container.")
		}

		let fileManager = NSFileManager.defaultManager()
		if !fileManager.fileExistsAtPath(url.path!) {
			do {
				try fileManager.createDirectoryAtURL(url, withIntermediateDirectories: false, attributes: nil)
			} catch let error as NSError {
				fatalError("Failed to create the container. Message: \(error.description)")
			}
		}

		if let modelConfiguration = modelConfiguration where !model.configurations.contains(modelConfiguration) {
			fatalError("The managed object model does not contain the specified configuration.")
		}

		let storeURL = url.URLByAppendingPathComponent("Database.sqlite3")

    do {
      try persistentStoreCoordinator.addPersistentStoreWithType(NSSQLiteStoreType,
				configuration: modelConfiguration,
				URL: storeURL,
				options: nil)
    } catch _ as NSError {
      try! NSFileManager.defaultManager().removeItemAtURL(storeURL)
			NSLog("SQLite store deleted.")

      try! persistentStoreCoordinator.addPersistentStoreWithType(NSSQLiteStoreType,
				configuration: modelConfiguration,
				URL: storeURL,
				options: nil)
    }

		rootSavingContext = ObjectContext(parent: .PersistentStore(persistentStoreCoordinator),
		                            concurrencyType: .MainQueueConcurrencyType,
		                            mergePolicy: MergePolicy.RaiseError.cocoaValue)

		mainContext = ObjectContext(parent: .Context(rootSavingContext),
		                            concurrencyType: .MainQueueConcurrencyType,
		                            mergePolicy: MergePolicy.RaiseError.cocoaValue)

		super.init()

		observeSavingNotificationsOf(rootSavingContext)
		observeSavingNotificationsOf(mainContext)
	}

	public func prepare<Result>(@noescape action: ObjectContext -> Result) -> Result {
		return mainContext.prepare { action(mainContext) }
	}

	public func save(action: ObjectContext -> Void) -> SignalProducer<Void, NSError> {
		let (producer, observer) = SignalProducer<Void, NSError>.buffer(0)

		rootSavingContext.perform {
			action(rootSavingContext)
			do {
				try rootSavingContext.save()
				observer.sendCompleted()
			} catch let e as NSError {
				observer.sendFailed(e)
			}
		}

		return producer
	}

  public func privateQueueRootContext(mergePolicy: MergePolicy = .PreferMemoryWhenMerging) -> ObjectContext {
    let context = ObjectContext(parent: .PersistentStore(persistentStoreCoordinator),
			concurrencyType: .PrivateQueueConcurrencyType,
			mergePolicy: mergePolicy.cocoaValue)
		observeSavingNotificationsOf(context)

    return context
  }

	public func mainQueueRootContext(mergePolicy: MergePolicy = .PreferMemoryWhenMerging) -> ObjectContext {
		let context = ObjectContext(parent: .PersistentStore(persistentStoreCoordinator),
			concurrencyType: .MainQueueConcurrencyType,
			mergePolicy: mergePolicy.cocoaValue)
		observeSavingNotificationsOf(context)

		return context
	}

	private func observeSavingNotificationsOf<C: NSManagedObjectContext>(context: C) {
		let center = NSNotificationCenter.defaultCenter()

		center.rac_notifications(NSManagedObjectContextWillSaveNotification,
			object: context)
			.takeUntil(context.willDeinitProducer)
			.startWithNext { _ in
				self._isSaving.modify { $0 + 1 }
			}

		center.rac_notifications(NSManagedObjectContextDidSaveNotification,
			object: context)
			.takeUntil(context.willDeinitProducer)
			.startWithNext { _ in
				self._isSaving.modify { $0 - 1 }
		}
	}
}

public enum MergePolicy {
	case Custom(NSMergePolicy)
  case PreferMemoryWhenMerging
  case PreferStoreWhenMerging
  case OverwriteStore
  case RollbackToStore<commit_msg>The initialiser of Container is now throwable and propagates all errors it might encounter.<commit_after>//
//  DataStore
//  Galleon
//
//  Created by <NAME> on 11/8/2015.
//  Copyright © 2015 <NAME>. All rights reserved.
//

import Foundation
import ReactiveCocoa
import CoreData

/// Container manages a Core Data persistent store and coordinates between requested object contexts.
final public class Container: Base {
	public let url: NSURL
	public let model: NSManagedObjectModel
	public let modelConfiguration: String?

	public let mainContext: ObjectContext
	public let rootSavingContext: ObjectContext

	internal let persistentStoreCoordinator: NSPersistentStoreCoordinator

	private var _isSaving = MutableProperty<UInt>(0)
	private(set) public lazy var isSaving: AnyProperty<Bool> = { [unowned self] in
		return AnyProperty<Bool>(initialValue: false,
			producer: self._isSaving.producer
				.map { $0 != 0 }
				.skipRepeats())
	}()

	public init(url: NSURL, model: NSManagedObjectModel, modelConfiguration: String? = nil) throws {
		self.url = url
		self.model = model
		self.modelConfiguration = modelConfiguration

		let coordinator = NSPersistentStoreCoordinator(managedObjectModel: model)
		self.persistentStoreCoordinator = coordinator

		if url.pathExtension != "mdcontainer" {
			throw Error.InvalidFileExtension
		}

		let fileManager = NSFileManager.defaultManager()
		if !fileManager.fileExistsAtPath(url.path!) {
			do {
				try fileManager.createDirectoryAtURL(url, withIntermediateDirectories: false, attributes: nil)
			} catch let error as NSError {
				throw Error.CannotCreateContainer(error)
			}
		}

		if let modelConfiguration = modelConfiguration where !model.configurations.contains(modelConfiguration) {
			throw Error.ModelConfigurationNotFound
		}

		let storeURL = url.URLByAppendingPathComponent("Database.sqlite3")

		do {
			try persistentStoreCoordinator.addPersistentStoreWithType(NSSQLiteStoreType,
			                                                          configuration: modelConfiguration,
			                                                          URL: storeURL,
			                                                          options: nil)
		} catch let error as NSError {
			throw Error.CannotAddPersistentStore(error)
		}

		rootSavingContext = ObjectContext(parent: .PersistentStore(persistentStoreCoordinator),
		                            concurrencyType: .MainQueueConcurrencyType,
		                            mergePolicy: MergePolicy.RaiseError.cocoaValue)

		mainContext = ObjectContext(parent: .Context(rootSavingContext),
		                            concurrencyType: .MainQueueConcurrencyType,
		                            mergePolicy: MergePolicy.RaiseError.cocoaValue)

		super.init()

		observeSavingNotificationsOf(rootSavingContext)
		observeSavingNotificationsOf(mainContext)
	}

	public func prepare<Result>(@noescape action: ObjectContext -> Result) -> Result {
		return mainContext.prepare { action(mainContext) }
	}

	public func save(action: ObjectContext -> Void) -> SignalProducer<Void, NSError> {
		let (producer, observer) = SignalProducer<Void, NSError>.buffer(0)

		rootSavingContext.perform {
			action(rootSavingContext)
			do {
				try rootSavingContext.save()
				observer.sendCompleted()
			} catch let e as NSError {
				observer.sendFailed(e)
			}
		}

		return producer
	}

  public func privateQueueRootContext(mergePolicy: MergePolicy = .PreferMemoryWhenMerging) -> ObjectContext {
    let context = ObjectContext(parent: .PersistentStore(persistentStoreCoordinator),
			concurrencyType: .PrivateQueueConcurrencyType,
			mergePolicy: mergePolicy.cocoaValue)
		observeSavingNotificationsOf(context)

    return context
  }

	public func mainQueueRootContext(mergePolicy: MergePolicy = .PreferMemoryWhenMerging) -> ObjectContext {
		let context = ObjectContext(parent: .PersistentStore(persistentStoreCoordinator),
			concurrencyType: .MainQueueConcurrencyType,
			mergePolicy: mergePolicy.cocoaValue)
		observeSavingNotificationsOf(context)

		return context
	}

	private func observeSavingNotificationsOf<C: NSManagedObjectContext>(context: C) {
		let center = NSNotificationCenter.defaultCenter()

		center.rac_notifications(NSManagedObjectContextWillSaveNotification,
			object: context)
			.takeUntil(context.willDeinitProducer)
			.startWithNext { _ in
				self._isSaving.modify { $0 + 1 }
			}

		center.rac_notifications(NSManagedObjectContextDidSaveNotification,
			object: context)
			.takeUntil(context.willDeinitProducer)
			.startWithNext { _ in
				self._isSaving.modify { $0 - 1 }
		}
	}

	// Container Error
	public enum Error: ErrorType {
		case InvalidFileExtension
		case CannotCreateContainer(NSError)
		case ModelConfigurationNotFound
		case CannotAddPersistentStore(NSError)
	}
}

public enum MergePolicy {
	case Custom(NSMergePolicy)
  case PreferMemoryWhenMerging
  case PreferStoreWhenMerging
  case OverwriteStore
  case RollbackToStore
{'fraction_non_alphanumeric': 0.07735598076332753, 'fraction_numerical': 0.004092909035096695, 'mean_word_length': 3.9994884910485933, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4480699', 'n_tokens_mistral': 3063, 'n_tokens_neox': 2695, 'n_words': 617}