Unit-тести для Angular

Unit-тести для Angular

Unit-тестування – це важлива частина процесу розробки на поточний момент і вона стає невід’ємною частиною. Це підвищує якість коду і впевненість розробників. У даній статті розповідається про те, як створювати unit-тести для Angular, що використовувати і чому.

Для написання unit-тестів використовується фреймворк Jasmine, а виконуються тести в Karma. Karma – це JavaScript движок для запуску тестів, які виконуються у браузері.

Є дві основні концепції тестування TDD і BDD:

  • TDD (test driven development) – розробка на основі тестів. Спочатку пишемо тести, а потім під них пишемо код. Концепція орієнтована на перевірку кожного блоку коду, написаного розробником. Саме для цього і використовуються unit-тести.
  • BDD (behavior driven development) – розробка на основі поведінки. Це відгалуження від TDD. Концепція орієнтована на перевірку бізнес-сценаріїв, реалізованих в коді.

Фреймворк Jasmine прекрасно підходить і для TDD, і для BDD.

Особливості unit-тестів

  • Кожен тест повинен виконуватися дуже швидко
  • Кожен тест повинен бути ізольований від всіх залежностей
  • Тест повинен мати чітко визначений результат виконання для кожного набору вхідних даних
  • Найменший блок коду (unit) може бути перевірений окремим тестом

Коли ми пишемо unit-тести

  • На самому початку або в кінці розробки
  • Якщо ми додали нову фічу в коді, ми повинні покрити її роботу тестом
  • Якщо ми змінили існуючу фичу в коді, ми також повинні змінити / додати для неї тест

Чому варто використовувати Jasmine

  • За замовчуванням інтегрована з Karma
  • Надає зручний функціонал, наприклад, Spy, fake
  • Легко інтегрується зі звітністю

Чому варто використовувати Karma

  • Angular CLI вміє працювати з Karma з коробки.
  • Дозволяє автоматизувати тести для різних браузерів і девайсів.
  • Стежить за змінами файлів без ручного перезапуску
  • Добре задокументована
  • Легко інтегрується з CI-серверами

Приклад проекту

Для запуску тестування на Angular не потрібно нічого налаштовувати. Завдяки Angular CLI всі налаштування (Jasmine і Karma) створюються автоматично.

// створюємо новий Angular проект 
ng new angular-unit-testing
// запускаємо тестування 
npm test

І ви відразу ж побачите результат в браузері:

Результат тестування

Для тестування різних особливостей Angular був створенний приклад проекту. Це простий to-do список. Нижче скріншот і посилання на проект.

Приклад проекту

Конфігурація тестів в Angular

Коли ми запускаємо npm test або ng test, Angular використовує настройки з файлу angular.json. Зверніть увагу на test-параметр цього файлу.
Ми вказали test.ts як основний файл для запуску і karma.conf.js як конфігурацію для Karma.

//angular.json
" Test " : {
          " Builder " : " @ angular-devkit / build-angular: karma " ,
          " Options " : {
            " Main " : " src / test.ts " ,
            " Polyfills " : " src / polyfills.ts " ,
            " TsConfig " : " src / tsconfig.spec.json " ,
            " KarmaConfig " : " src / karma.conf.js " ,
            " Styles " : [
              " Src / styles.css "
            ],
            " Scripts " : [],
            " Assets " : [
              " Src / favicon.ico " ,
              " Src / assets "
            ]
          }
        },

У файлі test.ts ми завантажуємо всі файли проекту з розширенням .spec.ts для тестування.

//test.ts
// Then we find all the tests.
const  context  =  require . context ( './' ,  true ,  / \. spec \. ts $ / ) ;
// And load the modules.
context . keys ( ) . map ( context ) ;

А в файлі karma.conf.js ми визначаємо настройки звітів, порт, типи браузерів для тестування і тп.

//karma.conf.js
module . exports  =  function  ( config )  {
  config . set ( {
    basePath : '' ,
    frameworks : [ 'jasmine' ,  '@ angular-devkit / build-angular' ] ,
    plugins : [
      require ( 'karma-jasmine' ) ,
      require ( 'karma-chrome-launcher' ) ,
      require ( 'karma-jasmine-html-reporter' ) ,
      require ( 'karma-coverage-istanbul-reporter' ) ,
      require ( '@ angular-devkit / build-angular / plugins / karma' )
    ] ,
    client : {
      clearContext : false  // leave Jasmine Spec Runner output visible in browser
    } ,
    coverageIstanbulReporter : {
      dir : require ( 'path' ) . join ( __dirname ,  '../coverage/todoApp' ) ,
      reports : [ 'html' ,  'lcovonly' ,  'text-summary' ] ,
      fixWebpackSourcePaths : true
    } ,
    reporters : [ 'progress' ,  'kjhtml' ] ,
    port : 9876 ,
    colors : true ,
    logLevel : config . LOG_INFO ,
    autoWatch : true ,
    browsers : [ 'Chrome' ] ,
    singleRun : false ,
    restartOnFileChange : true
  } ) ;
} ;

Основні функції

TestBed

Це базовий блок для створення тестового модуля. TestBed створює динамічний тестовий модуль, який емулює роботу NgModule. Вхідні дані для TestBed.configureTestingModule() і @NgModule абсолютно однакові. І саме TestBed.configureTestingModule() є конфігурацією тестового файлу.

//sample.spec.ts
describe ( 'AppComponent' ,  ( )  =>  {
  let  appComponent : AppComponent ;

  beforeEach ( async ( ( )  =>  {
    TestBed . configureTestingModule ( {
      imports : [
        RouterTestingModule
      ] ,
      declarations : [
        AppComponent
      ] ,
      schemas : [  NO_ERRORS_SCHEMA  ]
    } ) . compileComponents ( ) ;
  } ) ) ;
} ) ;

Конфігурація завжди розміщується у beforeEach() функції, яка запускається перед кожним тестом.

compileComponents()

У цьому ж файлі ми використовуємо compileComponents() метод. Якщо ви створюєте компонент, який використовує зовнішні файли для визначення styleUrls, templateUrl, тоді ви повинні отримати ці файли з файлової системи до того, як компонент буде створений. Це асинхронна операція і тому ми обертаємо конфігурацію в асинхронну функцію.

Фактично відбувається наступне: при запуску тестування підтягується зовнішній файл, а потім починається компіляція компонента. Якщо ви запускаєте тести з Angular CLI, тоді асинхронна функція не потрібна. Оскільки CLI компілює весь код до запуску тестів.

Чому дві beforeEach() функції

Якщо ви подивіться на файл нижче, то побачите, що ми використовуємо дві beforeEach() функції. Оскільки compileComponents() запускається асинхронно, ми хочемо бути впевнені, що компонент буде скомпільовано до того, як ми створимо fixture (надає доступ до примірника компонента). Друга beforeEach() запускається після першої.

//app.component.spec.ts
describe ( 'AppComponent' ,  ( )  =>  {

  let  appComponent : AppComponent ;

  beforeEach ( async ( ( )  =>  {
    TestBed . configureTestingModule ( {
      imports : [
        RouterTestingModule
      ] ,
      declarations : [
        AppComponent
      ] ,
      schemas : [  NO_ERRORS_SCHEMA  ]
    } ) . compileComponents ( ) ;
  } ) ) ;

  beforeEach ( ( )  =>  {

    const  fixture  =  TestBed . createComponent ( AppComponent ) ;
    appComponent  =  fixture . debugElement . componentInstance ;
    appComponent . itemList  =  [ { id : 1 ,  name : 'todolist' ,  description : 'This is todo list app' } ,
                      { Id : 2 ,  name : 'unit test' ,  description : 'Write unit tests' } ] ;
  } ) ;

  it ( 'should create the app' ,  ( )  =>  {
    expect ( appComponent ) . toBeTruthy ( ) ;
  } ) ;
} ) ;

Навіщо використовувати NO_ERRORS_SCHEMA

Давайте виберемо app.component.ts компонент з проекту і протестуємо його. Подивіться на spec файл, який Angular CLI згенерував для нас, коли ми виконали команду

ng nc

Була додана f (рядок 5) до функції describe(), щоб виконувати тільки цей файл під час тестування. Це називається Focused Tests.

//app.component.spec.ts
import  {  TestBed ,  async  }  from  '@ angular / core / testing' ;
import  {  RouterTestingModule  }  from  '@ angular / router / testing' ;
import  {  AppComponent  }  from  './app.component' ;

fdescribe ( 'AppComponent' ,  ( )  =>  {
  beforeEach ( async ( ( )  =>  {
    TestBed . configureTestingModule ( {
      imports : [
        RouterTestingModule
      ] ,
      declarations : [
        AppComponent
      ] ,
    } ) . compileComponents ( ) ;
  } ) ) ;

  it ( 'should create the app' ,  ( )  =>  {
    const  fixture  =  TestBed . createComponent ( AppComponent ) ;
    const  app  =  fixture . debugElement . componentInstance ;
    expect ( app ) . toBeTruthy ( ) ;
  } ) ;

} ) ;

Запустивши npm test, ми побачимо багато помилок через вкладеності компонентів, тому що основний компонент містить інші компоненти, такі як header, footer. Ми повинні оголосити їх в app.component.spec.ts в масиві declarations (рядок 11).

Тестування

Інший спосіб – використання NO_ERRORS_SCHEMA. Ця установка дозволяє ангуляр ігнорувати всі невідомі теги при тестуванні компонента.

NO_ERRORS_SCHEMA

Tестування компонентів

У нашому компоненті є метод deleteItem(). Давайте його протестуємо.

//app.component.spec.ts 
import  {  TestBed ,  async  }  from  '@ angular / core / testing' ;
import  {  RouterTestingModule  }  from  '@ angular / router / testing' ;
import  {  AppComponent  }  from  './app.component' ;
import  {  NO_ERRORS_SCHEMA  }  from  '@ angular / core' ;

fdescribe ( 'AppComponent' ,  ( )  =>  {

  let  appComponent : AppComponent ;

  beforeEach ( async ( ( )  =>  {
    TestBed . configureTestingModule ( {
      imports : [
        RouterTestingModule
      ] ,
      declarations : [
        AppComponent
      ] ,
      schemas : [  NO_ERRORS_SCHEMA  ]
    } ) . compileComponents ( ) ;
  } ) ) ;

  beforeEach ( ( )  =>  {

    const  fixture  =  TestBed . createComponent ( AppComponent ) ;
    appComponent  =  fixture . debugElement . componentInstance ;
    appComponent . itemList  =  [ { id : 1 ,  name : 'todolist' ,  description : 'This is todo list app' } ,
                      { Id : 2 ,  name : 'unit test' ,  description : 'Write unit tests' } ] ;
  } ) ;

  it ( 'should create the app' ,  ( )  =>  {
    expect ( appComponent ) . toBeTruthy ( ) ;
  } ) ;

  it ( 'should delete an item with existing item' ,  ( )  =>  {

    // given this item
    const  item  =  { id : 1 ,  name : 'todolist' ,  description : 'This is todo list app' } ;
    // execute the test case
    appComponent . deleteItem ( item ) ;
    // assertion
    expect ( appComponent . itemList . length ) . toBe ( 1 ) ;
  } ) ;

  it ( 'should not delete an item with new item' ,  ( )  =>  {

    // given this item
    const  item  =  { id : 3 ,  name : 'todolist' ,  description : 'This is todo list app' } ;
    // execute the test case
    appComponent . deleteItem ( item ) ;
    // assertion
    expect ( appComponent . itemList . length ) . toBe ( 2 ) ;
  } ) ;


} ) ;
//app.component.ts 
deleteItem ( item : any ) {
    console . log ( item ) ;
    this . itemList  =  this . itemList . filter ( ( itm : any )  =>  itm . id ! == item . id ) ;
}

Тестування DOM

Давайте протестуємо footer компонент, в якому у нас є footerText і numberOfItems Змінимо їх в компоненті і перевіримо шаблон footer.

Тестування DOM
//footer.component.spec.ts
import  {  async ,  ComponentFixture ,  TestBed  }  from  '@ angular / core / testing' ;
import  {  DebugElement  }  from  '@ angular / core' ;
import  {  By  }  from  '@ angular / platform-browser' ;

import  {  FooterComponent  }  from  './footer.component' ;

fdescribe ( 'FooterComponent' ,  ( )  =>  {
  let  component : FooterComponent ;
  let  fixture : ComponentFixture < FooterComponent > ;

  beforeEach ( async ( ( )  =>  {
    TestBed . configureTestingModule ( {
      declarations : [  FooterComponent  ]
    } )
    . compileComponents ( ) ;
  } ) ) ;

  beforeEach ( ( )  =>  {
    fixture  =  TestBed . createComponent ( FooterComponent ) ;
    component  =  fixture . componentInstance ;
    fixture . detectChanges ( ) ;
  } ) ;

  it ( 'should create' ,  ( )  =>  {
    expect ( component ) . toBeTruthy ( ) ;
  } ) ;

  it ( 'Shoud display footerText as Unit Testing' ,  ( )  =>  {

    component . footerText  =  'Unit Testing' ;
    fixture . detectChanges ( ) ;

    const  bannerDe : DebugElement  =  fixture . debugElement ;
    const  headingDe  =  bannerDe . query ( By . css ( 'h2' ) ) ;
    const  h2 : HTMLElement  =  headingDe . nativeElement ;

    expect ( h2 . textContent ) . toEqual ( 'Unit Testing' ) ;

  } ) ;

  it ( 'Shoud display numberOfItems as 10' ,  ( )  =>  {

    component . numberOfItems  =  10 ;
    fixture . detectChanges ( ) ;

    const  bannerDe : DebugElement  =  fixture . debugElement ;
    const  headingDe  =  bannerDe . query ( By . css ( 'h1' ) ) ;
    const  h1 : HTMLElement  =  headingDe . nativeElement ;

    expect ( h1 . textContent ) . toContain ( '10' ) ;

  } ) ;

} ) ;

Зверніть увагу на fixture.detectChanges(). Ми змінили footerText і numberOfItems в тесті і викликали механізм виявлення змін ангуляр для того, щоб DOM був оновлений відповідно до змін до запуску тесту.

Нижче результат тесту в браузері. Оскільки footer є автономним компонентом, ми бачимо результат рендеринга в Karma браузері.

Тестування

Тестування pipe

Тестувати Angular pipe дуже просто, оскільки вони не мають залежностей. Є pipe, який показує поточний рік після тексту в footer. Давайте його протестуємо:

//copyright.pipe.spec.ts
import  {  CopyrightPipe  }  from  './copyright.pipe' ;

fdescribe ( 'CopyrightPipe' ,  ( )  =>  {

  it ( 'create an instance' ,  ( )  =>  {
    const  pipe  =  new  CopyrightPipe ( ) ;
    expect ( pipe ) . toBeTruthy ( ) ;
  } ) ;

  it ( 'should display current year at the end' ,  ( )  =>  {
    const  pipe  =  new  CopyrightPipe ( ) ;

    const  result  =  pipe . transform ( 'Pipe Testing' ) ;

    expect ( result ) . toBe ( 'Pipe Testing'  +  new  Date ( ) . getFullYear ( ) ) ;
  } ) ;
} ) ;

Тестування сервісів

Сервіси теж досить просто тестувати. У нас є один сервіс в додатку. Давайте протестуємо метод addItem() в сервісі:

//app.service.spec.ts 
import  {  TestBed  }  from  '@ angular / core / testing' ;

import  {  AppService  }  from  './app.service' ;

fdescribe ( 'AppService' ,  ( )  =>  {
  beforeEach ( ( )  =>  TestBed . configureTestingModule ( { } ) ) ;
  let  service : AppService ;

  beforeEach ( ( )  =>  {
    service  =  TestBed . get ( AppService ) ;
    service . totalItems  =  [ { id : 1 ,  name : 'todolist' ,  description : 'This is todo list app' } ] ;
  } ) ;

  it ( 'should be created' ,  ( )  =>  {
    expect ( service ) . toBeTruthy ( ) ;
  } ) ;

  it ( 'should add item to total items' ,  ( )  =>  {

    const  item  =  { id : 2 ,  name : 'seconditem' ,  description : 'This is second item' } ;

    service . addItems ( item ) ;

    expect ( service . totalItems . length ) . toBe ( 2 ) ;
  } ) ;


} ) ;

Покриття коду тестами

Покриття коду показує як багато відсотків коду покрито unit-тестами. Зазвичай хорошим показником є ​​80% покриття.

Angular CLI дозволяє згенерувати звіт про покриття додатки тестами в окремій директорії, званої coverage. Щоб згенерувати звіт потрібно запустити тести з прапором – code-coverage.

ng test - no-watch - code-coverage
Покриття коду тестами

Якщо ви запустите index.html з цієї папки, то побачите повний звіт.

Покриття коду тестами

Практичні поради

  • Переконайтеся, що тест запускається в ізольованому оточенні, без зовнішніх залежностей. Тоді він буде виконуватися швидко.
  • Переконайтеся, що ваш додаток покрито як мінімум на 80% коду тестами.
  • Коли тестируете сервіси, завжди використовуйте spy з Jasmine фреймворка для залежностей. Це дозволить виконувати тести набагато швидше.
  • Коли ви підписуєтеся на Observable під час тесту, переконайтеся, що ви обробляєте і success, і failure результат. Це позбавить вас від непотрібних помилок.
  • Коли тестируете компонент з сервіс-залежностями, завжди використовуйте mock сервіс замість реального сервісу.
  • Усі зміни TestBedConfiguration повинні бути в beforeEach, щоб не дублювати однаковий код для кожного тесту.
  • Коли тестируете компоненти, завжди звертайтеся до DOM через debugElement замість nativeElement. Тому що debugElement надає абстрактний шар для середовища виконання. Це зменшує кількість помилок.
  • Використовуйте By.css замість queryselector, коли запускаєте тестування на сервері. Справа в тому, що queryselector працює тільки в браузері. Якщо ви запустите тестування на сервері, то воно впаде.
  • Завжди використовуйте fixture.detectChanges() замість ComponentFixtureAutoDetect. ComponentFixtureAutoDetect не знає про синхронний оновленні компонента.
  • Викликайте compileComponents(), якщо ви запускаєте тестування не в CLI середовищі.
  • Використовуйте RXJS marble тестування завжди, коли тестируете observable.
  • Використовуйте PageObject модель для перевикористання функцій між компонентами.
  • Не зловживайте NO_ERRORS_SCHEMA. Ця установка дозволяє ангуляр ігнорувати атрибути і нерозпізнані теги. Використовуйте заглушки компонентів.
  • Ніколи не виконуйте ніяких налаштувань після виклику compileComponents()compileComponents() є асинхронним і повинен виконуватися в async функції всередині beforeEach. Наступні настройки виконуйте в наступному синхронному beforeEach.

Висновок

Використовувати unit-тести в Angular додатку дуже зручно, якщо ви розумієте базові принципи тестування Angular, Jasmine і Karma.

Переклад статті Unit-тесты для приложения на Angular 2+

Тести в Angular

У Angular для тестів використовуються два інструменти:
Jasmine – фреймворк для написання тестів, і
Karma – інструмент для запуску цих тестів в браузері.

Jasmine

методи Jasmine

  • describe(description, function) – метод застосовується для угруповання взаємозалежних тестів
  • beforeEach(function) – метод застосовується для призначення завдання, яке повинно виконуватися перед кожним тестом
  • afterEach(function) – метод застосовується для призначення завдання, яке повинно виконуватися після кожного тестом
  • it(description, function) – метод застосовується для виконання тесту
  • expect(value) – метод застосовується для ідентифікації результату тесту
  • toBe(value) – метод застосовується для завдання очікуваного значення тесту: метод порівнює результат із значенням
  • toEqual(object) – перевіряє, що результатом є той самий об’єкт, що і заданий значення
  • toMatch(regexp) – перевіряє, що результат відповідає заданому регулярному виразу
  • toBeDefined() – перевіряє, що результат визначений
  • toBeUndefined() – перевіряє, що результат не визначений
  • toBeNull() – перевіряє, що результат дорівнює Null
  • toBeTruthy() – перевіряє, що результат є квазіістінним
  • toBeFalsy() – перевіряє, що результат є квазіложним
  • toContain(substring) – перевіряє, що результат містить задану підрядок
  • toBeLessThan(value) – перевіряє, що результат менше заданого значення
  • toBeGreaterThan(value) – перевіряє, що результат більше заданого значення

Клас TestBed і його методи

Клас TestBed– відповідає за моделювання середовища додатку Angular для виконання тестів.

  • TestBed.configureTestingModule – налаштовуємо тестовий модуль;
  • TestBed.createComponent – отримуємо екземпляр компонента;
  • compileComponents– застосовується для компіляції компонентів. compileComponents повертає Promise, який дозволяє налаштувати основні змінні для тесту після компіляції компонента (наприклад, коли ми використовуємо зовнішній шаблон і т.д.);
beforeEach( async(() => { 
    TestBed.configureTestingModule({ 
        declarations:[TestComponent]
    });
    TestBed.compileComponents().then(() => {
        fixture = TestBed.createComponent(TestComponent);
        component = fixture.componentInstance;
    });
}));

Зверніть увагу на метод async, який займається тим, що відстежує всі асинхронні завдання всередині нього, приховуючи від нас складність асинхронного виконання.

ComponentFixture

Результатом методу createComponent є об’єкт ComponentFixture, який надає властивості і методи для тестування компонента:

  • componentInstance – повертає об’єкт компонента;
  • nativeElement – повертає об’єкт DOM, який представляє керуючий елемент для компонента;
  • debugElement – повертає тестовий керуючий елемент для компонента;
  • detectChanges() – примушує тестову середу виявляти зміни стану і відображати їх в шаблоні компонента;
  • whenStable() – повертає об’єкт Promise, дозволяються при повній обробці всіх змін (використовується з асинхронність, наприклад, коли ви отримуєте дані асіхронно від будь-якої фейковий служби)
let fixture: ComponentFixture <MyComponent>>; 
     let component: MyComponent; 
     let dataSource = new MockDataSource();
    
    beforeEach(async(() => { TestBed.configureTestingModule({
            declarations: [ MyComponent ], 
            providers: [ { provide: RestDataSource, useValue: dataSource } ] }); 
    TestBed.compileComponents().then(() => { 
            fixture = TestBed.createComponent( MyComponent );

          // debugElement = fixture.debugElement; 
            component = fixture.componentInstance;
     });
}));

ComponentFixture.debugElement

Властивість ComponentFixture.debugElement – повертає об’єкт типу DebugElement (кореневог елементу шаблону компонента).

debugElement має ряд властивостей і методів:

  • nativeElement – повертає об’єкт, який представляє об’єкт HTML в DOM;
  • children – повертає масив ‘дочірніх’ об’єктів типу DebugElement;
  • query(selectorFunction) – функція selectorFunction отримує в якості параметра об’єкт типу DebugElement для кожного елемента HTML в шаблоні; функція повертає перший об’єкт типу DebugElement, для якого функція поверне true;
  • queryAll(selectorFunction) – аналогічно query, але поверне всі об’єкти типу DebugElement;
  • triggerEventHandler(name, event) – ініціюємо подію, наприклад, за допомогою декоратора @HostListener;

Клас By

Клас By дозволяє знаходити елементи в шаблоні компонента завдяки своїм методам:

  • By.all
  • By.css(selector)
  • By.directive(type)

Unit-тести: тестування сервісів

angular-cli при створенні сервісу генерує файл виду name_service.spec.ts як болванку для тесту. В даному файлі за допомогою хелперів TestBed і inject створюється наша сутність.

import { TestBed, inject } from '@ angular/core/testing';

TestBedдопомагає настроїти модуль, в якому ми повинні вказати provider для нашого сервісу. inject – це хелпер, який допомагає запустити injector по заданому провайдеру, завдяки йому ми отримуємо примірник нашого сервісу (service: ArticleService).

Далі ми проводимо простий тест на те, що сервіс існує:

expect(service).toBeTruthy();

Повний код тесту, який спочатку згенерував angular-cli:

import { TestBed, inject } from '@ angular/core/testing';
import { ArticleService } from './article.service';

  describe('ArticleService', ()=>{
     beforeEach(() =>{ TestBed.configureTestingModule({
      providers: [ ArticleService ] 
    });
  });  
     
  it( 'should be created', inject([ArticleService], ( service: ArticleService ) => {
    expect(service).toBeTruthy>();
  }));
});

Припустимо, в сервісі у нас є метод sum, який повертає результат складання двох чисел:

sum(a: number, b: number): number { return a + b; }

Протестуємо метод sum:

it( 'should return sun', inject([ ArticleService ], ( service: ArticleService ) => { 
    expect(service.sum( 5, 5)).toBe(10); 
}));

Щоб запустити тести необхідно виконати команду (ng test в package.json):

npm run test

Працює з асинхронним методом в сервісі

Асинхронний метод в сервісі sum.service.ts:

// async method 
sumAsync( a: number,  b: number): Promise<any> { return new Promise(function(resolve, reject) { 
        setTimeout(function(){
            resolve( a + b); 
       }, 500 ); });
}

Тестуємо в sum.service.spec.ts:

Для емуляції асинхронности в Angular присутній метод fakeAsync. Також для емуляції асинхронности присутні два додаткових методи, які ми використовуємо всередині методу fakeAsyncflush (flush всередині fakeAsync скине всі асинхронности, тобто виконає їх синхронно), tick – приймає параметр (к-ть мілісекунд), після закінчення яких виконається код в методі fakeAsync.

// test async method
it('should return async sum', fakeAsync(inject([ SumService ], ( service: SumService ) => { 
    service.SumAsync(5, 5).Then( function(data){ // повернутий результат ми можемо затверджувати:
        expect(data).toBe(10);});     
        // тести пройшли і якщо випаде помилка, так як виконується асинхронна операція ніхто чекати не буде
        // можна скористатися методом flush(). flush всередині fakeAsync скине всі асинхронности, тобто виконає їх синхронно.
        // flush ();
        // OR:
        // або ми можемо скористатися всередині fakeAsync методом tick, в який можна передати кількість мілісекунд
        tick>(500); 
})));

Метод whenStable

Метод whenStable повертає об’єкт Promise, дозволяється при повній обробці всіх змін (використовується з асинхронність, наприклад, коли ви отримуєте дані асіхронно від будь-якої фейковий служби). Наприклад, це дає нам можливість дочекатися виконання Observable і лише потім протестувати наші зміни.

@Injectable() class MockDataSource { public data = [ { item: 1, item: 2 } ];
    getData(): Observable { return new Observable( obs => {
            setTimeout(() => obs.next(this.data), 500); 
      })
    }
}
             
describe("TestComponent", ()=>{
    let fixture: ComponentFixture; 
    let component: TestComponent;
    // створюємо фіктивну службу, яка для тесту підміняє службу компонента 
    let dataSource = new MockDataSource();

    beforeEach( async(() => { 
      TestBed.configureTestingModule({
            declarations: [ TestComponent ],
            providers: [ { provide: RestDataSource, useValue: dataSource } ]
      });
      TestBed.compileComponents().then(() => {
            fixture = TestBed.createComponent( TestComponent );
            component = fixture.componentInstance;
      });
    }));

    it("async operation success", () => {
        fixture.detectChanges();
        fixture.whenStable().then(() => {
            expect(component.getItems().length).toBe(2);
        }); 
    });
});

Unit-тести: тестування httpClient

Розглянемо як тестувати сервіси, які містять http-запити.

Щоб тестувати http-запити необхідно також налаштувати proxy. У файлі karma.conf.js вкажіть параметр proxies:

proxies: { '/api': { 'target': 'http://localhost:3000/api', 'secure': false, 'changeOrigin': true, 'logLevel': 'info'} }

Якщо консоль буде майоріти помилками виду StaticInjectorError(DynamicTestModule)[HttpClient], то це означає, що в компоненті використовуються http-запити і необхідно імпортувати HttpClientModule:

imports: [ HttpClientModule, ],
@Injectable ({ providedIn: 'root' })
export class CatsService{
    constructor(private httpClient: HttpClient){ }

    getCats(): Observable<any> { return this.httpClient.get('/api/cats'); }
         
    getCat(_id): Observable<any> { return this.httpClient.get(`/api/cat/${_ id}`); }
}

Протестуємо запит на отримання cat по id.

import { TestBed, inject } from '@angular/core/testing'; 
import { CatsService } from './cats.service';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';

describe('CatsService', () => { 
    // HttpClientTestingModule - 
    // HttpTestingController - 
    // допомогою HttpClientTestingModule і HttpTestingController ми можемо управляти http-запитами 
    beforeEach(()  =>  { 
        TestBed.configureTestingModule ({
            imports: [ HttpClientTestingModule ],
            providers: [ CatsService ] 
        });
    });

    it('should be created', inject([ CatsService ], (service: CatsService) => {
        expect(service).toBeTruthy();
    }));
    
    // так як ми робимо unit-тести, то ми не тестуємо сам httpClient 
    // (він протестований розробниками Angular)

    it('should get one cat', inject([CatsService, HttpTestingController], (service: CatsService, backend: HttpTestingController) => {
        // створимо фейковий об'єкт cat (він приходить з сервера)
        const mockCat =  { name: 'asdasd' };
        service.getCat('5b22e86df062b20530788e2d').subscribe(function(cat) {
            // так як приходить об'єкт, то для порівняння використовуємо toEqual
            expect(cat).toEqual(mockCat);
        });
                
        // сформуємо сервіс backend, щоб він повертав нам потрібні дані

        backend.expectOne({
            method: 'Get',
            url: '/api/cat/5b22e86df062b20530788e2d'
        }).flush(mockCat) 
        // flush дозволяє скинути і вказати, що повинен повернути запит 
    }));
>});

Unit-тести: тестування pipe

Створимо pipe, який буде перевертати рядок – reverse-string.

@Pipe ({ name: 'reverseString' }) 
export class ReverseStringPipe implements PipeTransform {
    transform (value: string): string {
        if(typeof value !== 'string') {
            throw new Error('Error on Reverse: not string') 
        }
        
        let str = '';
        for(let i = value.length - 1; i >= 0; i-){
            str +=  value.charAt(i);
        }
        Return str; 
    } 
}

Фільтри ми можемо тестувати як звичайний js-код, тобто тестувати клас, а в цьому класі тестувати метод.

import { ReverseStringPipe } from './reverse-string.pipe';

describe('ReverseStringPipe', () => {
    let pipe;
    beforeEach(() => {
        pipe = new ReverseStringPipe(); 
    });

    // тест на те, що pipe існує
    it('create an instance', () => {
        expect(pipe).ToBeTruthy();
    });

    // тест на те, щo рядок 'перевертається'
    it('reverse success', () => {
        expect(pipe.Transform('abcde')).ToBeTruthy('edcba');
    });

    // * перевіряємо на те, що виключення (exception) було викинуто
    it('should throw on error', () => {
        // всередині expect exception потрібно звернути в функцію
        expect(() => {
            pipe.Transform(1212);
        }).toThrowError('Error on Reverse: not string');
    });
});

Хелпери beforeEach

Хелпер beforeEach дозволяє задати код, який буде виконаний перед кожним тестом. Наприклад, щоб кожен раз не створювати екземпляр pipe (дивіться приклад – Unit-тести: тестування pipe) ми можемо винести його в beforeEach:

describe('ReverseStringPipe', () => {
    let pipe;
    beforeEach(() => {
        pipe = new ReverseStringPipe();
    });
});

Unit-тести: Тестуємо компонент

У компоненті ми будемо отримувати за допомогою методу компонента getCat і сервісу по id певну cat і виводити cat.name в шаблоні. Ми повинні перевірити, що в методі викликається сервіс і то що значення записується в змінну компонента cat.

// cat-info.component.ts 
import { Component, OnInit } from '@angular/core'; 
import { CatsService } from '../../services/cats.service';
      
@Component ({
  selector: 'app-cat-info',
  templateUrl: './cat-info.component.html',
  styleUrls: [ './cat-info.component.css' ] 
}) 
export class CatInfoComponent implements OnInit {
    public cat: any;

    constructor(private catService: CatsService) { }

    ngOnInit() { this.getCat(); }
        
    getCat() { 
        let self = this; 
        self.catService.getCat("5b22e86df062b20530788e2d").subscribe(function(data) {
            self.cat = data;
        });
    }
}
// cat-info.component.spec.ts 
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CatInfoComponent } from './cat-info.component'; 
// імпортуємо так як в компоненті використовуємо запити до апі 
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { CatsService } from "../../services/cats.service"; 
import { of } from "rxjs";

describe('CatInfoComponent', () => { 
    let component: CatInfoComponent; 
    let fixture: ComponentFixture <CatInfoComponent>;
    let catService: CatsService;
    let spy: jasmine. Spy;
    let mockCat;

    beforeEach(async(() => { TestBed.configureTestingModule({
            imports: [ HttpClientTestingModule ],
            declarations: [ CatInfoComponent ],
            providers: [ CatsService ]
    }).compileComponents();
})); 

beforeEach(() => {
    fixture = TestBed.createComponent(CatInfoComponent);
    
    // instance компонента
    component = fixture.componentInstance;
    
    // injector - це сутність, яка дозволяє створювати залежності для компонента
    // (ми отримуємо injector для нашого конкретного компонента) . Отримуємо CatsService:
    catService = fixture.debugElement.injector.get(CatsService);
    mockCat = {
       Name: "asas"
    };
    // нам потрібен шпигун на метод getCat; також повернемо значення, яке повертає метод getCat
    spy = spyOn(catService, 'getCat').and.returnValue(of(mockCat))
        fixture.detectChanges();
    });

    // перевіряємо на існування компонента
    it('should create', () => {
        expect (component ).ToBeTruthy();
    });

    // перевіримо що метод сервісу викликається
    // для цього нам будуть потрібні спеціальні суті - шпигуни
    // (jasmine spy, https://jasmine.github.io/api/2.9/Spy.html).
    // Коли ви створюєте сутність spy, то вона буде зберігати всі виклики
    // і звернення до цього spy, також вона може повертати будь-які
    // значення замість реальної суті. 

    it('should call catService.getCat', () => { 
        // шпигун був викликаний як завгодно раз
        expect(spy.calls.any()).toBeTruthy();
    });

    // перевіримо що сервіс записує значення після виклику сервісу
    it('should set cat(cat.name)', () => {
        expect(component.Cat.Name).ToEqual('asas');
    });
});

Unit-тести: Тестуємо директиву

Наша директива Emit (передає наверх) батьківського компоненту кількість кліків.

// count-click.directive.ts
@Directive({ selector: '[appCountClick]'}) 
export class CountClickDirective {

    @Output() changes = new EventEmitter<number>();
    private count = 0;

    @HostListener('click')
    onClick() {
        this.count++;
        this.changes.emit(this.count);
    }
}

Використання директиви:

<div appCountClick(changes) = "getClick ($ event)"> click me </ div>

Для директиви Angular в заготівки робить екземпляр від класу директиви; цього мало, але так як директиви дуже різнобічні розробники Angular вирішили, що цього достатньо. Нам необхідно протестувати роботу декораторів @Output і @HostListener, для цього потрібно навісити кудись подію і висновок. Тому створимо тестовий компонент в файлі count-click.directive.spec.ts:

// count-click.directive.spec.ts
// тестовий компонент 
@Component({ 
    template: `<div appCountClick (changes) = "outCount = $event"> click me </ div> ` 
})

export class TestCountComponent { public outCount = 0; }

describe('CountClickDirective', () => {
    let testCountComponent, fixture;

    // инициализируем тестовий компонент
    beforeEach(() => { 
        // налаштуємо модуль
        TestBed.ConfigureTestingModule({
            declarations: [ TestCountComponent, CountClickDirective ]
        });

        fixture = TestBed.createComponent(TestCountComponent);
        // отримаємо екземпляр компонента
        testCountComponent = fixture.componentInstance;
    })

    it('should create an instance', () => { 
        const directive = new CountClickDirective();
        expect(directive).toBeTruthy();
    });  
          
    // перевіримо кліки по диву
    it('should count click', () => {
        // отримуємо div, для якого ми ставимо директиву appCountClick 
        let div =  fixture.NativeElement.QuerySelector('div');
        div.Click();
        expect(testCountComponent.outCount).toBe(1);
    });
});

Повне керівництво по функції calc()

В CSS є особлива функція calc(), застосовувана для виконання простих розрахунків. Ось приклад її використання:

.main-content {
  /* Відняти 80px из 100vh */
  height: calc(100vh - 80px);
}

Функція calc() і значення CSS-властивостей

Єдине місце, де можна використовувати функцію calc() – це значення CSS-властивостей. Погляньте на наступні приклади, в яких ми, використовуючи цю функцію, задаємо значення різних властивостей.

.el {
  font-size: calc(3vw + 2px);
  width:     calc(100% - 20px);
  height:    calc(100vh - 20px);
  padding:   calc(1vw + 5px);
}

Функцію calc() можна застосовувати і для установки будь-якої окремої частини властивості:

.el {
  margin: 10px calc(2vw + 5px);
  border-radius: 15px calc(15px / 3) 4px 2px;
  transition: transform calc(1s - 120ms);
}

Ця функція може навіть бути частиною іншої функції, яка відповідає за формування частини якогось властивості! Наприклад, тут calc() використовується для настройки позицій зміни кольору градієнта:

.el {
  background: #1E88E5 linear-gradient(
    to bottom,
    #1E88E5,
    #1E88E5 calc(50% - 10px),
    #3949AB calc(50% + 10px),
    #3949AB
  );
}

Функція calc() – це засіб для роботи з числовими властивостями

Зверніть увагу на те, що у всіх вищенаведених прикладах ми використовуємо функцію calc() при роботі з числовими властивостями. Ми ще поговоримо про деякі особливості роботи з числовими властивостями (вони пов’язані з тим, що іноді не потрібно використання одиниць виміру). Зараз же відзначимо, що дана функція призначена для виконання операцій з числами, а не з рядками або з чимось ще.

.el {
  /* Невірно! */
  counter-reset: calc("My " + "counter");
}
.el::before {
  /* Невіно! */
  content: calc("Candyman " * 3);
}

Існує безліч одиниць виміру, які можна застосовувати в CSS для вказівки розмірів елементів і їх частин: px, %, em, rem, in, mm, cm, pt, pc, ex, ch, vh, vw, vmin, vmax. Всі ці одиниці вимірювання можна використовувати з функцією calc().

Ця функція вміє працювати і з числами, застосовуваними без вказівки одиниць виміру:

line-height: calc(1.2 * 1.2);

Її можна використовувати і для обчислення кутів:

transform: rotate(calc(10deg * 5));

Цю функцію можна застосовувати і в тих випадках, коли в переданому їй виразі ніяких обчислень не виконується:

.el {
  width: calc(20px);
}

Функцію calc() не можна застосовувати в медіа-запитах

Хоча ця функція призначена для встфновлення значень CSS-властивостей, вона, на жаль, не працює в медіа-запитах:

/* Не працює! */
@media (min-width: calc(40rem + 1px)) {
  /* Шише, за 40rem */
}

Якщо колись подібні конструкції виявляться працездатними – це буде дуже добре, так як це дозволить створювати взаємовиключні медіа-запити, що виглядають досить логічно (наприклад – такі, які показані вище).

Використання різних одиниць виміру в одному вираженні

Допустимість використання різних одиниць виміру в одному вираженні, ймовірно, можна назвати найціннішою можливістю calc(). Подібне застосовується майже в кожному з вищенаведених прикладів. Ось, просто для того, щоб звернути на це вашу увагу, ще один приклад, в якому показано використання різних одиниць виміру:

width: calc(100% - 20px);

Цей вислів читається так: «Ширина дорівнює ширині елемента, з якої віднімається 20 пікселів».

У ситуації, коли ширина елемента може змінюватися, абсолютно неможливо заздалегідь обчислити потрібне значення, користуючись тільки показниками, вираженими в пікселях. Іншими словами, не можна провести препроцессінг calc(), використовуючи щось на зразок Sass і намагаючись отримати щось на зразок поліфілла. Так робити цього і не потрібно, з огляду на те, що calc() дуже добре підтримують браузери. Сенс тут у тому, що подібні обчислення, в яких змішують значення, виражені в різних одиницях виміру, повинні бути зроблені в браузері (під час «виконання» сторінки). А саме в можливості виконання таких обчислень і полягає основна цінність calc().

Ось ще кілька прикладів використання значень, виражених в різних одиницях виміру:

transform: rotate(calc(1turn + 45deg));

animation-delay: calc(1s + 15ms);

Ці вирази, ймовірно, можна піддати препроцессінгу, так як в них змішані значення, одиниці виміру яких не пов’язані з чим-небудь, що визначаються під час роботи сторінки в браузері.

Порівняння calc() з обчисленнями, що обробляються препроцесорів

Ми тільки що сказали про те, що найкорисніша можливість calc() не піддається препроцессінгу. Але препроцессінг дає розробнику деякі можливості, що збігаються з можливостями calc(). Наприклад, при використанні Sass теж можна обчислювати значення властивостей:

$padding: 1rem;

.el[data-padding="extra"] {
  padding: $padding + 2rem;;
  margin-bottom: $padding * 2;; 
}

Тут можуть проводитися обчислення із зазначенням одиниць виміру, тут можна складати величини, виражені в одних і тих же одиницях виміру, можна множити якісь величини на значення, одиниці вимірювання яких не вказані. Але виконувати обчислення зі значеннями, вираженими в різних одиницях виміру, тут не можна. На подібні обчислення накладаються обмеження, що нагадують обмеження calc() (наприклад, числа, на які щось множать або ділять, повинні представляти собою значення без одиниць виміру).

Розкриття сенсу використовуваних в CSS числових значень

Навіть якщо не користуватися можливостями, які досяжні лише за допомогою calc(), цю функцію можна застосувати для розкриття сенсу застосовуваних в CSS значень. Припустимо, потрібно, щоб значення якогось властивості становило б в точності 1/7 ширини елемента:

.el {
  /* Це краще зрозуміти, */
  width: calc(100% / 7);

  /* ніж це */
  width: 14.2857142857%;
}

Такий підхід може виявитися корисним в чомусь схожому на якогось самописного CSS-API:

[data-columns="7"] .col { width: calc(100% / 7); }
[data-columns="6"] .col { width: calc(100% / 6); }
[data-columns="5"] .col { width: calc(100% / 5); }
[data-columns="4"] .col { width: calc(100% / 4); }
[data-columns="3"] .col { width: calc(100% / 3); }
[data-columns="2"] .col { width: calc(100% / 2); }

Математичні оператори функції calc()

У виразах, що обчислюються за допомогою calc(), можна використовувати оператори +, -, * і /. Але в їх застосуванні є деякі особливості.

При додаванні + і відніманні – необхідно використовувати значення з зазначеними одиницями вимірювання

.el {
  /* Вірно */
  margin: calc(10px + 10px);

  /* Невірно */
  margin: calc(10px + 5);
}

Зверніть увагу на те, що використання некоректних значень призводить до того, що конкретне оголошення також стає некоректним.

При розподілі / потрібно, щоб у другого числа не була б вказана одиниця виміру

.el {
  /* Вірно */
  margin: calc(30px / 3);

  /* Невірно */
  margin: calc(30px / 10px);

  /* Невірно (на ноль делити неможна) */
  margin: calc(30px / 0);
}

При множенні * у одного з чисел не повинна бути вказана одиниця виміру:

.el {
  /* Вірно */
  margin: calc(10px * 3);

  /* Вірно */
  margin: calc(3 * 10px);

  /* Невірно */
  margin: calc(30px * 3px);
}

Про важливість прогалин

Прогалини, які використовуються у виразах, важливі в операціях додавання і віднімання:

.el {
  /* Вірно */
  font-size: calc(3vw + 2px);

  /* Невірно */
  font-size: calc(3vw+2px);

  /* Вірно */
  font-size: calc(3vw - 2px);

  /* Невірно */
  font-size: calc(3vw-2px);
}

Тут цілком можна використовувати і негативні числа (наприклад – в конструкції на зразок calc(5vw — -5px)), але це – приклад ситуації, в якій прогалини не тільки необхідні, але ще й корисні в плані зрозумілості виразів.

Таб Аткінс сказав мені, що причина, по якій оператори додавання і віднімання повинні виділятися прогалинами, насправді, полягає в особливостях парсинга виразів. Не можу сказати, що я в повній мірі це зрозумів, але, наприклад, парсер обробляє вираз 2px-3px як число 2 з одиницею виміру px-3px. А настільки дивна одиниця виміру точно нікому не знадобиться. При парсінгу виразів з оператором складання виникають свої проблеми. Наприклад, парсер може прийняти цей оператор за частину синтаксичної конструкції, використовуваної при описі чисел. Я подумав було, що пробіл потрібен для правильної обробки синтаксису для користувача властивостей ( –), але це не так.

При множенні і діленні прогалини навколо операторів не потрібні. Але я вважаю, що можна порекомендувати використовувати прогалини і з цими операторами – заради підвищення читабельності коду, і заради того, щоб не забувати про прогалини і при введенні виразів, які використовують додавання і віднімання.

Прогалини, що відокремлюють дужки calc() від виразу, ніякої ролі не грають. Вираз, при бажанні, можна навіть виділити, перенісши на новий рядок:

.el {
  /* Можливо */
  width: calc(
    100%     /   3
  );
}

Правда, тут варто проявляти обережність. Між ім’ям функції calc і першої відкриває дужкою прогалин бути не повинно:

.el {
  /* Невірно */
  width: calc (100% / 3);
}

Вкладені конструкції: calc(calc())

Працюючи з функцією calc(), можна використовувати вкладені конструкції, але в цьому немає реальної необхідності. Це аналогічно використанню дужок без calc:

.el {
  width: calc(
    calc(100% / 3)
    -
    calc(1rem * 2)
  );
}

Немає сенсу будувати вкладені конструкції з функцій calc(), так як те ж саме можна переписати, використовуючи лише дужки:

.el {
  width: calc(
   (100% / 3)
    -
   (1rem * 2)
  );
}

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

.el {
  width: calc(100% / 3 - 1rem * 2);
}

Але в тому випадку, якщо здається, що додаткові дужки дозволяє зробити код зрозуміліше, їх цілком можна використовувати. Крім того, якщо без дужок, грунтуючись лише на пріоритеті операторів, вираз буде обчислюватися неправильно, то такий вислів потребує дужках:

.el {
  width: calc(100% + 2rem / 2);

  /* зовсім інакше */
  width: calc((100% + 2rem) / 2);
}

Призначені для користувача CSS-властивості і calc()

Ми вже дізналися про одну з чудових можливостей calc(), про обчисленнях, в яких використовуються значення з різними одиницями вимірів. Ще одна цікава можливість цієї функції полягає в тому, як її можна застосовувати до призначених для користувача CSS-властивостями. Призначеним для користувача властивостям можуть бути призначені значення, які можна використовувати в обчисленнях:

html {
  --spacing: 10px;
}

.module {
  padding: calc(var(--spacing) * 2);
}

Упевнений, нескладно уявити собі набір CSS-стилів, в якому безліч налаштувань виконується в одному місці за допомогою установки значень користувальницьких CSS-властивостей. Ці значення потім будуть використовуватися в усьому CSS-коді.

Призначені для користувача властивості, крім того, можуть посилатися один на одного (зверніть увагу на те, що calc() тут не використовується). Отримані значення можуть використовуватися для установки значень інших CSS-властивостей (а ось тут вже без calc() не обійтися).

html {
  --spacing: 10px;
  --spacing-L: var(--spacing) * 2;
  --spacing-XL: var(--spacing) * 3;
}

.module[data-spacing="XL"] {
  padding: calc(var(--spacing-XL));
}

Кому-то це може здатися не дуже зручним, так як при зверненні до призначеного для користувача властивості потрібно пам’ятати про calc(). Але я вважаю це цікавим з точки зору читабельності коду.

Призначення одиниць вимірювання призначеним для користувача властивостей після оголошення цих властивостей

Припустимо, ми знаходимося в ситуації, коли в призначене для користувача властивість має сенс записати число без одиниць виміру, або в ситуації, коли подібне число зручно буде, перед реальним використанням, як-то перетворити, не користуючись одиницями вимірами. У подібних випадках для користувача властивості можна призначити значення без вказівки одиниць виміру. Коли потрібно буде перетворити це число в нове, виражене в деяких одиницях виміру, його можна буде помножити на 1 з зазначенням потрібних одиниць виміру:

html {
  --importantNumber: 2;
}

.el {
  padding: calc(var(--importantNumber) * 1rem);
}

Робота з коліром

При описі коліру з використанням форматів, таких, як RGB і HSL, використовуються числа. З цими числами можна попрацювати в calc(). Наприклад, можна задати якісь базові HSL-значення, а потім міняти їх так, як потрібно (ось приклад):

html {
  --H: 100;
  --S: 100%;
  --L: 50%;
}

.el {
  background: hsl(
    calc(var(--H) + 20),
    calc(var(--S) - 10%),
    calc(var(--L) + 30%)
  )
}

Не можна комбінувати calc() і attr()

CSS-функція attr() може здаватися досить привабливою. І правда: береш значення атрибута з HTML, а потім його використовуєш. Але…

<div data-color="red">...</div>
div {
  /* Цього робити неможна */
  color: attr(data-color);
}

На жаль, ця функція не розуміє «типів» значень. В результаті attr() підходить лише для роботи з рядками і для установки з її допомогою CSS-властивості content. Тобто – така конструкція виявляється цілком робочої:

div::before {
  content: attr(data-color);
}

Я сказав тут про це через те, що у кого-небудь може виникнути бажання спробувати витягти з HTML-коду за допомогою attr() якесь число і використовувати його в обчисленнях:

<div class="grid" data-columns="7" data-gap="2">...</div>
.grid {
  display: grid;

  /* Ни один из этих примеров работать не будет */
  grid-template-columns: repeat(attr(data-columns), 1fr);
  grid-gap: calc(1rem * attr(data-gap));
}

Правда, добре те, що це особливого значення не має, так як призначені для користувача властивості в HTML відмінно підходять для виконання таких завдань:

<div class="grid" style="--columns: 7; --gap: 2rem;">...</div>
.grid {
display: grid;

/* Працює! */
grid-template-columns: repeat(var(--columns), 1fr);
grid-gap: calc(var(--gap));
}

Браузерна підтримка

Тут можна дізнатися про підтримку функції calc()браузерами. Якщо говорити про сучасних браузерах, то рівень підтримки calc()складає понад 97%. Якщо ж потрібно підтримувати досить старі браузери (на кшталт IE 8 або Firefox 3.6), тоді зазвичай надходять так: додають перед властивістю, для обчислення значення якого використовується calc(), таке ж властивість, значення якого задається в форматі, який зрозумілий старим браузерам:

.el {
  width: 92%;
  width: calc(100% - 2rem);
}

У функції calc() існує чимало відомих проблем, але від них страждають лише старі браузери. Ось деякі з них:

Браузер Firefox нижче 59 версії не підтримує calc() у функціях, які використовуються для завдання кольору. Наприклад: color: hsl(calc(60 * 2), 100%, 50%).
IE 9-11 НЕ отрендеріть тінь, задану властивість box-shadow, в тому випадку, якщо для визначення будь-якого із значень використовується calc().
Ні IE 9-11, ні Edge не підтримують конструкцію виду width: calc() в застосуванні до осередків таблиць.

Переклад статті “A Complete Guide to calc() in CSS

Reactjs, Material-UI з JSS.

Material-UI – ReactJS‘s фреймворк, що надає готові google рішення для швидкої і досить простий web розробки.

Material-UI досить велика бібліотека, де ключовою частиною react компонентів і стилізації є @material-ui/core (метою її використання повинен бути немаленький проект).

Цей туториал не включає в себе просунуте використання material-ui. Це міні мануал по стилізації компонентів.

Використовувати material компоненти дійсно не складно, але є нюанси використання стилізації. Про це власне і піде мова.

Розглянемо наступне

  1. JSS і трохи про синтаксис;
  2. Стилізація класiв react, material компонентів;
  3. Стилізація функціональних з використанням хуков react, material компонентів;
  4. Стилізація за допомогою styledComponent react, material компонентів;
  5. Провайдер theme;
  6. Перевизначення material компонентів;

1. JSS і трохи про синтаксис

JSS (бібліотека) – стилізація css мовою JavaScript у декларативному стилі вагою в 6кб, без урахування установки плагінів. JSS НЕ компілюється в inline-style і може повторно перевикористовуватися, уникаючи конфліктів, за рахунок генерації унікальних імен класів. Має можливість працювати за допомогою server-side в nodejs і має розширення [.js]. У JSS немає обмеження на установку стилів для псевдо-селектор або псевдо-елементів та подібному. Описується JSS в стилі camelCase. Однією з ключових можливостей JSS – можливість архітектурного підходу в описі стилів.

Так, і ця штука використовується в material-ui за для семантики.

Для того щоб почати використовувати JSS необхідно викликати його певний метод перекинувши в якості аргументу об’єкт стилів, який поверне функцію, викликавши яку ми отримаємо генератор JSS класів, наприклад:

/src/components/JssComponent
JssComponent.js

import React from "react";

import useStyles from "./style";

export default function JssComponent() {
  const classes = useStyles();

  return (
    <div className={classes.root}>
      <h1>That's JssComponent</h1>
      <h2 className="basic_h2">I use JSS</h2>
      <div className={classes.content_div}>
        Any Text
      </div>
    </div>
  );
}

style.js


import { createUseStyles } from "react-jss";

const rootStyle = {
  width: 400,
  border: "1px solid grey",
  margin: "0px auto",
  padding: 15
};

const fontStyle = {
  fontWeight: "bold",
  fontStyle: "italic"
};

const useStyles = createUseStyles({
  root: {
    ...rootStyle,
    "& h1": {
      textAlign: "center",
      color: "purple"
    },
    "& .basic_h2": {
      textAlign: "right",
      color: "green",
      cursor: "pointer",
      "&:hover": {
        color: "purple"
      }
    }
  },
  content_div: {
    textAlign: "justify",
    ...fontStyle
  }
});

export default useStyles;

2. Стилізація класів react, material компонентів

React не відмовився від підтримки написання інтерфейсів на класах. Наступним чином використовуються стилі в класових material компонентів:

/src/components/ClassComponent
ClassComponent.js

import React from "react";
import { withStyles } from "@material-ui/core/styles";
import Button from "@material-ui/core/Button";

import styles from "./style";

class ClassComponent extends React.Component {
  render() {
    const { classes } = this.props;

    return <Button className={classes.button}>ClassComponent</Button>;
  }
}

export default withStyles(styles)(ClassComponent);

style.js

// Простой js объект описывающий стили компонента
const styles = {
    button: {
      backgroundColor: "grey",
      border: 0,
      borderRadius: 3,
      boxShadow: "0 3px 5px 2px",
      color: "black",
      height: 48,
      padding: "0 30px",
      margin: 10
    }
  };
  
  export default styles;

3. Стилізація функціональних material компонентів з використанням хуков react

Починаючи з 16.8 релізу React з’явилася нова можливість в написанні інтерфейсів на хуках:

/src/components/HookComponent
HookComponent.js

import React from "react";
import Button from "@material-ui/core/Button";

import useStyles from "./style";

const HookComponent = props => {
  const classes = useStyles();

  return <Button className={classes.button}>HookComponent</Button>;
};

export default HookComponent;

style.js

// Импортируем метод для создания функции, вызвав которую мы получим функцию для генерации классов 
import { makeStyles } from "@material-ui/core/styles";

const useStyles = makeStyles({
  button: {
    backgroundColor: "grey",
    border: 0,
    borderRadius: 3,
    boxShadow: "0 3px 5px 2px",
    color: "black",
    height: 48,
    padding: "0 30px",
    margin: 10
  }
});

export default useStyles;

4. Стилізація за допомогою styledComponent react, material компонентів

styledComponent – принцип стилізації компонентів без методу, який повертає функцію для генерації класів.

/src/components/StyledComponent
StyledComponent.js

import React from "react";
// Импортируем метод для стилизации любого типа компонентов
import { styled } from "@material-ui/core/styles";
// Предоставляем компонент для стилизации
import Button from "@material-ui/core/Button";

import styles from './style';

// Стилизованный компонент на все времена
const ButtonComponent = styled(Button)({ ...styles });

export default function StyledComponent() {
  return <ButtonComponent>StyledComponent</ButtonComponent>;
}

style.js


const styles = {
    backgroundColor: "grey",
    border: 0,
    borderRadius: 3,
    boxShadow: "0 3px 5px 2px",
    color: "black",
    height: 48,
    padding: "0 30px",
    margin: 10
  };
  export default styles;

5. Провайдер theme.

Theming – це надання дефолтних стилів проекту, або його частини, з огляду на вкладеність компонентів, концептуально схожий з контекстом в React. У material-ui вже є готовий об’єкт theme заданий за замовчуванням, але ви можете створити його самі під свої потреби, попередньо забезпечивши його доступність через provider, таким чином ви надасте функцію з об’єктом theme як аргумент, для методів material з написання стилів, наприклад :

/src/components
App.js

import React from 'react';
import { ThemeProvider } from "@material-ui/core/styles";

import  ThemedComponent from './ThemedComponent/ThemedComponent';
import theme from './theme';

function App() {
  return (
        <ThemeProvider theme={theme}>
          < ThemedComponent />  
        </ThemeProvider>
  );
}

export default App;

theme.js

// Простой js объект дэфолтных стилей
const theme = {
    button: {
      backgroundColor: "grey",
      border: 0,
      borderRadius: 3,
      boxShadow: "0 3px 5px 2px",
      color: "black",
      height: 48,
      padding: "0 30px"
    }
  };

  export default theme;

/src/components/ThemedComponent
ThemedComponent.js

import React from "react";

import useStyles from "./style";

const ThemedComponent = props => {
  const classes = useStyles();

  return <button className={classes.button}>ThemedComponent</button>;
};

export default ThemedComponent;

style.js

// Метод принимает функцию с агрументом theme
import { makeStyles } from "@material-ui/core/styles";

const useStyles = makeStyles(theme => {
  return {
    button: { ...theme.button, margin: 10 }
  };
});

export default useStyles;

6. Перевизначення material компонентів

У material-ui кожному компоненту вже встановлені відповідні стилі, тобто імпорт будь-якого material компонента надасть вже стилізований компонент з наявними класами. Створення theme за допомогою спеціального api material - createMuiTheme можна не тільки надати theme, але і перевизначати задані дефолтні класи material компонентів за допомогою спеціального властивості overrides, наприклад:

/src/components
App.js

import React from 'react';
import { ThemeProvider } from "@material-ui/core/styles";

import CreatedThemeComponent from './CreatedThemeComponent/CreatedThemeComponent';
import createdTheme from './createdTheme';

function App() {
  return (
        <ThemeProvider theme={createdTheme}>
          <CreatedThemeComponent />  
        </ThemeProvider>
  );
}

export default App;

/ src / components
createdTheme.js

import { createMuiTheme } from "@material-ui/core/styles";

const createdTheme = createMuiTheme({
    overrides: {
      MuiButton: {
        root: {
          color: "red",
          border: "1px solid grey",
          margin: 10
        }
      }
    }
  });
  
export default createdTheme;

/src/components/CreatedThemeComponent
CreatedThemeComponent.js

import React from "react";
import { Button } from "@material-ui/core";

const CreatedThemeComponent = props => {
  return <Button>CreatedThemeComponent</Button>;
};

export default CreatedThemeComponent;

підсумки:

  • JSS і його особливості;
  • JSS стилізація react, material компонентів за рахунок api material-ui;

Переклад статті “Reactjs, Material-UI with JSS. Краткий гайд

Посібник для покупців біткойнів у 2020 році

2020 рік обіцяє біткойну багато. Напевно, ви вже в курсі, що в травні очікується халвінг біткойну, тобто скорочення вдвічі винагороди за Майнінг нових монет. Що наближається економічна криза і політична невизначеність в США і по всьому світу надають ідеальні умови для різкого зростання ціни біткойну в 2020р і далі.

Посібник для покупців біткойнів у 2020 році

№1: Розберіться, що насправді таке біткойн

Занадто багато хто прийшов на ринок біткойну, навіть не розібравшись, що він із себе представляє. Ці люди просто вважають біткойн новим і високоефективним активом, яким не слід нехтувати.

За останнє десятиліття ціна біткойну виросла з $ 1 до $ 10k. Історичний максимум в кінці 2017 року на деяких кріптовалютних біржах взагалі досягав $ 20 000.

Всі ці цифри феноменальні і викликають запаморочення у трейдерів зі світу традиційних фінансів.

Однак ці цифри не відображають революційну складову біткойну. Показників зростання ціни недостатньо, щоб передати, що біткойн може дати світові в майбутньому.

Якщо ви вирішили купити біткойни, вам слід, перш за все, розібратися, як біткойн будує більш справедливу грошову систему для всіх. Необхідно зрозуміти, чому поточна грошово-фінансова система недосконала і не підлягає виправленню. Коли ви у всьому цьому розберетеся, ви зрозумієте, що біткойн – це набагато більше, ніж просто об’єкт інвестування.

№2: Приділіть час на вивчення принципів роботи біткойну

Якщо ви розібралися, що насправді таке біткойн і чому він надає унікальну можливість зробити світ в майбутньому справедливіше, настійно рекомендую на цьому не зупинятися.

Не шкодуйте часу, щоб дізнатися, як біткойнов працює.

Для цього немає нічого кращого, ніж прочитання опублікованої в 2009 р Сатоши Накамото вайтпейпера «біткойн: цифрова пірінгова готівки». Цей документ містить повний огляд внутрішніх принципів роботи блокчейна біткойну.

Прочитавши цей документ, ви, перш за все, зрозумієте, як транзакції пов’язані один з одним в блоках. Також ви дізнаєтеся, як блоки отримують мітку часу, перш ніж будуть пов’язані незмінним чином.

У документі також представлена основа консенсусного алгоритму, використовуваного блокчейном біткойну: знамените доказ виконання роботи PoW, що знаходиться в центрі всього процесу Майнінг.

Також докладно пояснюється концепція винагород для Майнер, що підтверджують блоки транзакцій, обумовлених необхідністю мотивувати людей на участь в мережі біткойну і надання своєї обчислювальної потужності для забезпечення її безпеки.

Документ також описує організацію мережі біткойну і дозволяє зрозуміти, чому вузли вважають найдовшу ланцюжок транзакцій коректної і такий, яку слід продовжувати.

Нарешті, в вайтпейпере Сатоши Накамото обговорюється необхідність в прозорості всіх транзакцій в блокчейне біткойну для публіки, щоб позбутися від довірених третіх сторін, присутніх в традиційних платіжних системах.

Важливо прочитати вайтпейпер, тому що це дасть вам технічні основи для докладного розуміння роботи блокчейна біткойну, а також надасть ключові концепції, які необхідно знати для можливих подальших поглиблених досліджень тих чи інших тем.

№3: Не будьте жадібними до біткойну

Феноменальний успіх біткойну за останнє десятиліття зводив людей з розуму. Число людей, які сподіваються стати мільйонерами завдяки біткойну, до сих пір високо. Але, як я вже пояснював, ці люди з-за такого ставлення не беруть до уваги головне.

Кінець 2017 р – ідеальний приклад того, про що я тільки що написав. Скільки людей купували біткойни, керуючись виключно власною жадібністю, і потім розчарувалися і майже все втратили?

Подібні пригоди наздогнали дуже багатьох. Деякі стали жертвами безсовісних шахраїв, повіривши в чудо. Інші втратили все, забувши про безпеку своїх біткойнів. Нарешті, деякі просто здалися, що призвело до ще більш швидкому хляпанню спекулятивної бульбашки, яка виникла навколо біткойну в кінці 2017 р

Як би там не було, з біткойнов не можна потрапляти в пастку жадібності. Не забувайте, що до біткойнов застосовно головне правило інвестування: «Не інвестуйте гроші, які не можете дозволити собі втратити».

№4: Приймайте рішення про покупку самостійно

Біткойн все ще дуже молодий, і його ринок залишається надзвичайно волатильним. Тому, коли ціни зазнають суттєвих змін вгору або вниз, соціальні мережі починає лихоманити. Якщо не будете обережні, можна заразитися страхом втраченої вигоди.

Страх втраченої вигоди змушує робити поганий вибір при покупці біткойнів. Його слід уникати будь-яку ціну.

Кращий рада, який тут можна дати, – купувати біткойни виходячи виключно з власних інстинктів.

Не виконуйте чужих порад. Складіть власну думку, проаналізувавши ринок, і прийміть рішення, яке не обов’язково буде вірним, але воно буде вашим і вся відповідальність буде на вас.

Якщо ви не можете ігнорувати те, що говорять про біткойн в соцмережах, досить відключити свій аккаунт в Твіттері або Фейсбуці, щоб не піддаватися масовим ефектів, що виникають там, коли ринок біткойну виходить з-під контролю.

№5: Не будьте емоційні при покупці біткойнів

У будь-якому інвестуванні необхідно упевнитися, що ви не дієте емоційно. Емоції часто поганий радник. Ваші рішення повинні бути раціональними. Якщо ви втомилися, відкладіть прийняття рішень про покупку або продаж.

Можна купувати біткойни, тому що ви хочете інвестувати і отримати прибуток. Можна купувати їх, тому що ви вірите в біткойн революцію і хочете приєднатися до табору Ходлер. Що б ви не вибрали, повністю віддавайте собі в цьому звіт і не піддавайтеся емоціям.

Ви будете приймати кращі рішення, якщо будете дотримуватися власних стратегій і в будь-якій ситуації діяти раціонально.

№6: Зберігайте біткойни на апаратній гаманці

Неухильно дотримуючись першим правилам цього керівництва з виживання в світі біткойну, ви краще зрозумієте, як забезпечується безпека ваших біткойнів за допомогою системи публічних і приватних ключів.

Таким чином, ви зрозумієте наступну фразу, таку поширену в світі біткойнов:

«Не ваші ключі – не ваші біткойни».

Так, якщо ви тримаєте біткойни на біржовий платформі, такий як Coinbase або Binance, то вони насправді вам не належать, тому що у вас немає ключів від них. Сумний досвід зломів торгових платформ, якими відзначена історія біткойну, повинен спонукати вас дуже серйозно ставитися до безпеки ваших біткойнів.

Щоб убезпечити ваші біткойни, немає нічого кращого, ніж зберігати їх на апаратній гаманці, такому як Ledger Nano S або Trezor One.

Як би там не було, варто запам’ятати важливе правило: ніколи не зберігайте ваші біткойни на біржі довше, ніж необхідно.

№7: Будьте терплячі і ставитеся до біткойну як до довгострокової інвестиції

Останнє правило особливо важливо, якщо ви збираєтеся вперше купити біткойни в 2020 р Ви повинні розуміти, що біткойн будує більш справедливу грошово-фінансову систему майбутнього. Майбутнє будується не за один день.

Біткойн прийшов надовго, тому необхідно бути терплячим і грати довгостроково.

Не робіть помилку, весь час стежачи за ціною активу. Якщо ви купили біткойни, помістіть їх в безпечне місце і мислите довгостроково. Прийміть стратегію Ходлер біткойну, яка в 90% випадків виявлялася найкращою. Будьте терплячі і станьте біткойнерамі, повіривши в кращий світ майбутнього, в центрі якого буде стояти біткойн.

Висновки

Рік, що настав обіцяє стати для біткойну видатним. Якщо ви вже біткойнер, то не буде зайвим згадати правила виживання в безжальному світі біткойну. Якщо ж ви новий інвестор в цій галузі, який бажає придбати свої перші біткойни, то цей посібник з виживання дозволить вам зробити перші кроки в цьому світі з повним знанням фактів.

Сподіваємося, так ви зрозумієте, що біткойн – це революція для завтрашнього світу, і, головне, зможете уникнути головних пасток, в які потрапляє переважна більшість інвесторів через неуцтво і жадібності. Так що не залишайтеся в невіданні щодо біткойну і завжди прагнете поглибити свої знання. У разі біткойну як ніколи справедливо наступне правило:

«Те, чого ви не знаєте, чи варто вам грошей».

Переклад статті “A Short Survival Guide For Bitcoin Buyers In 2020 And Beyond

Створення власних синтаксичних конструкцій для JavaScript з використанням Babel

Oгляд

З початку давайте поглянемо на те, чого ми доб’ємося, діставшись до кінця цього матеріалу:

function @@ foo(a, b, c) {
  return a + b + c;
}
console.log(foo(1, 2)(3)); // 6

Ми збираємося реалізувати синтаксичну конструкцію @@, яка дозволяє каррірувати функції. Цей синтаксис схожий на той, що використовується для створення функцій-генераторів , але в нашому випадку замість знака * між ключовим словом function і ім’ям функції розміщується послідовність символів @@. В результаті при оголошенні функцій можна використовувати конструкцію виду function @@ name(arg1, arg2).

У вищенаведеному прикладі при роботі з функцією foo можна скористатися її частковим застосуванням . Виклик функції foo з передачею їй такої кількості параметрів, яке менше ніж кількість необхідних їй аргументів, призведе до повернення нової функції, здатної прийняти залишилися аргументи:

foo(1, 2, 3); // 6

const bar = foo(1, 2); // (n) => 1 + 2 + n
bar(3); // 6

Я вибрав саме послідовність символів @@ тому, що в іменах змінних можна використовувати символ @. Це означає, що синтаксично коректно виявиться і конструкція виду function@@foo(){}. Крім того, «оператор» @ застосовується для функцій-декораторів , а мені хотілося використовувати щось абсолютно нове. В результаті я і вибрав конструкцію @@.

Для того щоб домогтися поставленої мети, нам потрібно виконати наступні дії:

  • Створити форк парсеру Babel.
  • Створити власний плагін Babel для трансформації коду.

Виглядає як щось неможливе?
Насправді, нічого страшного тут немає, ми разом все детально розберемо. Я сподіваюся, що ви, коли це дочитаєте, будете майстерно володіти тонкощами Babel.

Створення ФОРКу Babel

Зайдіть в репозиторій Babel на GitHub і натисніть на кнопку Fork, яка знаходиться в лівій верхній частині сторінки.

Створення ФОРКу Babel
Створення ФОРКу Babel

І, до речі, якщо тільки що ви вперше створили форк популярного опенсорсний проекту – прийміть вітання!

Тепер клонуйте форк Babel на свій комп’ютер і підготуйте його до роботи .

$ git clone https://github.com/tanhauhau/babel.git

# set up
$ cd babel
$ make bootstrap
$ make build

Зараз дозвольте мені в двох словах розповісти про організацію сховища Babel.

Babel використовує монорепозіторій. Всі пакети (наприклад – @babel/core@babel/parser@babel/plugin-transform-react-jsxі так далі) розташовані в папці packages/. Виглядає це так:

- doc
- packages
  - babel-core
  - babel-parser
  - babel-plugin-transform-react-jsx
  - ...
- Gulpfile.js
- Makefile
- ...

Зазначу, що в Babel для автоматизації завдань використовується Makefile . При складанні проекту, що виконується командою make build, в якості менеджера завдань використовується Gulp .

Короткий курс по перетворенню коду в AST

Якщо ви не знайомі з такими поняттями, як «парсер» і «абстрактне синтаксичне дерево» (Abstract Syntax Tree, AST), то, перш ніж продовжувати читання, я настійно рекомендую вам поглянути на цей матеріал.

Якщо дуже коротко розповісти про те, що відбувається при парсінгу (синтаксичному аналізі) коду, то вийде наступне:

  • Код, представлений у вигляді рядка (тип string), виглядає як довгий список символів:f, u, n, c, t, i, o, n, , @, @, f, ...
  • На самому початку Babel виконує токенізацію коду. На цьому кроці Babel переглядає код та створює маркери. Наприклад – щось на кшталт function, @@, foo, (, a, ...
  • Потім токени пропускають через парсер для їх синтаксичного аналізу. Тут Babel, на основі специфікації мови JavaScript, створює абстрактне синтаксичне дерево.

Ось відмінний ресурс для тих, хто хоче більше дізнатися про компіляторах.

Якщо ви думаєте, що «компілятор» – це щось дуже складне і незрозуміле, то знайте, що насправді все не так вже й таємниче. Компіляція – це просто парсинг коду і створення на його основі нового коду, який ми назвемо XXX. XXX-код може бути представлений машинним кодом (мабуть, саме машинний код – це те, що першим спливає в свідомості більшості з нас при думці про компіляторі). Це може бути JavaScript-код, сумісний з застарілими браузерами. Власне, однією з основних функцій Babel є компіляція сучасного JS-коду в код, зрозумілий застарілим браузерам.

Розробка власного парсеру для Babel

Ми збираємося працювати в папці packages/babel-parser/:

- src/
  - tokenizer/
  - parser/
  - plugins/
    - jsx/
    - typescript/
    - flow/
    - ...
- test/

Ми вже говорили про токенізаціі і про парсінгу. Знайти код, який реалізує ці процеси, можна в папках з відповідними іменами. В папці plugins/ містяться плагіни (Plug-in), які розширюють можливості базового парсеру і додають в систему підтримку додаткових синтаксисів. Саме так, наприклад, реалізована підтримка jsx і flow.

Давайте вирішимо нашу задачу, скориставшись технікою розробки через тестування (Test-driven development, TDD). По-моєму, легше за все спочатку написати тест, а потім, поступово працюючи над системою, зробити так, щоб цей тест виконувався б без помилок. Такий підхід особливо хороший при роботі в незнайомій кодової базі. TDD спрощує розуміння того, в які місця коду потрібно внести зміни для реалізації задуманого функціоналу.

packages/babel-parser/test/curry-function.js

import { parse } from '../lib';

function getParser(code) {
  return () => parse(code, { sourceType: 'module' });
}

describe('curry function syntax', function() {
  it('should parse', function() {
    expect(getParser(`function @@ foo() {}`)()).toMatchSnapshot();
  });
});

Запуск тесту для babel-parserможна виконати так: TEST_ONLY=babel-parser TEST_GREP="curry function" make test-only. Це дозволить побачити помилки:

SyntaxError: Unexpected token (1:9)

at Parser.raise (packages/babel-parser/src/parser/location.js:39:63)
at Parser.raise [as unexpected] (packages/babel-parser/src/parser/util.js:133:16)
at Parser.unexpected [as parseIdentifierName] (packages/babel-parser/src/parser/expression.js:2090:18)
at Parser.parseIdentifierName [as parseIdentifier] (packages/babel-parser/src/parser/expression.js:2052:23)
at Parser.parseIdentifier (packages/babel-parser/src/parser/statement.js:1096:52)

Якщо ви виявите, що перегляд всіх тестів займає надто багато часу, то можете, для запуску потрібного тесту, викликати jest безпосередньо:

BABEL_ENV=test node_modules/.bin/jest -u packages/babel-parser/test/curry-function.js

Наш парсер виявив 2 токена @, начебто безневинних, там, де їх бути не повинно.

Звідки я це дізнався? Відповідь на це питання нам допоможе знайти використання режиму моніторингу коду, що запускається командою make watch.

Перегляд стека викликів призводить нас до packages / babel-parser / src / parser / expression.js, де викидається виключення this.unexpected().

Додамо в цей файл пару команд логування:

packages/babel-parser/src/parser/expression.js

parseIdentifierName(pos: number, liberal?: boolean): string {
  if (this.match(tt.name)) {
    // ...
  } else {
    console.log(this.state.type); // текущий токен
    console.log(this.lookahead().type); // следующий токен
    throw this.unexpected();
  }
}

Як видно, обидва токена – це @:

TokenType {
  label: '@',
  // ...
}

Як я дізнався про те, що конструкції this.state.type і this.lookahead().type дадуть мені поточний і наступний токени?
Про це я розповім в розділі даного матеріалу, присвяченому функціям this.eatthis.matchі this.next.

Перш ніж продовжувати – давайте підведемо короткі підсумки:

  • Ми написали тест для babel-parser.
  • Ми запустили тест за допомогою make test-only.
  • Ми скористалися режимом моніторингу коду за допомогою make watch.
  • Ми дізналися про стан парсера і вивели в консоль відомості про тип поточного токена ( this.state.type).

А зараз ми зробимо так, щоб 2 символи @сприймалися б не як окремі маркери, а як новий токен @@, той, який ми вирішили використовувати для каррінг функцій.

Новий токен: «@@»

Для початку заглянемо туди, де визначаються типи токенів. Мова йде про фото packages / babel-parser / src / tokenizer / types.js .

Тут можна знайти список токенов. Додамо сюди і визначення нового токена atat:

packages/babel-parser/src/tokenizer/types.js

export const types: { [name: string]: TokenType } = {
  // ...
  at: new TokenType('@'),
  atat: new TokenType('@@'),
};

Тепер давайте пошукаємо то місце коду, де, в процесі токенізаціі, створюються токени. Пошук послідовності символів tt.atв babel-parser/src/tokenizer приводить нас до файлу: packages / babel-parser / src / tokenizer / index.js . У babel-parser типи токенів імпортуються як tt.

Тепер, в тому випадку, якщо після поточного символу @ йде ще один @, створимо новий токен tt.atat замість токена tt.at:

packages/babel-parser/src/tokenizer/index.js

getTokenFromCode(code: number): void {
  switch (code) {
    // ...

    case charCodes.atSign:
      // якщо наступний символ - это `@`
      if (this.input.charCodeAt(this.state.pos + 1) === charCodes.atSign) {
        // создадим `tt.atat` вместо `tt.at`
        this.finishOp(tt.atat, 2);
      } else {
        this.finishOp(tt.at, 1);
      }
      return;
    // ...

  }
}

Якщо знову запустити тест – то можна помітити, що відомості про поточному і наступному токенах змінилися:

// поточний токен
TokenType {
  label: '@@',
  // ...
}

// наступний токен
TokenType {
  label: 'name',
  // ...
}

Це вже виглядає досить-таки непогано. Продовжимо роботу.

Новий парсер

Перш ніж рухатися далі – поглянемо на те, як функції-генератори представлені в AST.

AST для функції-генератора
AST для функції-генератора

Як бачите, на те, що це – функція-генератор, вказує атрибут generator: true сутності FunctionDeclaration.

Ми можемо застосувати аналогічний підхід для опису функції, що підтримує каррінг. А саме, ми можемо додати до FunctionDeclaration атрибут curry: true.

AST для функції, що підтримує каррінг
AST для функції, що підтримує каррінг

Власне кажучи, тепер у нас є план. Займемося його реалізацією.

Якщо пошукати в коді по слову FunctionDeclaration – можна вийти на функцію parseFunction, яка оголошена в packages / babel-parser / src / parser / statement.js . Тут можна знайти рядок, в якій встановлюється атрибут generator.  Додамо в код ще один рядок:

packages/babel-parser/src/parser/statement.js

export default class StatementParser extends ExpressionParser {
  // ...
  parseFunction<T: N.NormalFunction>(
    node: T,
    statement?: number = FUNC_NO_FLAGS,
    isAsync?: boolean = false
  ): T {
    // ...
    node.generator = this.eat(tt.star);
    node.curry = this.eat(tt.atat);
  }
}

Якщо ми знову запустимо тест, то нас чекатиме приємна несподіванка. Код успішно проходить тестування!

PASS  packages/babel-parser/test/curry-function.js
  curry function syntax
    ✓ should parse (12ms)

І це все? Що ми такого зробили, щоб тест дивним чином опинився пройденим?

Для того щоб це з’ясувати – давайте поговоримо про те, як працює парсинг. В процесі цієї розмови, сподіваюся, ви зрозумієте те, як подіяла на Babel рядок node.curry = this.eat(tt.atat);.

Як працює парсинг

Парсер отримує список токенов від системи токенізаціі коду і, по одному розглядаючи токени, будує AST. Для того щоб прийняти рішення про те, як саме використовувати маркери, і зрозуміти, який токен можна очікувати наступним, парсер звертається до специфікації граматики мови.

Специфікація граматики виглядає приблизно так:

...
ExponentiationExpression -> UnaryExpression
                            UpdateExpression ** ExponentiationExpression
MultiplicativeExpression -> ExponentiationExpression
                            MultiplicativeExpression ("*" or "/" or "%") ExponentiationExpression
AdditiveExpression       -> MultiplicativeExpression
                            AdditiveExpression + MultiplicativeExpression
                            AdditiveExpression - MultiplicativeExpression
...

Вона описує пріоритет виконання виразів або операторів. Наприклад, вираз AdditiveExpression може представляти одна з наступних конструкцій:

  • Вираз MultiplicativeExpression.
  • Вираз AdditiveExpression, за яким слід токен оператора « +», за яким слід вираз MultiplicativeExpression.
  • Вираз AdditiveExpression, за яким слід токен « -», за яким слід вираз MultiplicativeExpression.

В результаті, якщо у нас є вираз 1 + 2 * 3, то воно буде виглядати так:

(AdditiveExpression "+" 1 (MultiplicativeExpression "*" 2 3))

А ось таким воно не буде:

(MultiplicativeExpression "*" (AdditiveExpression "+" 1 2) 3)

Програма, з використанням цих правил, перетворюється в код, що видається парсером:

class Parser {
  // ...
  parseAdditiveExpression() {
    const left = this.parseMultiplicativeExpression();
    // якщо поточний токен - це `+` чи `-`
    if (this.match(tt.plus) || this.match(tt.minus)) {
      const operator = this.state.type;
      // перейти до наступного токену
      this.nextToken();
      const right = this.parseMultiplicativeExpression();

      // создати вузол
      this.finishNode(
        {
          operator,
          left,
          right,
        },
        'BinaryExpression'
      );
    } else {
      // вернути MultiplicativeExpression
      return left;
    }
  }
}

Зверніть увагу на те, що тут наведено надзвичайно спрощений варіант того, що насправді присутній в Babel. Але я сподіваюся, що цей фрагмент коду дозволяє проілюструвати сутність того, що відбувається.

Як бачите, парсер, за своєю природою, рекурсівен. Він переходить від конструкцій з найнижчим пріоритетом до конструкцій з найвищим пріоритетом. Наприклад – parseAdditiveExpression викликає parseMultiplicativeExpression, а ця конструкція викликає parseExponentiationExpression і так далі. Цей рекурсивний процес називають синтаксичним аналізом методом рекурсивного спуску ( Recursive Descent Parsing ).

Функції this.eat, this.match, this.next

Можливо, ви помітили, що в раніше наведених прикладах використовувалися деякі допоміжні функції, такі, як this.eatthis.matchthis.nextта інші. Це – внутрішні функції парсера Babel. Подібні функції, правда, не унікальні для Babel, вони зазвичай присутні і в інших парсером.

  • Функція this.matchповертає логічне значення, яке вказує на те, чи відповідає поточний токен заданій умові.
  • Функція this.nextздійснює переміщення за списком токенов вперед, до наступного токені.
  • Функція this.eatповертає той же, що повертає функція this.match, при цьому, якщо this.matchповертає true, то this.eatвиконує, перед поверненням true, виклик this.next.
  • Функція this.lookaheadдозволяє отримати наступний токен без переміщення вперед, що допомагає прийняти рішення по поточному вузлу.

Якщо ви знову поглянете на змінений нами код парсера, то виявите, що читати його стало набагато легше:

packages/babel-parser/src/parser/statement.js

export default class StatementParser extends ExpressionParser {
  parseStatementContent(/* ...*/) {
    // ...
    // NOTE: мы викликаємо match для перевірки поточного токену
    if (this.match(tt._function)) {
      this.next();
      // NOTE: у оголошення функції пріоритет вище, ніж у звичайного виразу
      this.parseFunction();
    }
  }
  // ...
  parseFunction(/* ... */) {
    // NOTE: ми викликаємо eat для перевірки існування необов'язкового токена
    node.generator = this.eat(tt.star);
    node.curry = this.eat(tt.atat);
    node.id = this.parseFunctionId();
  }
}

Можливо, вам цікаво дізнатися про те, як я зміг візуалізувати створений мною синтаксис в Babel AST Explorer, коли показував новий атрибут « curry», що з’явився в AST.

Це стало можливим завдяки тому, що я додав в Babel AST Explorer нову можливість, яка дозволяє завантажити в це засіб дослідження AST власний парсер.

Якщо перейти по шляху packages/babel-parser/lib, то можна знайти скомпільовану версію парсеру і карту коду. В панелі Babel AST Explorerможна побачити кнопку для завантаження власного парсеру. завантажившиpackages/babel-parser/lib/index.js можна візуалізувати AST, сгенерированное за допомогою власного парсеру.

Візуалізація AST
Візуалізація AST

Наш плагін для Babel

Тепер, коли завершена робота над парсером – давайте напишемо плагін для Babel.

Але, можливо, зараз у вас є деякі сумніви щодо того, як саме ми збираємося користуватися власним парсером Babel, особливо з огляду на те, який саме стек технологій ми застосовуємо для збірки проекту.

Правда, побоюватися тут нема чого. Плагін Babel може надавати можливості парсеру. Відповідну документацію можна знайти на сайті Babel.

babel-plugin-transformation-curry-function.js

import customParser from './custom-parser';

export default function ourBabelPlugin() {
  return {
    parserOverride(code, opts) {
      return customParser.parse(code, opts);
    },
  };
}

Так як ми створили форк парсеру Babel, це означає, що всі існуючі можливості парсеру, а також вбудовані плагіни, продовжать працювати абсолютно нормально.

Після того, як ми позбулися цих сумнівів, давайте поглянемо на те, як зробити функцію такою, щоб вона підтримувала б каррінг.

Якщо ви не винесли очікування і вже спробували додати наш плагін в свою систему збирання проекту, ви могли помітити, що функції, які підтримують каррінг, компілюються в звичайні функції.

Відбувається це через те, що після парсинга і трансформації коду Babel використовує @babel/generatorдля генерування коду з трансформованого AST. Так як @babel/generatorнічого не знає про новий атрибут curry, він його просто ігнорує.

Якщо коли-небудь функції, що підтримують каррінг, увійдуть в стандарт JavaScript, то вам, можливо, захочеться зробити PR на додавання сюди нового коду.

Для того щоб зробити так, щоб функція підтримувала б каррінг, її можна обернути в функцію вищого порядку currying:

function currying(fn) {
  const numParamsRequired = fn.length;
  function curryFactory(params) {
    return function (...args) {
      const newParams = params.concat(args);
      if (newParams.length >= numParamsRequired) {
        return fn(...newParams);
      }
      return curryFactory(newParams);
    }
  }
  return curryFactory([]);
}

Якщо вас цікавлять особливості реалізації механізму каррінг функцій в JS – погляньте на цей матеріал.

В результаті ми, перетворюючи функцію, яка підтримує каррінг, можемо вчинити так:

// з цього
function @@ foo(a, b, c) {
  return a + b + c;
}

// отримуєм це
const foo = currying(function foo(a, b, c) {
  return a + b + c;
})

Поки не звертатимемо на механізм підняття функцій в JavaScript, який дозволяє викликати функцію fooдо її визначення.

Ось як виглядає код трансформації:

babel-plugin-transformation-curry-function.js

export default function ourBabelPlugin() {
  return {
    // ...
<i>    visitor: {
      FunctionDeclaration(path) {
        if (path.get('curry').node) {
          // const foo = curry(function () { ... });
          path.node.curry = false;
          path.replaceWith(
            t.variableDeclaration('const', [
              t.variableDeclarator(
                t.identifier(path.get('id.name').node),
                t.callExpression(t.identifier('currying'), [
                  t.toExpression(path.node),
                ])
              ),
            ])
          );
        }
      },
    },</i>
  };
}

Вам набагато легше буде в ньому розібратися в тому випадку, якщо ви читали цей матеріал про трансформації в Babel.

Тепер перед нами виникає питання про те, як надати цьому механізму доступ до функції currying. Тут можна скористатися одним з двох підходів.

№1: можна припустити, що функція currying оголошена в глобальному контексті

Якщо це так, то справа вже зроблена.

Якщо ж при виконанні скомпільованої коду виявляється, що функція curryingне визначена, то ми зіткнемося з повідомленням про помилку, дивлячому як « currying is not defined». Воно дуже схоже на повідомлення « regeneratorRuntime is not defined ».

Тому, якщо хтось буде користуватися вашим плагіном babel-plugin-transformation-curry-function, то вам, можливо, доведеться повідомити йому про те, що йому, для забезпечення нормальної роботи цього плагіна, потрібно встановити поліфілл currying.

№2: можна скористатися babel / helpers

Можна додати нову допоміжну функцію в @babel/helpers. Ця розробка навряд чи буде об’єднана з офіційним репозиторієм @babel/helpers. В результаті вам доведеться знайти спосіб показати @babel/coreмісце розташування вашого коду @babel/helpers:

package.json

{
  "resolutions": {
    "@babel/helpers": "7.6.0--your-custom-forked-version",
  }

Я сам це не пробував, але думаю, що цей механізм буде працювати.

Додати нову допоміжну функцію в @babel/helpersдуже просто.

Спочатку треба перейти в файл packages / babel-helpers / src / helpers.js і додати туди новий запис:

helpers.currying = helper("7.6.0")`
  export default function currying(fn) {
    const numParamsRequired = fn.length;
    function curryFactory(params) {
      return function (...args) {
        const newParams = params.concat(args);
        if (newParams.length >= numParamsRequired) {
          return fn(...newParams);
        }
        return curryFactory(newParams);
      }
    }
    return curryFactory([]);
  }
`;

При описі допоміжної функції вказується необхідна версія @babel/core. Деякі складнощі тут може викликати експорт за замовчуванням ( export default) функції currying.

Для використання допоміжної функції досить просто викликати this.addHelper():

// ...
path.replaceWith(
  t.variableDeclaration('const', [
    t.variableDeclarator(
      t.identifier(path.get('id.name').node),
      t.callExpression(this.addHelper("currying"), [
        t.toExpression(path.node),
      ])
    ),
  ])
);

Команда this.addHelper, при необхідності, запровадить допоміжну функцію в верхню частину файлу, і поверне Identifier, який вказує на впроваджену функцію.

Підсумки

Тут ми поговорили про те, як модифікувати можливості парсеру Babel, ми написали власний плагін трансформації коду, коротко поговорили про @babel/generatorі про створення допоміжних функцій за допомогою @babel/helpers. Відомості, що стосуються трансформації коду, тут дано лише схематично. Детальніше про них можна почитати тут .

У процесі роботи ми торкнулися деяких особливостей роботи парсеров. Якщо вам ця тема цікава – то ось , ось і ось – ресурси, які вам знадобляться.

Виконана нами послідовність дій дуже схожа на частину того процесу, який виконується під час вступу до TC39 пропозиції нової можливості JavaScript. ось сторінка сховища TC39, на якій можна знайти відомості про поточні пропозиції. тут можна знайти більш докладні відомості про порядок роботи з подібними пропозиціями. При пропонуванні нової можливості JavaScript, той, хто її пропонує, зазвичай пише поліфілли або, роблячи форк Babel, готує демонстрацію, яка доводить працездатність пропозиції. Як ви могли переконатися, створення Форк парсеру або написання поліфілла – це не найскладніша частина процесу пропозиції нових можливостей JS. Складно визначити предметну область нововведення, спланувати і продумати варіанти його використання і прикордонні випадки; складно зібрати думки і пропозиції членів спільноти JavaScript-програмістів. Тому я хотів би висловити вдячність усім тим, хто знаходить в собі сили пропонувати TC39 нові можливості JavaScript, розвиваючи таким чином ця мова.

Ось сторінка на GitHub, яка дозволить вам побачити загальну картину того, чим ми тут займалися.

Переклад статті “Creating custom JavaScript syntax with Babel

Введення в Monero

Введення в Monero

Історія Monero

Monero – це криптовалюта, запущена 18 квітня 2014 роки як форк Bytecoin. Bytecoin (не плутати з Bitcoin) був першою цифровою валютою, що реалізує протокол CryptoNote (докладніше про це нижче по тексту). Як тільки ByteCoin сплив на форумі Bitcointalk, люди відкрили для себе безліч підозрілих властивостей, разом з тим фактом, що більше 80% всієї емісії були вже здобуті. Таким чином, співтовариство прийняло рішення його перевипустити, почавши з чистого аркуша, з нульовим премайном. Так з’явився Monero.

У перші місяці свого існування для користувачів Monero були доступні тільки утиліти командного рядка. Тому безліч користувачів продовжувало просити розробників, в той час до кінця не розуміють безлічі основних моментів в кодової базі Monero, створити графічний користувальницької інтерфейс (GUI). Вони прислухалися і почали роботу над ним, але раптово, 4 вересня 2014 року, на мережу Monero обрушилася витончена атака, так що розробникам довелося переглянути пріоритети і приділити весь час ядру, для того щоб система була стійка до атак (докладніше про це в документі MRL -2).

До кінця 2014 року, Рікардо спання (Riccardo Spagni) запустив Web-гаманець під брендом “MyMonero”, який доступний за адресою mymonero.com. Тим часом, для стороннього спостерігача здавалося, що нічого особливого в роботі над проектом не відбувається. Однак розробники виконали дуже серйозну роботу, таку як перенесення ланцюжка блоків на LMDB, яка дозволяла демона Monero працювати на комп’ютерах з обмеженою кількістю оперативної пам’яті. Також була реалізована багатомовна підтримка mnemonic-seed для полегшення створення резервних копій приватних ключів. Велика кількість коду і документації було написано для реалізації швидкої синхронізації ланцюжка блоків, більш швидкої роботи демона, розпочато інтеграція з I2P і т.д. В кінцевому підсумку проект графічного призначеного для користувача інтерфейсу був знову відновлений в початку 2016 р.

Взаємозамінність (fungibility)

Мета Monero – створення мережі взаємозамінної валюти. Що таке “взаємозамінність” (fungibility) і чому вона така важлива?

Взаємозамінність – це найважливіша властивість будь-якої валюти. Ця властивість робить можливим 100% обмін однієї одиниці валюти на іншу. Тут не повинно бути відмінностей. Кожна монета має мати однакову вартість.

У мережі Bitcoin кожну транзакція може бути відстежено. Це може створити проблеми при отриманні переказів від незнайомих джерел, а також в подальшому при спробі витратити ці кошти. Ви можете бути звинувачені в злочинах при яких ці кошти були використані. Це значно занижує вартість подібних “брудних” коштів. Інша проблема з відстеженням транзакцій полягає в тому, що інші можуть дізнатися ваш баланс або визначити на що ви витрачаєте ваші кошти.

Однак ви можете спробувати замісти сліди ваших транзакцій. Ця техніка називається “мікшування” і може бути здійснена різними способами. Іноді централізовано, іноді децентралізовано, але тут завжди є ймовірність побачити, що певні монети пройшли через мікшер. Використання цього прийому так чи інакше веде до проблем, тому що для мікшування, як правило, використовуються “брудні” монети. Необов’язкова приватність не вирішує проблему відсутності взаємозамінності валюти.

Ви можете спробувати замісти сліди, але якщо ви намагаєтеся мікшувати ваші монети використовуючи “coinjoin” або будь-який інший “анонімайзер”, то ці транзакції все одно будуть відзначені як “потенційно підозріла” активність, так як відразу видно, що монети були мікшувати, навіть якщо самі ви залишаєтеся анонімними.

Так що не плутайте анонімність з взаємозамінністю. Технологія мікшування працює тільки в тому випадку, якщо вона використовується за умовчанням усіма користувачами. Якщо все і завжди мікшують транзакції, то ніхто не може стверджувати щось конкретне про дані в ланцюзі блоків.

Кільцеві підписи (Ring signatures)

Кільцеві підписи використовуються для приховування реальних входів транзакцій таким чином, що неможливо сказати що за історія у кожного виходу цієї транзакції в ланцюжку блоків.

Кільцевий підпис (ring signature) – це такий електронний підпис, який дозволяє одному з учасників групи (званої кільцем) виконати підписання деякого повідомлення від імені всієї групи, при цьому не буде достеменно відомо, хто з учасників групи виконав підписання. Кільцеві підписи та їх застосування – CryptoWiki

Кільцеві підписи застосовуються до кожного входу будь-якої транзакції. Відправник випадковим чином вибирає інші виходи з тієї ж сумою з ланцюжка блоків і підписує їх своїм приватним ключем. Відправник не потребує будь-якому підтвердження від власників інших виходів. Все це можна зробити в від’єднаному від мережі режимі, щоб безпечно створити і пізніше відправити в мережу транзакцію з уже підключеного комп’ютера.

Ймовірно, ви запитайте себе, як визначаються подвійні витрати, якщо присутнє правдоподібне заперечення для кожного виходу транзакції? Відповідь знову дає математика. Разом з транзакцією публікується так званий “key image”. Він доводить, що один з входів кільцевої підписи реальний і коли відправник намагається зробити подвійну витрату, “key image” буде абсолютно ідентичний. Більше інформації про криптографії лежить в його основі ви можете знайти тут.

Так як у всій мережі забезпечено дотримання кільцевих підписів, всі монети завжди перемішані. Це і робить їх взаємозамінними на рівні протоколу в Monero. Якщо ми порівняємо це з функціями приватності реалізованими в Bitcoin, ZCash або Dash, ми явно можемо побачити різницю: якщо прозорі транзакції можливі в мережі, регулюючі органи зможуть вимагати прозорість в певних обставинах, так що взаємозамінність стане неможливою.

І останнє, але не менш важливе – це перевірена криптографія. Вона існує з 2001 року, так що ми можемо припускати, що вона досить надійна. На відміну від ZCash, яка дуже нова і все ще недостатньо перевірена.

Stealth-адреси

У Monero реалізовані “Stealth-адреси”, у вас одина (публічна) адреса, який ви можете дати кому завгодно, не дозволяючи спостерігачам знати що-небудь про історію транзакцій або балансі цієї адреси. Система адрес Monero використовує два приватних ключа: viewkey (ключ для перегляду) і spendkey (ключ для приватного проведення).

Приватний ключ для витрат працює як аналогічний приватний ключ в Bitcoin – їм ви підписуєте транзакції. Ключі для перегляду же використовується для пошуку вхідних платежів в ланцюжку блоків. Тільки якщо у вас є доступ до ключа для перегляду ви можете дізнатися точний вихід транзакції, пов’язаний з вашою адресою Monero.

У Bitcoin (і більшості інших криптовалют) багаторазове використання адрес відбувається постійно і це колосально зменшує псевдоанонімність мережі. Stealth-адреси надають простий спосіб захистити і поліпшити секретність. Дані ланцюжка блоків не покажуть зв’язків між декількома транзакціями.

Незважаючи на це, не всі так прекрасно: якщо ви використовуєте адресу в декількох місцях, можна пов’язати транзакції поза ланцюжка блоків. Якщо ви виводите кошти з біржі і використовуєте ту саму адресу в вашому інтернет-магазині, в якому ви продаєте рослини, правоохоронні органи мають можливість зв’язати ваші транзакції грунтуючись на даних використання однієї й тієї ж адреси. З цієї причини рекомендується використовувати техніку “одноразових адрес” для кожного з сервісів. Всі кошти надійдуть на один і той же обліковий запис, але зв’язати транзакції поза ланцюгом блоків буде неможливо.

Конфіденційні транзакції RingCT

Кільцеві конфіденційні транзакції – це нова система підписів запропонована вченим Шеном Нетер (Shen Noether) в документі MRL-5. Ви можете знайти його першу редакцію в журналі Ledger. Ця система підписів заснована на дослідженні конфіденційних транзакцій Грегорі Максвелла (Gregory Maxwell), але адаптована для роботи з кільцевими підписами.

Ця технологія дозволяє користувачам приховати суми транзакцій. І вона є “останньою частиною мозаїки” для повної анонімності в мережі Monero. Технологія також вирішує деякі прикордонні випадки, які можуть скомпрометувати конфіденційність Monero. RingCT був активований в мережі Monero 9-го січня 2016 р Спочатку RingCT був необов’язковим, але у вересні 2017 після запланованого жорсткого поновлення технологія RingCT в Monero стала обов’язковою, без будь-якої можливості її обійти.

Kovri – I2P

Дані ланцюжка блоків це тільки один вектор атаки на секретність користувачів криптовалюти. Відомо, що компанія Chainanalysis намагається ідентифікувати користувачів через їх IP-адреси. Проект Kovri ставить своїм завданням реалізацію I2P-маршрутизатора на мові C++, який в кінцевому підсумку дозволить користувачам Monero приховувати свої IP-адреси під час відправки транзакцій. Kovri ще не інтегрований в Monero і все ще знаходиться на ранній стадії розробки.

Висновок

Monero – це дуже важлива і революційна технологія. Вона дозволяє приховати відправника, одержувача, суми і історію транзакцій. Monero на відміну від ZCash не вимагає довіри до учасників “trusted setup” і працює в польових умовах. Конфіденційність гарантується протоколом і Monero надає набагато більший набір засобів для анонімізації, ніж мікшери Bitcoin або будь-яка інша криптовалюта з периферійними функціями приватності. Monero дозволяє користувачам здійснювати конфіденційні транзакції в децентралізованої мережі і встояти перед можливими спробами регулювання урядом. Monero – це справжня цифрова готівка.

Переклад статті “Introduction to Monero

Соціальний контракт біткойнів

Біткойн – це новий соціальний і економічний інститут. Він настільки відрізняється від наших існуючих інститутів, що нам варто ставитися до нього скептично і задавати якомога більше складних, гострих питань, перш ніж ніж довірити йому яку-небудь економічну цінність. Відповіді на деякі питання прийдуть лише з часом, але це не означає, що ми не можемо побудувати свої теорії або системи. Одна з таких систем, що дозволили мені краще зрозуміти біткойн, це теорія соціального контракту.

По-перше, фіатние гроші – це результат соціального контракту: люди передали державі контроль за грошовою масою і всіма функціями грошей. Держава в свою чергу використовує цю владу, щоб управляти економікою, перерозподіляти багатства і боротися зі злочинністю. Але багато хто не усвідомлює, що біткойн також працює через соціальний контракт.

Соціальний прошарок і її правила – це серце біткойну.

І ця теорія соціального контракту може відповісти на деякі важливі питання: чому виник біткойн? Хто визначив його властивості? Хто керує ним сьогодні? Чи може критичний баг «вбити» біткойн?

Теорія соціального контракту

Теорія соціального контракту починається з уявного експерименту. Уявіть гіпотетичний стан речей, повний насильства, при якому людям стає нестерпно жити. Ведені бажанням поліпшити свою ситуацію, люди збираються і колективно вирішують наділити владою Левіафана – суверенну державу – щоб він їх захищав. Кожен відмовляється від частини своєї особистої свободи (ну, ви знаєте, право красти, вбивати, грабувати каравани і так далі), в той час як Левіафан наділяється владою створювати закони, застосовувати їх і захищати людей від насильства.

Але ця теорія не обмежена взаєминами між людьми і державою. Ми можемо застосувати той же уявний експеримент і до економіки. Якщо достатня кількість людей незадоволені бартерної системою, вони можуть спільно домовитися використовувати гроші, кредити і що б то не було ще, щоб поліпшити якість своїх торгових операцій.

Процес появи грошей або кредитів відбувається непомітно. Кожна людина задає собі питання, який результат він хотів би отримати і як цього можна досягти. Якщо велика кількість людей в суспільстві бажають однакового результату, ми можемо назвати такий результат «фокальною точкою», або соціальним контрактом.

Гроші як соціальний контракт

Протягом історії уряду, які контролювали гроші, зловживали своєю владою найрізноманітнішими способами: вони заморожували рахунки, забороняли певним особам і групам осіб здійснювати транзакції, друкували велику кількість грошей і роздмухували грошову пропозицію – що іноді призводило до гіперінфляції.

Коли уряди перетинали межу, зловживаючи своєю владою, люди втрачали довіру до соціального контракту, який наділяє уряд цією владою. Вони поверталися до угоди, що зберігав більшість переваг (наявність єдиного засобу обміну, міри вартості і одиниці обліку) без гірших проблем (зловживання урядом): до товарних грошей.

Гроші дають важливий урок: чим більше і чим більш цінним стає соціальний інститут, тим більше він привертає охочих отримати над ним контроль.

Проте новий контракт з товарних грошей виявився таким же нестабільним, і це було проблемою. Давайте візьмемо, наприклад, золотий стандарт. Саме по собі золото було занадто незручно ділити, переміщати і зберігати. Тому люди швидко винайшли надбудову і торгували з представляють золото паперовими грошима, в той час як фізично золото нікуди не переміщалася. Оскільки паперові гроші легко зробити, виникла необхідність в надійному центральному органі, який стежив би за грошовою масою. Саме звідси урядами був зроблений маленький крок, щоб відокремити вартість паперових грошей від забезпечувфння їх товаром, в результаті чого були знову створені фіатние гроші.

Тут криється цінний урок: ви можете погоджуватися з тим, що перебуваєте в жахливій ситуації, і ви можете бути згодні змінити її, але виник в результаті соціальний контракт сильний лише до тих пір, поки заслуговує довіру. Без стабільного інституту, який проводить його в життя, контракт втрачає довіру з боку людей і розпадається.

Правила біткойнів

Коли Сатоши Накамото винайшов біткойн, він не придумував новий соціальний контракт. Сатоши зробив дещо інше: він використовував технологію для вирішення багатьох старих проблем і реалізував старий соціальним контракт новим, кращим способом. Він встановив наступні правила:

1) Тільки власник токена може призвести підпис для його використання (опір конфіскації).
2) Будь-хто може здійснювати транзакції і зберігати біткойни без дозволу з боку (опір цензурі).
3) Всього буде існувати 21 мільйон біткойнів, випущених по передбачуваному графіку (опір інфляції).
4) Всі користувачі повинні мати можливість перевірити правила біткойнів (опір підробкам).

Біткойн як нова форма соціального інституту

Гроші дають нам важливий урок: чим більше і цінніше стає соціальний інститут, тим більше з’являється бажаючих отримати над ним контроль. Саме тому інституту потрібен захист, який він може отримати лише від іншого могутнього гравця: держави. Згодом захист перетворюється в контроль, яким держава починає зловживати. Коли соціальний інститут втрачає свою цінність для людей, його замінює новий інститут, і весь цикл повторюється заново.

Сатоши спробував розірвати це порочне коло двома способами. По-перше, його безпеку забезпечує не впливовий центральний орган (такий, як уряд), а спеціально створений висококонкурентний ринок. Він перетворює безпеку в товар, а провайдерів безпеки (Майнер) – в беззубих товаровиробників. І, по-друге, Сатоши знайшов спосіб змусити цих конкуруючих провайдерів безпеки досягти консенсусу про те, хто і чим володіє в кожен конкретний проміжок часу. Протокол біткойнів автоматизує соціальний контракт, а сам соціум визначає правила біткойнів, грунтуючись на консенсусі його користувачів. Вони сімбіотічни і не можуть існувати одне без одного. Соціальний шар і його правила – це душа біткойнів. Але шар протоколу дозволяє всього цього заробити,

Розглядаючи біткойн як соціальний контракт, який працює і автоматизований завдяки технічному шару, ми можемо відповісти на філософські питання про цей феномен.

Хто може змінити правила біткойнів?

Правила контракту постійно переглядаються на рівні соціального шару. Протокол біткойнів лише автоматизує їх. Біткойн, будучи мережею комп’ютерів, виникає тоді, коли багато людей запускають його ПО на своїх комп’ютерах, наступних загальному зводу правил (можна сказати, що вони «говорять однією мовою»). Ви є учасником в мережі до тих пір, поки виконуєте ці загальні правила. Якщо я захочу в односторонньому порядку змінити правила роботи біткойнів на своєму комп’ютері, це ніяк не вплине на решту мережу – в результаті цього я лише буду вигнаний з мережі, оскільки моє ПО не зможе розуміти інших (я тепер кажу «на іншій мові»).

Єдиний спосіб змінити правила мережі біткойнів – запропонувати зміни в соціальний контракт. Кожна така пропозиція має бути добровільно прийнята іншими учасниками мережі, і вона зможе стати правилом лише в тому випадку, якщо достатня кількість людей включать його в свій локальний звід правил. Переконати в чому-небудь мільйони людей – не така проста робота, і ця необхідність практично виключає будь-які спірні зміни, які ніколи не отримають широкої громадської підтримки. Ось чому мережу біткойнів може змінюватися лише в сторону, яка відображатиме бажання її учасників, і в той же час є неймовірно стійкою до спроб змін з боку недобросовісних осіб.

Чи може помилка в коді вбити біткойн?

У вересні 2018 у найпопулярнішому зводі правил біткойнів виникла програмна помилка. Баг відкривав два напрямки можливої ​​атаки: він дозволяв хакеру відключити ПО біткойнів на інших комп’ютерах (так, щоб вони більше не могли звіряти правила – що ламало захист від контрафакту) і потенційно витратити один і той же токен двічі (ламаючи захист від інфляції).

Розробники біткойнів швидко виправили цей баг, надавши мережі оновлений звід правил, який закривав описані вище діри. І хоча баг був виявлений вчасно і не завдав ніякої шкоди, багато людей задалися питанням: наскільки небезпечним він міг би стати? Змогла б мережу біткойнів продовжити жити з інфляцією після того, як захист була б зламана?

Теорія соціального контракту дозволяє відповісти на ці питання негативно. Правила біткойнів закріплені на соціальному шарі, а ПО лише автоматизує їх. Там, де соціальний контракт розходиться з шаром протоколу, останній буде завжди неправий. Відмова шару протоколу тимчасово застосовувати правила контракту не несе будь-яких постійних наслідків для дійсності самого договору.

У біткойні самого по собі ніякої цінності немає. Цінність існує виключно на соціальному шарі.

Ось що б сталося: результати потенційної експлуатації бага були б виправлені шляхом реорганізації блокчейна, що скасовує нанесений хакером шкоду. Це розділило б мережу біткойна на два блокчейна, кожен зі своїм токеном: один з багом і один без нього. Кожен власник біткойнів отримав би однакову кількість токенов в кожній мережі, але вартість цих токенов визначалася б виключно ринковим способом, тобто тим, скільки інші люди були б готові за них віддати.

Тут важливо зрозуміти, що токен біткойну сам по собі не має ніякої цінності: це лише запис в цифровий книзі. Вартість існує виключно на соціальному шарі. Тому в описаному вище випадку саме соціальний консенсус вирішив би, який з двох токенов отримає економічну підтримку. Найімовірніше, вся економічна цінність перетекла б в нову, позбавлену бага мережу.

Коли ПО біткойну успішно автоматизує правила соціального контракту, обидва шари синхронізуються. А коли ПО тимчасово не може виконувати свою функцію, саме соціальний контракт виконує роль маяка, показуючи правильний напрямок руху. Останній виявлений великий баг напевно буде не останнім в історії біткойнів. Теорія соціального контракту дає нам впевненість, що баги ніяк не загрожують соціальному інституту біткойна.

Чи загрожують Форк біткойну правилом відсутності інфляції?

Ще один популярний філософське питання концентрується на концепті «ФОРКОМ». Оскільки ПО біткойну відкрито (що дозволяє користувачам перевіряти, що їх звід правил робить саме те, що повинен робити), кожен може скопіювати його і внести свої зміни. Це і називається «Форком», поділом мережі. Однак, як і в більш ранніх прикладах, ці зміни вносяться лише на протокольному, а не соціальному рівні. Без зміни правил соціального шару єдиним результатом поділу мережі біткойнов буде лише виняток автора ФОРКОМ з мережі.

Якщо ви хочете розділити мережу біткойнов, щоб «форкнутая» ланцюжок продовжила існування, вам доведеться спершу розділити соціальний контракт. Вам потрібно буде переконати якнайбільше людей в тому, що запропонований вами новий звід правил буде для них краще, щоб вони захотіли на нього перейти. Такі Форк дуже рідкісні і важкоздійсненним, оскільки вимагають залучення тисяч людей. Використання цього процесу для створення цінності за складністю схоже на ведення президентської кампанії.

Повторюся, ключовим моментом є розуміння того, що вся цінність токенов міститься виключно в соціальному контракті. Токени самі по собі не несуть будь-які цінні папери, вони отримують її від соціального консенсусу. Форк протоколу не означає автоматичного ФОРКОМ соціального контракту, так що новий токен за замовчуванням не буде нічого коштувати. У рідкісному випадку, коли розділяється сам соціальний контракт (як в історії з появою Bitcoin Cash), ви залишитеся з двома слабшими соціальними контрактами, з меншою кількістю учасників у кожного в порівнянні зі старим контрактом.

Гроші в цілому і біткойни зокрема можна розглядати як соціальні контракти між людьми в суспільстві. Біткойн не є новим контрактом; це лише реалізація контракту, який можна відстежити на сотні років назад. У порівнянні з попередніми спробами, біткойн значно краще, оскільки для забезпечення власної безпеки він створює надконкурентному ринок. Соціальний і протокольні шари біткойнів взаємно підсилюють один одного, і їх взаємозв’язок дає нам уявлення про такі малозрозумілих концепціях, як зміни правил, Форк або помилки в протоколі.

Переклад статті “Unpacking Bitcoin’s Social Contract

Майбутнє, рухоме децентралізованими грошима

Майбутнє, рухоме децентралізованими грошима

Кріптоекономічна гра, вперше описана в статті Сатоши Накамото «біткойн: цифрова пірінгова готівка» поки привела до пікової вартості понад $300 млрд, накопиченої розрізненої групою людей, що діють в своїх незалежних інтересах.

Варто на хвилинку зробити паузу і задуматися про те, що це означає: вперше за як мінімум 5000-річну історію грошей валюта, що випускається прозорим, незмінним і незалежно верифіковані кодом органічно конкурує в глобальному масштабі як з фіатнимі національними валютами, так і з традиційними засобами заощадження, такими як золото і срібло. У добу глобалізації люди вільно можуть порівняти біткойн і подібні йому інклюзивні теоретико-ігрові головоломки з альтернативами – дефляційними керованими згори валютами або громіздкими фізичними товарними ресурсами – і раціонально вибрати перший варіант.

Поява криптовалют, цифрових активів, цифрового золота – називайте, як хочете, – супроводжувалося надзвичайною волатильністю, яка збережеться ще як мінімум протягом найближчого десятиліття. Але в довгостроковій перспективі на вільному ринку тверді гроші, такі як біткойн, стануть найкращим варіантом засобу заощадження у тих, хто просто хоче уникнути невпинного розмивання свого капіталу (якщо вони настільки дурні, щоб тримати істотну його частина навіть в «кращих» глобальних резервних валютах, таких як долар США). І коли потенціал зростання при накопиченні цієї нового різновиду заощаджень досить знизиться, люди нарешті перестануть спекулювати, і волатильність скоротиться. Тоді її не тільки нарешті буде можна спокійно витрачати, але вона пошириться настільки, що витрачати її буде надзвичайно легко.

На це може піти десятиліття – може більше, може менше. Будуть і ті, хто стане чинити опір змінам, погрожуючи силою. Не виключено, що сам біткойн буде знищений або узурповане. Але будьте впевнені: на вільному валютному ринку вільнодумні раціональні учасники неминуче віддадуть перевагу децентралізовані гроші з детерминистическими правилами, і як раз через децентралізованої природи їх буде неможливо зупинити. Джинн вийшов з пляшки.

Справжнє питання зараз у тому, як це змінить світ? Як буде виглядати перша глобальна децентралізована грошова система?

Менше урядової влади і контролю

Коли децентралізована криптовалюта стане функціонуючою глобальною мірою вартості, це підірве Бреттон-Вудський систему, при якій статус долара як головної резервної валюти дав США рясну геополітичну перевагу. Коли довіру до грошей гарантується децентралізованим протоколом, а не фінансовими інститутами, здатність держав здійснювати контроль за допомогою тиску на ці фінансові інститути (або прямого управління ними) зменшиться або зникне. І оскільки держави не зможуть контролювати такі протоколи, вони позбудуться тієї влади, яку їм дає контроль над грошовою масою.

Економічна влада була ключовою складовою великої стратегії Заходу після Другої світової війни. Звідси випливає, що втрата здатності до такої влади, швидше за все, змінить глобальну політику. Як писав в 2016р Клаус Шваб, засновник і голова Всесвітнього економічного форуму, в своєму есе «Четверта промислова революція», від здатності урядів спритно адаптуватися до нових технологій, перерозподіляються і децентралізованих влада, в кінцевому рахунку буде залежати їх виживання.

Уряди все частіше стануть діяти на випередження, причому першими, швидше за все, будуть ті, хто може найбільше виграти від адаптації. Свідчення цього вже можна побачити в сприятливого нормативно-правовій базі, введеної в таких периферійних державах, як Мальта і Ліхтенштейн, які можуть виграти від реорганізації глобальних фінансів. Є також країни-ізгої, які можуть скористатися новою грошовою системою для нейтралізації традиційних заходів, що здійснюються для їх ізоляції.

Суть в тому, що вже є такі уряди – і їх буде ставати все більше, – які хочуть використовувати нові тверді гроші у власних інтересах. Такі раціональні кроки в довгостроковій перспективі посилять статус і вартість децентралізованих грошей. Чим більше вони вже використовуються, тим більше вони будуть використовуватися.

Через безліч можливих наслідків підриву сформованого світопорядку децентралізованої грошовою системою дуже складно з упевненістю спрогнозувати переможців і переможених. Безумовно лише те, що, хоча при глобальному підриві динаміки влади будуть переможці і переможені, сукупний ефект для світового населення буде позитивним. Якщо ви стурбовані тим, що авторитарні режими, розширять вплив, що призведе до зростання утисків і страждання, – не забувайте, що у кожної монети є дві сторони. Хоча поточні противники усталеною гегемонії американського долара приймуть нові децентралізовані гроші в корисливих цілях, сукупний довгостроковий результат буде позитивним для людської свободи.

Не забувайте, що централізований контроль над грошима дає державам неймовірну владу. Здатність уряду в односторонньому порядку заморожувати рахунки дисидентів, наприклад, давно виступала механізмом контролю. Яскравий недавній приклад – випадок в Саудівській Аравії, коли були арештовані 11 принців, 4 міністра і десятки колишніх міністрів і відомих бізнесменів і їх активи на більш ніж $30 млрд були заморожені. На думку більшості, таким чином правлячий король Салман вирішив розчистити шлях для вступу на трон свого сина.

Також варто врахувати, що, оскільки централізоване друкування грошей виступає головним способом фінансування сучасних війн, зменшення такої здатності призведе до поступового скорочення глобальної мілітаризації.

Демократизація доступу до капіталу

Зниження можливості урядів воювати з власними громадянами і один з одним і результуючий геополітичний підрив – це, напевно, найбільш очевидні переваги децентралізованого майбутнього грошей, але також істотні демократизація доступу до капіталу та підвищення ефективності.

Кріптовалютние екосистемиможуть створити глобальні децентралізовані економічні мережі, що об’єднують капітал і розподіляють ризик в таких масштабах, які в минулому були досяжні тільки для урядів. У світі, де активи реєструються в незалежних реєстрах – блокчейнах, які може переглядати будь-який (і здійснювати транзакції з ким завгодно), бізнеси отримають можливість секьюритизировать, наприклад, парк безпілотних електричних вантажівок для доставки, продаючи частку володіння окремими вантажівками, що обслуговують певний район, замість того щоб об’єднувати всі вантажівки в єдиний пул і створювати облігації на основі сукупного грошового потоку з нього (що є єдиним дієвим способом залучення капіталу для т кого бізнесу при поточній фінансовій системі). Так люди зможуть інвестувати локально.

Менше фінансових втрат

Вище описаний приклад повністю автоматичного фінансового продукту, здатного взаємодіяти з постачальниками і роздрібними інвесторами без нагляду з боку централізованих органів, але з можливістю автоматичного аудиту за запитом. Це підводить нас до ще одного величезного перевазі децентралізації грошей: скорочення витрачених ресурсів, які зараз йдуть фінансовим посередникам. Децентралізовані гроші майбутнього, поліпшені завдяки функціоналу смарт-контрактів, будуть повністю автоматичними, тому після складання контрактів не буде необхідності в цілій армії високооплачуваних посередників, які повинні ставити на них свій відбиток.

Тоді як децентралізація грошової системи демократизує доступ до фінансових продуктів, дефляційна природа, властива твердим грошам, може навіть вести до зниження необхідності шукати подібні фінансові продукти. Не забувайте, що при поточному статус-кво навіть в «кращих» валютах, таких як долар США, ви втрачаєте близько 2% на рік через інфляцію. Тому, якщо сьогодні люди мотивовані шукати фінансові інструменти, щоб компенсувати втрати, коли гроші більше не будуть інфляційними, потреба їх витрачати знизиться. Чим більше людей будуть комфортно себе почувати, тримаючи велику частину свого багатства в нових грошах, тим менше буде мотивації, щоб гарячково розподіляти наявні заощадження, які раніше приносили негативний приріст.

Більш раціональні інвестиції

Все це матиме далекосяжні наслідки. Зокрема, люди стануть більш схильні інвестувати раціонально. Замість того щоб необдумано вкладати капітал в усі підряд, що, як ви сподіваєтеся, буде краще, ніж негативний приріст 2%, люди будуть вибирати довгострокові фінансові продукти з доданою вартістю. Зниження тиску може навіть допомогти запобігти недобросовісний фінансовий облік і мислення швидкого збагачення, що викликала остання фінансова криза.

Також відбудеться нормалізація штучно роздутих в ціні класів активів, таких як нерухомість, яка зараз використовується багатими як звалища, що сильно шкодить середньому класу в найбажаніших районах кращих міст світу. Нормалізація вартості нерухомості повинна привести до відродження життя в міських центрах і поліпшенню якості життя середнього класу.

Кінець інфляції, до якого призведе децентралізована валюта, також вирівняє ігрове поле, від чого виграють бідні. Зараз тільки ті, хто володіє багатою готівкою, можуть дозволити собі інвестувати її, щоб уникнути інфляційних втрат. Вони також зазвичай мають доступ до професійних інвестиційним сервісів, що забезпечує їм кращі результати. Однак бідні і середній клас, як правило, не мають надлишку готівки та / або доступу до професійних ресурсів, необхідних для правильного його вкладення в високоефективні активи. Це означає, що вони диспропорційно піддаються інфляційним втратам централізованих фіатних валют. Таким чином, кінець інфляції означатиме, що ті, у кого грошей менше за все, не будуть каратися відмовою у доступі за фінансовий покерний стіл тільки через відсутність у них ресурсів.

Переклад статті “A Liberal Future Driven by Decentralized Money

Технологія блокчейн розширить ринок блокових продуктів

Технологія блокчейн розширить ринок блокових продуктів

Аналітики стверджують, що протягом наступних чотирьох років ринок продуктів і послуг, на основі блокових ланцюжків досягне $ 14 млрд. Чи вірно це, поки можна тільки здогадуватися, але одне можна сказати напевно – у технології блокчейн ринок продуктів активно розширюється, охоплюючи все більшу сферу використання.

Численні опитування показують, що більше половини компаній зі списку Fortune 500 в даний час активно працюють над різними технічними дослідженнями і проектами. Найбільші центри обробки даних прогнозують, що всі користувачі блокових ланцюжків стануть їх споживачами на довгі роки.

Безсумнівно, що в перспективі на ринку з’являться всілякі блокові додатки, але простір для прийняття рішень в сфері управління ланцюгами поставок (SCM) здається особливо дозрілим для цієї технології. Управління ефективністю постачальників і питаннями відповідності між все більш великими і динамічними мережами є лише частина проблем, з якими наявні інструменти погано справляються. Для простоти використання їх версій з підтримкою блокового ланцюжка спільно з традиційними технологіями, у багатьох випадках будуть безпосередньо враховуватися їх поточні недоліки.

На поточний момент вже можна ідентифікувати провідні додатки-кандидати, оскільки вони представляють собою важливі цілі, які в більшій мірі будуть сприяти впровадженню технології блокових ланцюжків. Одним з найбільш важливих є управління контрактами, тому що процеси контрактації були належним чином розвинені і жахливо неадекватні.

Фактично, анатомія контрактів не змінилася суттєво за останні десятиліття. Незалежно від причин, процеси, пов’язані з переговорами, авторськими правами, виконанням і продовженням, не можна назвати добре скоординованими діями. І, відверто кажучи, це усього лише верхівка айсберга поточних проблем. Вони не були створені для сучасних мереж, хоча змушене використовуються для управління ними.

На щастя, після появи хмарних технологій, за допомогою яких статичні контракти повільно, але вірно замінюються динамічними «розумними» угодами. Але блокчейн здатний вивести цю ідею на абсолютно новий рівень. В майбутньому контракти будуть розглядатися як підключення набори даних, здатні до «самовиконання». Іншими словами, вони будуть відслідковувати свою власну ефективність. Це неминуче.

На додаток до забезпечення видимості в режимі реального часу, додаткові переваги будуть включати подальше скорочення транзакційних витрат, більш тісна співпраця між усіма гравцями, внесок яких є життєво важливим для досягнення намічених результатів. Підвищені рівні довіри – це те, що необхідно, тому що всі уповноважені боку в рамках смарт-контракту блокового ланцюжка систематично беруть участь у перевірці транзакцій і забезпечують можливість її роботи.

Блокові мережі дозволяють передавати дані безпосередньо між учасниками мережі без необхідності посередників. Не тільки транзакційні дані, але і будь-які елементи і зобов’язання сторін можуть підтримуватися на декількох комп’ютерних системах, контрольованих усіма що у угоді об’єктами. Майте на увазі, в той час як учасники смарт-контрактів в мережі можуть переглядати свої записи, їх оновлення може відбуватися тільки з дозволу більшості користувачів.

Оскільки блок-ланцюжок містить точний і перевіряється звіт про всі відповідних транзакціях, він може забезпечити надійний спосіб для мультиагентних систем обмінюватися інформацією, шляхом забезпечення єдиного консенсусу.

У інтерв’ю з представником консалтингової фірми з управління витратами SpendMatters Томом Фінном, він заявив, що необхідно розширювати можливості цих систем за допомогою технології штучного інтелекту і машинного навчання.

Неможливість укласти контракти з урахуванням широти нюансу в сучасних угодах про постачання продуктів і підтримувати управління більш сучасними механізмами, заснованими на отриманих результатах, є широко визнаною і дорогої проблемою. Оцінки свідчать, що за минулий період 2018 року нездатність належним чином управляти ефективними контрактами обходиться покупцям в надмірних витратах до 20% в порівнянні з аналогічним періодом минулого року. По суті, блок-ланцюжок усуває цю проблему.

Фінн зазначив, що blockchain ефективно забезпечує взаємодію між людьми і машинами в неприборканому, повністю мережевому контексті, оскільки він забезпечує єдину платформу даних.

Щоб блокова технологія закріпилася, вона потребує відповідного додатку. Поки важко уявити собі більш своєчасну або основну розподілену платформу управління смарт-контрактами, яка б забезпечувала інтегрований моніторинг якості і ефективності постачальників.

Переклад статті “Blockchain Contract Management — A Perfect Application