Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Store image meta data in arrays and adds unit tests #43

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ lib-cov
*.out
*.pid
*.gz
*.iml

pids
logs
results

node_modules
coverage
57 changes: 57 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,63 @@ PostSchema.plugin(attachments, {
var Post = mongoose.model('Post', PostSchema);
```

#### Arrays of attachments

To store attachment metadata in arrays, set the `array` flag in the plugin definition to `true`:

```javascript
var mongoose = require('mongoose');
var attachments = require('mongoose-attachments-localfs');

var PostSchema = new mongoose.Schema({
title: String,
description: String
});

PostSchema.plugin(attachments, {
directory: 'achievements',
storage: {
providerName: 'localfs'
},
properties: {
images: {
array: true,
styles: {
small: {
resize: '150x150'
},
medium: {
resize: '120x120'
}
}
}
}
});

var Post = mongoose.model('Post', PostSchema);
```

..then later:

```javascript
var post = new Post();
// post.images.length == 0

// attach an image
post.attach('images', {
path: '/path/to/file'
}, function(error) {
// post.images.length == 1

// attach another image
post.attach('images', {
path: '/path/to/another/file'
}, function(error) {
// post.images.length == 2
});
});
```

#### Using with Express.js uploads

Assuming that the HTML form sent a file in a field called 'image':
Expand Down
33 changes: 30 additions & 3 deletions lib/attachments.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ var fs = require('fs');
var path = require('path');
var async = require('async');
var existsFn = fs.exists || path.exists;
var os = require('os');

// keeps a global registry of storage providers
var providersRegistry = {};
Expand Down Expand Up @@ -90,7 +91,7 @@ function findImageMagickFormats(options, callback) {

var plugin = function(schema, options) {
options = options || {};
if(typeof(options.directory) !== 'string') throw new Error('option "directory" is required');
if(typeof(options.directory) !== 'string') options.directory = os.tmpdir();
if(typeof(options.properties) !== 'object') throw new Error('option "properties" is required');
if(typeof(options.storage) !== 'object') throw new Error('option "storage" is required');
if(typeof(options.idAsDirectory) !== 'boolean') options.idAsDirectory = false;
Expand All @@ -115,6 +116,7 @@ var plugin = function(schema, options) {
if(!propertyOptions) throw new Error('property "' + propertyName + '" requires an specification');

var styles = propertyOptions.styles || {};
var isArray = !!propertyOptions.array;
var styleNames = Object.keys(styles);
if(styleNames.length == 0) throw new Error('property "' + propertyName + '" needs to define at least one style');

Expand All @@ -137,6 +139,11 @@ var plugin = function(schema, options) {
};
});

if(isArray) {
// wrap the object literal in an array
addOp[propertyName] = [addOp[propertyName]];
}

// Add the Property
schema.add(addOp);
}); // for each property name
Expand All @@ -158,6 +165,12 @@ var plugin = function(schema, options) {
// No original name provided? We infer it from the path
attachmentInfo.name = path.basename(attachmentInfo.path);
}

if(propertyOptions.array) {
var propModel = selfModel[propertyName];
var arrayEntryModel = propModel.create();
}

existsFn(attachmentInfo.path, function(exists) {
if(!exists) return cb(new Error('file to attach at path "' + attachmentInfo.path + '" does not exists'));
fs.stat(attachmentInfo.path, function(err, stats) {
Expand All @@ -178,7 +191,12 @@ var plugin = function(schema, options) {
var finishConversion = function(styleFilePath, atts, cb) {
var ext = path.extname(styleFilePath);
var filenameId = options.filenameId ? selfModel[options.filenameId] : selfModel.id;
var storageStylePath = '/' + options.directory + '/' + [filenameId,styleName].join( options.idAsDirectory ? "/":"-") + ext;

if(propertyOptions.array) {
filenameId = options.filenameId ? arrayEntryModel[options.filenameId] : arrayEntryModel.id;
}

var storageStylePath = path.join(options.directory, [filenameId, propertyName + "-" + styleName].join(options.idAsDirectory ? "/" : "-") + ext);

fs.stat(styleFilePath, function(err, stats) {
if(err) return cb(err);
Expand Down Expand Up @@ -209,7 +227,7 @@ var plugin = function(schema, options) {
var styleFileExt = styleOptions['$format'] ? ('.' + styleOptions['$format']) : fileExt;
var styleFileName = path.basename(attachmentInfo.path, fileExt);
styleFileName += '-' + styleName + styleFileExt;
var styleFilePath = path.join(path.dirname(attachmentInfo.path), styleFileName);
var styleFilePath = path.join(path.dirname(options.directory), styleFileName);
var convertArgs = [attachmentInfo.path]; // source file name

// add all the transformations args
Expand Down Expand Up @@ -274,6 +292,11 @@ var plugin = function(schema, options) {

// Finally Update the Model
var propModel = selfModel[propertyName];

if(propertyOptions.array) {
propModel = arrayEntryModel;
}

if(storageResults.length > 0) { // only update the model if a transformation was performed.
storageResults.forEach(function(styleStorage) {
var modelStyle = propModel[styleStorage.style.name];
Expand All @@ -291,6 +314,10 @@ var plugin = function(schema, options) {
modelStyle.dims.w = styleStorage.features.width;
}
});

if(propertyOptions.array) {
selfModel[propertyName].push(propModel);
}
}

stylesToReset.forEach(function(resetStyleName) {
Expand Down
12 changes: 9 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,25 @@
"description": "Mongoose.js Attachments plugin. Supports ImageMagick styles",
"keywords": ["mongoose", "s3", "imagemagick", "uploads", "attachments"],
"version": "0.1.0",
"homepage": "https://github.com/firebaseco/mongoose-attachments",
"homepage": "https://github.com/heapsource/mongoose-attachments",
"repository": {
"type": "git",
"url": "git://github.com/firebaseco/mongoose-attachments.git"
"url": "git://github.com/heapsource/mongoose-attachments.git"
},
"main": "index.js",
"scripts": {
"test": "make test"
"test": "istanbul cover ./node_modules/mocha/bin/_mocha"
},
"dependencies": {
"async": "0.1.x",
"imagemagick": "0.1.x"
},
"devDependencies": {
"mocha": "^1.18",
"should": "^3.2",
"mongoose": "^3.8",
"istanbul": "^0.2"
},
"engines": {
"node": "*"
}
Expand Down
27 changes: 27 additions & 0 deletions test/fixtures/StubSchema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
var attachments = require('./StubStorageProvider');
var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var StubSchema = new Schema({
name: {
type: String,
required: true
}
});

StubSchema.plugin(attachments, {
storage: {
providerName: 'testProvider'
},
properties: {
image: {
styles: {
image: {
'$format': 'jpg'
}
}
}
}
});

module.exports = mongoose.model('Foo', StubSchema);
28 changes: 28 additions & 0 deletions test/fixtures/StubSchemaWithArrayProperty.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
var attachments = require('./StubStorageProvider');
var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var StubSchemaWithArrayProperty = new Schema({
name: {
type: String,
required: true
}
});

StubSchemaWithArrayProperty.plugin(attachments, {
storage: {
providerName: 'testProvider'
},
properties: {
images: {
array: true,
styles: {
image: {
'$format': 'jpg'
}
}
}
}
});

module.exports = mongoose.model('Bar', StubSchemaWithArrayProperty);
34 changes: 34 additions & 0 deletions test/fixtures/StubSchemaWithMultipleFields.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
var attachments = require('./StubStorageProvider');
var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var StubSchema = new Schema({
name: {
type: String,
required: true
}
});

StubSchema.plugin(attachments, {
storage: {
providerName: 'testProvider'
},
properties: {
image1: {
styles: {
image: {
'$format': 'jpg'
}
}
},
image2: {
styles: {
image: {
'$format': 'jpg'
}
}
}
}
});

module.exports = mongoose.model('Baz', StubSchema);
19 changes: 19 additions & 0 deletions test/fixtures/StubStorageProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
var attachments = require('../../lib/attachments'),
util = require('util');

function StubStorageProvider(options) {
attachments.StorageProvider.call(this, options);
}
util.inherits(StubStorageProvider, attachments.StorageProvider);

StubStorageProvider.prototype.createOrReplace = function(attachment, callback) {
callback(null, attachment);
};

StubStorageProvider.prototype.getUrl = function( path ){
return path;
};

attachments.registerStorageProvider('testProvider', StubStorageProvider);

module.exports = attachments;
Binary file added test/fixtures/node_js_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
82 changes: 82 additions & 0 deletions test/testAttachments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
var should = require('should');
var Foo = require('./fixtures/StubSchema');
var Bar = require('./fixtures/StubSchemaWithArrayProperty');
var Baz = require('./fixtures/StubSchemaWithMultipleFields');
var path = require('path');
var async = require('async');

describe('Attachments', function() {

it('should add an attachment', function(done) {
var foo = new Foo();
foo.attach('image', {
path: path.resolve(__dirname, "./fixtures/node_js_logo.png")
}, function(error) {
should(error).be.null;
foo.image.image.oname.should.equal("node_js_logo.png");

done();
});
})

it('should add an attachment to an array', function(done) {
var bar = new Bar();
bar.attach('images', {
path: path.resolve(__dirname, './fixtures/node_js_logo.png')
}, function(error) {
should(error).be.null;
Array.isArray(bar.images).should.be.true;
bar.images.length.should.equal(1);
bar.images[0].image.oname.should.equal('node_js_logo.png');

done();
});
})

it('should create unique names for multiple attachments', function(done) {
var bar = new Bar();

async.parallel([function(callback) {
bar.attach('images', {
path: path.resolve(__dirname, './fixtures/node_js_logo.png')
}, callback);
}, function(callback) {
bar.attach('images', {
path: path.resolve(__dirname, './fixtures/node_js_logo.png')
}, callback);
}], function(error) {
if(error) throw error;

bar.images.length.should.equal(2);
bar.images[0].image.path.should.not.equal(bar.images[1].image.path);

// should have used the sub-document id for the path name
path.basename(bar.images[0].image.path).should.startWith(bar.images[0].id);

// and not the containing document's id
path.basename(bar.images[0].image.path).should.not.startWith(bar.id);

done();
});
})

it('should allow multiple fields with the same style names', function(done) {
var baz = new Baz();

async.parallel([function(callback) {
baz.attach('image1', {
path: path.resolve(__dirname, './fixtures/node_js_logo.png')
}, callback);
}, function(callback) {
baz.attach('image2', {
path: path.resolve(__dirname, './fixtures/node_js_logo.png')
}, callback);
}], function(error) {
if(error) throw error;

baz.image1.image.path.should.not.equal(baz.image2.image.path);

done();
});
})
})
Loading