It probably goes without saying that most apps need an initial dataset in order to do something useful. Typically you'll seed your databse with some combination of randomly generated and hard-coded documents. In this post we'll be investigating the latter by demonstrating how your app can read text files. In the examples we'll use JSON files, however the same technique can apply to any text-based format like CSV, XML, etc. Later on, we'll learn how to share those same files between projects using a package.
Let's suppose you're building a Doctor Who themed app, and you need a collection of companions. Each companion's information is contained in a separate JSON file. For simplicity, we'll only deal with two companions: Martha and Rose.
private/companions/martha.json
{
"name": "Martha Jones",
"actress": "Freema Agyeman",
"firstEpisode": "Smith and Jones"
}
private/companions/rose.json
{
"name": "Rose Tyler",
"actress": "Billie Piper",
"firstEpisode": "Rose"
}
Note that files under the
private
directory will not be served to the client.
Now that we have our JSON files, we can use the Assets API to read and parse their content:
server/main.js
Meteor.startup(function() {
var rose = JSON.parse(Assets.getText('companions/rose.json'));
var martha = JSON.parse(Assets.getText('companions/martha.json'));
var companions = [rose, martha];
_.each(companions, function(companion) {
// replace this with something like Companions.insert(companion);
console.log(companion);
});
});
Congratulations - you now know how to read external files to populate your database. Best of all, we didn't need to jump through any ridiculous hoops to determine the file paths.
Next let's suppose you have other projects which also need to know about the Doctor's friends. Clearly we don't want to copy the files into every app. Fortunately, we can solve this by adding a package.
Create a new local package with:
$ meteor create --package doctor
Move your previous JSON files into the doctor
package and rearrange the directory to look like the following:
doctor/
├── companions
│ ├── martha.json
│ └── rose.json
├── doctor.js
└── package.js
package.js
Package.describe({
summary: 'The Doctor.'
});
Package.onUse(function(api) {
// use the new packaging system
api.versionsFrom('1.0');
// all assets will be exported through this global variable
api.export('Doctor', 'server');
// add the files without modification
api.addAssets('companions/rose.json', 'server');
api.addAssets('companions/martha.json', 'server');
// doctor.js will load the JSON files for us using Assets.getText
api.addFiles('doctor.js', 'server');
});
doctor.js
// read and parse the JSON files just as we did before
var rose = JSON.parse(Assets.getText('companions/rose.json'));
var martha = JSON.parse(Assets.getText('companions/martha.json'));
// export the companions via the global variable Doctor
Doctor = {companions: [rose, martha]};
Here are the important points:
Assets.getText
can be called from within a package as long as the assets are exposed viaaddFiles
.- After reading and parsing the text,
companions
is added to a global variable calledDoctor
which is then exported. - The JSON files are added with
api.addAssets
so they won't be transformed.
Next we need to add the package to our app:
$ meteor add doctor
Finally, we can replace the original startup code with this simplified version:
server/main.js
Meteor.startup(function() {
_.each(Doctor.companions, function(companion) {
console.log(companion);
});
});
That's it! Now you can use this approach to factor text assets out of your codebase and reuse them in other projects.
Special thanks to ekate who showed me the necessary packaging techniques at the last SF devshop.
People assume that time is a strict progression of cause to effect, but actually from a non-linear, non-subjective viewpoint, it's more like a big ball of wibbly-wobbly, timey-wimey stuff. - The Doctor