• Zoia.js: ещё один веб-фреймворк на Node
    +1

    Для apt-get лучше указать зависимости в README.md, и дать пользователю самому это сделать. Да, это несколько сложнее для пользователя (целое лишнее действие), но зато более понятно, что именно произойдёт в итоге.


    По поводу если Docker не используется, то можно оформить вашу CMS в виде NPM пакета, который можно глобально (npm install -g ...) установить. Либо можно подсмотреть как делают другие, например упомянутый ниже KeystoneJS.


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

  • Zoia.js: ещё один веб-фреймворк на Node
    0

    Общие принципы изложил в другом комментарии. Здесь напишу подробнее, хотя сдаётся мне, что лучше оформить это уже отдельной статьёй, возможно позже.


    Итак, как я делаю это у себя:


    Есть 2 Dockerfile (в др. комментарии), в production версию копируется весь код приложения, и для него устанавливаются зависимости (npm install и т.п.), полученный образ тегается и пушится в реестр, он готов к использованию. Стоит учесть, что вся конфигурация задаётся через переменные окружения, согласно 12factor, это позволяет не влазить каждый раз в код для изменения настроек, а просто перезапустить контейнер с новым окружением.


    Для разработки используется другой Dockerfile (также в др. комментарии). В нём устанавливаюстя лишь те компоненты, которые я не могу пробросить извне (imagemagick в моём случае), остальное: код и node_modules, через volume монтируется внутрь контейнера и оттуда запускается. При этом (у меня Linux, не знаю как будет на Windows), благодаря nodemon, при изменении файлов автоматически перезапускается приложение.


    Вот как-то так. Возможно, не совсем понятно объяснил, не стесняйтесь спрашивать и уточнять.

  • Zoia.js: ещё один веб-фреймворк на Node
    0
    1. рад, что оказался полезным;
      2. вы не должны обновлять их сами, в документации написано:


      You should avoid RUN apt-get upgrade or dist-upgrade, as many of the “essential” packages from the parent images won’t upgrade inside an unprivileged container. If a package contained in the parent image is out-of-date, you should contact its maintainers.

      3. если я правильно понял вопрос: когда вы собираете докер-образ, то нет другого способа выполнять сборочные команды, кроме как через RUN. Но рекомендуется объединять вызов множества команд в один RUN, т.е.:


      RUN apt update && \
      apt install ... && \
      apt ...

      вместо


      RUN apt update
      RUN apt install
      RUN apt ...

      Docker sees the initial and modified instructions as identical and reuses the cache from previous steps. As a result the apt-get update is NOT executed because the build uses the cached version. Because the apt-get update is not run, your build can potentially get an outdated version of the curl and nginx packages.

      А вот уже после того как контейнер запущен, вы можете выполнить в нём команду с помощью exec. Но нужно учитывать, что любые изменения в работающем контейнере потеряются, после его удаления (или остановки, если запускали с флагом --rm).


      4. если приложение падает внутри контейнера, то контейнер останавливается вместе с ним с кодом ошибки, докер это видит и перезапускает (если указано --restart always). При разработке вам всё ещё может быть удобно использовать forever или nodemon, для перезапуска процесса при изменении исходных файлов, например. Для такого случая я у себя использую 2 Dockerfile: prod и dev, для продакшена и разработки, соответственно:



    Dockerfile
    # production версия
    FROM node:8.4-alpine
    
    WORKDIR /service
    # устанавливаются все необходимые пакеты
    RUN apk update && apk add imagemagick
    # копируется код
    COPY . /service
    ENV NODE_ENV production
    # устанавливаются зависимости самого приложения, только после того, как удостоверимся, что код приложения находится внутри образа
    RUN npm install --production
    
    EXPOSE 8080
    CMD ["node", "--harmony", "./index.js"]

    и для разработки:


    Dockerfile.dev
    FROM node:8.4-alpine
    
    RUN apk update && apk add imagemagick
    # весь код, включая установленные зависимости будут примонтированы внутрь контейнера как volume, внутри контейнера их не устанавливаю
    VOLUME /service
    WORKDIR /service
    
    EXPOSE 8080
    # условный nodemon для запуска приложения, который отслеживает изменения в файлах
    CMD ["nodemon", "--harmony", "./index.js"]

    Соответственно, при разработке, и все остальные части приложения (БД, например) тоже крутятся внутри докера. Я использую Docker Compose, который позволяет декларативно описать все опции и зависимости контейнеров:


    docker-compose.yml
    version: '3'
    
    services:
      mongodb:
        image: mongo:3.4
        volumes:
          - mongodb-data:/data/db
      backend:
        image: registry.gitlab.com/my-team/my-repo:latest # образ, который хотим использовать
        build: # если указана опция build, то image не будет скачиваться, но соберётся локально с этим именем и тегом
          context: . # директория, в которой лежит Dockerfile
          dockerfile: Dockerfile.dev # указываем какой Dockerfile хотим использовать
        restart: always
        volumes: # указываем
          - .:/service # монтируем приложение на время разработки
          - static-data:/static
        depends_on: # опция, которая позволяет указать зависимости этого сервиса
          - mongodb
    
    volumes:
      static-data:
      mongodb-data:

    В реальности у меня всё несколько сложнее, но как пример приложения с одной БД, он вполне подходит.


    7. Mongoose (да и любые другие ORM/ODM) значительно более удобен, по сравнению с прямым обращением к БД. Он позволяет хранить структуру, правила валидации и различне методы моделей в одном месте и не дублировать логику (ту же валидацию) по всему приложению. Думаю, со временем вы к этому сами придёте.

  • Zoia.js: ещё один веб-фреймворк на Node
    +1

    8. ну и по поводу установки через curl ... | sudo bash ... — это ужасная практика, которая учит пользователей запускать непроверенные скрипты, полученные через интернет (не говоря уже про sudo). Не каждый полезет внутрь читать и разбираться в bash'евских закорючках, чтобы понять что же делает этот скрипт.

  • Zoia.js: ещё один веб-фреймворк на Node
    0

    Спасибо за быстрый ответ.


    1. тут будет несколько подпунктов:
      1. есть готовый официальный образ, основанный как раз на Debian, можно использовать его как основу и не придётся возиться с установкой ноды самому;
      2. не стоит внутри докера делать apt-get upgrade, лучше использовать новую версию родительского образа (в данном случае Debian);
      3. каждый вызов RUN кешируется и сохраняется как отдельный слой, обычно этого стараются избегать;
      4. на мой взгляд не стоит использовать forever внутри докер-контейнера (разве что при разработке, если нужен автоматический перезапуск сервера при изменении файлов), лучше при старте указать --restart always, тогда упавший процесс будет перезапускаться самим докером (см. docker run --help);
    2. под рукой не windows-машины, но я уверен, что там это будет работать нормально.
    3. нашел, где есть стрелочные функции, но там те же let that = this. Стрелочные функции не имеют своего контекста, они наследуют родительскийй, поэтому в этом трюке нет смысла.
    4. это очень удобно на самом деле, вы можете использовать npm version ..., который сам изменит версию в package.json и поставит тег в git; важно, чтобы все текущие изменения были закоммичены, иначе он упадёт с ошибкой. Рекомендую в вопросе версионирования следовать semver.
    5. всё же рекомендуется в гите хранить только пример конфига, а реальные создавать локально и не версионировать, потому что если я захочу изменить конфиг для себя, то я могу столкнуться с конфликтами при pull'е новой версии кода; к тому же, ваш конфиг содержит некоторые секреты.
    6. на вкус и цвет… :)
    7. только заметил, а почему вот здесь вы не используете, например, mongoose?
  • Zoia.js: ещё один веб-фреймворк на Node
    +3

    Есть пара вопросов:


    1. где можно посмотреть Dockerfile?
    2. зачем вы делаете вот так require(path.join(__dirname, '..', 'etc', 'config.js')), когда можно писать просто require('../etc/config.js')?
    3. почему не используете стрелочные функции, а вместо этого let that = this, как, например, тут?
    4. почему версия хранится в файле version.js, а не в package.json?
    5. если это, конфиг приложения, то почему он находится в системе контроля версий?
    6. почему Express, а не стильный-модный-молодёжный Koa? (хотя это дело вкуса, признаю)

    И ради всего святого, не делайте так никогда (тем более с sudo):


    wget -q https://xtremespb.github.io/zoia/zoia_install && sudo bash zoia_install
  • Новинки JavaScript: Асинхронные итераторы
    0

    Когда вызывается return или мы выходим из генератора, то всё равно возвращается Promise, содержащий IteratorResult. Вот пример с кодом:


    async function* values () {
      yield 1
      yield 2
    }
    
    function handle () {
      const iter = values()
      console.log(iter.next()) // Promise { value: 1, done: false }
      console.log(iter.next()) // Promise { value: 2, done: false }
      // следующего элемента нет
      console.log(iter.next()) // Promise { value: undefined, done: true }
    }
    handle()

    Т.е. последний промис разрешается сразу и возвращает done = true. Вы можете запустить этот код и проверить самостоятельно.


    Зачем тут чушь пишите?

    В каком месте я написал чушь?

  • Новинки JavaScript: Асинхронные итераторы
    0
    А если сильно извратиться — можно ли чисто теоретически снаружи повлиять на наш генератор

    Да, вы можете в next() передать какое-нибудь значение:


    function* generator () {
      let value = yield 'Hi!'
      console.log('Hello %s!', value)
    }
    
    const iterator = generator()
    console.log(iterator.next().value) // выведет "Hi!"
    iterator.next('World') // выведет "Hello World!"

    Таким образом можно передать генератору что угодно.

  • Новинки JavaScript: Асинхронные итераторы
    –1
    А снаружи-то как вовремя узнать что больше элементов не будет?

    Снаружи "вовремя" будет тогда, когда зарезолвится промис, т.к. пока это не произойдёт ваш цикл не сможет пойти дальше. А когда промис резолвится, то он возвращает состояние итератора, где указано завершился итератор или нет.


    Чему будет равно свойство done итератора во время выполнения оператора await в генераторе?

    Оно будет равно false. Станет равным true лишь при выходе из функции-генератора явно (по return) или неявно (кончилось тело функции).

  • Новинки JavaScript: Асинхронные итераторы
    0

    Сейчас и для обычных итераторов их нельзя применить. Для этого сначала нужно преобразовать в массив (с помощью Array.from или spread оператора [ ...iterable ]), а потом над ним уже совершать операции.
    В качестве эксперимента я пишу библиотеку, которая добавляет эти методы прямо к итератору (изменяет его прототип, как делает SugarJS), но она далека от завершения, к тому же есть множество более качественных альтернатив: Wu, Lazy.JS и т.д.

  • Новинки JavaScript: Асинхронные итераторы
    +3

    Давайте избавимся от for-await-of и посмотрим как это можно обработать вручную. Допустим, у нас есть удалённая очередь queue с несколькими элементами.
    Пример для IteratorResult<Promise>:


    const queue = ... // queue это итератор
    while (true) {
      const { value, done } = queue.next()
      // done всегда будет false, т.к. из генератора синхронно(!) мы не можем узнать закончилась ли очередь
      const result = await value // здесь мы дожидаемся разрешения value
      if (result === null) { break } // вот здесь нужно проверить, что нам вернулось пустое значение. Но дело в том, что null может быть вполне валидным значением, а не индикатором пустой очереди
      // обрабатываем  result; следующая итерация
    }

    Как видите, этот подход имеет недостаток — у нас нет четкого понимания, что очередь пуста. Мы не можем трактовать null как конец, если только не приняли некое соглашение, что null — это всегда конец очереди.


    В случае с Promise<IteratorResult> всё несколько иначе:


    const queue = ...
    while (true) {
      const { value, done } = await queue.next() // здесь у нас есть Promise, который возвращает текущее состояние итератора
      if (done) { break } // и есть четкое понимание когда стоит прекратить цикл
      // обрабатываем value; следующая итерация
    }

    Т.е. при подходе Promise<IteratorResult> у нас есть возможность без всяких соглашений четко дать понять, что очередь пуста, можно выходить из цикла. queue, например, может при каждом вызове next() помимо получения элемента спрашивать у очереди сколько элементов осталось и при значении 0 вернуть done = true, чтобы прервать цикл и не создать последующих запросов.

  • Новинки JavaScript: Асинхронные итераторы
    0

    Судя по всему, поддержку недавно добавили в V8. Не использую Chrome, так что не могу проверить.

  • Subversion vs. Git: Развенчивание мифов о развенчивании мифов
    +2

    Для этого придуман Git LFS.

  • Писать веб-сайты на ассемблере полезно и приятно
    +5
    php/python/ruby etc. сами по себе интерпретируемые языки

    неправда. Они компилируются в байткод, который потом исполняется виртуальной машиной.


    умудряются напихать каких-то бешеных абстракций

    которые нужны, чтобы код от разных вендоров работал "искаропки" и имел одинаковые интерфейсы.


    ORM — «птичий» язык доступа к БД, зачем когда есть SQL

    затем, чтобы вы могли выбрать данные из одной СУБД (например MySQL), "сджойнить" их с данными из другой (например MongoDB, мне это приходилось делать), закешировать на некоторое время, и всё это без кучи бойлерплейт кода каждый раз. Для любителей SQL есть Query Builder'ы, которые генерят запросы для разных диалектов SQL.


    Шаблонизаторы — еще один птичий язык

    а что вы предлагаете взамен? Только PHP, насколько я знаю, позволяет инлайнить код прямо в HTML, остальным языкам (React не рассматриваем) нужны шаблонизаторы. Хорошо, что есть широкоиспользуемые языки для шаблонов (Mustache, Jade/Pug и т.д.), которые позволяют не учить 100500 разных синтаксисов, а везде делать одинаково (имел несчастье работать с angular1 на фронте и php на бэкенде, один шаблонизатор немного сгладил боль).


    переаллокации памяти и перекачивание данных между буферами

    оверхед неизбежен, но это плата за скорость разработки.


    как работает веб сервер — активизируется заново для каждого обращения

    это не так. Какой-нибудь apache ещё может порождать по процессу на запрос, но, к счастью, к нему прибегают всё реже. nginx, например, имеет пул процессов, которые обрабатывают запросы пользователей. Здесь можно мне возразить, что процесс всё же порождается, но только это не веб-сервер, а какой-нибудь PHP-интерпретатор. Да, это так, но только если вы не используете fastCGI. В остальных языках чаще используется какой-нибудь встроенный в веб-фреймворк (Node.JS, Ruby Unicorn и т.д.) HTTP сервер (хотя мне тоже такое решение не особо нравится).


    на Си — вполне разумное решение

    нет. Это очень опасное занятие. Даже матёрые программисты допускают глупые ошибки, которые могут очень дорого обойтись. Все эти "интерпретируемые языки" зачастую неплохо отлажены и не имеют таких багов, по крайней мере, их сложнее эксплуатировать. Если хотите убер-скорость, но чтобы было безопасно, взгляните на Rust, с его zero-cost abstractions, и какой-нибудь веб-фреймворк для него или на Go и его решения.


    P.S. Сам я веб-разработчик и сейчас, как раз, изучаю Rust, но всё равно не стал бы на нём писать веб.

  • Консоль разработчика Google Chrome: десять неочевидных полезностей
    +2

    Пункты 1, 2, 5, 6, 9 и 10 также работают и в ФФ. Пункт 7 inspect(...) в ФФ делает тоже самое, что и dir(...) в Хроме.
    А ещё, при выборе элемента в инспекторе он становится доступен как $0 в консоли (ФФ и Хром).
    А все обработчики событий (пункт 3) в ФФ можно посмотреть, если в инспекторе рядом с элементом кликнуть на кнопочку ev.

  • Node.js 7.0.0 зарелизился. Встречайте async/await без babel
    0

    Ни разу не приходилось запускать node с sudo за время использования nvm, а до этого приходилось лишь пару раз. Но, если бы пришлось, то скорее всего прописал бы полный путь до бинарника (sudo ~<username>/.nvm/versions/node/<version>/bin/node). Да, неудобно, но если это не разовый запуск (например крон), то можно создать симлинк в /usr/bin или создать алиас или скрипт:


    #!/path/to/node
    
    console.log('Hello World!')

    Сейчас вообще стараюсь не запускать проекты на хост-машине напрямую. Толкаю всё в докер, а с хост машины дёргается docker-compose exec some-periodic-task node /src/task.js — криво, неудобно, но лучше не придумал.

  • Node.js 7.0.0 зарелизился. Встречайте async/await без babel
    0
    Ещё можно использовать NVM:
    nvm install v7.0.0
    nvm alias default v7.0.0
    
  • MongoDB хранение деревьев
    +1
    По поводу первого пункта мы можем долго спорить. Всё же я считаю, что если вы храните любые данные, пришедшие от юзера, сколько бы и какими бы они ни были, то это, мягко говоря, странно. Когда вы проектируете сервис, то с большой долей вероятности знаете чего хотите и примерно представляете, что будет дальше. Исходя из этого проектируете модель данных. Вообще у меня есть сомнения, что мы говорим об одном и том же, изначально мне показалось, что вы предлагаете сохранять любые данные от юзера (100-мегабайтный JSON, картинка с котятами и т.п.) как значение, а потом из него пытаться выбрать нужные поля.

    По поводу "не сохраняет". Это не важно. Даже, если злоумышленник может получить список всех юзеров сервиса (включая их личные данные), то это уже плохо.
  • MongoDB хранение деревьев
    +5
    Фильтровать при записи нужно как раз для того, чтобы не тратить каждый раз ресурсы на фильтрацию при отдаче. А ещё это в какой-то мере поможет уберечься от NoSQL-иъекций.
  • Убийцы оптимизации
    +6
    Разные потребности бывают :)
  • Семь удивительных «возможностей» Javascript
    +2
    Можно ещё вот так:
    Array
      .apply(null, new Array(10))
      .map((_, i) => i);
    
  • Создание изоморфного приложения на React и Flummox
    +1
    Пока что только JS (Express, Restify, коллега настойчиво советует Loopback) и PHP (legacy код).
  • Создание изоморфного приложения на React и Flummox
    +1
    Спасибо за ответ.

    Вы спрашиваете что-то вроде: «а как мне выкинуть из Catberry основную часть и использовать вместо выкинутого куска React, но так чтобы вы все остальное работало также»

    Нет же. Я просто сказал, что мне хочется именно реакт с его компонентами.

    текущая архитектура и подход React никогда не позволят ему реализовать прогрессивный рендеринг

    К сожалению приходится чем-то жертвовать.
  • Создание изоморфного приложения на React и Flummox
    +1
    API можно/нужно писать на том, что удобнее. Главное — HTTP интерфейс для клиента. У себя в pet-project я решил разделить renderer-app и API на кучку модных нынче микросервисов, которые общаются между собой и клиентом по HTTP.
  • Создание изоморфного приложения на React и Flummox
    +1
    Каким-то чудом Este.JS обошел меня стороной. Выглядит очень интересным, спасибо, обязательно утащу оттуда какие-нибудь решения. Насчёт него пока ничего не могу сказать, т.к. внутрь особо не смотрел. Но интересно, как они победили синглтоны и прочие грабли.
    На Catberry смотрел несколько месяцев назад, но тогда мне не понравилось, что нужно компоненты держать в catberry_* каталогах, меня крайне раздражают вендор-префиксы. Ещё хотелось для шаблонизации использовать именно React, т.к. он чисто внешне близок к HTML — опять же, на мой вкус.
    Ещё, я раньше не сталкивался с термином progressive rendering в данном контексте, можете немного пояснить его?

    P.S. Думаю, по поводу Catberry pragmadash лучше может ответить.
  • Создание изоморфного приложения на React и Flummox
    0
    Да, с виду это немного странно. Но суть здесь в том, что на один Store может быть подписано несколько компонентов, и при его обновлении, обновятся все связанные компоненты.
  • Создание изоморфного приложения на React и Flummox
    0
    MemoryStorage, например, не должен ничего знать о HTTP и, тем более, не должен бросать HTTP ошибку.
    Ошибки в app.js лучше проверять одним try-catch блоком, чтобы не возникало повисших соединений.
    Ещё в MemoryStorage мы явно возвращаем Promise. Это можно избежать, если функция явно объявлена как async.
    Ну и надо писать больше тестов :)

    Уверен, есть ещё недочёты, но пока не могу их вспомнить.
  • ES6 и за его пределами. Глава 1: ES? Настоящее и Будущее
    +3
    По теме могу порекомендовать блог 2ality (en), его автор довольно подробно пишет о новинках из ES6.
  • Создание изоморфного приложения на React и Flummox
    +1
    По поводу webpack'а не подскажу. Решение специально пока что не искал. По поводу вопросов:

    1. большой проект у меня пока что только пишется, но уже могу сказать, что некоторые части лучше выносить в отдельные модули или хотя бы папки (отделить, например блог, галлерею и т.п., от остального сайта).
    2. из подводных камней, некоторые неожиданные проблемы с async/await, но о них я написал в статье (про заверните в try-catch иначе не увидите ошибок). Также недавно столкнулся с проблемой интернационализации приложения с помощью react-intl. Всё в целом хорошо, но хранение переводов — небольшая боль. Нет дефолтных переводов, надо везде таскать все переводы для текущего состояния иначе ловим исключения. А ещё нужно приучить себя всегда закрывать теги: <MyComponent /> — работает, <MyComponent></MyComponent> — тоже работает, <MyComponent> (не закрыл) — не скомпилится. И ещё одно, вы могли заметить, что при рендеринге на сервере делается запрос к API, и после рендеринга и запуска скриптов на клиенте делается снова такой же запрос. Я сейчас работаю над устранением этого досадного бага. По итогам намерен написать статью. Вы можете погуглить решения по словам rehydrate/dehydrate (так называлось это в fluxible) или же serialize/unserialize. Автор flummox уже вроде как запилил фикс, Но я ещё не смотрел.
    3. babel просто транслирует ES6/ES7 → ES5, он не выполняет код сам. Node.JS шустрая, основной затык обычно в запросах к БД. А вот с React на сервере уже не всё так хорошо. Синтетические тесты (ab -c 5 -t 10 ...) TODO списка на моём i7 с 8GiB памяти показали всего лишь около 200RPS, что не очень плохо, если сравнивать с каким-нибудь PHP. Но довольно медленно, если сравнить с другими шаблонизаторами на Node.JS. И при увеличении количества компонентов, которые рендерятся для текущего состояния, производительность продолжает падать. Этого можно избежать в некоторых случаях, если использовать некоторые оптимизации.
    4. серебрянной пули не существует :) Уже давно существуют всякие Meteor'ы, Derby.JS и т.д. Но это полноценные фреймворки и иногда с ними нужно бороться, чтобы сделать что-то нестандартное (сам не работал, но знающие люди так говорят).

    Желаю удачи в вашем проекте :)
  • Создание изоморфного приложения на React и Flummox
    0
    Это такое гибридное (хотя это не совсем правильный термин) приложение, которое использует один и тот же код для рендеринга на клиенте (в данном случае в браузере) и на сервере. Это позволяет не дублировать логику и шаблоны, как в случае с обычными приложениями. Это можно проиллюстрировать такой картинкой:
    Картинка
    image