Skip to content
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
89 changes: 14 additions & 75 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,87 +1,26 @@
HW1
===

<h4>КиноБыстро.</h4>
Сначала

<h4>Что будем делать?</h4>

Предположим, что мы решили открыть startup продажи билетов в кино через мобильные телефоны.
<pre>
npm install
</pre>

<h4>Что мы имеем на руках:</h4>
Чтобы запустить тесты на ноде

<ul>
<li>Список кинотеатров + дополнительную информацию о каждом из них</li>
<li>Списко фильмов для каждого кинотетра + дополнительную информацию к каждому фильму.</li>
<li>О пользователе мы знаем координату, где он находится в данный момент.</li>
<li>О реальном мире мы знаем текущию дату и время.</li>
</ul>
<pre>
npm test
</pre>

<h4>Домашнее задание 1</h4>
Чтобы запустить тесты в браузере (PhantomJS)

<ol>
<li>
<div>Творческое</div>
Придумать модели, которые нужны для нашего сервиса.
<div>*. По сложности модели могут быть любыми.</div>
</li>
<li>
<div>Реализовать функции, которые создают ваши модели. Например</div>
<pre>
function createCircle(radius, position, options) {
options = options || {};
return {
radius: radius,
position: position,
color: options.color || "black"
};
}
</pre>
Совет: Не пишите функции, которые зависят более чем от 3-5 параметров <br/>
Cовет: В опциях должны лежать неважные параметры
</li>
<li>
<div>Советую написать небольшую "базу" кинотетров (2-3) и фильмов в них (3-5). Например, так:</div>
<pre>
var circles = [1, 2, 3, 4, 5].map(function (number) {
return createCircle(Math.abs(5 - number), {
x: number * number,
y: number + number,
color: number % 2 ? "black" + number : undefined
});
});
</pre>
</li>
<li>
<div>Реализовать некоторую сущность, которая умеет искать нужный фильм для пользователя</div>
<pre>
var manager = {};
<pre>
npm run karma
</pre>

manager.findByFilmName = function (film) {
/*бизнес лапша*/
return /*..*/;
}
Чтобы запустить то, что нельзя называть

manager.sortByUserPosition = function (film) {
/*бизнес лапша*/
return /*..*/;
}
</pre>
Мощность функционала зависит только от вас. <br/>
Совет. Было бы круто если было бы можно писать код в таком стиле:<br/>
<pre>
collection
.findByFilmName(name)
.sortByUserPosition()
.getTop(10);
npm run grunt-jslint
</pre>
</li>

<li>
<div>Тесты. А точнее, примеры использования.</div>
На следующем занятии я научу вас писать тесты:
<ul>
<li>Браузерные</li>
<li>Консольные</li>
</ul>
</li>
</ol>
29 changes: 29 additions & 0 deletions kino/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Logs
logs
*.log

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules

.idea
54 changes: 54 additions & 0 deletions kino/Gruntfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
'use strict';

module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-browserify');
grunt.loadNpmTasks('grunt-jslint');
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
browserify: {
dist: {
files: {
'public/build/bundle.js': ['public/src/**/*.js']
}
}
},
jslint: { // configure the task
// lint your project's server code
server: {
src: [
'**/*.js',
'!public/**/*.js',
'!node_modules/**/*.js'
],
directives: {
nomen: true,
node: true
},
options: {
errorsOnly: false,
failOnError: false
}
},
// lint your project's client code
client: {
src: [
'public/src/**/*.js'
],
directives: {
nomen: true,
browser: true,
node: true
},
options: {
errorsOnly: false,
failOnError: false
}
}
}
});


grunt.registerTask('default', ['jslint', 'browserify']);

};
90 changes: 90 additions & 0 deletions kino/benchmark.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
'use strict';

var BasicQuery = require("./cinema/query").BasicQuery,
_ = require("lodash");

function isEven(x) {
return x % 2 === 0;
}

function has123(x) {
return (/1|2|3/).test(x.toString());
}

function desc(x) {
return -x;
}

function minusCmp(x, y) {
return y - x;
}

function mod500moreThan200(x) {
return x % 500 >= 200;
}

var normalArrayMethodsVsImprovedFilters = {
funcA: function query() {
return new BasicQuery(_.range(300000))
.orderBy(desc)
.filter(has123)
.filter(isEven)
.filter(mod500moreThan200)
.top(30)
.toArray();
},
funcB: function arrayMethods() {
return _.range(300000)
.sort(function (x, y) {
if (x < y) {
return 1;
}
if (x > y) {
return -1;
}
return 0;
})
.filter(has123)
.filter(isEven)
.filter(mod500moreThan200)
.slice(0, 30);
},
count: 5
};

var benchmarks = [normalArrayMethodsVsImprovedFilters];

function runFunc(func, times) {
var res, start, end, i;
start = new Date();
for (i = 0; i < times; i += 1) {
res = func();
}
end = new Date();
return {
res: res,
time: end - start
};
}

function runBm(bm, times) {

var bmARes = runFunc(bm.funcA, times),
bmBRes = runFunc(bm.funcB, times);
if (!_.isEqual(bmARes.res, bmBRes.res)) {
console.log(bmARes);
console.log(bmBRes);
throw "They are not equal!";
}
return {
nameA: bm.funcA.name,
nameB: bm.funcB.name,
timeA: bmARes.time,
timeB: bmBRes.time
};
}

benchmarks.forEach(function (bm) {
var res = runBm(bm, bm.count);
console.log(res);
});
75 changes: 75 additions & 0 deletions kino/cinema/cinemaQuery.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
'use strict';

var _ = require("lodash"),
query = require("./query"),
dist = require('geo-distance-safe');
var maxDate = new Date(-8640000000000000);

function getSoonestFilmTime(cinema, filmId, date) {
var soonestFilmScheduleItem = cinema.timeTable.reduce(function (prevBest, item) {
if (item.filmId !== filmId || item.date < date) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

функция reduce хороша, но бывает читаемость кода из-за нее падает.
Может стоит сначала отфильтровать по id фильма, затем отсортировать по дате и взять первый?

return prevBest;
}
if (!prevBest) {
return item;
}
return item.date < prevBest.date ? item : prevBest;
}, null);
return soonestFilmScheduleItem ? soonestFilmScheduleItem.date : maxDate;
}

function nearnessOrderer(coords) {
return function (cinema) {
return dist.between(coords, cinema.coords);
};
}

function soonestFilmDateOrderer(filmId, date) {
return function (cinema) {
return getSoonestFilmTime(cinema, filmId, date);
};
}

function BaseCinemaQuery() {
query.BasicQuery.apply(this, arguments);
}
function OrdFilterCinemaQuery() {
query.OrdFilterQuery.apply(this, arguments);
}
function RangeCinemaQuery() {
query.RangeQuery.apply(this, arguments);
}

BaseCinemaQuery.prototype = Object.create(query.BasicQuery.prototype);
OrdFilterCinemaQuery.prototype = Object.create(query.OrdFilterQuery.prototype);
RangeCinemaQuery.prototype = Object.create(query.RangeQuery.prototype);
[BaseCinemaQuery, OrdFilterCinemaQuery, RangeCinemaQuery].forEach(function (CinemaQuery) {
CinemaQuery.prototype.ctors = {
Basic: BaseCinemaQuery,
OrdFilter: OrdFilterCinemaQuery,
Range: RangeCinemaQuery
};
CinemaQuery.prototype.withFilm = function (filmId, date) {
return this.filter(function (cinema) {
return cinema.timeTable.some(function (entry) {
return entry.filmId === filmId && entry.date >= date;
});
});
};
CinemaQuery.prototype.orderByNearness = function (pos) {
return this.orderBy(nearnessOrderer(pos));
};
CinemaQuery.prototype.thenByNearness = function (pos) {
return this.thenBy(nearnessOrderer(pos));
};
CinemaQuery.prototype.orderBySoonest = function (filmId, date) {
return this.orderBy(soonestFilmDateOrderer(filmId, date));
};
CinemaQuery.prototype.thenBySoonest = function (filmId, date) {
return this.thenBy(soonestFilmDateOrderer(filmId, date));
};
});



exports.BaseCinemaQuery = BaseCinemaQuery;
38 changes: 38 additions & 0 deletions kino/cinema/filmQuery.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
'use strict';

var _ = require("lodash"),
query = require("./query");

function nameFilter(name) {
return function (film) {
return film.name === name;
};
}

function BaseFilmQuery() {
query.BasicQuery.apply(this, arguments);
}
function OrdFilterFilmQuery() {
query.OrdFilterQuery.apply(this, arguments);
}
function RangeFilmQuery() {
query.RangeQuery.apply(this, arguments);
}

BaseFilmQuery.prototype = Object.create(query.BasicQuery.prototype);
OrdFilterFilmQuery.prototype = Object.create(query.OrdFilterQuery.prototype);
RangeFilmQuery.prototype = Object.create(query.RangeQuery.prototype);
[BaseFilmQuery, OrdFilterFilmQuery, RangeFilmQuery].forEach(function (FilmQuery) {
FilmQuery.prototype.ctors = {
Basic: BaseFilmQuery,
OrdFilter: OrdFilterFilmQuery,
Range: RangeFilmQuery
};
FilmQuery.prototype.withName = function (name) {
return this.filter(nameFilter(name));
};
});



exports.BaseFilmQuery = BaseFilmQuery;
Loading