Skip to content

Commit

Permalink
Protocolize DataSource types
Browse files Browse the repository at this point in the history
This should eventually drop the classes and
only use the protocols. Sensible in today's
Swift.
  • Loading branch information
helje5 committed Nov 23, 2024
1 parent 32ae98c commit ad557ac
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 68 deletions.
40 changes: 35 additions & 5 deletions Sources/ZeeQL/Access/AccessDataSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,30 @@
// Copyright © 2017-2024 ZeeZide GmbH. All rights reserved.
//

public protocol AccessDataSourceType: DataSourceType {

var log : ZeeQLLogger { get }

var entityName : String? { get set }
var entity : Entity? { get }
var fetchSpecificationName : String? { get set }

var auxiliaryQualifier : Qualifier? { get set }
var isFetchEnabled : Bool { get set }
var qualifierBindings : Any? { get set }

var qualifierBindingKeys : [ String ] { get }

func fetchGlobalIDs(yield: ( GlobalID ) throws -> Void) throws

// TODO: remove `_`
func _primaryFetchObjects (_ fs: FetchSpecification,
yield: ( Object ) throws -> Void) throws
func _primaryFetchCount (_ fs: FetchSpecification) throws -> Int
func _primaryFetchGlobalIDs(_ fs: FetchSpecification,
yield: ( GlobalID ) throws -> Void) throws
}

/**
* This class has a set of operations targetted at SQL based applications. It
* has three major subclasses with specific characteristics:
Expand Down Expand Up @@ -35,7 +59,9 @@
* mapping, that is, it returns ``AdaptorRecord`` objects and works directly on
* top of an ``AdaptorChannel``.
*/
open class AccessDataSource<Object: SwiftObject> : DataSource<Object> {
open class AccessDataSource<Object: SwiftObject> : DataSource<Object>,
AccessDataSourceType
{
// TODO: Both DataSource and AccessDataSource should be protocols w/ PATs now,
// generics are good enough in Swift now.

Expand Down Expand Up @@ -102,21 +128,24 @@ open class AccessDataSource<Object: SwiftObject> : DataSource<Object> {
yield: ( GlobalID ) throws -> Void) throws {
fatalError("implement in subclass: \(#function)")
}


// MARK: - Fetch Convenience

override open func fetchObjects(yield: ( Object ) -> Void) throws {
// `iteratorForObjects` in GETobjects
try _primaryFetchObjects(try fetchSpecificationForFetch(), yield: yield)
}
override open func fetchCount() throws -> Int {
open func fetchCount() throws -> Int {
return try _primaryFetchCount(try fetchSpecificationForFetch())
}
open func fetchGlobalIDs(yield: ( GlobalID ) throws -> Void) throws {
try _primaryFetchGlobalIDs(try fetchSpecificationForFetch(), yield: yield)
}
}

public extension AccessDataSourceType {

/**
* This method takes the name of a fetch specification. It looks up the fetch
* spec in the `Entity` associated with the datasource and then binds the
Expand All @@ -140,8 +169,8 @@ open class AccessDataSource<Object: SwiftObject> : DataSource<Object> {
* - keysAndValues: The key/value pairs to apply as bindings.
* - Returns: The fetched objects.
*/
public func fetchObjects(_ fetchSpecificationName: String,
_ keysAndValues: Any...) throws -> [ Object ]
func fetchObjects(_ fetchSpecificationName: String,
_ keysAndValues: Any...) throws -> [ Object ]
{
guard let findEntity = entity else {
// TBD: improve exception
Expand Down Expand Up @@ -172,6 +201,7 @@ open class AccessDataSource<Object: SwiftObject> : DataSource<Object> {

// MARK: - Bindings

@inlinable
var qualifierBindingKeys : [ String ] {
let q = fetchSpecification?.qualifier
let aux = auxiliaryQualifier
Expand Down
54 changes: 36 additions & 18 deletions Sources/ZeeQL/Access/ActiveDataSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,58 @@
// Copyright © 2017-2024 ZeeZide GmbH. All rights reserved.
//

public protocol ActiveDataSourceType: AccessDataSourceType
where Object: ActiveRecordType
{

var database : Database { get set }

init(database: Database, entity: Entity)

func createObject() -> Object
func insertObject(_ object: Object) throws
}

extension ActiveDataSourceType {

@inlinable
public init(database: Database) {
if let t = Object.self as? EntityType.Type {
self.init(database: database, entity: t.entity)
}
else {
fatalError("Could not determine entity from object")
}
}

}

/**
* Used to query `DatabaseObject`s from a `Database`.
*
* W/o it you usually create an
* Database object with an Adaptor and then use that database object to
* acquire an DatabaseChannel.
*
* W/o it you usually create a
* ``Database`` object with an ``Adaptor`` and then use that database object to
* acquire an ``DatabaseChannel``.
*
* Naming convention:
* - `find...` - a method which returns a single object and uses LIMIT 1
* - `fetch..` - a method which returns a List of objects
* - `iteratorFor` - a method which returns an Iterator of fetched objects
* - `perform...` - a method which applies a change to the database
*
* Example:
*
* let persons = db.datasource(Person.self)
* ```swift
* let persons = db.datasource(Person.self)
* ```
*/
open class ActiveDataSource<Object: ActiveRecordType> : AccessDataSource<Object>
open class ActiveDataSource<Object: ActiveRecordType>: AccessDataSource<Object>,
ActiveDataSourceType
{

open var database : Database
let _entity : Entity

public init(database: Database, entity: Entity) {
required public init(database: Database, entity: Entity) {
self.database = database
self._entity = entity

Expand All @@ -38,16 +66,6 @@ open class ActiveDataSource<Object: ActiveRecordType> : AccessDataSource<Object>
self.log = database.log
}

@inlinable
public convenience init(database: Database) {
if let t = Object.self as? EntityType.Type {
self.init(database: database, entity: t.entity)
}
else {
fatalError("Could not determine entity from object")
}
}


// MARK: - Create

Expand Down
61 changes: 39 additions & 22 deletions Sources/ZeeQL/Access/AdaptorDataSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,39 @@
// Copyright © 2017-2020 ZeeZide GmbH. All rights reserved.
//

public protocol AdaptorDataSourceType: AccessDataSourceType
where Object == AdaptorRecord
{
var adaptor : Adaptor { get }

init(adaptor: Adaptor, entity: Entity?)
}

public extension AdaptorDataSourceType {

@inlinable
init(adaptor: Adaptor) { self.init(adaptor: adaptor, entity: nil) }

@inlinable
func findEntity(for _entityName: String?) -> Entity? {
let ename : String
if let entityName = _entityName {
ename = entityName
}
else if let fs = fetchSpecification, let entityName = fs.entityName {
ename = entityName
}
else {
return nil
}

/* retrieve model of adaptor */

guard let model = adaptor.model else { return nil }
return model[entity: ename]
}
}

/**
* This datasource operates at the adaptor level, that is, it does not return
* DatabaseObject instances but plain records.
Expand All @@ -17,37 +50,21 @@
*
* THREAD: this class is for use in one thread.
*/
open class AdaptorDataSource : AccessDataSource<AdaptorRecord> {
open class AdaptorDataSource : AccessDataSource<AdaptorRecord>,
AdaptorDataSourceType
{
// TBD: do we need a way to initialize a datasource with an EOAdaptorChannel?

let adaptor : Adaptor
public let adaptor : Adaptor
let _entity : Entity?

public init(adaptor: Adaptor, entity: Entity? = nil) {
required public init(adaptor: Adaptor, entity: Entity?) {
self.adaptor = adaptor
self._entity = entity
}

override open var entity : Entity? {
if let entity = _entity { return entity } // set explicitly

/* determine name of datasource entity */

let ename : String
if let entityName = _entityName {
ename = entityName
}
else if let fs = fetchSpecification, let entityName = fs.entityName {
ename = entityName
}
else {
return nil
}

/* retrieve model of adaptor */

guard let model = adaptor.model else { return nil }
return model[entity: ename]
return _entity ?? findEntity(for: _entityName)
}

override
Expand Down
14 changes: 12 additions & 2 deletions Sources/ZeeQL/Access/DatabaseDataSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,27 @@
// Copyright © 2017-2024 ZeeZide GmbH. All rights reserved.
//

public protocol DatabaseDataSourceType: AccessDataSourceType
where Object: DatabaseObject
{

var objectContext : ObjectTrackingContext { get }
var database : Database? { get }

init(_ oc: ObjectTrackingContext, entityName: String)
}

/**
* A datasource which works on top of an EditingContext. That is, the editing
* context does all the fetching.
*/
open class DatabaseDataSource<Object: DatabaseObject>
: AccessDataSource<Object>
: AccessDataSource<Object>, DatabaseDataSourceType
{

public let objectContext : ObjectTrackingContext

public init(_ oc: ObjectTrackingContext, entityName: String) {
required public init(_ oc: ObjectTrackingContext, entityName: String) {
self.objectContext = oc

super.init()
Expand Down
4 changes: 3 additions & 1 deletion Sources/ZeeQL/Control/ArrayDataSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ open class ArrayDataSource<Object: SwiftObject> : DataSource<Object>,
}

@inlinable
override open func fetchCount() throws -> Int {
open func fetchCount() throws -> Int {
return objects.count
}

Expand Down Expand Up @@ -94,6 +94,8 @@ open class ArrayDataSource<Object: SwiftObject> : DataSource<Object>,
}
}
}

// MARK: - Description

public func appendToDescription(_ ms: inout String) {
ms += " #objects=\(objects.count)"
Expand Down
Loading

0 comments on commit ad557ac

Please sign in to comment.