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

Feature/worker #3

Merged
merged 7 commits into from
Nov 19, 2016
Merged
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
80 changes: 8 additions & 72 deletions app/controllers/generator.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import Ember from 'ember';
import { task, timeout } from 'ember-concurrency';
import fileSaver from 'npm:file-saver';
import faker from 'npm:faker';
import PapaParse from 'npm:papaparse';

const {
computed
computed,
inject: { service }
} = Ember;

export default Ember.Controller.extend({
interface: service('generation-interface'),

queryParams: ['activeGeneratorId'],
activeGeneratorId: null,

Expand All @@ -17,56 +17,6 @@ export default Ember.Controller.extend({
yield generator.save();
}).enqueue(),

generateCSVTask: task(function *(rows) {
let csvArray = yield this.get('_generateCSVStringTask').perform(rows);
let csvString = PapaParse.unparse(csvArray);
this._saveCSV(csvString);
}).drop(),

/*
Saves csvString
@params { String }
*/
_saveCSV(csvString) {
let blob = new Blob([csvString], {
type: "text/csv; charset=utf-8;"
});
fileSaver.saveAs(blob, 'data.csv');
},

/*
Chunks in csv string
@params { Number }
@returns { Promise }
*/
_generateCSVStringTask: task(function *(rows) {
let headers = this.get('headers');
let array = [headers];

// Stores functions in array to avoid lookups in loop
let generatorFuncs = this.get('generatorFuncs');

let generatorFuncsLength = generatorFuncs.length;

for (let i = rows; i--;) {
if (i % 100 === 0) {
yield timeout(20);
}

let row = [];
for (let i = 0; i < generatorFuncsLength; i++) {
row[i] = generatorFuncs[i]();
}
array.push(row);
}

return array;
}),

// CP's

generators: computed.alias('model'),

/*
Returns generator from queryParams
@returns { DS.Model }
Expand All @@ -77,22 +27,6 @@ export default Ember.Controller.extend({
}
}),

/*
Returns faker methods from generator paths
@returns { Array }
*/
generatorFuncs: computed('generators.[]', {
get() {
return this.get('generators')
.map(g =>{
let path = g.get('fakerPath').split('.');
return faker[path[0]][path[1]];
});
}
}),

headers: computed.mapBy('generators.[]', 'name'),

actions: {
addGenerator() {
let genCount = this.get('model.length') + 1;
Expand All @@ -105,8 +39,10 @@ export default Ember.Controller.extend({
generator.destroyRecord();
},

generateCSV(rows) {
this.generateCSV(rows);
generate() {
let generators = this.get('model');
let rows = this.get('rowCount');
this.get('interface.generateTask').perform(generators, rows, true);
}
}
});
158 changes: 158 additions & 0 deletions app/services/generation-interface.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/* global Worker */

import Ember from 'ember';
import faker from 'npm:faker';
import Papa from 'npm:papaparse';
import fileSaver from 'npm:file-saver';
import { task, timeout } from 'ember-concurrency';

const {
computed,
RSVP
} = Ember;

const SHOULD_USE_WORKERS_NUMBER = 500;

export default Ember.Service.extend({

/**
* Starts generation of mock data csvString
* @param { Array } models
* @param { Number } rows
* @param { boolean } shouldIncludeHeaders
* @return { TaskInstance }
*/
generateTask: task(function* (models, rows, shouldIncludeHeaders) {
// Create Session Model
this.setProperties({
generators: models,
rows: rows,
shouldIncludeHeaders: shouldIncludeHeaders
});

let stringTask;
if (this.get('shouldUseWorker')) {
stringTask = this.get('_workerTask').perform();
} else {
stringTask = this.get('_chunkTask').perform();
}
let csvString = yield stringTask;

this._saveCSV(csvString);
}).drop(),

/*
Saves csvString
@params { String }
*/
_saveCSV(csvString) {
let blob = new Blob([csvString], {
type: "text/csv; charset=utf-8;"
});
fileSaver.saveAs(blob, 'data.csv');
},

/**
*
*/
_chunkTask: task(function *() {
let array = [];
if (this.get('shouldIncludeHeaders')) {
let headers = this.get('headers');
array = [headers];
}

// Stores functions in array to avoid lookups in loop
let generatorFuncs = this.get('generatorFuncs');
let rows = this.get('rows');
let generatorFuncsLength = generatorFuncs.length;

for (let i = rows; i--;) {
if (i % 100 === 0) {
yield timeout(20);
}

let row = [];
for (let i = 0; i < generatorFuncsLength; i++) {
row[i] = generatorFuncs[i]();
}
array.push(row);
}

return Papa.unparse(array);
}),

/**
*
*/
_workerTask: task(function *() {
let data = {
paths: this.get('generatorPaths'),
rows: this.get('rows')
};

if (this.get('shouldIncludeHeaders')) {
data.headers = this.get('headers');
}

let worker = this._createWorker();
worker.postMessage(data);

let workerPromise = new RSVP.Promise((resolve, reject) =>{
worker.addEventListener('message', ({ data }) => resolve(data));
worker.addEventListener('error', (e) => reject(e));
});

return yield workerPromise;
}),

/**
* Creates gen-data worker
* @return { Worker }
*/
_createWorker() {
return new Worker('/assets/gen-data.js');
},

// CPS
headers: computed.mapBy('generators.[]', 'name'),

/**
*Returns faker methods from generator paths
* @type { Array }
*/
generatorFuncs: computed('generatorPaths.[]', {
get() {
return this.get('generatorPaths').map(path =>{
return faker[path[0]][path[1]];
});
}
}),

/**
* Paths for generators
* @type { Array }
*/
generatorPaths: computed('generators.[]', {
get() {
return this.get('generators').map(g =>{
return g.get('fakerPath').split('.');
});
}
}),

/**
* Decides whether or not to use a worker
* @type { Boolean }
*/
shouldUseWorker: computed('rows', {
get() {
let rows = this.get('rows');
if (Worker) {
return (rows > SHOULD_USE_WORKERS_NUMBER) ? true : false;
} else {
return false;
}
}
})
});
4 changes: 2 additions & 2 deletions app/templates/generator.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
<div class="column">
<p class="control has-addons">
{{input type="number" value=rowCount}}
<a class="button is-primary {{if generateCSVTask.isRunning 'is-loading'}}"
{{action (perform generateCSVTask rowCount)}}
<a class="button is-primary {{if interface.generateTask.isRunning 'is-loading'}}"
{{action 'generate'}}
>
Generate
</a>
Expand Down
7 changes: 7 additions & 0 deletions ember-cli-build.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ var EmberApp = require('ember-cli/lib/broccoli/ember-app');

module.exports = function(defaults) {
var app = new EmberApp(defaults, {
nodeModulesToVendor: [
'node_modules/faker/build/build/locales/en',
'node_modules/papaparse'
]
// Add options here
});

Expand All @@ -21,6 +25,9 @@ module.exports = function(defaults) {
// along with the exports of each module as its value.

app.import('bower_components/bulma/css/bulma.css');
app.import('vendor/workers/gen-data.js', { outputFile: '/assets/gen-data.js'});
app.import('vendor/papaparse.min.js', { outputFile: '/assets/papaparse.js' });
app.import('vendor/faker.en.js', { outputFile: '/assets/faker.js'});

return app.toTree();
};
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"ember-cli-htmlbars-inline-precompile": "^0.3.3",
"ember-cli-inject-live-reload": "^1.4.1",
"ember-cli-jshint": "^1.0.4",
"ember-cli-node-modules-to-vendor": "0.1.1",
"ember-cli-page-object": "1.6.0",
"ember-cli-qunit": "^3.0.1",
"ember-cli-release": "^0.2.9",
Expand Down
Loading