Skip to content

Commit 17024ef

Browse files
committed
Add 'arguments' option
1 parent 9e4bdd9 commit 17024ef

5 files changed

Lines changed: 95 additions & 18 deletions

File tree

Sources/UnmanagedData/Commands/Run.swift

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ struct Run: ParsableCommand {
1919
@Option(name: .customLong("prune"), help: "Remove old generated files")
2020
var prune: Bool = false
2121

22+
@Option(help: "Additional arguments to pass to templates. Each argument can have an explicit value or will have an implicit `true` value. Arguments should be passed one by one (e.g. --arguments arg1=value --arguments arg2). Arguments are accessible in templates via `arguments.<name>`.")
23+
var arguments: [String] = []
24+
2225
var modelXMLURL: URL { modelPath.url.appendingPathComponent("contents") }
2326

2427
mutating func validate() throws {
@@ -33,7 +36,20 @@ struct Run: ParsableCommand {
3336

3437
mutating func run() throws {
3538
print("UnmanagedData: Will execute 'run' command")
36-
var config = try RunnableConfig(xcdatamodel: modelPath, output: outputPath, templates: templatePaths, prune: prune)
39+
var arguments: [String: String] = [:]
40+
41+
for arg in self.arguments {
42+
let comps = arg.components(separatedBy: "=")
43+
if comps.count == 1 {
44+
arguments[comps[0]] = "true"
45+
} else if comps.count == 2 {
46+
arguments[comps[0]] = comps[1]
47+
} else {
48+
throw ValidationError("Invalid argument: \(arg)")
49+
}
50+
}
51+
52+
var config = try RunnableConfig(xcdatamodel: modelPath, output: outputPath, templates: templatePaths, prune: prune, arguments: arguments)
3753
try config.run()
3854
}
3955
}

Sources/UnmanagedData/RunnableConfig.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ struct RunnableConfig: Decodable {
1414
var output: Path
1515
var templates: [Path]
1616
var prune: Bool
17+
var arguments: [String: String]
1718

1819
var modelXMLURL: URL { xcdatamodel.url.appendingPathComponent("contents") }
1920
private lazy var resultContentPerFile: [URL: String] = [:]
@@ -23,6 +24,7 @@ struct RunnableConfig: Decodable {
2324
case output
2425
case templates
2526
case prune
27+
case arguments
2628
}
2729

2830
init(from decoder: Decoder) throws {
@@ -31,6 +33,7 @@ struct RunnableConfig: Decodable {
3133
self.output = try container.decode(Path.self, forKey: .output)
3234
self.templates = try container.decode([Path].self, forKey: .templates)
3335
self.prune = try container.decodeIfPresent(Bool.self, forKey: .prune) ?? false
36+
self.arguments = try container.decodeIfPresent([String: String].self, forKey: .arguments) ?? [:]
3437
}
3538
}
3639

@@ -44,10 +47,10 @@ extension RunnableConfig {
4447
config.output = config.output.isRelative ? basePath + config.output : config.output
4548
config.templates = config.templates.map { $0.isRelative ? basePath + $0 : $0 }
4649

47-
self = try RunnableConfig(xcdatamodel: config.xcdatamodel, output: config.output, templates: config.templates, prune: config.prune)
50+
self = try RunnableConfig(xcdatamodel: config.xcdatamodel, output: config.output, templates: config.templates, prune: config.prune, arguments: config.arguments)
4851
}
4952

50-
init(xcdatamodel: Path, output: Path, templates: [Path], prune: Bool) throws {
53+
init(xcdatamodel: Path, output: Path, templates: [Path], prune: Bool, arguments: [String: String]) throws {
5154
self.xcdatamodel = xcdatamodel
5255
self.output = output
5356
self.prune = prune
@@ -67,6 +70,7 @@ extension RunnableConfig {
6770
}
6871

6972
self.templates = allPaths
73+
self.arguments = arguments
7074
}
7175
}
7276

@@ -101,7 +105,9 @@ extension RunnableConfig {
101105
let model = try XMLDecoder().decode(CoreDataModel.self, from: data)
102106
model.populateMissingData()
103107

104-
let dictionary = try DictionaryEncoder().encode(model)
108+
var dictionary = try DictionaryEncoder().encode(model)
109+
dictionary["arguments"] = arguments
110+
105111
let regexp = try NSRegularExpression(pattern: "^\\/\\/\\s?unmanageddata:file:(?<filename>.+?)$(?<content>.+?)^\\/\\/\\s?unmanageddata:file:end$", options: [.dotMatchesLineSeparators, .anchorsMatchLines])
106112

107113
for templatePath in templates {

Templates/OutputModel.stencil

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
{% macro relationMockValueType relation %}{% if relation.toMany %}[{{ relation.destinationEntity }}OutputModelMock]{% else %}{{ relation.destinationEntity }}OutputModelMock{% endif %}{% endmacro %}
33
{% macro addAnyType entity %}{% if entity.allRelationships or entity.allFetchedProperties %}any {%+ endif %}{% endmacro %}
44

5-
5+
import Combine
66
import Foundation
7+
{% if arguments.dbModuleName %}import {{ arguments.dbModuleName }}{% endif +%}
78

89
{% for entity in entities %}
910
public protocol {{ entity.name }}OutputModel{% if entity.parentName %}: {{ entity.parentName }}OutputModel{% endif %} {
@@ -23,6 +24,10 @@ public protocol {{ entity.name }}OutputModel{% if entity.parentName %}: {{ entit
2324
{% for property in entity.fetchedProperties %}
2425
var {{ property.name }}: [{{ property.name|upperFirstLetter }}OutputModelType]{% if property.isOptional %}?{% endif +%} { get }
2526
{% endfor %}
27+
28+
{% if not entity.parentName %}
29+
var didChangePublisher: PassthroughSubject<Void, Never> { get }
30+
{% endif %}
2631
}
2732

2833
{% endfor %}
@@ -63,3 +68,44 @@ public extension {{ entity.name }}OutputModel {
6368
}
6469
{% endfor %}
6570
// unmanageddata:file:end
71+
72+
// unmanageddata:file:OutputModelMock.generated.swift
73+
import Combine
74+
import Foundation
75+
{% if arguments.dbModuleName %}import {{ arguments.dbModuleName }}{% endif +%}
76+
77+
{% for entity in entities %}
78+
public class {{ entity.name }}OutputModelMock: {%+ if entity.parentName %}{{ entity.parentName }}OutputModelMock, {%+ endif %}{{ entity.name }}OutputModel {
79+
{% for attribute in entity.attributes %}
80+
private var __{{ attribute.name }}: {{ attribute.swiftType }}{% if attribute.isOptional %}?{% else %}!{% endif +%}
81+
public var {{ attribute.name }}: {{ attribute.swiftType }}{% if attribute.isOptional %}?{% endif +%} {
82+
get { __{{ attribute.name }} }
83+
set { __{{ attribute.name }} = newValue }
84+
}
85+
{% endfor %}
86+
87+
{% for relation in entity.relationships %}
88+
private var __{{ relation.name }}: {%+ call relationMockValueType relation %}{% if relation.isOptional %}?{% else %}!{% endif +%}
89+
public var {{ relation.name }}: {%+ call relationMockValueType relation %}{% if relation.isOptional %}?{% endif +%} {
90+
get { __{{ relation.name }} }
91+
set { __{{ relation.name }} = newValue }
92+
}
93+
{% endfor %}
94+
95+
{% for property in entity.fetchedProperties %}
96+
private var __{{ property.name }}: [{{ property.fetchRequest.entity }}OutputModelMock]{% if property.isOptional %}?{% else %}!{% endif +%}
97+
public var {{ property.name }}: [{{ property.fetchRequest.entity }}OutputModelMock]{% if property.isOptional %}?{% endif +%} {
98+
get { __{{ property.name }} }
99+
set { __{{ property.name }} = newValue }
100+
}
101+
{% endfor %}
102+
103+
public let didChangePublisher: PassthroughSubject<Void, Never> = PassthroughSubject()
104+
105+
{% if not entity.parentName %}
106+
public init() {}
107+
{% endif %}
108+
}
109+
110+
{% endfor %}
111+
// unmanageddata:file:end

Templates/UMDObject.stencil

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{% macro relationValueRead relation %}{% if relation.toMany %}(__managedObject.{{ relation.name }}?.{% if relation.isOrdered %}array{% else %}allObjects{% endif %} as? [{{ relation.destinationClassName }}]){% else %}__managedObject.{{ relation.name }}{% endif %}{% endmacro %}
33

44
import CoreData
5+
{% if arguments.dbModuleName %}import {{ arguments.dbModuleName }}{% endif +%}
56

67
class UMDObject {}
78

@@ -12,7 +13,7 @@ protocol UMDObjectConvertable {
1213

1314
private let umdTransformMap = [
1415
{% for entity in entities %}
15-
"{{ entity.className }}": { (object: NSManagedObject) -> UMDObject in return {{ entity.className }}.umdObject(object as! {{ entity.className }}) },
16+
"{{ entity.className }}": { (object: NSManagedObject) -> UMDObject in return {%+ if arguments.dbModuleName %}{{ arguments.dbModuleName }}.{% endif %}{{ entity.className }}.umdObject(object as! {%+ if arguments.dbModuleName %}{{ arguments.dbModuleName }}.{% endif %}{{ entity.className }}) },
1617
{% endfor %}
1718
]
1819

@@ -34,30 +35,33 @@ extension Array where Element == NSManagedObject {
3435

3536
{% for entity in entities %}
3637

37-
extension UMDObjectConvertable where Self == {{ entity.className }} {
38+
extension UMDObjectConvertable where Self == {%+ if arguments.dbModuleName %}{{ arguments.dbModuleName }}.{% endif %}{{ entity.className }} {
3839
func umdObject() -> UMD{{ entity.name }} { (self as NSManagedObject).umdObject() as! UMD{{ entity.name }} }
3940
}
4041

41-
extension {{ entity.className }} {
42-
class func umdObject(_ object: {{ entity.className }}) -> UMD{{ entity.name }} { UMD{{ entity.name }}(object) }
42+
extension {%+ if arguments.dbModuleName %}{{ arguments.dbModuleName }}.{% endif %}{{ entity.className }} {
43+
class func umdObject(_ object: {%+ if arguments.dbModuleName %}{{ arguments.dbModuleName }}.{% endif %}{{ entity.className }}) -> UMD{{ entity.name }} { UMD{{ entity.name }}(object) }
4344
}
4445

45-
extension Array where Element == {{ entity.className }} {
46+
extension Array where Element == {%+ if arguments.dbModuleName %}{{ arguments.dbModuleName }}.{% endif %}{{ entity.className }} {
4647
func umdObject() -> [UMD{{ entity.name }}] { map { ($0 as NSManagedObject).umdObject() as! UMD{{ entity.name }} } }
4748
}
4849

4950
{% endfor %}
5051

5152
{% for entity in entities %}
5253
// unmanageddata:file:UMD{{ entity.name }}.generated.swift
54+
import Combine
5355
import CoreData
5456
import Foundation
57+
{% if arguments.dbModuleName %}import {{ arguments.dbModuleName }}{% endif +%}
5558

5659
class UMD{{ entity.name }}{% if entity.parentName %}: UMD{{ entity.parentName }}{% else %}: UMDObject{% endif %} {
5760
private let __managedObject: {{ entity.className }}
5861
private let __context: NSManagedObjectContext
5962
{% if not entity.parentName %}
6063
private var __observer: NSObjectProtocol?
64+
public let didChangePublisher: PassthroughSubject<Void, Never> = PassthroughSubject()
6165
{% endif %}
6266

6367
// MARK: - Attributes
@@ -120,7 +124,7 @@ class UMD{{ entity.name }}{% if entity.parentName %}: UMD{{ entity.parentName }}
120124
}
121125

122126
private func subsribeContext() {
123-
__observer = NotificationCenter.default.addObserver(forName: .NSManagedObjectContextObjectsDidChange, object: __context, queue: nil) { [weak self] notification in
127+
__observer = NotificationCenter.default.addObserver(forName: .NSManagedObjectContextDidSaveObjectIDs, object: __context, queue: nil) { [weak self] notification in
124128
self?.process(notification: notification)
125129
}
126130
}
@@ -137,21 +141,21 @@ class UMD{{ entity.name }}{% if entity.parentName %}: UMD{{ entity.parentName }}
137141
var hasChanges: Bool = false
138142
var isDeleted: Bool = false
139143

140-
if hasChanges == false, let objects = notification.userInfo?[NSUpdatedObjectsKey] as? NSSet {
141-
if objects.first(where: { ($0 as? NSManagedObject)?.objectID == objectID }) != nil {
144+
if hasChanges == false, let objects = notification.userInfo?[NSUpdatedObjectIDsKey] as? Set<NSManagedObjectID> {
145+
if objects.first(where: { $0 == objectID }) != nil {
142146
hasChanges = true
143147
}
144148
}
145149

146-
if hasChanges == false, let objects = notification.userInfo?[NSDeletedObjectsKey] as? NSSet {
147-
if objects.first(where: { ($0 as? NSManagedObject)?.objectID == objectID }) != nil {
150+
if hasChanges == false, let objects = notification.userInfo?[NSDeletedObjectIDsKey] as? Set<NSManagedObjectID> {
151+
if objects.first(where: { $0 == objectID }) != nil {
148152
hasChanges = true
149153
isDeleted = true
150154
}
151155
}
152156

153-
if hasChanges == false, let objects = notification.userInfo?[NSRefreshedObjectsKey] as? NSSet {
154-
if objects.first(where: { ($0 as? NSManagedObject)?.objectID == objectID }) != nil {
157+
if hasChanges == false, let objects = notification.userInfo?[NSRefreshedObjectIDsKey] as? Set<NSManagedObjectID> {
158+
if objects.first(where: { $0 == objectID }) != nil {
155159
hasChanges = true
156160
}
157161
}
@@ -167,7 +171,9 @@ class UMD{{ entity.name }}{% if entity.parentName %}: UMD{{ entity.parentName }}
167171

168172
public func willChange() {}
169173

170-
public func didChange() {}
174+
public func didChange() {
175+
didChangePublisher.send()
176+
}
171177

172178
public func didDelete() {}
173179
{% endif %}

umd.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,6 @@ output:
88
./Generated/
99

1010
prune: false
11+
12+
arguments:
13+
dbModuleName: CoreDB

0 commit comments

Comments
 (0)