Skip to content

Commit 56560ab

Browse files
author
Anis Kadri
committed
Merge branch 'IvanKarpan-feature/xcdatamodel'
2 parents 916d824 + 9027b9a commit 56560ab

File tree

9 files changed

+306
-4
lines changed

9 files changed

+306
-4
lines changed

lib/pbxFile.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,14 @@ function defaultEncoding(fileRef) {
9494
}
9595

9696
function detectGroup(fileRef) {
97-
var filetype = fileRef.lastKnownFileType || fileRef.explicitFileType,
97+
var extension = path.extname(fileRef.basename).substring(1),
98+
filetype = fileRef.lastKnownFileType || fileRef.explicitFileType,
9899
groupName = GROUP_BY_FILETYPE[unquoted(filetype)];
99100

101+
if (extension === 'xcdatamodeld') {
102+
return 'Sources';
103+
}
104+
100105
if (!groupName) {
101106
return DEFAULT_GROUP;
102107
}
@@ -154,6 +159,7 @@ function pbxFile(filepath, opt) {
154159

155160
self = this;
156161

162+
this.basename = path.basename(filepath);
157163
this.lastKnownFileType = opt.lastKnownFileType || detectType(filepath);
158164
this.group = detectGroup(self);
159165

@@ -163,7 +169,6 @@ function pbxFile(filepath, opt) {
163169
this.dirname = path.dirname(filepath);
164170
}
165171

166-
this.basename = path.basename(filepath);
167172
this.path = defaultPath(this, filepath);
168173
this.fileEncoding = this.defaultEncoding = opt.defaultEncoding || defaultEncoding(self);
169174

lib/pbxProject.js

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ var util = require('util'),
88
pbxFile = require('./pbxFile'),
99
fs = require('fs'),
1010
parser = require('./parser/pbxproj'),
11+
plist = require('simple-plist'),
1112
COMMENT_KEY = /_comment$/
1213

1314
function pbxProject(filename) {
@@ -507,6 +508,27 @@ pbxProject.prototype.removeFromPbxFileReferenceSection = function(file) {
507508
return file;
508509
}
509510

511+
pbxProject.prototype.addToXcVersionGroupSection = function(file) {
512+
if (!file.models || !file.currentModel) {
513+
throw new Error("Cannot create a XCVersionGroup section from not a data model document file");
514+
}
515+
516+
var commentKey = f("%s_comment", file.fileRef);
517+
518+
if (!this.xcVersionGroupSection()[file.fileRef]) {
519+
this.xcVersionGroupSection()[file.fileRef] = {
520+
isa: 'XCVersionGroup',
521+
children: file.models.map(function (el) { return el.fileRef; }),
522+
currentVersion: file.currentModel.fileRef,
523+
name: path.basename(file.path),
524+
path: file.path,
525+
sourceTree: '"<group>"',
526+
versionGroupType: 'wrapper.xcdatamodel'
527+
};
528+
this.xcVersionGroupSection()[commentKey] = path.basename(file.path);
529+
}
530+
}
531+
510532
pbxProject.prototype.addToPluginsPbxGroup = function(file) {
511533
var pluginsGroup = this.pbxGroupByName('Plugins');
512534
pluginsGroup.children.push(pbxGroupChild(file));
@@ -805,6 +827,14 @@ pbxProject.prototype.pbxNativeTargetSection = function() {
805827
return this.hash.project.objects['PBXNativeTarget'];
806828
}
807829

830+
pbxProject.prototype.xcVersionGroupSection = function () {
831+
if (typeof this.hash.project.objects['XCVersionGroup'] !== 'object') {
832+
this.hash.project.objects['XCVersionGroup'] = {};
833+
}
834+
835+
return this.hash.project.objects['XCVersionGroup'];
836+
}
837+
808838
pbxProject.prototype.pbxXCConfigurationList = function() {
809839
return this.hash.project.objects['XCConfigurationList'];
810840
}
@@ -830,6 +860,22 @@ pbxProject.prototype.pbxTargetByName = function(name) {
830860
return this.pbxItemByComment(name, 'PBXNativeTarget');
831861
}
832862

863+
pbxProject.prototype.findTargetKey = function(name) {
864+
var targets = this.hash.project.objects['PBXNativeTarget'];
865+
866+
for (var key in targets) {
867+
// only look for comments
868+
if (COMMENT_KEY.test(key)) continue;
869+
870+
var target = targets[key];
871+
if (target.name === name) {
872+
return key;
873+
}
874+
}
875+
876+
return null;
877+
}
878+
833879
pbxProject.prototype.pbxItemByComment = function(name, pbxSectionName) {
834880
var section = this.hash.project.objects[pbxSectionName],
835881
key, itemKey;
@@ -1704,5 +1750,61 @@ pbxProject.prototype.getBuildConfigByName = function(name) {
17041750
return target;
17051751
}
17061752

1753+
pbxProject.prototype.addDataModelDocument = function(filePath, group, opt) {
1754+
if (!group) {
1755+
group = 'Resources';
1756+
}
1757+
if (!this.getPBXGroupByKey(group)) {
1758+
group = this.findPBXGroupKey({ name: group });
1759+
}
1760+
1761+
var file = new pbxFile(filePath, opt);
1762+
1763+
if (!file || this.hasFile(file.path)) return null;
1764+
1765+
file.fileRef = this.generateUuid();
1766+
this.addToPbxGroup(file, group);
1767+
1768+
if (!file) return false;
1769+
1770+
file.target = opt ? opt.target : undefined;
1771+
file.uuid = this.generateUuid();
1772+
1773+
this.addToPbxBuildFileSection(file);
1774+
this.addToPbxSourcesBuildPhase(file);
1775+
1776+
file.models = [];
1777+
var currentVersionName;
1778+
var modelFiles = fs.readdirSync(file.path);
1779+
for (var index in modelFiles) {
1780+
var modelFileName = modelFiles[index];
1781+
var modelFilePath = path.join(filePath, modelFileName);
1782+
1783+
if (modelFileName == '.xccurrentversion') {
1784+
currentVersionName = plist.readFileSync(modelFilePath)._XCCurrentVersionName;
1785+
continue;
1786+
}
1787+
1788+
var modelFile = new pbxFile(modelFilePath);
1789+
modelFile.fileRef = this.generateUuid();
1790+
1791+
this.addToPbxFileReferenceSection(modelFile);
1792+
1793+
file.models.push(modelFile);
1794+
1795+
if (currentVersionName && currentVersionName === modelFileName) {
1796+
file.currentModel = modelFile;
1797+
}
1798+
}
1799+
1800+
if (!file.currentModel) {
1801+
file.currentModel = file.models[0];
1802+
}
1803+
1804+
this.addToXcVersionGroupSection(file);
1805+
1806+
return file;
1807+
}
1808+
17071809

17081810
module.exports = pbxProject;

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@
33
"name": "xcode",
44
"description": "parser for xcodeproj/project.pbxproj files",
55
"version": "0.8.2",
6-
"main":"index.js",
6+
"main": "index.js",
77
"repository": {
88
"url": "https://github.com/alunny/node-xcode.git"
99
},
1010
"engines": {
1111
"node": ">=0.6.7"
1212
},
1313
"dependencies": {
14+
"node-uuid":"1.3.3",
1415
"pegjs":"0.6.2",
15-
"node-uuid":"1.3.3"
16+
"simple-plist": "0.0.4"
1617
},
1718
"devDependencies": {
1819
"nodeunit":"0.9.0"

test/dataModelDocument.js

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
var jsonProject = require('./fixtures/full-project')
2+
fullProjectStr = JSON.stringify(jsonProject),
3+
path = require('path'),
4+
pbx = require('../lib/pbxProject'),
5+
pbxFile = require('../lib/pbxFile'),
6+
proj = new pbx('.'),
7+
singleDataModelFilePath = __dirname + '/fixtures/single-data-model.xcdatamodeld',
8+
multipleDataModelFilePath = __dirname + '/fixtures/multiple-data-model.xcdatamodeld';
9+
10+
function cleanHash() {
11+
return JSON.parse(fullProjectStr);
12+
}
13+
14+
exports.setUp = function (callback) {
15+
proj.hash = cleanHash();
16+
callback();
17+
}
18+
19+
exports.dataModelDocument = {
20+
'should return a pbxFile': function (test) {
21+
var newFile = proj.addDataModelDocument(singleDataModelFilePath);
22+
23+
test.equal(newFile.constructor, pbxFile);
24+
test.done()
25+
},
26+
'should set a uuid on the pbxFile': function (test) {
27+
var newFile = proj.addDataModelDocument(singleDataModelFilePath);
28+
29+
test.ok(newFile.uuid);
30+
test.done()
31+
},
32+
'should set a fileRef on the pbxFile': function (test) {
33+
var newFile = proj.addDataModelDocument(singleDataModelFilePath);
34+
35+
test.ok(newFile.fileRef);
36+
test.done()
37+
},
38+
'should set an optional target on the pbxFile': function (test) {
39+
var newFile = proj.addDataModelDocument(singleDataModelFilePath, undefined, { target: target }),
40+
target = proj.findTargetKey('TestApp');
41+
42+
test.equal(newFile.target, target);
43+
test.done()
44+
},
45+
'should populate the PBXBuildFile section with 2 fields': function (test) {
46+
var newFile = proj.addDataModelDocument(singleDataModelFilePath),
47+
buildFileSection = proj.pbxBuildFileSection(),
48+
bfsLength = Object.keys(buildFileSection).length;
49+
50+
test.equal(59 + 1, bfsLength);
51+
test.ok(buildFileSection[newFile.uuid]);
52+
test.ok(buildFileSection[newFile.uuid + '_comment']);
53+
54+
test.done();
55+
},
56+
'should populate the PBXFileReference section with 2 fields for single model document': function (test) {
57+
var newFile = proj.addDataModelDocument(singleDataModelFilePath),
58+
fileRefSection = proj.pbxFileReferenceSection(),
59+
frsLength = Object.keys(fileRefSection).length;
60+
61+
test.equal(66 + 2, frsLength);
62+
test.ok(fileRefSection[newFile.models[0].fileRef]);
63+
test.ok(fileRefSection[newFile.models[0].fileRef + '_comment']);
64+
65+
test.done();
66+
},
67+
'should populate the PBXFileReference section with 2 fields for each model of a model document': function (test) {
68+
var newFile = proj.addDataModelDocument(multipleDataModelFilePath),
69+
fileRefSection = proj.pbxFileReferenceSection(),
70+
frsLength = Object.keys(fileRefSection).length;
71+
72+
test.equal(66 + 2 * 2, frsLength);
73+
test.ok(fileRefSection[newFile.models[0].fileRef]);
74+
test.ok(fileRefSection[newFile.models[0].fileRef + '_comment']);
75+
test.ok(fileRefSection[newFile.models[1].fileRef]);
76+
test.ok(fileRefSection[newFile.models[1].fileRef + '_comment']);
77+
78+
test.done();
79+
},
80+
'should add to resources group by default': function (test) {
81+
var newFile = proj.addDataModelDocument(singleDataModelFilePath);
82+
groupChildren = proj.pbxGroupByName('Resources').children,
83+
found = false;
84+
85+
for (var index in groupChildren) {
86+
if (groupChildren[index].comment === 'single-data-model.xcdatamodeld') {
87+
found = true;
88+
break;
89+
}
90+
}
91+
test.ok(found);
92+
test.done();
93+
},
94+
'should add to group specified by key': function (test) {
95+
var group = 'Frameworks',
96+
newFile = proj.addDataModelDocument(singleDataModelFilePath, proj.findPBXGroupKey({ name: group }));
97+
groupChildren = proj.pbxGroupByName(group).children;
98+
99+
var found = false;
100+
for (var index in groupChildren) {
101+
if (groupChildren[index].comment === path.basename(singleDataModelFilePath)) {
102+
found = true;
103+
break;
104+
}
105+
}
106+
test.ok(found);
107+
test.done();
108+
},
109+
'should add to group specified by name': function (test) {
110+
var group = 'Frameworks',
111+
newFile = proj.addDataModelDocument(singleDataModelFilePath, group);
112+
groupChildren = proj.pbxGroupByName(group).children;
113+
114+
var found = false;
115+
for (var index in groupChildren) {
116+
if (groupChildren[index].comment === path.basename(singleDataModelFilePath)) {
117+
found = true;
118+
break;
119+
}
120+
}
121+
test.ok(found);
122+
test.done();
123+
},
124+
'should add to the PBXSourcesBuildPhase': function (test) {
125+
var newFile = proj.addDataModelDocument(singleDataModelFilePath),
126+
sources = proj.pbxSourcesBuildPhaseObj();
127+
128+
test.equal(sources.files.length, 2 + 1);
129+
test.done();
130+
},
131+
'should create a XCVersionGroup section': function (test) {
132+
var newFile = proj.addDataModelDocument(singleDataModelFilePath),
133+
xcVersionGroupSection = proj.xcVersionGroupSection();
134+
135+
test.ok(xcVersionGroupSection[newFile.fileRef]);
136+
test.done();
137+
},
138+
'should populate the XCVersionGroup comment correctly': function (test) {
139+
var newFile = proj.addDataModelDocument(singleDataModelFilePath),
140+
xcVersionGroupSection = proj.xcVersionGroupSection(),
141+
commentKey = newFile.fileRef + '_comment';
142+
143+
test.equal(xcVersionGroupSection[commentKey], path.basename(singleDataModelFilePath));
144+
test.done();
145+
},
146+
'should add the XCVersionGroup object correctly': function (test) {
147+
var newFile = proj.addDataModelDocument(singleDataModelFilePath),
148+
xcVersionGroupSection = proj.xcVersionGroupSection(),
149+
xcVersionGroupEntry = xcVersionGroupSection[newFile.fileRef];
150+
151+
test.equal(xcVersionGroupEntry.isa, 'XCVersionGroup');
152+
test.equal(xcVersionGroupEntry.children[0], newFile.models[0].fileRef);
153+
test.equal(xcVersionGroupEntry.currentVersion, newFile.currentModel.fileRef);
154+
test.equal(xcVersionGroupEntry.name, path.basename(singleDataModelFilePath));
155+
test.equal(xcVersionGroupEntry.path, singleDataModelFilePath);
156+
test.equal(xcVersionGroupEntry.sourceTree, '"<group>"');
157+
test.equal(xcVersionGroupEntry.versionGroupType, 'wrapper.xcdatamodel');
158+
159+
test.done();
160+
}
161+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>_XCCurrentVersionName</key>
6+
<string>2.xcdatamodel</string>
7+
</dict>
8+
</plist>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="9057" systemVersion="15B42" minimumToolsVersion="Xcode 7.0">
3+
<elements/>
4+
</model>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="9057" systemVersion="15B42" minimumToolsVersion="Xcode 7.0">
3+
<elements/>
4+
</model>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="9057" systemVersion="15B42" minimumToolsVersion="Xcode 7.0">
3+
<elements/>
4+
</model>

0 commit comments

Comments
 (0)