Создает копию оригинального массива, состоящую только из уникальных элементов этого массива.

TestBed и ComponentFixtures - классы. async, fakeAsync, inject - впсомогательные функции. Все эти штуки являются частью пакета @angular/core/testing и служат интсрументами для написания тестов в Angular.

Тесты в Jasmine называются спецификациями, поэтому такие файлы имеют расширение .spec.ts. Важное условие - Karma по умолчанию автоматически ищет все файлы спецификации рекурсивно внутри директории src/app, поэтому все тестовые файлы должны располагаться в этой директории, чтобы быть протестированными.

describe - глобальная функция Jasmine.

Пример теста:

1
2
3
4
5
6
7
import { Pastebin } from './pastebin';

describe('Pastebin', () => {
  it('should create an instance of Pastebin', () => {
    expect(new Pastebin()).toBeTruthy();
  });
});

Что здесь происходит? Строкой

1
import { Pastebin } from './pastebin'
импортируется реальный, существующий класс Pastebin, который будет тестироваться. Создается глобальная функция describe с названием набора тестов и функцией - фактической реализацией данного теста, внутри которой создается функция it (спецификация) с описанием того, что будет тестироваться - будем проверять, что класс Pastebin может создавать свой экземпляр. Для этого испльзуем assertion-библиотеку Jasmine и его метод - expect:

1
expect(new Pastebin()).toBeTruthy();

мы ожидаем (expect), что вызов new Pastebin() создаст экземпляр класса Pastebin и вспомогательная функция toBeTruthy() подтвердит, что это правда.

Функция toBeTruthy() - вспомогательная функция. Другими примерами являются toBedefined(), toBe(), toContaian().

1
2
3
4
5
6
7
8
9
10
11
12
13
it('should accept values', () => {
  let pastebin = new Pastebin();
  pastebin = {
    id: 123,
    title: 'Hello Jasmine',
    language: 'javascript',
    paste: 'print message'
  };
  expect(pastebin.id).toEqual(123);
  expect(pastebin.title).toEqual('Hello Jasmine');
  expect(pastebin.language).toEqual('javascript');
  expect(pastebin.paste).toEqual('print message');
});

beforeEach() - глобальная функция Jasmine, которая вызывается на исполнение перед каждой спецификацией it в блоке describe (beforeEach is called once before every it block in a test). TestBed - класс из Jasmine, который создает модуль тестирования, аналогичный реальному модулю @NgModule. У класса TestBed есть метод configureTestingModule, с помощью которого можно настроить этот модуль. Другими словами, с помощью класса TestBed можно создать и настроить любую тестовую среду.

При написании тестов не редка ситуация, когда надо иметь фиксированное воспроизводимое много раз состояние программы. Например, такая-то кнопочка нажата, такой-то класс содержит такие-то значения.

Чтобы не приходилось каждый раз вручную создавать подобное состояние программы используются fixture (фикстуры). Фикстуры позволяют сохранить состояние системы в файл, а потом его от туда загрузить.

async() - функция jasmine, припомощи которой выполняются все асинхронные операции в тестах. Даже не так - эта функция обеспечивает поддержку всех необходимых при тестировании асинхронных операций; она отслеживает все асинхронные задачи в тестах.

compileComponents() - ?

angular core testing utils list

inject() - позволяет добавлять зависимости в функции beforeEach() и it().

  • внедрение зависимости в beforeEach():
1
2
3
4
beforeEach(inject([Dependency, AClass], (dep, object) => {
  // some code that uses dep and object
  // ...
}));
  • внедрение зависимости в it:
1
2
3
4
it('...', inject([AClass], (object) => {
  object.doSomething();
  expect(...);
})

flush() - ?

expectOne() - ?

spyOn() - функция Jasmine, которая перехватывает все вызовы реального метода.


uniq()

Создает копию оригинального массива, состоящую только из уникальных элементов этого массива.

const arrOrg = [1, 2, 3, 1, 2, 3];
const arrUniq = _.uniq(arrOrg);
console.log("uniq array - ", arrUniq); // => [1, 2, 3]

// FILE NAME // year-month-day-name 2013-01-15-archlinux-slim.md

// CODE SNIPPET

// ...

// INLINE LINK link

// ANNOUNCEED LINK text

// INLINE IMAGE image title


Шаблон раннего возврата (return early pattern) в JavaScript - это простой способ уменьшить число else-операторов в теле функции до нуля. Есть много причин, чтобы предпочесть этот шаблон использованию else-операторов.

  • Уменьшение общего количества кода на странице
  • Уменьшение длины строк кода упрощением логической сложности
  • Улучшение удобочитаемости кода

Суть шаблона раннего возврата (return early pattern) заключается в том, чтобы заменить необходимость дополнительных условий в функциях JavaScript, используя ключевое слово return в теле оператора if.

Создадим функцию, которая ведет себя по-разному при определенных условиях (примечание: это тривиальный и надуманный пример, просто чтобы получить представление о сути вопроса):

function willItBlend (someObject) {
var ItWillBlend;

if (someObject.blendable === 'It will blend') {
itWillBlend = true;
} else {
itWillBlend = false;
}

return itWillBlend;
}

Хотя данная функция кажется читаемой, добавим в нее еще одно условие для проверки того, что функция вызывается для работы с объектом вместо undefined:

function willItBlend(someObject) {
var itWillBlend;

if (typeof someObject === 'object') {
if (someObject.blendable === 'It will blend') {
itWillBlend = true;
} else {
itWillBlend = false;
}
} else {
itWillBlend = false;
}

return itWillBlend;
}

Эта функция понятной и ее имя является описательным, однако она представляется все-же излишне сложной. Можем ли мы использовать шаблон раннего возврата (return early pattern) для повышения удобочитаемости и уменьшения количества else-выражений?

Попробуем:

function willItBlend(someObject) {
if (typeof someObject !== 'object') {
return false;
}

if (someObject.blendable !== 'It will blend') {
return false;
}

return true;
}

Используя шаблон раннего возврата (return early pattern), мы смогли удалить все ненужные else-инструкции и сделать функцию намного яснее и более краткой.

Бонус: одно условное выражение

Мы можем дополнительно переписать функцию для использования только одного оператора if.

Проверим:

function willItBlend(someObject) {
if (
typeof someObject !== 'object' ||
someObject.blendable !== 'It will blend'
) {
return false;
}

return true;
}

Метод call

Метод call и apply достаточно древние - они оба из стандарта ES3. Суть создания и работы методов - заставить некоторую произвольную функцию работать на каком-то объекте. Точнее - использовать контекст этого объекта.

Чтобы было понятнее, начнем с примера. Есть функция, которая работает таким образом:

function getName() {
  const name = this.name
  console.log(name)
}

Что будет, если вызвать эту функцию таким образом?

getName()

Сложный вопрос, так как мы видим, что в теле функции используется ключевое слово this. Это - ссылка на текущий объект и на свойство name этого объекта. Что это за объект? Неизвестно. Все будет зависеть от того, где будет вызвана функция getName() на исполнение. Допустим, если вызвать функцию в консоли браузера, то парсер обратиться к глобальному объекту window и попробует найти у него свойство name. В итоге мы получим undefined, так как вряд ли у объекта window есть свойство name.

При помощи метода call можно “привязать” функцию getName() к любому объекту. Другими словами - привязать контекст функции getName() к контексту объекта. Как?

Допустим, у нас есть объект:

const user1 = {
  name: 'Ivan',
  position: 'frontent developer',
  salary: 1200
}

Мы сделаем так:

const result1 = getName.call(user1)

Что будет происходить здесь? В этом случае ключевое слово this (в теле функции getName) будет указывать на объект user1, у которого есть свойство name. Как результат - в константе result1 будет находиться значение Ivan.

Если создадим еще один объект:

const user2 = {
  name: 'Joshua',
  position: 'ui designer',
  salary: 2200
}

И на нем вызовем функцию getName():

const result2 = getName.call(user2)

Что будет? В этом случае функция getName будет ссылаться уже на объект user2! И в константе result2 уже будет значение свойства name объекта user2 - Joshua.

call - более сложный вариант

Рассмотрим более сложный случай - когда функция принимает аргумент(ы). Допустим, такая функция,

function promote(position, salary) {
  this.salary = salary
  this.position = position
  return this.position + ' earns ' + this.salary + ' dollars'
}

И есть два объекта:

const junior = {
  name: 'Fritz',
  position: 'junior developer',
  salary: 1000
}
const middle = {
  name: 'Max',
  position: 'middle developer',
  salary: 2000
}
const senior = {
  name: 'Manu',
  position: 'senior developer',
  salary: 3000
}

Вызовем функцию promote() на каждом из объектов:

const result1 = promote.call(junior, 'super junior developer', 1200)
const result2 = promote.call(middle, 'super middle developer', 2200)
const result3 = promote.call(senior, 'super senior developer', 3200)

Что здесь произойдет? При каждом вызове функции promote() у нее будет разный контекст выполнения - объект junior, middle или senior. И обращаться функция promote() будет каждый раз к свойствам разных объектов - junior, middle или senior. Соответственно, результат тоже будет разным:

super junior developer earns 1200 dollars
super middle developer earns 2200 dollars
super senior developer earns 3200 dollars

Метод apply

Метод apply очень похож на метод call, за одним исключением. Аргументы метод apply принимает в виде массива, а не в виде списка, как метод call.

Проиллюстрируем пример - переделаем вызов функции promote() при помощи метода apply:

const resultA = promote.apply(junior, [ 'super junior developer', 1200 ])
const resultB = promote.apply(middle, [ 'super middle developer', 2200 ])
const resultC = promote.apply(senior, [ 'super senior developer', 3200 ])

Видим, что аргументы передаются в виде массива, но результат будет тот же. Как часто упоминается в различных туториалах, это сделано для того, чтобы передать неограниченное и заранее неизвестное количество аргументов.

Метод bind

Этот метод похож на предыдущие два - call и apply. Однако, у него есть два отличия - этот метод появился в стандарте ES5; и второе отличие - этот метод позволяет выполнить отложенный вызов функции.

То есть - метод call или apply вызываются на исполнение сразу, и результат их работы мы получаем сразу. С методом bind немного иначе:

const resultAA = promote.bind(junior)
const resultBB = promote.bind(middle)
const resultCC = promote.bind(senior)

Что мы сдесь сделали? Мы вызвали функции promote() и передали ей контекст выполнения; результат - экземпляр функции promote() с уже встроенным контекстом выполнения; экземпляр помещен в константу resultAA, resultBB или resultCC. Другими словами - это готовые функции с контекстом.

Осталось вызвать любую из них и передать ей аргументы, которые она ожидает:

const resultAAA = resultAA('super junior developer', 1200)
const resultBBB = resultBB('super middle developer', 2200)
const resultCCC = resultCC('super senior developer', 3200)

Получим результат:

super junior developer earns 1200 dollars
super middle developer earns 2200 dollars
super senior developer earns 3200 dollars

Видим - мы получили отложенный вызов каждой из функций.

MongoDB

Приступили к самому основному - операциям создания, чтения, изменения и удаления документов в MongoDB.

Аббревиатура для этих четырех операций - CRUD ( Create, Read, Update, Delete ).

В этом обзоре будет рассмотрен процесс создания документа - Create.

Два основных метода для создания нового документа в коллекции - метод insert и метод save.

Метод insert()

Команда создания нового документа в коллекции выглядит так:

db.COLLECTION_NAME.insert(document)

COLLECTION_NAME - это имя коллекции, в которой будет создаваться новый документ. document - это JavaScript-объект.

Создам новый документ в коллекции customers базы данных users. Для этого перейду в эту базу данных:

> use users
switched to db users

Создам коллецию customers в базе данных users:

> db.createCollection('customers')
{ "ok" : 1 }

Создам в коллекции customers новый документ. Можно использовать как одинарные, так и двойные кавычки - дело вкуса:

> db.customers.insert( { name: 'Leanne Graham', username: 'Bret', email: 'sincere@april.biz' } )
WriteResult({ "nInserted" : 1 })

MongoDB выдает отчет о выполнении команды в строке:

...
WriteResult({ "nInserted" : 1 })

Видно, что операция WriteResult успешно выполнилась - nInserted в значении true.

Как хорошо видно, создаваемый документ - это объект. Если нужно создать сразу несколько документов, то методу insert передается массив этих объектов:

> db.customers.insert( [ { name: 'Ervin Howell', username: 'Antonette', email: 'shanna@melissa.tv' }, { name: 'Clementine Bauch', username: 'Samantha', email: 'nathan@yesenia.net' } ] )
BulkWriteResult({
	"writeErrors" : [ ],
	"writeConcernErrors" : [ ],
	"nInserted" : 2,
	"nUpserted" : 0,
	"nMatched" : 0,
	"nModified" : 0,
	"nRemoved" : 0,
	"upserted" : [ ]
})

BulkWriteResult выдает подробную информацию о выполненных операциях. Видно, что была выполнена только операция создания документа - “nInserted” : 2.

Просмотр списка документов

Вывести список созданных документов в коллекции customers можно при помощи метода .find():

> db.customers.find()
{ "_id" : ObjectId("59200cdc2bd83e7289a5cae4"), "name" : "Leanne Graham", "username" : "Bret", "email" : "sincere@april.biz" }
{ "_id" : ObjectId("59200e3b2bd83e7289a5cae5"), "name" : "Ervin Howell", "username" : "Antonette", "email" : "shanna@melissa.tv" }
{ "_id" : ObjectId("59200e3b2bd83e7289a5cae6"), "name" : "Clementine Bauch", "username" : "Samantha", "email" : "nathan@yesenia.net" }
>

Видно, что у каждого созданного мною документа есть ключ _id со значением ObjectId(). Этого ключа я не указывал при создании документа.

Все правильно - этот ключ и его значение MongoDB генерирует автоматически и присваивает каждому создаваемому документу. Таким образом MongoDB делает все документы уникальными - нет ни одного документа с одинаковым _id.

Можно сделать вывод метода .find() более читабельным, если подключить к нему по цепочке еще один метод - .pretty():

> db.customers.find().pretty()
{
	"_id" : ObjectId("59200cdc2bd83e7289a5cae4"),
	"name" : "Leanne Graham",
	"username" : "Bret",
	"email" : "sincere@april.biz"
}
{
	"_id" : ObjectId("59200e3b2bd83e7289a5cae5"),
	"name" : "Ervin Howell",
	"username" : "Antonette",
	"email" : "shanna@melissa.tv"
}
{
	"_id" : ObjectId("59200e3b2bd83e7289a5cae6"),
	"name" : "Clementine Bauch",
	"username" : "Samantha",
	"email" : "nathan@yesenia.net"
}
>

Метод save()

С помощью метода save() также можно создавать новый документ в коллекции. Создам еще один документ:

> db.customers.save( { name: 'Patricia Lebsack', username: 'Karianne', email: 'julianne.conner@kory.org' } )
WriteResult({ "nInserted" : 1 })

Сообщение от MongoDB говорит мне, что операция была выполнена успешно. Посмотрю на результат:

> db.customers.find()
{ "_id" : ObjectId("59200cdc2bd83e7289a5cae4"), "name" : "Leanne Graham", "username" : "Bret", "email" : "sincere@april.biz" }
{ "_id" : ObjectId("59200e3b2bd83e7289a5cae5"), "name" : "Ervin Howell", "username" : "Antonette", "email" : "shanna@melissa.tv" }
{ "_id" : ObjectId("59200e3b2bd83e7289a5cae6"), "name" : "Clementine Bauch", "username" : "Samantha", "email" : "nathan@yesenia.net" }
{ "_id" : ObjectId("592013152bd83e7289a5cae7"), "name" : "Patricia Lebsack", "username" : "Karianne", "email" : "julianne.conner@kory.org" }
>

Отличие метода save() от метода insert() заключается в том, что если при создании документа будет передан ключ _id уже существующего документа, то существующий документ будет перезаписан новым.

Вот у меня создан документ:

> db.customers.save( { name: 'Chelsey Dietrich', username: 'kamren', email: 'Lucio_Hettinger@annie.ca' } )
WriteResult({ "nInserted" : 1 })

И он успешно добавлен в коллекцию customers с уникальным ключом “_id” : ObjectId(“5920166b986c86064996f59e”):

> db.customers.find()
{ "_id" : ObjectId("59200cdc2bd83e7289a5cae4"), "name" : "Leanne Graham", "username" : "Bret", "email" : "sincere@april.biz" }
{ "_id" : ObjectId("59200e3b2bd83e7289a5cae5"), "name" : "Ervin Howell", "username" : "Antonette", "email" : "shanna@melissa.tv" }
{ "_id" : ObjectId("59200e3b2bd83e7289a5cae6"), "name" : "Clementine Bauch", "username" : "Samantha", "email" : "nathan@yesenia.net" }
{ "_id" : ObjectId("5920166b986c86064996f59e"), "name" : "Chelsey Dietrich", "username" : "kamren", "email" : "Lucio_Hettinger@annie.ca" }
> 

Теперь я создаю новый документ, но передаю в него существующую пару ключ-значение “_id” : ObjectId(“5920166b986c86064996f59e”):

> db.customers.save( { "_id" : ObjectId("5920166b986c86064996f59e"), name: 'Chelsey Dietrich', username: 'chelsey', email: 'c.dietrich@gmail.com' } )
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

Смотрю результат:

> db.customers.find()
{ "_id" : ObjectId("59200cdc2bd83e7289a5cae4"), "name" : "Leanne Graham", "username" : "Bret", "email" : "sincere@april.biz" }
{ "_id" : ObjectId("59200e3b2bd83e7289a5cae5"), "name" : "Ervin Howell", "username" : "Antonette", "email" : "shanna@melissa.tv" }
{ "_id" : ObjectId("59200e3b2bd83e7289a5cae6"), "name" : "Clementine Bauch", "username" : "Samantha", "email" : "nathan@yesenia.net" }
{ "_id" : ObjectId("5920166b986c86064996f59e"), "name" : "Chelsey Dietrich", "username" : "chelsey", "email" : "c.dietrich@gmail.com" }
>

… и вижу, что последний документ был полностью перезаписан. Вуаля.

На этом все.