Book Manager
Application to manage eBooks.
Motivation
After having bought several books, I saw the need to be able to manage and synchronize them, because it always took me too much time to do this task, by copying the relevant files manually.
Capabilities
- Import books from directory or listing (HTML dump)
- Edit book information
- Gather book information from meta data
- Support for multiple storages
- Sync to device
The implementation started as a web application, but with the possibility in mind to create an Electron app.
this.add = function(item) {
if (!(item instanceof QueueItem)) {
throw new TypeError('Item must be an instance of QueueItem')
}
this.items.push(item)
if (this.state === 'idle') {
this.processNext();
}
};
Command queue.
Technologies
- Node, Express
- Bower, Grunt, Mocha, Istanbul
- Backbone, Underscore, Lodash
- Browserify, Less, Uglify, JSHint
- jQuery, Handlebars, Moment, Bootstrap, Font Awesome
Highlights
- Usage of JS modules and other ES2016 capabilities
- Code coverage with Istanbul
- Usage of Streams, Promises
- Acceptance tests with 'jasmine-jquery'
- Backend tests with 'supertest'
Transformer test in Sublime

Mocha/Istanbul test run in Terminal

this.handle = function(item) {
if (!(item instanceof QueueItem)) {
throw new TypeError('Item must be instance of QueueItem, but "' + (typeof item) + '" was given')
}
const handleName = 'handle' + item.__proto__.constructor.name;
if (typeof handlers[handleName] === 'function') {
return handlers[handleName](item)
}
else {
return Q.fcall(function() {
throw new Error('No handler found to handle item of type ' + item.__proto__.constructor.name)
})
}
};
Queue handler.
it('should process items with handler', function() {
const tracker = {
fn: function() {}
}
const handler = {
handle: function(item) {
tracker.fn(item)
return Q.fcall(function() {})
}
}
const spy = expect.spyOn(tracker, 'fn')
const queue = new Queue({handler: handler});
const item = new JobQueueItem({
type: 'folder_present',
target: 'dummy'
});
queue.add(item);
expect(spy).toHaveBeenCalledWith(item)
});
Queue handler test.
const BooksListPage = {
components: {
BooksTable: BooksTable,
BooksCards: BooksCards,
BooksFilter: BooksFilter,
BooksDisplay: BooksDisplay
},
data: function() {
return {
display: 'table'
}
},
template: `
<section>
<h1>Books</h1>
<books-filter></books-filter>
<books-display></books-display>
<div class="btn-group" data-toggle="buttons">
<label class="btn btn-default active" @click="display = 'table'">
<input type="radio" name="display"><i class="fa fa-th-list"></i>
</label>
<label class="btn btn-default" @click="display = 'cards'">
<input type="radio" name="display"><i class="fa fa-th-large"></i>
</label>
</div>
<component :is="booksComponent" :books="$store.state.books"></component>
</section>
`,
computed: {
booksComponent: function() {
if (this.display == 'cards') {
return BooksCards
}
return BooksTable
}
}
}
Page component to manage rendering of books (`BooksListPage`). The book listing can be displayed in either table view or card view, using different components for the layout.
Layout
Using Gravit to work on the layout of the application.
Pages

Imports Page
