Статьи

Простая система управления контентом в Синатре

Одна из идей, выдвинутых на недавнем саммите Awesome Manchester Hackathon, была выдвинута Томом Окли для простой системы управления контентом, в которой использовался Markdown . Идея не была выбрана в качестве одного из проектов Хакатона, но я подумал, что она отлично подходит для написания на Синатре (он сам по себе легкий и уже имеет поддержку для Markdown). Итак, после того, как хакатон закончился, я решил попробовать его построить.

Я подумал, что было бы интересно (и очень полезно), если бы я задокументировал прогресс, который я сделал на RubySource, так как я продвинулся… так что это первая часть серии!

Моя идея состоит в том, чтобы создать некоторое промежуточное программное обеспечение Sinatra, которое можно прикрепить к проекту, чтобы добавить простую поддержку управления контентом. Я также хочу иметь тегируемые страницы и возможность расширений (таких как модули списков навигации, модули загрузки файлов, модули электронной почты и т. Д.).

MongoDB и Mongoid

Я решил использовать MongoDB для настойчивости. Я использовал это немного в прошлом и думал, что это будет хорошо здесь. MongoDB — это быстрое и масштабируемое хранилище данных NoSQL . Он использует концепцию коллекций документов вместо таблиц и строк. Большим преимуществом является то, что он не имеет схемы и поэтому не требует никаких миграций. Кроме того, вы можете добавлять новые поля на лету (фактически документы в одной коллекции даже не должны иметь одинаковые поля), что делает его очень гибким и подходящим для быстрой разработки.

Я решил использовать Mongoid в качестве ORM, так как мне понравился внешний вид синтаксиса, он хорошо документирован и находится в стадии активной разработки. Я обнаружил, что установка MongoDB локально — это немного болезненно, но документация MongoDB должна помочь вам. Как только он установлен, вам нужно запустить сервер mongod . Это зависит от операционной системы, поэтому обратитесь к документации о том, как это сделать.

Настройка

Для начала я создал следующий Gemfile:

 source "https://rubygems.org" ruby "2.0.0" gem "sinatra" gem "slim" gem "sass" gem "mongoid" gem "redcarpet" 

Прежде всего, Mongoid требует как минимум Ruby 1.9.3, поэтому он помогает поместить это в Gemfile (особенно для случая, когда мы перейдем к развертыванию на Heroku позже.) Очевидно, нам нужен Sinatra. Я буду использовать Slim для логики представления и Sass для стилей. Это исключительно мой выбор и мой выбор по умолчанию в настоящее время. Честно говоря, не будет такого большого стиля, но всегда проще использовать Sass. Я уже упоминал Mongoid, которая оставляет Red Carpet — реализацию уценки, созданную добрыми людьми из GitHub.

Чтобы убедиться, что все установлено, запустите bundle install из командной строки.

Mongoid также требует немного настройки. Для этого создайте файл с именем mongoid.yml и сохраните его в том же каталоге. Вот настройки по умолчанию, которые я нашел подходящими для начала работы с Mongoid:

 development: sessions: default: database: cms hosts: - localhost:27017 production: sessions: default: uri: <%= ENV['MONGOHQ<em>URL'] %> options: skip_version_check: true safe: true 

В том же каталоге нам нужно создать файл с именем main.rb, куда пойдут наши основные функции. Чтобы все пошло как по маслу, я решил погрузиться и создать классическое приложение, а позже перенесу его в модульное. В начале main.rb нам нужно запросить соответствующие гемы и настроить Mongoid:

 require 'sinatra' require 'sinatra/reloader' if development? require 'mongoid' require 'slim' require 'redcarpet' configure do Mongoid.load!("./mongoid.yml") end 

Класс страницы

Теперь мы переходим к сердцу нашей CMS — модели страницы. Начнем с простого — дайте ему заголовок и поле содержимого:

 class Page include Mongoid::Document field :title, type: String field :content, type: String end 

Чтобы использовать Mongoid в классе, все, что вам нужно сделать, это включить модель Mongoid::Document . После этого вы можете определить поля, которые будут использоваться в базе данных. Наши два поля — это «заголовок» и «контент», и оба они имеют тип string. Теперь мы можем попробовать создать несколько страниц!

Создать несколько страниц

У нас пока нет определенных маршрутов, но это не значит, что мы не можем создавать страницы. Всегда полезно иметь возможность запускать нашу программу через консоль. Откройте терминал и запустите IRB, используя следующую команду:

 $> irb 

Это должно дать вам приглашение ruby, похожее на приведенное ниже, где нам может потребоваться файл main.rb:

 2.0.0-p0 :001 > require './main' => true 

Теперь мы можем добавить несколько страниц:

 2.0.0-p0 :002 > hello = Page.new => # 2.0.0-p0 :003 > hello.title = "Hello World!" => "Hello World!" 2.0.0-p0 :004 > hello.content = " This is our first page " => " This is our first page " 2.0.0-p0 :005 > hello.save => true 

Мы только что создали нашу первую страницу! Это делается с использованием new метода. Мы назначаем страницу переменной hello . У этого есть методы установки title и content обеспеченного Mongoid. После того, как они были установлены, мы используем метод save для фиксации изменений в базе данных.

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

 2.0.0-p0 :006 > Page.create(title: "Markdown Page", content: "This page uses markdown") => # 

Мы можем проверить, что страницы были созданы путем запроса базы данных. Прежде всего, давайте проверим, сколько страниц было сохранено, используя метод count :

 2.0.0p0 :007 > Page.count => 2 

Это хорошая новость — в базе данных сохранено 2 страницы, как мы и ожидали. Существует множество способов поиска страниц с использованием Mongoid. Вот несколько запросов, которые мы могли бы использовать:

 2.0.0p0 :009 > Page.first => # Page.last => #, title: "Hello World!", content: " This is our first page "> 

first и last методы работают точно так же, как говорят на банке, и находят первый и последний документы в базе данных соответственно. Мы также можем найти конкретный документ, используя метод find со строкой id документа в качестве аргумента:

 2.0.0p0 :011 > Page.find("5173f574a39401776a000002") => # 

Возможно, мы не всегда знаем идентификатор документа, поэтому мы также можем искать по разным полям, используя методы find_by и where :

 2.0.0p0 :012 > Page.find_by(title: "Hello World!") => #This is our first page "> 2.0.0p0 :013 > Page.where(title: "Hello World!").first => #This is our first page "> 

Хотя они выглядят одинаково, есть find_by разница: find_by возвращает объект Page, тогда как where возвращает критерий MongoDB, который является прокси-объектом, который можно объединить в цепочку. Он не запрашивает базу данных, пока не будут выполнены все критерии.

Просмотр страниц

Теперь, когда у нас есть несколько страниц, давайте создадим несколько маршрутов и видов, чтобы мы могли их видеть!

Прежде всего, мы создадим страницу индекса, которая перечисляет все страницы в базе данных. Добавьте следующий маршрут в конец main.rb:

 get '/pages' do @pages = Page.all @title = "Simple CMS: Page List" slim :index end 

Это находит все страницы в базе данных и сохраняет их в виде массива в неизменяемом экземпляре @pages . Мы также используем переменную экземпляра @title для хранения заголовка страницы (это будет использоваться в представлении макета). Затем мы визуализируем представление, называемое index, используя slim.
Нам нужно создать это представление сейчас. Создайте файл с именем index.slim и сохраните его в папке с именем views в корневом каталоге. Поместите следующий код в этот файл:

 h1 Pages -if @pages.any? ul.pages - @pages.each do |page| == slim :page, :locals => {page: page} - else p No pages here! 

Это просто список всех страниц, но он использует часть для отображения фактической информации о каждой странице в строке == slim :page, :locals => {page: page} . Это означает, что нам нужно создать другое представление с именем page.slim (также сохраненное в папке views), содержащее следующий код:

 li a href="/pages/#{page.id}" =page.title 

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

Часть страницы имела ссылку на каждую отдельную страницу. Нам нужно написать обработчик маршрута, чтобы справиться с этим. Это используется для отображения каждой отдельной страницы — поместите следующий код ниже:

 get '/pages/:id' do @page = Page.find(params[:id]) @title = @page.title slim :show end 

Он находит страницу на основе идентификатора, @page в URL, и создает переменную экземпляра с именем @page для хранения объекта Page . Мы также сохраняем заголовок страницы в переменной экземпляра @title .

Далее, давайте создадим страницу для просмотра реальной страницы. Сохраните следующий код в каталоге views как ‘show.slim’:

 h1= @page.title - if @page.content == markdown @page.content 

Это очень простое представление, которое показывает заголовок в качестве заголовка уровня 1 и использует метод markdown для отображения содержимого (если оно действительно есть). Обратите внимание, что оба они являются методами переменной экземпляра @page созданной в обработчике маршрута.

Прежде чем мы протестируем это, нам нужно создать макет для приложения. Вот базовый макет HTML5, который отлично подойдет:

 doctype html html head title= @title || "Simple Sinatra CMS" body h1 a href="/pages" Simple Sinatra CMS == yield 

Сохраните приведенный выше фрагмент кода как «layout.slim» в каталоге views, затем запустите сервер, введя ruby main.rb в терминал, затем перейдите по адресу http: // localhost: 4567 / pages в вашем браузере, и вы должны увидеть список страниц, аналогичный следующему:

Screenshot1

Попробуй перемещаться по всем страницам!

screenshot2

Создание страниц

Это было весело, но у нас есть только две страницы, поэтому нам нужно создавать больше … и в браузере тоже.

Прежде всего, нам нужно создать обработчик маршрута для страницы, на которую мы добавим новую страницу. Добавьте следующий код в «main.rb», но убедитесь, что он идет перед маршрутом «/ pages /: id» (это потому, что Sinatra всегда ищет первый маршрут, который соответствует URL и «/ pages /: id» соответствует ‘/ pages / new’, установив params [: id] в ‘new’):

 get '/pages/new' do @page = Page.new slim :new end 

Это просто создает новый объект страницы и сохраняет его в переменной экземпляра @page , которая будет доступна в представлении. Возможно, нам сейчас не понадобится этот объект, но в будущем мы можем использовать ту же форму для редактирования страницы, что и для создания новой. Форма редактирования будет содержать ссылки на редактируемую страницу, которая будет переменной экземпляра @page . Если бы этого не было, мы бы получили ошибки, поэтому мы просто создаем новый объект Page чтобы избежать этого.

Затем мы отображаем новый вид, который нам лучше создать в папке ‘views’, вот код:

 h1 New Page form action="/pages" method="POST" fieldset legend Create a new page == slim :form input type="submit" value="Create" 

Это использует частичное для фактической формы (поэтому мы можем повторно использовать его на странице редактирования). Нам также нужно сохранить следующее как «form.slim»:

 label for="title" Title: input.title type="text" name="page[title]" value="#{@page.title}" size="32" label for="content" Content: textarea.content name="page[content]" rows="12" cols="72" ==@page.content 

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

Теперь нам нужно разобраться, что происходит при отправке этой формы. Мы можем использовать обработчик маршрута, чтобы справиться с этим. Мы используем URL «/ pages», поскольку это то, что было в атрибуте «action» формы. Но в этом случае мы используем HTTP-глагол «post», так как этот метод используется формой при отправке:

 post '/pages' do page = Page.create(params[:page]) redirect to("/pages/#{page.id}") end 

В этом действии мы создаем новый объект Page используя метод create который мы использовали в IRB ранее. params[:page] — это хэш всех значений, введенных в форму. Затем мы используем полезное redirect Синатры и методы для перенаправления на URL для вновь созданной страницы.

Теперь, когда все маршруты на месте, нам просто нужно добавить ссылку на страницу индекса, чтобы создать новую страницу. Сделайте следующую строку где-нибудь около вершины index.slim:

 a href='/pages/new' Add a new page 

Перезагрузите сервер (удерживая Ctrl + C, затем снова введите ruby main ) и попытайтесь создать несколько новых страниц.

Screenshot3

Эврика! У нас есть начало нашей простой системы управления контентом. Мы можем создавать новые страницы с помощью Markdown и просматривать их.
Во второй части мы рассмотрим редактирование и удаление страниц, а также способы создания постоянных ссылок с «красивыми URL-адресами» на основе заголовка страницы.

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