четверг, 21 апреля 2016 г.

Создание среды для Unit-тестирования JavaScript с помощью Jasmine и Karma (в IntelliJ IDEA). Пошаговое руководство.


Технические средства


  • Node.js (https://nodejs.org) - программная платформа, основанная на движке браузера Chrome V8; позволяет выполнять JavaScript код
  • Karma (https://karma-runner.github.io) - настраиваемый движок, который позволяет запускать тесты (TestRunner)
  • Jasmine(http://jasmine.github.io/) - js-библиотека для unit-тестирования JavaScript
  • PhantomJS(http://phantomjs.org/)  - полноценный браузер на основе WebKit из консоли
  • IntelliJ IDEA(https://www.jetbrains.com/idea/) - IDE-среда для разработки (к сожалению, до сих пор в компании используется уже сильно устаревшая версия 13)
  • Плагины IntelliJ IDEA для Node.js и Karma - дополнительные модули(plugins) для возможности запуска тестов Karma-Jasmine в IDE

Установка в системе


1. Скачать установочный файл Node.js и установить всё по умолчанию (https://nodejs.org/en/ (Версия 5.x) )

2.  Проверить в Windows PowerShell (Все программы/Стандартные/Windows PowerShell/Windows PowerShell)

Команда: node -v

3. Установить библиотеки karma,jasmine,phantomjs в nodejs npm, для чего выполнить команды из под WindowsShell

    npm install -g karma
    npm install -g jasmine
    npm install -g jasmine-core
    npm install -g karma-cli
    npm install -g karma-jasmine
    npm install -g phantomjs-prebuilt
    npm install -g karma-phantomjs-launcher

Установка в IntellijIDEA(13)

В среде

1.Установить плагин Node.js

2. Установить плагин Karma

В проекте

1. Создать в проекте файл конфигурации запуска тестов для Karma, karma.conf.js  

Удобнее, в корне проекта или папке с тестами, но это не очень важно, т.к. затем надо будет этот путь прописывать в конфигурации запуска тестов на выполнение.

Пример karma.config.js
module.exports = function(config) {
    config.set({
        basePath: '',
       // файлы, в которых находится исходный код, необходимые библиотеки, сами тесты
        files: [
            "./base/js/ext-4.2/ext-all.js",
            "./base/js/FormStik.js",
            "./base/js/isuprTrains.js",
            "./test/js/isuprTrains.Test.js"
        ],
        // указать конкретную библиотеку для тестирования
        frameworks: ['jasmine'],
        // если тестировать в phantomjs, т.е. без явного запуска браузера
        browsers: ["PhantomJS"]
    });
};

2. Создать тестовый класс с использованием jasmine, c именем <имя js файла>.Test.js (имя на самом деле не принципиально - оно настраивается в конфиге karma.config.js)

Пример jasmine- unit-теста
describe("First Test", function() {
    it("makes testing JavaScript awesome!", function() {
        expect(true).toBe(true);
    });
});

3. Создать запускающую конфигурацию в проекте


4.Выполнить конфигурацию (Run)

Результат выглядит приблизительно так:


среда, 13 апреля 2016 г.

Про то, как надо делать API

Попалась отличная статья(перевод) на хабре, о том, как надо делать REST API: Разработка web API (перевод)
Жаль, что я не прочитал её на несколько месяцев раньше, когда я переделывал некоторую функциональность на одном из проектов. Хотя приятно, что до некоторых идей получилось додуматься самостоятельно :)

Из интро выяснилось откуда ноги растут: "Это краткий перевод основных тезисов из брошюры «Web API Design. Crafting Interfaces that Developers Love» Брайана Маллоя из компании Apigee Labs"

В общем, читать и перечитывать, пока в голове не застрянет накрепко :)

четверг, 11 февраля 2016 г.

Мои последние достижения на coursera (Build Complete Web Solutions.The Hong Kong University of Science and Technology)

Решил поучиться новым технологиям в области frontend-a. А то что всё сервер да сервер. Хочется иногда чего-то "творческого" :)
На coursera пару месяцев назад началась специализация  Build Complete Web Solutions Гонконгского университета (якобы, номер 1 в Азии). Пока решил всю специализацию не проходить - да и не очень понятно зачем мне это - зато поучиться на отдельных курсах. К моменту начала coursera ещё не стала сильно жадной, и на ряде курсов можно было получить оценку, не покупая сертификат (последний курс прошёл уже без оценок).
В двух словах - интересно и познавательно. Буду пробовать дальше саморазвиваться в эту сторону.
А результат самих курсов, на данный момент, на картинке :


вторник, 9 февраля 2016 г.

Google Sheets. Копирование данных листа google sheet из одного документа в другой.

Дано

1. Документ-источник (google sheet), в котором ведутся некоторые данные.
2. Документ-приёмник (google sheet), в котром на одном из листов надо использовать данные из документа-источника.

Описание решения

В документе-приёмнике в скриптах, пишется код, который запускается регулярно триггером (в моём случае каждый час), и копирует полностью (т.е. накрывает/перезаписывает диапазон ячеек целиком) данные. А также и цветовые схемы (font,background colors) и примечания ячеек, входящих в диапазон.

Код скрипта

function readFomVolonteersQuestionnaire() {
  var RANGE_NAME = "A1:AT202";
  var DEST_SHEET_NAME = "Диапазон";
  
  // Документ - источник
  var spreadsheet = SpreadsheetApp.openById('13d...'); 
  
  var sheet = spreadsheet.getSheets()[0];     // лист с данными (первый в источнике)
  
  var range = sheet.getRange(RANGE_NAME);
  var values = range.getValues();
  var backgroundColors = range.getBackgrounds();// цвет фона
  var fontColors = range.getFontColors();// цвет текста
  var notes = range.getNotes();  // примечания

  
  // Текущий документ (приёмник данных)
  var ss = SpreadsheetApp.getActiveSpreadsheet();  // активный документ (из которого запускается скрипт)
  var SSsheet = ss.getSheetByName(DEST_SHEET_NAME);
  var ssRange = SSsheet.getRange(RANGE_NAME);
  ssRange.setValues(values);
  ssRange.setBackgrounds(backgroundColors);
  ssRange.setFontColors(fontColors);
  ssRange.setNotes(notes);  // примечания 
}

пятница, 4 декабря 2015 г.

Как сделать простую плановую рассылку, используя google sheets

1.Создать таблицу в google sheets
2.Заполнить столбцы : электронная почта(B) и текст(C)
3.Войти в редактор скриптов: Инструменты\Редактор скриптов
4.Код наподобие этого:
function sendEmails() {
  var sheet = SpreadsheetApp.getActiveSheet();
  var startRow = 2;  // First row of data to process
  var numRows = 10;   // Number of rows to process
  // Fetch the range of cells A2:B3
  var dataRange = sheet.getRange(startRow, 1, numRows, 4)
  // Fetch values for each row in the Range.
  var data = dataRange.getValues();
  for (i in data) {
    var row = data[i];
    var name = row[0];  // First column
    var emailAddress = row[1];  // Second column
    if (emailAddress != '') {
      var message = 'Сообщение на русском языке: ' + row[1] + ' ::: ' + row[2] + ' ::: ' + row[3] ;//row[2];       // Третий столбец
      var subject = "Sending emails from a Spreadsheet";
      MailApp.sendEmail(emailAddress, subject, message);    
    };
  }
}
5.Настроить планировщик(триггер) регулярного запуска скрипта рассылки
  5.1.Инструменты\Редактор скриптов\Триггеры текущего проекта (кнопка с часами)
  5.2.Добавить триггер ("Триггеры не настроены.Нажмите здесь, чтобы добавить триггер")
    5.2.1 Выбрать нужную функцию (в нашем случае sendEmails), "динамичный", нужный таймер, регулярность
    5.2.2 Сохранить
    5.2.3 Дать разрешение(авторизацию) скрипту, которую он попросит при сохранении.

Это всё.

Примечания
Разумеется столбцы могут быть любые, а не только A и B - это отразится в индексах, в скрипте 

четверг, 5 ноября 2015 г.

Форматирование кода в блоге

Так как я не нашёл в "родных" средствах blogger, хоть какого-то форматирование кода, а некрасивый код вставлять надоело, то теперь использую простой до невозможности проект http://hilite.me/
Выбрал язык, вставил в левое окошко, нажал на кнопку, скопировал результат из окна. Короче, здесь копипаст рулит :)

Решение для "Unable to locate Spring NamespaceHandler for XML schema namespace"

Дано

Jar-файл, собранный с зависимыми библиотеками прямо внутри (это когда просят сделать весь проект в одном файле, и тогда сторонние библиотеки разжимаются и копируются в целевой jar в виде классов);
Конфигурация spring в xml-файлах, также внутри jar-а
Внешний проект запускает метод, в котором создаётся ApplicationContext spring-а.
Для сборки используется ant

Проблема

При попытке загрузить конфиг во внешнем проекта получаеv ошибку при создании ApplicationContext : "Unable to locate Spring NamespaceHandler for XML schema namespace"

Что дали поиски проблемы 
В двух словах, при сборке нескольких spring-библиотек в один файл, перекрываются схемы и другие настройки, которые хранятся в META-INF/spring.* файлах, которые для каждой библиотеки спринга свои. Т.е. остаются в итоге те, что собирались последними - они просто накрывают предыдущие.

Источники

http://stackoverflow.com/questions/8523997/unable-to-locate-spring-namespacehandler-for-xml-schema-namespace-http-www-sp/8574629#8574629
http://stackoverflow.com/questions/23574979/creating-an-uber-jar-with-spring-dependencies (это уже почти решение)

Решение (быстрое)

По мотивам описанного на stackoverflow был сделан отдельный ant-скрипт, который создаёт общие META-INF/spring.* схемы и настройки. Затем эти файлы были добавлены в папку с META-INF, которая подкладывается в финальный jar при сборке.
А в процессе сборки jar-а, исключаются те spring-настройки, которые разжимаются из spring-библиотек.

Скрипт ant, который собирает из отдельных spring-настроек, всё в общие настройки для всех библиотек spring-а, которые используются в проекте.

<?xml version="1.0" encoding="UTF-8"?>
<project name="springschemamake" default="create.spring.schema.full">

    <taskdef resource="net/sf/antcontrib/antlib.xml" classpath="lib/ant-contrib-1.0b3.jar"/>

    <property name="lib.dir" value="${basedir}/lib"/>
    <property name="dist.dir" value="${basedir}/templib"/>
    <target name="create.spring.schema.full">

        <!-- iterate over the dependencies -->
        <for param="file">
            <path>
                <fileset dir="${lib.dir}">
                    <include name="**/*spring*.jar"/>
                </fileset>
            </path>
            <sequential>

                <local name="jar.name"/>
                <basename  property="jar.name" file="@{file}"/>
                <!-- put the spring.* jars into special sub-directories -->
                <mkdir dir="${dist.dir}/${jar.name}"/>
                <unzip dest="${dist.dir}/${jar.name}" src="@{file}">
                    <patternset>
                        <include name="**/META-INF/spring.*"/>
                    </patternset>
                </unzip>

            </sequential>
        </for>

        <mkdir dir="${dist.dir}/META-INF"/>
        <!-- build the concatenated spring.* files -->
        <mkdir dir="${dist.dir}/META-INF"/>
        <concat destfile="${dist.dir}/META-INF/spring.handlers" fixlastline="true">
            <fileset dir="${dist.dir}/" includes="*/*/spring.handlers"/>
        </concat>
        <concat destfile="${dist.dir}/META-INF/spring.schemas" fixlastline="true">
            <fileset dir="${dist.dir}/" includes="*/*/spring.schemas"/>
        </concat>
        <concat destfile="${dist.dir}/META-INF/spring.tooling" fixlastline="true">
            <fileset dir="${dist.dir}/" includes="*/*/spring.tooling"/>
        </concat>
        <concat destfile="${dist.dir}/META-INF/spring.factories" fixlastline="true">
            <fileset dir="${dist.dir}/" includes="*/*/spring.factories"/>
        </concat>

        <delete includeemptydirs="true">
            <fileset dir="${dist.dir}" includes = "**/*spring*/**" excludes="/META-INF/**"/>
        </delete>
    </target>
</project>

Решение правильное, но пока не сделанное
Включить сборку схем spring-а в общий ant-скрипт. Он и до такого включения был довольно большой, поэтому потребуется некоторое время на рефакторинг и реализацию.