<commit_before>//
//  DALService.swift
//  Skopelos
//
//  Created by <NAME> on 30/07/2016.
//  Copyright © 2016 <NAME>. All rights reserved.
//

import Foundation
import CoreData

public struct DALServiceConstants {
    static let handleDALServiceErrorNotification = "handleDALServiceErrorNotification"
}

open class DALService: NSObject {
    
    let coreDataStack: CoreDataStackProtocol
    
    deinit {
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: DALServiceConstants.handleDALServiceErrorNotification), object: nil)
    }
    
    public init(coreDataStack cds: CoreDataStackProtocol) {
        coreDataStack = cds
        super.init()
        NotificationCenter.default.addObserver(self,
                                                         selector: #selector(receiveErrorNotification),
                                                         name: NSNotification.Name(rawValue: DALServiceConstants.handleDALServiceErrorNotification),
                                                         object: nil)
    }
    
    @objc func receiveErrorNotification(_ notification: Notification) {
        guard let userInfo = (notification as NSNotification).userInfo, let error = userInfo["error"]  else { return }
        handle(error: error as! NSError)
    }
    
    open func handle(error: NSError) {
        // override in subclasses
    }
    
    fileprivate func slaveContext() -> NSManagedObjectContext {
        let slaveContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
        slaveContext.parent = coreDataStack.mainContext
        
        return slaveContext
    }
}

extension DALService: DALProtocol {

    @discardableResult
    public func read(_ statements: @escaping (NSManagedObjectContext) -> Void) -> Self {
        let context = coreDataStack.mainContext
        context.performAndWait {
            statements(context)
        }

        return self
    }
    
    @discardableResult
    public func writeSync(_ changes: @escaping (NSManagedObjectContext) -> Void) -> Self {
        return writeSync(changes, completion:nil)
    }
    
    @discardableResult
    public func writeSync(_ changes: @escaping (NSManagedObjectContext) -> Void, completion: ((NSError?) -> Void)?) -> Self {
        let context = slaveContext()
        context.performAndWait {
            changes(context)
            do {
                try context.save()
                self.coreDataStack.save(completion)
            } catch let error as NSError {
                fatalError("Failed to save main context: \(error.localizedDescription), \(error.userInfo)")
            }
        }
        
        return self
    }
    
    public func writeAsync(_ changes: @escaping (NSManagedObjectContext) -> Void) -> Void {
        return writeAsync(changes, completion:nil)
    }
    
    public func writeAsync(_ changes: @escaping (NSManagedObjectContext) -> Void, completion: ((NSError?) -> Void)?) -> Void {
        let context = slaveContext()
        context.perform {<commit_msg>Use a single slave context to avoid undesired concurrent updates<commit_after>//
//  DALService.swift
//  Skopelos
//
//  Created by <NAME> on 30/07/2016.
//  Copyright © 2016 <NAME>. All rights reserved.
//

import Foundation
import CoreData

public struct DALServiceConstants {
    static let handleDALServiceErrorNotification = "handleDALServiceErrorNotification"
}

open class DALService: NSObject {
    
    let coreDataStack: CoreDataStackProtocol
    
    deinit {
        NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: DALServiceConstants.handleDALServiceErrorNotification), object: nil)
    }
    
    public init(coreDataStack cds: CoreDataStackProtocol) {
        coreDataStack = cds
        super.init()
        NotificationCenter.default.addObserver(self,
                                                         selector: #selector(receiveErrorNotification),
                                                         name: NSNotification.Name(rawValue: DALServiceConstants.handleDALServiceErrorNotification),
                                                         object: nil)
    }
    
    @objc func receiveErrorNotification(_ notification: Notification) {
        guard let userInfo = (notification as NSNotification).userInfo, let error = userInfo["error"]  else { return }
        handle(error: error as! NSError)
    }
    
    open func handle(error: NSError) {
        // override in subclasses
    }
    
    lazy fileprivate var slaveContext: NSManagedObjectContext = {
        let slaveContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
        slaveContext.parent = coreDataStack.mainContext
        
        return slaveContext
    }()
}

extension DALService: DALProtocol {

    @discardableResult
    public func read(_ statements: @escaping (NSManagedObjectContext) -> Void) -> Self {
        let context = coreDataStack.mainContext
        context.performAndWait {
            statements(context)
        }

        return self
    }
    
    @discardableResult
    public func writeSync(_ changes: @escaping (NSManagedObjectContext) -> Void) -> Self {
        return writeSync(changes, completion:nil)
    }
    
    @discardableResult
    public func writeSync(_ changes: @escaping (NSManagedObjectContext) -> Void, completion: ((NSError?) -> Void)?) -> Self {
        let context = slaveContext
        context.performAndWait {
            changes(context)
            do {
                try context.save()
                self.coreDataStack.save(completion)
            } catch let error as NSError {
                fatalError("Failed to save main context: \(error.localizedDescription), \(error.userInfo)")
            }
        }
        
        return self
    }
    
    public func writeAsync(_ changes: @escaping (NSManagedObjectContext) -> Void) -> Void {
        return writeAsync(changes, completion:nil)
    }
    
    public func writeAsync(_ changes: @escaping (NSManagedObjectContext) -> Void, completion: ((NSError?) -> Void)?) -> Void {
        let context = slaveContext
        context.perform {
{'fraction_non_alphanumeric': 0.08307742039760789, 'fraction_numerical': 0.0038791013415225475, 'mean_word_length': 2.28101802757158, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 32, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2995056', 'n_tokens_mistral': 1609, 'n_tokens_neox': 1527, 'n_words': 382}