Как подключиться к базе данных с помощью CSS

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

К старту курса по Fullstack-разработке на Python делимся материалом о том, как при помощи современных возможностей CSS и JS — ворклетов и API Houdini — подключиться к базе данных и выполнять запросы к ней. За подробностями приглашаем под кат.


Как это свойственно твитам, на той неделе в сети витал этот твит:

Рекрутеры: «Мы ищем того, кто может подключиться к базе данных с помощью CSS»
Рекрутеры: «Мы ищем того, кто может подключиться к базе данных с помощью CSS»

Давненько мне не попадалось достойных щитпостинговых проектов — ещё с тех времён, когда я даже не знал слова «щитпост». Потому отчасти меня вдохновил предыдущий проект на основе блокчейн-стартапа, когда его инвесторы захотели запечатлеть свои лица на 3D-кубах.

Это напоминает о старых добрых временах на заре интернета, когда всё было в диковинку. Но я не стремлюсь к предписаниям, поэтому избавлю вас от истории моей жизни. Лучше расскажу о том, как я справился со своим новым проектом — sqlcss.xyz:

Он о том, как подключаться к БД из CSS. Работает только в Chrome, зато можно сделать запрос в любую БД SQLite через CSS. Как именно? Набор новых API Houdini даёт браузеру управлять CSS с помощью JavaScript и объектной модели браузера: создавать ваши стили CSS, добавлять ваши свойства и т. д.

Возможно, самая важная функциональность Houdini — это CSS Paint Worklet. Она позволяет «рисовать» на элементе, как в Canvas, работая с ним в браузере, как с изображением в CSS. Поиграть с примерами можно на houdini.how. Но этот ворклет — лишь часть API воркера, сам контекст сanvas тоже сильно урезан и песочница для рисования в CSS-коде меньше, чем можно ожидать.

Это значит, что нет доступа к сети Можно распрощаться с fetch и XmlHttpRequest. А ещё нет drawText в контексте рисования. На всякий случай, если вы надеялись обойти эти проблемы — другие API JS тоже исчезли. Но не всё потеряно. Разберём процесс поэтапно.

1. Установка БД

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

Есть библиотека sql.js. Это буквально версия SQLite, скомпилированная в WebAssembly и через emscripten в старую добрую ASM.js. Воспользоваться версией для WASM нельзя: она должна получать двоичный файл по сети. В версии для ASM этого ограничения нет, и весь код доступен в одном модуле.

Хотя в PaintWorklet доступ к сети внутри воркера ограничен, import кода в модуле ES6 выполнить можно. Иными словами, внутри файла должна быть инструкция export. Но в sql.js нет сборки исключительно для ES6, поэтому скрипт пришлось изменить.

А теперь главный вопрос: можно ли установить БД в ворклете?

const SQL = await initSqlJs({
  locateFile: file => `./${file}`,
});

const DB = new SQL.Database();

Получилось! Ошибок нет. Но и данных тоже нет, так что добавим их.

2. Запросы к БД

Самое простое, что можно сделать вначале — добавить фиктивные данные. В Sql.js есть пара функций именно для этого:

DB.run('CREATE TABLE test (name TEXT NOT NULL)')
DB.run(
  'INSERT INTO test VALUES (?), (?), (?), (?)',
  ['A', 'B', 'C', 'D']
)

Теперь у нас есть тестовая таблица со значениями. Хотя не уверен, как будет структурирован результат. Отправим запрос и получим значения:

const result = DB.exec('SELECT * FROM test')
console.log(result)

Теперь неплохо визуализировать результаты.

3. Простой способ визуализации

Я думал, это так же просто, как писать текст в сanvas:

class SqlDB {
  async paint(ctx, geom, properties) {
    const result = DB.exec('SELECT * FROM test');
    ctx.font = '32px monospace';
    ctx.drawText(JSON.stringify(result), 0, 0, geom.width);
  }
}

Но нет: это было бы слишком просто. Контекст здесь не такой, как у элемента в Canvas, то есть осталась только часть контекста. Можно рисовать контуры и кривые, так что отсутствие удобного API — препятствие, но делу оно не помешает.

4. Текст без API для текста

К счастью, библиотека opentype.js предлагает решение. Она позволяет проанализировать файл шрифта, а затем, получив строку текста, сгенерировать формы букв каждого символа. Итог этой операции — объект path со строкой, которую можно отобразить в контексте рисования.

На этот раз, чтобы импортировать библиотеку opentype.js, вносить изменения не нужно: она уже есть в JSPM. Зададим JSPM npm-пакет, и он автоматически создаст модуль ES6, который можно импортировать прямо в браузер. Это здорово, ведь мне не хотелось возиться с пакетным инструментом ради проекта-шутки.

import opentype from 'https://ga.jspm.io/npm:opentype.js@1.3.4/dist/opentype.module.js'

opentype.load('fonts/firasans.otf')

Здесь проблема, ведь шрифт OpenType нужно загрузить по сети. Я не могу этого сделать. Опять сорвалось! Или могу? Есть метод parse, который принимает буфер массива. Просто кодируем шрифт в base64 и декодируем его в модуле:

import opentype from 'https://ga.jspm.io/npm:opentype.js@1.3.4/dist/opentype.module.js'
import base64 from 'https://ga.jspm.io/npm:base64-js@1.5.1/index.js'

const font = 'T1RUTwAKAIAAAwA ... 3 days later ... wAYABkAGgAbABwAIAKM'

export default opentype.parse(base64.toByteArray(font).buffer)

Я говорил, что в ворклете нет API для обработки строк base64? Нет даже atob и btoa. Пришлось найти реализацию на обычном JS. Этот код я поместил в отдельный файл: не очень это… эргономично — работать с 200 Кб строки кодированного шрифта вместе с оставшейся частью кода.

Вот как, злоупотребив модулем ES, я загрузил шрифт.

5. Отображение результатов, другой простой способ

Теперь всю тяжёлую работу выполняет библиотека opentype. Всё, что нужно — немного матемологии, чтобы красиво подравнять код:

import font from './font.js'

const SQL = await initSqlJs({
  locateFile: file => `./${file}`,
});

const DB = new SQL.Database();

DB.run('CREATE TABLE test (name TEXT NOT NULL)')
DB.run(
  'INSERT INTO test VALUES (?), (?), (?), (?)',
  ['A', 'B', 'C', 'D']
)

class SqlDB {
  async paint(ctx, geom, properties) {
    const query = DB.exec('SELECT * FROM test')
    const result = query[0].values.join(', ')

    const size = 48
    const width = font.getAdvanceWidth(queryResults, size)
    const point = {
      x: (geom.width / 2) - (width / 2),
      y: geom.height / 2
    }

    const path = font.getPath(result, point.x, point.y, size)
    path.draw(ctx)
  }
}

registerPaint('sql-db', SqlDb)

Подправим HTML и CSS и посмотрим, что получится:

<html>
  <head>
    <script>
      CSS.paintWorklet.addModule('./cssdb.js')
    </script>
    <style>
      main {
        width: 100vw;
        height: 100vh;
        background: paint(sql-db);
      }
    </style>
  </head>
  <body>
    <main></main>
  </body>
</html>

Работает, но не хватает CSS, а запрос я захардкодил.

6. Запросы к базе данных через CSS

Запросы к БД лучше делать с помощью CSS: на самом деле это единственный способ взаимодействовать с воркером рисования вне его контекста — у этого воркера нет API обмена сообщениями, как у обычных воркеров.

Чтобы сделать запрос, потребуется свойство CSS. Определяя inputProperties, подпишемся на изменения нового свойства: при изменении значения свойства оно отобразится повторно. Слушатели событий не нужны:

class SqlDb {
  static get inputProperties() {
    return [
      '--sql-query',
    ]
  }

  async paint(ctx, geom, properties) {
    // ...
    const query = DB.exec(String(properties.get('--sql-query')))
  }
}

Это типизированные свойства CSS, но они помещены в класс CSSProperty, который сам по себе не особенно полезен. Чтобы использовать его, как показано выше, придётся вручную преобразовать его в строку, число или что-то ещё. Снова чуть подправим CSS:

main {
  // ...
  --sql-query: SELECT name FROM test;
}

Кавычки убраны специально, иначе пришлось бы удалять их из строки перед её передачей в БД. Всё работает, миссия выполнена. Если вы уже попробовали sqlcss.xyz, то наверняка заметили, что я на этом не остановился — после небольшого рефакторинга сделал ещё пару изменений.

7. Локальный файл базы данных

Концепция доказана, но можно добиться большего. Хардкодить схему БД и сами данные — это плохо. Здорово делать запрос в любую БД, когда у вас есть её файл. Нужно просто прочитать его и кодировать в base64, как файл шрифта:

const fileInput = document.getElementById('db-file')
fileInput.onchange = () => {
  const reader = new FileReader()
  reader.readAsDataURL(fileInput.files[0])

  reader.onload = () => {
    document.documentElement.style.setProperty(
        '--sql-database',
        `url('${reader.result}')`
    )
  }
}

Для этого я создал свойство CSS, где БД SQLite указывается в виде URI из данных в формате base64. URI данных здесь только для подтверждения его валидности с точки зрения DOM — разбор выполним на стороне воркера. Остаётся упростить выполнение запросов, иначе придётся погружаться в отладчик, чтобы работать с CSS элемента.

8. Пишем запросы к БД

Это, возможно, самая простая часть проекта. Точки с запятой в нашем свойстве CSS — не большая проблема. SQLite до неё нет дела. Если она найдётся во входных данных, проще её удалить:

const queryInput = document.getElementById('db-query')
queryInput.onchange = () => {
  let query = queryInput.value;
  if (query.endsWith(';')) {
    query = query.slice(0, -1)
  }

    document.documentElement.style.setProperty(
    '--sql-query',
    queryInput.value
  )
}

Теперь можно использовать CSS для импорта и просмотра локальной БД. Но как красиво отображать результаты, когда их много и всё нужно разделить на отдельные строки? Это уже другая тема, но если вы хотите разобраться в ней, вот весь код проекта.

Продолжить погружение в CSS и другие веб-технологии вы сможете на наших курсах:

  • Профессия Fullstack-разработчик на Python

  • Профессия Frontend-разработчик

Узнайте подробности здесь.

Профессии и курсы

Data Science и Machine Learning

  • Профессия Data Scientist

  • Профессия Data Analyst

  • Курс «Математика для Data Science»

  • Курс «Математика и Machine Learning для Data Science»

  • Курс по Data Engineering

  • Курс «Machine Learning и Deep Learning»

  • Курс по Machine Learning

Python, веб-разработка

  • Профессия Fullstack-разработчик на Python

  • Курс «Python для веб-разработки»

  • Профессия Frontend-разработчик

  • Профессия Веб-разработчик

Мобильная разработка

  • Профессия iOS-разработчик

  • Профессия Android-разработчик

Java и C#

  • Профессия Java-разработчик

  • Профессия QA-инженер на JAVA

  • Профессия C#-разработчик

  • Профессия Разработчик игр на Unity

От основ — в глубину

  • Курс «Алгоритмы и структуры данных»

  • Профессия C++ разработчик

  • Профессия Этичный хакер

А также

  • Курс по DevOps

  • Все курсы

Источник: https://habr.com/ru/company/skillfactory/blog/650499/


Интересные статьи

Интересные статьи

Однажды студент рассказал как сдал зачет по программному продукту, а на следующем курсе не сумел его использовать. Рассказал и пожал плечами. А я запомнил, что цель любого курса – решение собственных ...
YELP — зарубежная сеть, которая помогает людям находить местные предприятия и услуги, основываясь на отзывах, предпочтениях и рекомендациях. В текущей статей будет проведен определенный...
Одной из главных задач, которая встает перед системным администратором при внедрении любой информационной системы — обеспечение сохранности хранящихся в ней данных. Наиболее оптимальным с...
Хотелось бы поделиться своим кейсом, где нам удалось сравнить результаты от двух близких методов: «холодных» звонков и переписок. Расскажем, как мы добивались автоматизации процесса, ...
Page Objects могут быть использованы как мощный метод абстракции (изоляции) ваших тестов от технической реализации. Важно помнить, их (Page Objects) можно использовать для увеличения стабильности...