Статьи

Просто сделай это: выучи Синатру, часть третья

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

Добавление некоторого стиля

На данный момент наше приложение работает нормально, но выглядит немного неуклюже. Давайте разберемся с этим, создав таблицу стилей. Убедитесь, что в файле layout.slim есть следующая строка кода:

link rel="stylesheet" media="screen, projection" href="/styles.css" 

Теперь создайте файл с именем ‘styles.css’ и сохраните его в общей папке, затем добавьте следующие строки CSS:

 .completed{ text-decoration: line-through; } .tasks{ padding:0; list-style:none; width:400px; } .task{ position:relative; padding:2px 0 2px 28px; border-bottom: dotted 1px #ccc; } form.update{ position:absolute; bottom:2px; left:0; } form.update input{ background:white; color:white; padding:0 2px; border:solid 1px gray; cursor:pointer; } .tasks li.completed form.update input{ color:#47FD6B; } form.delete{ display:inline; } form.delete input{ background:none; cursor:pointer; border:none; } направо .completed{ text-decoration: line-through; } .tasks{ padding:0; list-style:none; width:400px; } .task{ position:relative; padding:2px 0 2px 28px; border-bottom: dotted 1px #ccc; } form.update{ position:absolute; bottom:2px; left:0; } form.update input{ background:white; color:white; padding:0 2px; border:solid 1px gray; cursor:pointer; } .tasks li.completed form.update input{ color:#47FD6B; } form.delete{ display:inline; } form.delete input{ background:none; cursor:pointer; border:none; } 

Перезагрузите страницу, и вы увидите, что она выглядит намного лучше и похожа на фактический список задач. Вы могли также заметить, что стили ссылаются на класс «завершен». В данный момент мы показываем, что задача выполнена, добавив дату, когда она была завершена, но это выглядит не очень хорошо, поэтому давайте изменим представление задачи, чтобы вместо нее добавлялся класс выполненной задачи. Затем мы можем использовать нашу таблицу стилей, чтобы завершенные задачи выглядели иначе. Откройте файл task.slim и измените его следующим образом:

 li.task id=task.id class=(task.completed_at.nil? ? "" : "completed") = task.name form.update action="/task/#{task.id}" method="POST" input type="hidden" name="_method" value="PUT" -if task.completed_at.nil? input type="submit" value=" " title="Complete Task" -else input type="submit" value="✓" title="Uncomplete Task" form.delete action="/task/#{task.id}" method="POST" input type="hidden" name="_method" value="DELETE" input type="submit" value="×" title="Delete Task" 

Ключевая строка здесь вверху, в которой используется троичный оператор, чтобы проверить, завершена ли задача, и добавить класс выполненного, если он был. CSS гарантирует, что задачи вычеркнуты, поскольку они выполнены. Если вы попробуете приложение сейчас, оно начинает чувствовать себя намного лучше и естественнее — нажмите на поле, чтобы пометить задачу как выполненную и получить визуальную обратную связь.

Списки задач

Теперь мы собираемся добавить возможность добавлять несколько списков задач. Для этого нам нужно создать класс List. Списки будут содержать много задач. DataMapper решает эту проблему, используя связи, чтобы показать связь между списками и задачами. Это делается путем добавления строки внизу каждого класса. Модель задачи использует объявление belongs_to а модель List использует объявление has n . В фоновом режиме это добавит свойство list_id к классу Task, который используется для отслеживания того, к какому списку относится задача, хотя у нас не должно быть необходимости обращаться к этому свойству напрямую. Каждый класс также получает несколько дополнительных методов, поэтому вы можете получить доступ к спискам задач с помощью List.tasks и получить доступ к списку задач с помощью Task.list . Откройте main.rb и добавьте класс List, а также измените класс Task, чтобы он выглядел следующим образом:

 class Task include DataMapper::Resource property :id, Serial property :name, String, :required => true property :completed_at, DateTime belongs_to :list end class List include DataMapper::Resource property :id, Serial property :name, String, :required => true has n, :tasks, :constraint => :destroy end DataMapper.finalize 

Поскольку мы создали несколько новых моделей, нам необходимо обновить базовую базу данных. Это можно сделать с помощью auto_migrate auto_migrate! метод. Зайдите в консоль и откройте irb:

 $> irb require './main' DataMapper.auto_migrate! 

Обратите внимание, что это удалит все задачи, которые уже были в вашей базе данных ранее.

Добавление и удаление списков

Теперь нам нужно создать несколько обработчиков для работы со списками. Они очень похожи на обработчики задач из части 2 и перечислены здесь полностью, разместите их внизу main.rb:

 post '/new/list' do List.create params['list'] redirect to('/') end delete '/list/:id' do List.get(params[:id]).destroy redirect to('/') end 

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

 form.new action="/new/list" method="POST" input type="text" name="list[name]" input type="submit" value="+ List" ul.lists - @lists.each do |list| == slim :list, locals: { list: list } 

Это представление содержит переменную экземпляра @lists, которая представляет все списки в базе данных. Это еще не существует, поэтому нам нужно обновить соответствующий обработчик, в main.rb найдите следующий обработчик для корневого URL:

 get '/' do @tasks = Task.all slim :index end 

И измените его на следующее:

 get '/' do @lists = List.all(:order => [:name]) slim :index end 

Это ищет все списки, а не все задачи. Мы будем получать задачи из базы данных по списку за списком.

Последняя часть представления индекса ссылается на представление под названием «список», поэтому нам также необходимо создать его. Это будет представление, которое используется для каждого списка и будет отображать задачи. Создайте новый текстовый файл и сохраните следующее в папке представлений как «list.slim»:

 li.list h2= list.name form.new action="/#{list.id}" method="POST" input type="text" name="task[name]" ul.tasks - list.tasks.each do |task| == slim :task, locals: { task: task } form.destroy action="/list/#{list.id}" method="POST" input type="hidden" name="_method" value="DELETE" input type="submit" value="×" 

На самом деле это очень похоже на код, который мы изначально имели в представлении индекса. Он начинается с указания имени списка, а затем появляется форма, которая используется для добавления задачи. Далее следует список задач, который использует тот же вид задач из части 2. Последний бит кода — это форма, которая используется для доступа к обработчику удаления, поэтому список можно удалить.

Добавление задач в списки

Теперь у нас есть только одно небольшое изменение, которое мы должны сделать, чтобы гарантировать, что задачи добавляются правильно. Поскольку они принадлежат списку, нам нужно убедиться, что мы указываем, к какому списку добавляется задача. Это делается путем добавления идентификатора списка в URL, который создает задачу. Обратите внимание на эту строку в list.slim:

 form.new action="/#{list.id}" method="POST" 

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

 post '/' do Task.create params['task'] redirect to('/') end 

Это должно быть изменено на следующее:

 post '/:id' do List.get(params[:id]).tasks.create params['task'] redirect to('/') end 

Этот обработчик использует идентификатор списка, указанного в URL, а затем находит список в базе данных и создает новую задачу, которая принадлежит этому списку, используя параметры, указанные в форме.

Немного больше стиля

Мы почти у цели, осталось только немного стилизовать списки. Откройте файл styles.css и добавьте следующие строки:

 .lists{ padding:0; list-style:none; overflow:hidden; } .list{ float: left; width:23%; margin:0 1%; border-top:solid 5px #ccc; } 

Вам также необходимо убедиться, что вы удалили следующую строку из этого файла (она должна быть в строке 8):

 width:400px; 

Вот и все! Теперь у вас должно быть полнофункциональное приложение со списком дел, которое также выглядит как часть. И в довершение всего, main.rb по-прежнему весит менее 60 строк кода! В заключительной части этой серии мы рассмотрим добавление стиля с помощью Sass и его развертывание в Интернете с помощью Heroku .