WidLib – декларативный js-фреймворк для построения виджетов

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

    К сожалению, у автора проекта не хватило мужества довести его до ума. Простите.



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

    В отличие от большинства фреймворков, WidLib не претендует на универсальность: он предназначен для быстрого создания многостраничных диалоговых приложений.

    Спектр задач
    1. Виджеты заказа доставки
    2. Wi-Fi приложения (путеводитель по торговому центру или интерактивное меню при подключении к местному Wi-Fi)
    3. Калькуляторы (кредиты, пластиковые окна и двери)
    4. Формы подписки на сайте
    5. Викторины (игровые, а так же для маркетинговых акций и розыгрышей)
    6. Тесты (проверка компетенций, обучение, работа с кадрами)
    7. Онлайн-помощники (например, подбор туристического маршрута или ассистент колл-центра)
    8. Встраивание виджетов в мобильные приложения (здесь он похож на PhoneGap)
    9. Диалоговые приложения для соцсетей (опять-таки викторины, нелинейные опросы)
    10. Бронирование (билеты на самолет, номер в гостинице или время у стоматолога)
    11. и т.д.

    Чтобы создать DSL, мы спросили себя – каким мы хотим видеть предельно простой, но при этом гибкий язык описания (и в меньшей степени – программирования) этих диалоговых приложений. Мы использовали подход convention over configuration, знакомый по Ruby on Rails. Простые приложения пишутся в три строчки, сложные – чуть больше.
    Давайте рассмотрим на примере виджета по заказу пиццы на сайте:
    (DSL еще находится в доработке, поэтому предложения приветствуются.)
    Coffeescript
    widlib=require("widlib-server")
    server=widlib.init
      # Можно использовать любой шаблонизатор (index_template=Handlebars.compile("...")),
      # в функцию передается объект страницы, шаблон сохранится в @app.template
      template: index_template
      pages:
        type:
          # шаблоны можно также задавать индивидуально для каждой страницы
          template: type_template # @app.pages["type"].template
          body: "Выберите пиццу"
          # при клике на ссылку происходит событие submit, автоматический переход на следующую страницу (если не указано явно)
          # также у каждой страницы есть события onLeave, onEnter
          inputs: [
            { value: "Маргарита", type: "link", name: "type", price: 350 },
            { value: "Пепперони", type: "link", name: "type", price: 360 },
            { value: "Филадельфия", type: "link", name: "type", price: 370 },
            { value: "Четыре сыра", type: "link", name: "type", price: 380 },
          ]
    
        size:
          body: "Выберите размер"
          # если в параметре используем функцию - значение вычисляется лениво
          inputs: ->
            price = @session.input("type").price # session - хранит данные текущей сессии. @session.input("type") присвоился автоматически после страницы type.
            [
              { value: "30", type: "link", name: "size", price: price },
              { value: "40", type: "link", name: "size", price: price*1.2 },
              { value: "50", type: "link", name: "size", price: price*1.5 },
            ]
          # можно в явном виде указать следующую страницу или использовать функцию
          onSubmit: "address"
    
        address:
          body: "Введите адрес"
          inputs: [ { name: "address", type: "text", placeholder: "улица, дом, подъезд, квартира" }, { type: "submit", value: "Далее" } ]
    
        phone:
          body: "Введите телефон"
          inputs: [ { name: "phone", type: "text", placeholder: "+7 xxx xx xx" }, { type: "submit", value: "Далее" }]
          onSubmit: -> # также возможно использовать события onLoad, on
            @data("orders").push @session.values() # данные записываем в постоянное хранилище
            @data("email").push email_template(@session.values())
            "success" # возвращаем имя следующей страницы
    
        success:
          body: "Ваша пицца уже едет к вам"
          image: -> "/images/#{@session.value("type")}.jpg" # можно использовать дополнительные параметры, в данном случае image для view
    
      data:
        orders:
          type: "spreadsheet" # используя модули для различных API можно хранить или синхронизировать данные с внешними сервисами
          url: "https://docs.google.com/spreadsheet/ccc?key=0Au4e-jj1-69ZdEloMW03UExKLXIZFSUE"
        email:
          type: "email"
          to: "1@interactiff.net"
    
    server.listen "3000"
    

    Код без комментариев
    widlib=require("widlib-server")
    server=widlib.init
      template: index_template
      pages:
        type:
          template: type_template
          body: "Выберите пиццу"
          inputs: [
            { value: "Маргарита", type: "link", name: "type", price: 350 },
            { value: "Пепперони", type: "link", name: "type", price: 360 },
            { value: "Филадельфия", type: "link", name: "type", price: 370 },
            { value: "Четыре сыра", type: "link", name: "type", price: 380 },
          ]
    
        size:
          body: "Выберите размер"
          inputs: ->
            price = @session.value("type").price
            [
              { value: "30", type: "link", name: "size", price: price },
              { value: "40", type: "link", name: "size", price: price*1.2 },
              { value: "50", type: "link", name: "size", price: price*1.5 },
            ]
          onSubmit: "address"
    
        address:
          body: "Введите адрес"
          inputs: [ { name: "address", type: "text", placeholder: "улица, дом, подъезд, квартира" }, { type: "submit", value: "Далее" } ]
    
        phone:
          body: "Введите телефон"
          inputs: [ { name: "phone", type: "text", placeholder: "+7 xxx xx xx" }, { type: "submit", value: "Далее" }]
          onSubmit: -> # также возможно использовать события onLoad, on
            @data("orders").push @session.values()
            @data("email").push email_template(@session.values())
            "success" 
    
        success:
          body: "Ваша пицца уже едет к вам"
          image: -> "/images/#{@session.value("type")}.jpg"
    
      data:
        orders:
          type: "spreadsheet"
          url: "https://docs.google.com/spreadsheet/ccc?key=0Au4e-jj1-69ZdEloMW03UExKLXIZFSUE"
        email:
          type: "email"
          to: "1@interactiff.net"
    
    server.listen "3000"
    
    В переводе на JS
    var client, server, widlib;
    
    widlib = require("widlib-server");
    
    server = widlib.init({
      template: index_template,
      pages: {
        type: {
          template: type_template,
          body: "Выберите пиццу",
          inputs: [
            { value: "Маргарита", type: "link", name: "type", price: 350 },
            { value: "Пепперони", type: "link", name: "type", price: 360 },
            { value: "Филадельфия", type: "link", name: "type", price: 370 },
            { value: "Четыре сыра", type: "link", name: "type", price: 380 },
          ]
        },
        size: {
          body: "Выберите размер",
          inputs: function() {
            var price;
            price = this.session.input("type").price;
            return [
              { value: "30", type: "link", name: "size", price: price },
              { value: "40", type: "link", name: "size", price: price*1.2 },
              { value: "50", type: "link", name: "size", price: price*1.5 },
            ];
          },
          onSubmit: "address"
        },
        address: {
          body: "Введите адрес",
          inputs: [ { name: "address", type: "text", placeholder: "улица, дом, подъезд, квартира" }, { type: "submit", value: "Далее" } ]
        },
        phone: {
          body: "Введите телефон",
          inputs: [ { name: "phone", type: "text", placeholder: "+7 xxx xx xx" }, { type: "submit", value: "Далее" }],
          onSubmit: function() {
            this.data("orders").push(this.session.values());
            this.data("email").push(email_template(this.session.values()));
            return "success";
          }
        },
        success: {
          body: "Ваша пицца уже едет к вам",
          image: function() {
            return "/images/" + (this.session.value("type")) + ".jpg";
          }
        }
      },
      data: {
        orders: {
          type: "spreadsheet",
          url: "https://docs.google.com/spreadsheet/ccc?key=0Au4e-jj1-69ZdEloMW03UExKLXI3cGRlbkJteGZFSUE#gid=0"
        },
        email: {
          type: "email",
          to: "1@interactiff.net"
        }
      }
    });
    
    server.listen("3000");

    Первой строкой мы создаем объект widget и загружаем в него сценарий.
    Объект сценария состоит из страниц и данных. Каждая страница – отдельный экран, который увидит пользователь в этом виджете.

    Каждый объект с данными соответствует адаптеру для их хранения или передачи. В простейшем случае – это просто массив, в сложных – REST интерфейс, MongoDB, Google Spreadsheet и прочие.

    Мы можем использовать один и тот же сценарий, как на клиенте (в т.ч. standalone), так и на сервере node.js. Использование на сервере позволяет скрыть сценарий или его часть от пользователя, что может пригодиться для калькулятора кредита или викторины с розыгрышем призов, а также даёт доступ к динамическим данным и API, агрегированию и обработке пользовательской информации.
    Клиентская часть виджета доставки пиццы, в данном случае пустая:

    client=new Widlib.Client
      # здесь можно использовать тот же самый код, что и в серверной части.
      # pages: ...
      # data: ...
      server: "/"
      container: "#container"
    




    Разработчику даже не нужно заботиться, каким образом передавать информацию, и в каком случае обмениваться данными с сервером. Библиотека автоматически проверяет наличие страниц или данных в локальном сценарии, после чего незаметно для автора запрашивает их у сервера (или отправляет), используя RPC. Серверная версия обладает более полным функционалом, а клиентская работает быстрее и независимо, что позволяет ее использовать в мобильных приложениях, а также не беспокоить сервер по пустякам.

    Фичи
    • Декларативный стиль написания
    • Для простых случаев практически не требует навыков программирования
    • Возможно использовать готовые шаблоны (планируется также создать библиотеку шаблонов и визарды для создания виджетов обывателями)
    • Возможно использование декларативного html-шаблонизатора и биндинга rivets.js (подобен таковому в AngularJS). Скрипт связывания будет доступен в репозитории фреймворка
    • Независимость от библиотек для работы с DOM
    • Разделяемый код между клиентом и сервером
    • Незаметный fallback клиентского к серверному скрипту.


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

    P.S. для проекта будет доступна исчерпывающая документация, планируется библиотека примеров и визардов, а также сверхшустрый хостинг виджетов с плюшками в виде простого доступа к различным API.

    Only registered users can participate in poll. Log in, please.

    Нужен ли вам такой фреймворк?

    • 31.1%Да, нужен142
    • 47.8%Возможно пригодится218
    • 8.5%Нет, для этих целей я лучше использую… (прошу отписать в комментариях)39
    • 12.5%Я не занимаюсь такими вещами57
    • +48
    • 16.1k
    • 9
    Share post
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 9

      +52
      Почему не в 30 строчек?
        +1
        Интересная штука. Хотелось бы спросить вот такое: это как бы аналог Dashing shopify.github.io/dashing/ только на Node.js?
          0
          Да, есть похожие моменты. Там затруднительно встраивание на конечные страницы, разве что в Iframe. Да и WidLib дает разделяемый клиентский и серверный js-код. Не все пока пишут на ruby, к сожалению :)
          +1
          Отличный проект. На предыдущей работе была необходимость реализации форы бронирования для партнёров — чтобы можно было просто отдать удобный для вставки код… В итоге задача была отложена как не приоритетная и ресурсоемкая. Думаю, данный проект мог бы пригодится
            +1
            Извините. Но это как я понимаю не обязательно торговля и реклама.
            Опросы и тестирование вполне идет?
            Так что вполне возможно пригодится
              +1
              Да, конечно. В том числе многостраничные, нелинейные-адаптивные.
                +1
                Я это понял и у меня руки чешутся именно в этом направление. Программированное обучение — если точнее.
              +1
              Совсем недавно искали решение для создания виджетов, остановились на react.js от Facebook. Ваше решение бы лучше подошло.
                +1
                Пицца шляпа говорите… :)

                Only users with full accounts can post comments. Log in, please.