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

Attachments transform #135

Closed
36 changes: 36 additions & 0 deletions README.md
Expand Up @@ -180,7 +180,43 @@ export default Ember.Route.extend({
}
}
});
```

## Attachments

`Ember-Pouch` provides an `attachment` transform for your models, which makes working with attachments as simple as working with any other field.

Add a `DS.attr('attachment')` field to your model. Provide a default value for it to be an empty array.

```js
// myapp/models/photo-album.js
export default DS.Model.extend({
photos: DS.attr('attachment', {
defaultValue: function() {
return [];
}
});
});
```

Here, instances of `PhotoAlbum` have a `photos` field, which is an array of plain `Ember.Object`s, which have a `.name` and `.content_type`. Non-stubbed attachment also have a `.data` field; and stubbed attachments have a `.stub` instead.
```handlebars
<ul>
{{#each myalbum.photos as |photo|}}
<li>{{photo.name}}</li>
{{/each}}
</ul>
```

Attach new files by adding an `Ember.Object` with a `.name`, `.content_type` and `.data` to array of attachments.

```js
// somewhere in your controller/component:
myAlbum.get('photos').addObject(Ember.Object.create({
'name': 'kitten.jpg',
'content_type': 'image/jpg',
'data': btoa('hello world') // base64-encoded `String`, or a DOM `Blob`, or a `File`
}));
```

## Sample app
Expand Down
41 changes: 41 additions & 0 deletions addon/transforms/attachment.js
@@ -0,0 +1,41 @@
import Ember from 'ember';
import DS from 'ember-data';

const { isNone } = Ember;
const keys = Object.keys || Ember.keys;

export default DS.Transform.extend({
deserialize: function(serialized) {
if (isNone(serialized)) { return []; }

return keys(serialized).map(function (attachmentName) {
return Ember.Object.create({
name: attachmentName,
content_type: serialized[attachmentName]['content_type'],
data: serialized[attachmentName]['data'],
stub: serialized[attachmentName]['stub'],
length: serialized[attachmentName]['length'],
digest: serialized[attachmentName]['digest']
});
});
},

serialize: function(deserialized) {
if (!Ember.isArray(deserialized)) { return null; }

return deserialized.reduce(function (acc, attachment) {
const serialized = {
content_type: attachment.get('content_type'),
};
if (attachment.get('stub')) {
serialized.stub = true;
serialized.length = attachment.get('length');
serialized.digest = attachment.get('digest');
} else {
serialized.data = attachment.get('data');
}
acc[attachment.get('name')] = serialized;
return acc;
}, {});
}
});
1 change: 1 addition & 0 deletions app/transforms/attachment.js
@@ -0,0 +1 @@
export { default } from 'ember-pouch/transforms/attachment';
74 changes: 74 additions & 0 deletions tests/unit/transforms/attachment-test.js
@@ -0,0 +1,74 @@
import { moduleFor, test } from 'ember-qunit';

import Ember from 'ember';

let testSerializedData = {
'hello.txt': {
content_type: 'text/plain',
data: 'aGVsbG8gd29ybGQ=',
digest: "md5-7mkg+nM0HN26sZkLN8KVSA=="
// CouchDB doesn't add 'length'
},
'stub.txt': {
stub: true,
content_type: 'text/plain',
digest: "md5-7mkg+nM0HN26sZkLN8KVSA==",
length: 11
},
};

let testDeserializedData = [
Ember.Object.create({
name: 'hello.txt',
content_type: 'text/plain',
data: 'aGVsbG8gd29ybGQ=',
digest: 'md5-7mkg+nM0HN26sZkLN8KVSA=='
}),
Ember.Object.create({
name: 'stub.txt',
content_type: 'text/plain',
stub: true,
digest: 'md5-7mkg+nM0HN26sZkLN8KVSA==',
length: 11
})
];

moduleFor('transform:attachment', 'Unit | Transform | attachment', {});

test('it serializes an attachment', function(assert) {
let transform = this.subject();
assert.equal(transform.serialize(null), null);
assert.equal(transform.serialize(undefined), null);
assert.deepEqual(transform.serialize([]), {});

let serializedData = transform.serialize(testDeserializedData);

let hello = testDeserializedData[0].get('name');
assert.equal(hello, 'hello.txt');
assert.equal(serializedData[hello].content_type, testSerializedData[hello].content_type);
assert.equal(serializedData[hello].data, testSerializedData[hello].data);

let stub = testDeserializedData[1].get('name');
assert.equal(stub, 'stub.txt');
assert.equal(serializedData[stub].content_type, testSerializedData[stub].content_type);
assert.equal(serializedData[stub].stub, true);
});

test('it deserializes an attachment', function(assert) {
let transform = this.subject();
assert.deepEqual(transform.deserialize(null), []);
assert.deepEqual(transform.deserialize(undefined), []);

let deserializedData = transform.deserialize(testSerializedData);

assert.equal(deserializedData[0].get('name'), testDeserializedData[0].get('name'));
assert.equal(deserializedData[0].get('content_type'), testDeserializedData[0].get('content_type'));
assert.equal(deserializedData[0].get('data'), testDeserializedData[0].get('data'));
assert.equal(deserializedData[0].get('digest'), testDeserializedData[0].get('digest'));

assert.equal(deserializedData[1].get('name'), testDeserializedData[1].get('name'));
assert.equal(deserializedData[1].get('content_type'), testDeserializedData[1].get('content_type'));
assert.equal(deserializedData[1].get('stub'), true);
assert.equal(deserializedData[1].get('digest'), testDeserializedData[1].get('digest'));
assert.equal(deserializedData[1].get('length'), testDeserializedData[1].get('length'));
});