Derby.js TODO or not TODO

  • Tutorial


Одни программисты уже осознали открывающиеся перспективы и взялись за изучение Derby. Другие, с интересом наблюдают за происходящим, но не готовы в данный момент расстаться с их любимым фреймворком по разным причинам. Третьи минусуют Derby-туториалы, полагая, что это как-то остановит прогресс и надвигающуюся волну full-stack фреймворков.

Ну а наш паровоз набирает ход и сегодня мы углубимся в наших Derby-изысканиях.




Настраиваем окружение и создаем bare-проект.

/lib/app/index.js

app.get('/', function(page, model) {
  model.subscribe('todos', function(err) {
    if (!model.get('todos')) {
      model.add('todos', {text: 'Todo 1'});
      model.add('todos', {text: 'Todo 2'});
    }
    page.render();
  })
});


Здесь мы подписались на всю коллекцию todos. В дальнейшем все наши манипуляции с данными мы будем делать именно после того, как мы на них подписались, потому что до subscribe, наша модель пустая. Теперь добавляем пару объектов туда, если такой коллекции нету.
model.add — это обвертка над model.set. Единственное что она делает, это генерирует ид сама. Мы могли бы написать так:

var id = model.id(); //так генеририруется guid с помощью require('node-uuid').v4()
model.set('todos.' + id, {text: 'Todo 1'});


Наша коллекция — это js-объект. Если сделать model.get('todos'), то получим:

{
  "e1b8075c-de9a-458a-aa3c-e9b383691521":
    {
      "text":"Todo 1",
      "id":"e1b8075c-de9a-458a-aa3c-e9b383691521"
    },
  "26cd5f4a-c503-4c25-aeeb-a28c8c034d08":
    {
      "text":"Todo 2",
      "id":"26cd5f4a-c503-4c25-aeeb-a28c8c034d08"
    }
}


Это хорошо для того, если мы хотим доставать из нее объекты по ид:

var todo = model.get('todos.e1b8075c-de9a-458a-aa3c-e9b383691521');


Но если мы хотим вывести наши todos в html, то нам бы лучше массив. Знакомьтесь, у нас есть замечательная штука — filter:

app.get('/', function(page, model) {
  model.subscribe('todos', function(err) {
    if (!model.get('todos')) {
      model.add('todos', {text: 'Todo 1'});
      model.add('todos', {text: 'Todo 2'});
    }

  var filter = model.filter('todos');
  filter.ref('_page.todos');

  page.render();
  })
});


/views/app/index.html

<Body:>
  {#each _page.todos as :todo}
    <p>{:todo.text}</p>
  {/}


Здесь мы создаем фильтр для todos. Он динамически следит за изменениями этой коллекции и выводит результат в '_page.todos' с помощью refList, о котором чуть позже. Но фильтр не был бы фильтром, если бы не умел фильтровать. Мы можем делать например вот так:

var todos = model.filter('todos', function(todo) {
	return todo.text == 'Todo1';
}).get();


Здесь мы сразу извлекли отфильтрованный массив.

var todos = model.filter('todos').sort('text').get();


А тут мы еще и сделали сортировку по полю text.
Ни filter, ни sort не знают ничего про вашу бд. Они оперируют только данными, которые есть в модели. Заполняйте модель перед этим!

Дак что там с refList? Это так называемые references. Позволяют связывать данные между двумя path. Использовать на прямую их приходится редко, но их используют, например, filter и queries.

app.get('/', function(page, model) {
  model.subscribe('todos', function(err) {
    if (!model.get('todos')) {
      model.add('todos', {text: 'Todo 1'});
      model.add('todos', {text: 'Todo 2'});
    }
  
  var ids = Object.keys(model.get('todos'));
  model.set('_page.ids', ids)
  model.refList('_page.todos', 'todos', '_page.ids');

  page.render();
  })
});


ids — это список ид тех todo, которые мы хотим получить в результате. Также они задают очередность в массиве '_page.todos'. Мы можем менять '_page.ids' и это сразу отразится на '_page.todos'.

Давайте помучаем subscribe:

model.subscribe('todos', function(err) {
	// Подписались на всю коллекцию todos
});
model.subscribe('todos.e1b8075c-de9a-458a-aa3c-e9b383691521', function(err) {
	// Подписались на один объект
});
model.subscribe('todos.e1b8075c-de9a-458a-aa3c-e9b383691521.text', function(err) {
	// Подписались на одно поле одного объекта
});
model.subscribe('users', 'todos.e1b8075c-de9a-458a-aa3c-e9b383691521.text', function(err) {
	// Можно кстати совмещать, чтобы не плодить колбэков
});


Допустим мы сильно занятый человек и у нас миллион todos. И мы хотим подписаться только на те, текст которых содержит определенные символы. Зачем нам вся коллекция на клиенте? paths тут безполезен. filter тоже. Нам на помощь приходят Queries:

app.get('/', function(page, model) {
  var query = model.query('todos', {text: 'Todo 1'})
  model.subscribe(query, function(err) {
    if (!model.get('todos')) {
      model.add('todos', {text: 'Todo 1'});
      model.add('todos', {text: 'Todo 2'});
    }

    query.ref('_page.todos');

    page.render();
  })
});


{text: 'Todo 1'} — это Mongo Queries. То есть livedb-mongo адаптер пробрасывает этот объект напрямую в монго. Для других баз данных можно написать свои адаптеры и делать это как-то по другому.

Материалы по Derby.js
Поделиться публикацией
Похожие публикации
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 24
  • +2
    Дебри Derby…
    • 0
      Надо как-то заполнять вакуум. Вливайтесь!
      • 0
        Успеваете читать?
        • 0
          Нет, не успеваю — нет достаточной мотивации. :)
          У меня к этому интерес примерно такой же, как к чёрным дырам:
          пока что никак не влияет на мою жизнь, но для общего образования — полезно :)
          • 0
            На чём сами пишите?

            Я вот тут набросил для мотивации.
            • 0
              У меня есть средних размеров проект на php.
              Из фреймворков пользуюсь только ExtJS-ом.
              Для большинства задач фреймворки пишу для себя сам ))
              • 0
                Как надоест велосипедить, приходите к нам. Будем рады.
          • 0
            Ваш Туториал Дреби тоже добавьте в список статей.
            • 0
              Он там есть в списке для продолжающих. Называется FAQ. Потому что скорее это ответы на вопросы, чем туториал.
        • 0
          Володь, у тебя render в примерах вынесен из subscribe, это не будет приводить (в случае сервера) к тому, что на клиента будет передаваться (рендериться) страница без данных, а данные будут подтягиваться позже?
          • +1
            Ты правильно заметил. Всё будет как ты расписал. Примеры я исправил. Извиняюсь за невнимательность.
          • 0
            Очень заманчивая идея — использовать один код и на клиенте, и на сервере… И это действительно было бы замечательно, если бы JavaScript предоставлял традиционные способы работы с ООП. Ну невозможно получать удовольствия от программирования когда пишешь код вроде этого:
            Sleep().then(WakeUp, Coffee).then(Work).then(Sleep, Drugs).catch(DayOff).finally(Rip)
            Или возможно?
            • +1
              У людей, которые мало знают JavaScript, есть много мифов относительно него.
              Я вам рекомендую посмотреть это.
              Люблю JavaScript. Сам пишу на CoffeeScript`е. От него чуть больше удовольствия.

              Традиционные способы работы с ООП — это дело привычки. Ничем не лучше, чем, например, прототипная модель.

              Что вы хотели сказать вашим примером кода?
              • 0
                этим кодом хотел показать скучность JS, что callback hell в JS заменяется chain hell'ом при использовании обещаний (promise)…
                • 0
                  Асинхронность Node.js — это его идеология и даёт ему уникальные преимущества по сравнению с синхронными платформами.

                  К Node.js нужно привыкнуть, иначе, как вы и сказали, это может превратиться в callback hell.
                  Всякие promises и fibers — это попытки сделать так, чтобы ваш код выглядел синхронно. И испольуются в основном теми, кто мыслит в категориях синхронных платформ.

                  В умелых руках node.js код красив и прозрачен.

                  Скучность — это довольно субъективное понятие. В том видео, на которое я вам дал ссылку, один пожилой человек с огромным опытом в IT утверждает, например, что JavaScript — это очень веселый язык, просто недооцененный.
            • 0
              Спасибо за статью, но хотелось более реальный пример. Т.е. связка с Postgres, написание примитивного блога с ауторизацией, постами и комментами.
              • 0
                Спасибо за отзыв.

                Я попробую написать что-то из реальной жизни.

                В чем вы видите преимущества Postgres для блога в сравнении с Mongo?
                • 0
                  Вот исходники небольшого приложения с ауторизацей и с комментариями.
                  А вот большого.
                  • 0
                    Примеры, статичны, хотелось бы расписанный процесс создания такого приложения постепенно.
                    • 0
                      Я конечно понимаю что туториалов не бывает много :-)

                      Чтобы подробно описать процесс создания приложения из реальной жизни, нужно издать книгу. Я конечно подумаю об этом.
                • +1
                  Все таки не пойму,
                  filter.ref('_page.todos');
                  
                  что эта строчка делает-то?
                  • +2
                    Создается проекция (reference) фильтра (соответсвенно массива данных) на path '_page.todos'. Фильтр отслеживает изменения данных в модели (в данном случае коллекции todos) и соответствующим образом изменяет массив, находящийся в '_page.todos'.
                    • +1
                      а что в этом _page вообще находится? что это за объект? Я думал это то, что прокидывается по умолчанию в шаблон
                      • +2
                        В шаблон прокидывается всё, что есть в модели. И _page.todos, и todos в данном случае. Просто todos будет в виде объекта и вы не сможете использовать оператор each для него. По этому мы с помощью фильтра создаём массив в _page.todos.

                        Здесь я уже писал, что мы можем использовать любые локальные paths: _myLocalPath.todos, _path.myTodos, _my.super.path.with.todos и т.п. Но объект _page немного особенный, он очищается при каждом срабатывании роутера. Это удобно чтобы хранить данные, относящиеся к данной странице.

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.