Глава 7. Достойное выступление с JavaScript

Мы живём в эпоху возрождения JavaScript - это уже далеко не мальчик для битья любителей чистоты языков программирования. Rails внедрил современные технологии JavaScript, такие как React благодаря включению Webpacker: некого gem, привнёсшего поддержку webpack.

Будучи разработчиком Rails, важно иметь возможность сочетать эти технологии с вашими приложениями по мере необходимости, следовательно наша среда Docker должна поддерживать нс в этом предприятии.

В данной главе мы изучим возможность для работы с JavaScript в качестве части нашей разработки Rails. Мы также увидим как включать интерфейс React в своё приложение Rails устанавливая и настраивая Webpacker.

К концу этой главы наша среда разработки на основе Docker засверкает новыми гранями при помощи таких современных новшеств JavaScript.

Варианты интерфейса JavaScript

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

Если ваше приложение Rails не обслуживает ваш интерфейс, это означает что вы применяете своё приложение Rails в качестве некого уровня API. В этом случае у вас имеется отдельный интерфейс, обычно целиком написанный на JavaScript, каком- то из React, Ember, Vue.js или чего- то ещё. Такой подход выходит за рамки данной книги, так как он вовлечён в получение в вашей настройке очень специфичного JavaScript. Однако касательно основных понятий всё достаточно прямолинейно и вы в целом будете применять те навыки, которые вы уже изучили.

Вот общая схема:

  • Переименуйте свою службу web. Название важно. При данном подходе ваши приложения Rails в действительности это API или серверная часть, поэтому вам следует называть его так.

  • Создайте некий индивидуальный образ для исполнения своего приложения интерфейса JavfScript. Для своего JS интерфейса вам следует сделать то же самое и так же как мы это делали когда создавали некий персональный образ для исполнения своего приложения Rails. Его Dockerfile может собираться поверх стандартного образа Node.js, добавляя по мере необходимости специфичные для приложения настройки. .

  • В своём docker-compose.yml создайте обособленную службу интерфейса. Это будет обособленное приложение JavaScript. Через переменные среды вы будете настраивать конкретную оконечную точку API, которую она должна применять (имя домена и порт соответствующего API Rails).

С другой стороны, если вы применяете Rails для обслуживания своего интерфейса, это означает применение тех функциональностей, которые предоставляет Rails. Для обслуживания интерфейса JavaScript Rails предлагает два механизма: (основанный на Sprockets) конвейер ресурсов или новый подход Webpacker, добавленный в Rails 5.1.

Обычный конвейер ресурсов на основе Sprockets работает сразу после установки без какой бы то ни было особой настройки. Будучи частью самого исполняемого сервера Rails ваши ресурсы автоматически будут компилироваться и обслуживать ваши представления стандартным для Rails образом. Некий пример этого мы рассмотрим в своей следующей Главе 8, Проверка в среде Docker.

Получение же вашего приложения Rails работающим с Webpacker требует некоторой дополнительной настройки в Docker. Поскольку это настолько популярный подход, мы намерены предоставить вам руководство его настройки в оставшейся части данной главы.

Интерфейс JavaScript Rails с помощью Webpacker

При помощи gem с названием webpacker Rails имеет в своём составе некий способ для построения богатых интерфейсов JavaScript в ваших приложениях начиная с версии 5.1. Webpacker имеет модульную архитектуру, которая позволяет вам интегрировать различные технологии, такие как React, Ember, Vue.js или даже Elm.

Применяя в качестве примера React, давайте рассмотрим как бы нам пришлось настраивать Webpacker в своём приложении в Docker.

Перво- наперво, Webpacker требует Yarn и текущей версии Node. Это потребует некого обновления для нашего образа Docker:


​1: 	​FROM ruby:2.6​
​- 	​LABEL maintainer="rob@DockerForRailsDevelopers.com"​
​- 	
​- 	​# Включаем работу apt с источниками на основе https
​5: 	​RUN apt-get update -yqq && apt-get install -yqq --no-install-recommends \​ 
​- 	  ​apt-transport-https​                             
​- 	
​- 	​# Обеспечиваем установку современной версии Node​
​- 	​# См. https://github.com/yarnpkg/yarn/issues/2888​
​10: 	​RUN curl -sL https://deb.nodesource.com/setup_8.x | bash -​   
​- 	
​- 	​# Гарантируем самые последние пакеты для Yarn​
​- 	​RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -​  
​- 	​RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | \​
​15: 	  ​tee /etc/apt/sources.list.d/yarn.list​ 
​- 	
​- 	​# Устанавливаем пакеты​
​- 	​RUN apt-get update -yqq && apt-get install -yqq --no-install-recommends \​
​- 	  ​nodejs \​
​20: 	  ​yarn​    
​- 	
​- 	​COPY Gemfile* /usr/src/app/​
​- 	​WORKDIR /usr/src/app​
​- 	​RUN bundle install​
​25: 	
​- 	​COPY . /usr/src/app/​
​- 	
​- 	​CMD ["bin/rails", "s", "-b", "0.0.0.0"]​
		

Для установки современной версии Yarn нам придётся добавить репозиторий пакета Debian Yarn в список источников (строки 13- 15). Однако поскольку репозиторий пакета Yarn применяет HTTPS, нам приходится установить и пакет apt-transport-https (строки 5-6) что позволить его работу.

К несчастью существует проблема зависимости между Yarn и той (старой) версией Node.js, которая устанавливается по умолчанию. В строке 10 мы решаем это добавляя репозиторий пакета Node в свой перечень источников; это обеспечит нам установку более современной версии Node.

Наконец, в строке 20 мы добавили yarn в перечень устанавливаемых нами пакетов. Имея установленными yarn и некую современную версию Node, мы теперь готовы настраивать своё приложение на применение Webpacker.

Если бы мы знали о том что собираемся применять Webpacker при первом создании своего приложения, мы бы могли включить поддержку с помощью параметра --webpack - например:


​ 	​$ ​​rails​​ ​​new​​ ​​myapp​​ ​​--webpack=react​​ ​​<other​​ ​​options> 
		

Такой параметр --webpack=react сгенерировал бы поддержку React нашего приложения сразу при установке. Тем не менее, имея своё приложение уже созданным, добавление поддержки Webpacker потребует пары этапов, выполняемых вручную.

Вначале нам придётся обновить свой Gemfile чтобы он содержал Webpacker:


​ 	gem ​'webpacker'​, ​'~> 3.5'​ 
		

Затем мы запускаем bundle install через повторную сборку своего образа:


​ 	​$ ​​docker-compose​​ ​​build​​ ​​web​
		

Давайте остановим свою службу web, потому как она в настоящее время исполняет нашу старую версию без установленного gem Webpacker:


​ 	​$ ​​docker-compose​​ ​​stop​​ ​​web​
		

Теперь мы можем установить в своём приложении Webpacker:


​ 	​$ ​​docker-compose​​ ​​run​​ ​​web​​ ​​bin/rails​​ ​​webpacker:install
		
[Замечание]Ошибка переполнения inotify

К сожалению, при исполнении нашей предыдущей команды вы можете столкнуться со следующим сообщением об ошибке, которое появляется в результате ошибки в gem rb-inotify:


​ 	run() in thread failed: inotify event queue has overflowed.
		

Хотя это и кажется неаккуратным, похоже что оно не оказывает какого- либо существенного воздействия и вы спокойно можете его игнорировать.

с последующей интеграцией необходимого React Webpacker:


​ 	​$ ​​docker-compose​​ ​​run​​ ​​web​​ ​​bin/rails​​ ​​webpacker:install:react​
		

Хорошо, наше приложение настроено под Webpacker и React. Тем не менее, прежде чем мы сможем на самом деле сказать что мы всё сделали, нам требуется некий путь компиляции наших ресурсов React автоматически.

Компиляция ресурсов при помощи Webpacker

Выступая в качестве части Webpacker, Rails предоставляет двоичный файл webpack-dev-server. Это небольшой сервер, который работает в фоновом режиме, автоматически вырабатывая наши управляемые webpack- файлы.

Если бы вы вели разработку локально, это была бы просто другая команда, которую вы бы вызывали в своём терминале. Однако метод Docker состоит в исполнении этого в некоторой обособленной службе в её собственном контейнере.

Давайте добавим некую новую службу для этого в свой файл docker-compose.yml:


​1: 	version: ​'​​3'​
​- 	
​- 	services:
​- 	  web:
​5: 	    build: ​.​
​- 	    ports:
​- 	      - ​"​​3000:3000"​
​- 	    volumes:
​- 	      - ​.:/usr/src/app​
​10: 	    env_file:
​- 	      - ​.env/development/web​
​- 	      - ​.env/development/database​
​- 	    environment:
​- 	      - ​WEBPACKER_DEV_SERVER_HOST=webpack_dev_server​   
​15: 	
​- 	  webpack_dev_server:
​- 	    build: ​.​                          
​- 	    command: ​./bin/webpack-dev-server​ 
​- 	    ports:
​20: 	      - ​3035:3035​                   
​- 	    volumes:
​- 	      - ​.:/usr/src/app​              
​- 	    env_file:                       
​- 	      - ​.env/development/web​
​25: 	      - ​.env/development/database​   
​- 	    environment:
​- 	      - ​WEBPACKER_DEV_SERVER_HOST=0.0.0.0​ 
​- 	
​- 	  redis:
​30: 	    image: ​redis​
​- 	
​- 	  database:
​- 	    image: ​postgres​
​- 	    env_file:
​35: 	      - ​.env/development/database​
​- 	    volumes:
​- 	      - ​db_data:/var/lib/postgresql/data​
​- 	
​- 	volumes:
​40: 	  db_data:
 	   

Rails webpack_dev_server разработан для работы в самом корне вашего приложения Rails; именно по этой причине мы выполняем сборку из того же самого Dockerfile (строка 17), что и нашу службу web.

Хотя она и применяет те же самые образ и код, мы запускаем нашу новую службу другой командой. Вместо запуска своего сервера Rails мы исполняем саму команду ./bin/webpack_dev_server (строка 18).

Мы выставляем свою службу webpack-dev-server по порту со значением по умолчанию 3035 (строка 20).

Мы бы хотели, чтобы наша webpack-dev-server автоматически подцепляла и повторно компилировала бы наши изменения по мере того как мы локально выполняем разработку без необходимости перезапуска. Именно по этой причине в строке 22, как и в случае с нашей службой web мы монтируем свои локальные файлы в этот контейнер.

Сама команда webpacker-dev-server ожидает своего исполнения с теми же самыми настройками что и наше приложение Rails. К счастью, имея эти файлы извлечёнными, мы можем просто повторно воспользоваться теми же самыми env_file (строки 23- 25).

Однако для обеспечения того чтобы webpacker-dev-server отвечал на запросы с любого IP адреса мы устанавливаем WEBPACKER_DEV_SERVER_HOST в значение 0.0.0.0 (строка 27), во многом аналогично тому как мы поступали со своим сервером Rails.

Получив настроенной нашу службу webpack_dev_server, нам также требуется настроить некую переменную среды Rails для своей службы web с тем, чтобы она знала где искать эту webpacker-dev-server (строка 14).

Теперь нам требуется запустить свою службу web для применения нашего нового образа и подхвата этих новых изменений настроек:


​ 	​$ ​​docker-compose​​ ​​up​​ ​​-d​​ ​​web​
		

а затем запустить нашу новую службу:


​ 	​$ ​​docker-compose​​ ​​up​​ ​​-d​​ ​​webpack_dev_server​
		

Прикладное приложение React Hello World

теперь нашей целью является проверка того что мы можем настраивать своё применяющее Docker приложение Rails чтобы позволять нам разрабатывать современные приложения JavaScript с применением таких технологий как React. Чтобы покончить с этим, нам просто нужно показать как компилируется и корректно загружается с нашей настройкой некое простейшее приложение React.

Когда мы установили Webpacker, он добавил некий пример приложения React "Hello World" в app/javascript/packs/hello_react.jsx, которое строит некий <div>, выводящий "Hello React!":


​ 	import React from 'react'
​ 	import ReactDOM from 'react-dom'
​ 	import PropTypes from 'prop-types'
​ 	
​ 	const Hello = props => (
​ 	  <div>Hello {props.name}!</div>
​ 	)
​ 	
​ 	Hello.defaultProps = {
​ 	  name: 'World'
​ 	}
​ 	Hello.propTypes = {
​ 	  name: PropTypes.string
​ 	}
​ 	
​ 	document.addEventListener('DOMContentLoaded', () => {
​ 	  ReactDOM.render(
​ 	    <Hello name=​"React"​ />,
​ 	    document.body.appendChild(document.createElement('div')),
​ 	  )
​ 	})
 	   

Мы намерены воспользоваться этим приложением для проверки правильности установок Webpacker: Прежде всего нам требуется сгенерировать некую страницу, которая будет загружать это приложение React:


​ 	​$ ​​docker-compose​​ ​​exec​​ ​​web​​ ​​bin/rails​​ ​​g​​ ​​controller​​ ​​pages​​ ​​home​
​ 	      create  app/controllers/pages_controller.rb
​ 	       route  get 'pages/home'
​ 	      invoke  erb
​ 	      create    app/views/pages
​ 	      create    app/views/pages/home.html.erb
​ 	      invoke  helper
​ 	      create    app/helpers/pages_helper.rb
​ 	      invoke  assets
​ 	      invoke    coffee
​ 	      create      app/assets/javascripts/pages.coffee
​ 	      invoke    scss
​ 	      create      app/assets/stylesheets/pages.scss
		

Давайте изменим наше выработанное представление (app/views/pages/home.html.erb) для загрузки имеющегося приложения React; когда мы будем там, давайте удалим содержимое по умолчанию и зададим новый заголовок страницы:


​ 	​<%=​ javascript_pack_tag ​'hello_react'​ ​%>​
​ 	
​ 	<h1>React App</h1>
 	   

Хорошо, давайте посмотрим его вывод. Перейдите в http://localhost:3000/pages/home и вы должны обнаружить содержимое "Hello React!" отображаемым в данной странице. Это подтверждает что наше приложение React было правильно скомпилировано и загружено.

Автоматическое обновление также работает. Измените app/javascript/packs/hello_react.jsx для установки значения defaultProps.name на ваше имя:


​ 	​<%=​ javascript_pack_tag ​'hello_react'​ ​%>​
​ 	
​ 	<Hello name=​"<Your name>"​ />
 	   

Теперь, когда вы перезагрузите свой браузер, вы должны обнаружить обновление данной страницы (только если ваше имя, по случаю, не "React").

Это приложение не поражает воображение. Но имея эту основу вы теперь способны выполнять в своих приложениях Rails разработку при помощи React и строить всё что только пожелаете.

Беглый обзор

Ну всё - мы наконец сделали это. Теперь у нас имеется полноценной приложение Rails, работающее с Docker, которое целиком управляется через Compose. Красота, не так ли? Давайте быстро повторим то что мы прошли в этой главе:

  1. Мы установили Yarn и современную версию Node в соответствии с требованиями Webpacker.

  2. Мы установили необходимый gem Webpacker.

  3. Мы добавили некую новую службу в свой файл docker-compose.yml который запускает нашу webpack_dev_server для автоматической компиляции ресурсов JavaScript Webpacker.

  4. Для проверки того что всё что мы правильно настроили для компиляции и запуска приложения React мы создали некое приложение Hello World React.

Теперь когда наше приложение поднято и запущено во всём его цвете, далее мы переключим ваше внимание на установку и запуск наших проверок в среде Docker.