() translation by (you can also view the original English article)
После создания системы управления контентом (CMS) базовые структуры, и реальный сервер, используя поди Node.js вы готовы попробовать свои силы на другом языке.
В этот раз я с помощью языка Ruby для создания сервера. Я нашел, что путем создания такой же программы на нескольких языках, вы начнете получать новые идеи по улучшению способов реализации программы. Вы также видите больше возможностей, чтобы добавить функционал в программу. Давайте начнем.
Установка и загрузка библиотек
Программы на Ruby, вы должны иметь последнюю версию, установленную на вашей системе. Приходят многие операционные системы предварительно установленная с Рубином в эти дни (Linux и OS X), но они обычно имеют более старую версию. Этот учебник предполагает, что вы Ruby версии 2.4.
Самый простой способ обновить до последней версии Руби использовать РВМ. Чтобы установить RVM на Linux или Mac OS Х, введите следующую команду в терминале:
1 |
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 |
2 |
curl -sSL https://get.rvm.io | bash -s stable |
Это позволит создать безопасное подключение, чтобы скачать и установить РВМ. Это устанавливает последнюю стабильную версию Руби, а также. Вам придется перезагрузить вашу оболочку, чтобы завершить установку.
Для Windows, вы можете скачать установщик Windows Руби. В настоящее время, этот пакет составляет в Ruby 2.2.2, которая подходит для запуска библиотеки и скрипты в этом уроке.
После того, как язык Ruby установлен правильно, теперь вы можете установить библиотеки. Руби, как пойти и узлов, имеет менеджер пакетов для установки сторонних библиотек. В окне терминала введите следующее:
1 |
gem install sinatra
|
2 |
gem install ruby-handlebars
|
3 |
gem install kramdown
|
4 |
gem install slim
|
Это устанавливает Синатра, Руби руле, Kramdown, и тонкий библиотеки. Синатра-это платформа веб-приложений. Руби руле реализует руле шаблонизатора в Ruby. Kramdown является Markdown для HTML-конвертер. Тонкая Нефритовая аналог библиотеки, но он не содержит макроопределения Джейд. Таким образом, макросы, используемые в новостях и блоге индексы сейчас нормально Джейд.
Создание rubyPress.файл RB
В корневом каталоге создайте файл rubyPress.RB и добавьте следующий код. Я буду комментировать о каждом разделе по мере их добавления в файл.
1 |
#
|
2 |
# Load the Libraries.
|
3 |
#
|
4 |
require 'sinatra' # https://www.sinatrarb.com/ |
5 |
require 'ruby-handlebars' # https://github.com/vincent-psarga/ruby-handlebars |
6 |
require 'kramdown' # http://kramdown.gettalong.org |
7 |
require 'slim' # http://slim-lang.com/ |
8 |
require 'json' |
9 |
require 'date' |
Первое, что нужно сделать, это загрузить библиотеки. В отличие от с Node.js эти не загружаются в переменную. Библиотек Ruby добавить их функций в рамках программы.
1 |
#
|
2 |
# Setup the Handlebars engine.
|
3 |
#
|
4 |
$hbs = Handlebars::Handlebars.new |
5 |
|
6 |
#
|
7 |
# HandleBars Helper: date
|
8 |
#
|
9 |
# Description: This helper returns the current date
|
10 |
# based on the format given.
|
11 |
#
|
12 |
$hbs.register_helper('date') {|context, format| |
13 |
now = Date.today |
14 |
now.strftime(format) |
15 |
}
|
16 |
|
17 |
#
|
18 |
# HandleBars Helper: cdate
|
19 |
#
|
20 |
# Description: This helper returns the given date
|
21 |
# based on the format given.
|
22 |
#
|
23 |
$hbs.register_helper('cdate') {|context, date, format| |
24 |
day = Date.parse(date) |
25 |
day.strftime(format) |
26 |
}
|
27 |
|
28 |
#
|
29 |
# HandleBars Helper: save
|
30 |
#
|
31 |
# Description: This helper expects a
|
32 |
# "|" where the name
|
33 |
# is saved with the value for future
|
34 |
# expansions. It also returns the
|
35 |
# value directly.
|
36 |
#
|
37 |
$hbs.register_helper('save') {|context, name, text| |
38 |
#
|
39 |
# If the text parameter isn't there, then it is the
|
40 |
# goPress format all combined into the name. Split it
|
41 |
# out. The parameters are not String objects.
|
42 |
# Therefore, they need converted first.
|
43 |
#
|
44 |
name = String.try_convert(name) |
45 |
if name.count("|") > 0 |
46 |
parts = name.split('|') |
47 |
name = parts[0] |
48 |
text = parts[1] |
49 |
end
|
50 |
|
51 |
#
|
52 |
# Register the new helper.
|
53 |
#
|
54 |
$hbs.register_helper(name) {|context, value| |
55 |
text
|
56 |
}
|
57 |
|
58 |
#
|
59 |
# Return the text.
|
60 |
#
|
61 |
text
|
62 |
}
|
Библиотека руле инициализируется с различными определенными вспомогательными функциями. Определенные вспомогательные функции-дата, с дата, и сэкономить.
Дата вспомогательную функцию берет текущую дату и время, и форматирует ее в соответствии со строкой формата, передаваемые помощником. функция cdate за исключением прохождения дата первого. Сохранить помощник позволяет указать имя и значение. Он создает новый помощник с именем name и возвращает значение. Это позволяет создавать переменные, которые заданы раз и влиять на многих местах. Эта функция также принимает версию перейти, которая ожидает строку с именем, ‘|’ в качестве разделителя, и значение.
1 |
#
|
2 |
# Load Server Data.
|
3 |
#
|
4 |
$parts = {} |
5 |
$parts = JSON.parse(File.read './server.json') |
6 |
$styleDir = Dir.getwd + '/themes/styling/' + $parts['CurrentStyling'] |
7 |
$layoutDir = Dir.getwd + '/themes/layouts/' + $parts['CurrentLayout'] |
8 |
|
9 |
#
|
10 |
# Load the layouts and styles defaults.
|
11 |
#
|
12 |
$parts["layout"] = File.read $layoutDir + '/template.html' |
13 |
$parts["404"] = File.read $styleDir + '/404.html' |
14 |
$parts["footer"] = File.read $styleDir + '/footer.html' |
15 |
$parts["header"] = File.read $styleDir + '/header.html' |
16 |
$parts["sidebar"] = File.read $styleDir + '/sidebar.html' |
17 |
|
18 |
#
|
19 |
# Load all the page parts in the parts directory.
|
20 |
#
|
21 |
Dir.entries($parts["Sitebase"] + '/parts/').select {|f| |
22 |
if !File.directory? f |
23 |
$parts[File.basename(f, ".*")] = File.read $parts["Sitebase"] + '/parts/' + f |
24 |
end
|
25 |
}
|
26 |
|
27 |
#
|
28 |
# Setup server defaults:
|
29 |
#
|
30 |
port = $parts["ServerAddress"].split(":")[2] |
31 |
set :port, port |
Следующая часть кода для загрузки кэшируемые элементы веб-сайта. Это все в стили и макет для вашей темы, и элементы в части подкаталог. Глобальная переменная, $частей, сначала загружается с сервера.JSON-файл. Эта информация затем используется для загрузки нужной детали для макета и указанной теме. Руле шаблонизатор использует эту информацию, чтобы заполнить шаблоны.
1 |
#
|
2 |
# Define the routes for the CMS.
|
3 |
#
|
4 |
get '/' do |
5 |
page "main" |
6 |
end
|
7 |
|
8 |
get '/favicon.ico', :provides => 'ico' do |
9 |
File.read "#{$parts['Sitebase']}/images/favicon.ico" |
10 |
end
|
11 |
|
12 |
get '/stylesheets.css', :provides => 'css' do |
13 |
File.read "#{$parts["Sitebase"]}/css/final/final.css" |
14 |
end
|
15 |
|
16 |
get '/scripts.js', :provides => 'js' do |
17 |
File.read "#{$parts["Sitebase"]}/js/final/final.js" |
18 |
end
|
19 |
|
20 |
get '/images/:image', :provides => 'image' do |
21 |
File.read "#{$parts['Sitebase']}/images/#{parms['image']}" |
22 |
end
|
23 |
|
24 |
get '/posts/blogs/:blog' do |
25 |
post 'blogs', params['blog'], 'index' |
26 |
end
|
27 |
|
28 |
get '/posts/blogs/:blog/:post' do |
29 |
post 'blogs', params['blog'], params['post'] |
30 |
end
|
31 |
|
32 |
get '/posts/news/:news' do |
33 |
post 'news', params['news'], 'index' |
34 |
end
|
35 |
|
36 |
get '/posts/news/:news/:post' do |
37 |
post 'news', params['news'], params['post'] |
38 |
end
|
39 |
|
40 |
get '/:page' do |
41 |
page params['page'] |
42 |
end
|
Следующий раздел содержит определения для всех маршрутов. Синатра-это полный сервер совместимый остальные. Но для этой CMS, я буду только использовать глагол get. Каждый маршрут длится элементы от маршрута, чтобы перейти к функции получения нужной странице. В Sinatra название предшествует двоеточие указывает участке пути, чтобы передать в обработчик маршрута. Эти элементы находятся в параметры хэш-таблицы.
1 |
#
|
2 |
# Various functions used in the making of the server:
|
3 |
#
|
4 |
|
5 |
#
|
6 |
# Function: page
|
7 |
#
|
8 |
# Description: This function is for processing a page
|
9 |
# in the CMS.
|
10 |
#
|
11 |
# Inputs:
|
12 |
# pg The page name to lookup
|
13 |
#
|
14 |
def page(pg) |
15 |
processPage $parts["layout"], "#{$parts["Sitebase"]}/pages/#{pg}" |
16 |
end
|
Функция возвращает страницы имя страницы с трассы и передает макет в переменной $части вместе с полным путем к файлу подкачки нужен для processPage функции. Функция processPage принимает эту информацию и создает соответствующую страницу, которая затем возвращается. В Ruby, выход последней функции является возвращаемым значением функции.
1 |
#
|
2 |
# Function: post
|
3 |
#
|
4 |
# Description: This function is for processing a post type
|
5 |
# page in the CMS. All blog and news pages are
|
6 |
# post type pages.
|
7 |
#
|
8 |
# Inputs:
|
9 |
# type The type of the post
|
10 |
# cat The category of the post (blog, news)
|
11 |
# post The actual page of the post
|
12 |
#
|
13 |
def post(type, cat, post) |
14 |
processPage $parts["layout"], "#{$parts["Sitebase"]}/posts/#{type}/#{cat}/#{post}" |
15 |
end
|
Функция пост-это просто как функция страницы, за исключением того, что она работает на всех страницах типа пост. Эта функция ожидает сообщение тип, категория поста, и сам пост. Это позволит создать адрес для правильного отображение страницы.
1 |
#
|
2 |
# Function: figurePage
|
3 |
#
|
4 |
# Description: This function is to figure out the page
|
5 |
# type (ie: markdown, HTML, jade, etc), read
|
6 |
# the contents, and translate it to HTML.
|
7 |
#
|
8 |
# Inputs:
|
9 |
# page The address of the page
|
10 |
# without its extension.
|
11 |
#
|
12 |
def figurePage(page) |
13 |
result = "" |
14 |
|
15 |
if File.exist? page + ".html" |
16 |
#
|
17 |
# It's an HTML file.
|
18 |
#
|
19 |
result = File.read page + ".html" |
20 |
elsif File.exist? page + ".md" |
21 |
#
|
22 |
# It's a markdown file.
|
23 |
#
|
24 |
result = Kramdown::Document.new(File.read page + ".md").to_html |
25 |
|
26 |
#
|
27 |
# Fix the fancy quotes from Kramdown. It kills
|
28 |
# the Handlebars parser.
|
29 |
#
|
30 |
result.gsub!("“","\"") |
31 |
result.gsub!("”","\"") |
32 |
elsif File.exist? page + ".amber" |
33 |
#
|
34 |
# It's a jade file. Slim doesn't support
|
35 |
# macros. Therefore, not as powerful as straight jade.
|
36 |
# Also, we have to render any Handlebars first
|
37 |
# since the Slim engine dies on them.
|
38 |
#
|
39 |
File.write("./tmp.txt",$hbs.compile(File.read page + ".amber").call($parts)) |
40 |
result = Slim::Template.new("./tmp.txt").render() |
41 |
else
|
42 |
#
|
43 |
# Doesn't exist. Give the 404 page.
|
44 |
#
|
45 |
result = $parts["404"] |
46 |
end
|
47 |
|
48 |
#
|
49 |
# Return the results.
|
50 |
#
|
51 |
return result |
52 |
end
|
Функция figurePage использует функцию processPage, чтобы прочитать содержание страницы из файловой системы. Эта функция получает полный путь к файлу без расширения. затем figurePage тесты для файла с указанным именем с расширением HTML для чтения HTML-файл. Второй вариант для расширения MD для файла Markdown.
И наконец, он проверяет для расширением янтаря на файл Джейд. Помните: Янтарь-это имя библиотеки для обработки синтаксис Jade файлы в ходу. Я держал это же в функциональности. HTML-файл является просто сдал назад, в то время как все уценки и Жад файлы преобразуются в HTML, прежде чем перейти обратно.
Если файл не найден, то пользователь получит страницу 404. Таким образом, ваш “страница не найдена”, страница выглядит так же, как и любой другой странице, кроме содержания.
1 |
#
|
2 |
# Function: processPage
|
3 |
#
|
4 |
# Description: The function processes a page by getting
|
5 |
# its contents, combining with all the page
|
6 |
# parts using Handlebars, and processing the
|
7 |
# shortcodes.
|
8 |
#
|
9 |
# Inputs:
|
10 |
# layout The layout structure for the page
|
11 |
# page The complete path to the desired
|
12 |
# page without its extension.
|
13 |
#
|
14 |
def processPage(layout, page) |
15 |
#
|
16 |
# Get the page contents and name.
|
17 |
#
|
18 |
$parts["content"] = figurePage page |
19 |
$parts["PageName"] = File.basename page |
20 |
|
21 |
#
|
22 |
# Run the page through Handlebars engine.
|
23 |
#
|
24 |
begin
|
25 |
pageHB = $hbs.compile(layout).call($parts) |
26 |
rescue
|
27 |
pageHB = " |
28 |
Render Error
|
29 |
|
30 |
"
|
31 |
end
|
32 |
|
33 |
#
|
34 |
# Run the page through the shortcodes processor.
|
35 |
#
|
36 |
pageSH = processShortCodes pageHB |
37 |
|
38 |
#
|
39 |
# Run the page through the Handlebar engine again.
|
40 |
#
|
41 |
begin
|
42 |
pageFinal = $hbs.compile(pageSH).call($parts) |
43 |
rescue
|
44 |
pageFinal = " |
45 |
Render Error
|
46 |
|
47 |
" + pageSH |
48 |
end
|
49 |
|
50 |
#
|
51 |
# Return the results.
|
52 |
#
|
53 |
return pageFinal |
54 |
end
|
Функция processPage выполняет все дополнения шаблона на данные страницы. Он начинается с вызова функции figurePage получить содержание страницы. Затем он обрабатывает макет, переданный ему в руль, чтобы развернуть шаблон.
Затем функция processShortCode найдете и обработать все шорткоды в разделе. Результаты передаются в руль во второй раз в процесс любые макросы, оставленных в короткие. Пользователь получает конечный результат.
1 |
#
|
2 |
# Function: processShortCodes
|
3 |
#
|
4 |
# Description: This function takes the page and processes
|
5 |
# all of the shortcodes in the page.
|
6 |
#
|
7 |
# Inputs:
|
8 |
# page The contents of the page to
|
9 |
# process.
|
10 |
#
|
11 |
def processShortCodes(page) |
12 |
#
|
13 |
# Initialize the result variable for returning.
|
14 |
#
|
15 |
result = "" |
16 |
|
17 |
#
|
18 |
# Find the first shortcode
|
19 |
#
|
20 |
scregFind = /\-\[([^\]]*)\]\-/ |
21 |
match1 = scregFind.match(page) |
22 |
if match1 != nil |
23 |
#
|
24 |
# We found one! get the text before it
|
25 |
# into the result variable and initialize
|
26 |
# the name, param, and contents variables.
|
27 |
#
|
28 |
name = "" |
29 |
param = "" |
30 |
contents = "" |
31 |
nameLine = match1[1] |
32 |
loc1 = scregFind =~ page |
33 |
result = page[0, loc1] |
34 |
|
35 |
#
|
36 |
# Separate out the nameLine into a shortcode
|
37 |
# name and parameters.
|
38 |
#
|
39 |
match2 = /(\w+)(.*)*/.match(nameLine) |
40 |
if match2.length == 2 |
41 |
#
|
42 |
# Just a name was found.
|
43 |
#
|
44 |
name = match2[1] |
45 |
else
|
46 |
#
|
47 |
# A name and parameter were found.
|
48 |
#
|
49 |
name = match2[1] |
50 |
param = match2[2] |
51 |
end
|
52 |
|
53 |
#
|
54 |
# Find the closing shortcode
|
55 |
#
|
56 |
rest = page[loc1+match1[0].length, page.length] |
57 |
regEnd = Regexp.new("\\-\\[\\/#{name}\\]\\-") |
58 |
match3 = regEnd.match(rest) |
59 |
if match3 != nil |
60 |
#
|
61 |
# Get the contents the tags enclose.
|
62 |
#
|
63 |
loc2 = regEnd =~ rest |
64 |
contents = rest[0, loc2] |
65 |
|
66 |
#
|
67 |
# Search the contents for shortcodes.
|
68 |
#
|
69 |
contents = processShortCodes(contents) |
70 |
|
71 |
#
|
72 |
# If the shortcode exists, run it and include
|
73 |
# the results. Otherwise, add the contents to
|
74 |
# the result.
|
75 |
#
|
76 |
if $shortcodes.include?(name) |
77 |
result += $shortcodes[name].call(param, contents) |
78 |
else
|
79 |
result += contents |
80 |
end
|
81 |
|
82 |
#
|
83 |
# process the shortcodes in the rest of the
|
84 |
# page.
|
85 |
#
|
86 |
rest = rest[loc2 + match3[0].length, page.length] |
87 |
result += processShortCodes(rest) |
88 |
else
|
89 |
#
|
90 |
# There wasn't a closure. Therefore, just
|
91 |
# send the page back.
|
92 |
#
|
93 |
result = page |
94 |
end
|
95 |
else
|
96 |
#
|
97 |
# No shortcodes. Just return the page.
|
98 |
#
|
99 |
result = page |
100 |
end
|
101 |
|
102 |
return result |
103 |
end
|
Функция processShortCodes принимает заданный текст, находит каждый шорткод, и запускает указанный короткий номер с аргументами и содержимое шорткода. Я использую шорткод подпрограмму для обработки содержание на короткие номера, а также.
Шорткод-это HTML-тег, который как использует -[ и ]- разграничить открывающий тег и - [и ]- закрывающий тег. Открывающий тег содержит параметры шорткода, а также. Таким образом, пример шорткода будет:
1 |
-[box]- |
2 |
This is inside a box. |
3 |
-[/box]- |
Этот шорткод определяет короткий коробка без каких-либо параметров с содержанием это внутри коробки.. Короткий коробка обертывания содержимое в соответствующий HTML для того чтобы произвести рамку вокруг текста с текстом в центре окна. Если в дальнейшем вы хотите изменить, как поле отображается, вы только должны изменить определение шорткод. Это экономит много работы.
1 |
#
|
2 |
# Data Structure: $shortcodes
|
3 |
#
|
4 |
# Description: This data structure contains all
|
5 |
# the valid shortcodes names and the
|
6 |
# function. All shortcodes should
|
7 |
# receive the arguments and the
|
8 |
# that the shortcode encompasses.
|
9 |
#
|
10 |
$shortcodes = { |
11 |
"box" => lambda { |args, contents| |
12 |
return("#{contents}") |
13 |
},
|
14 |
'Column1'=> lambda { |args, contents| |
15 |
return("#{contents}") |
16 |
},
|
17 |
'Column2' => lambda { |args, contents| |
18 |
return("#{contents}") |
19 |
},
|
20 |
'Column1of3' => lambda { |args, contents| |
21 |
return("#{contents}") |
22 |
},
|
23 |
'Column2of3' => lambda { |args, contents| |
24 |
return("#{contents}") |
25 |
},
|
26 |
'Column3of3' => lambda { |args, contents| |
27 |
return("#{contents}") |
28 |
},
|
29 |
'php' => lambda { |args, contents| |
30 |
return("#{contents}") }, 'js' => lambda { |args, contents| return("#{contents}") }, "html" => lambda { |args, contents| return("#{contents}") }, 'css' => lambda {|args, contents| return("#{contents}") } } |
Последнее, что в файле-это $короткие хеш-таблицы, содержащей подпрограммы шорткод. Эти простые шорткоды, но вы можете создать другие шорткоды быть сложным, как вы хотите.
Все шорткоды должны принимать два параметра: args и содержание. Эти строки содержат параметры шорткода и содержание шорткоды окружают. Поскольку короткие номера внутри хэш-таблицы, я использовал лямбда-функции, чтобы определить их. Лямбда-функция-это функция без имени. Единственный способ для выполнения этих функций из хэш-массива.
Запуск сервера
После того как вы создали rubyPress.файл РБ с указанным выше содержанием, Вы можете запустить сервер с:
1 |
ruby rubyPress.rb |
Поскольку в рамках Синатра работает с Ruby на Rails конструкции шкафа, вы можете использовать военнопленных для запуска сервера. Военнопленных будут созданы файлы вашей системы для запуска вашего сервера локально то же как бы из размещенных сайтов. Вы можете установить для военнопленных с порошком, используя следующие команды в командной строке:
1 |
gem install powder
|
2 |
powder install
|
Порошок-это программа командной строки рутины для управления сайтами PoW на вашем компьютере. Чтобы получить военнопленных, чтобы увидеть ваш сайт, вы должны создать мягкую ссылку на свой проект в каталог ~/.каталог военнопленных. Если сервер в папку /Users/проверка/документов/каталог rubyPress, следует выполнить следующие команды:
1 |
cd ~/.pow
|
2 |
ln -s /Users/test/Documents/rubyPress rubyPress |
ЛН -ы создает мягкую ссылку в каталог, указанный во-первых, с именем, указанным во-вторых. Тогда пр будет настроить домен на компьютере с именем мягких ссылке. В приведенном выше примере, идя в http://rubypress.dev веб-сайт в браузере будет загружена страница с сервера.
Для запуска сервера, введите следующую после создания мягкой ссылке:
1 |
powder start |
Перезагрузить сервер после внесения некоторых изменений в код, введите следующее:
1 |
powder restart |



Зайдя на сайт в браузере будет результат на картинке выше. Пр будет настроить сайт по http://rubyPress.dev. Независимо от того, какой метод вы используете, чтобы запустить сайт, вы увидите тот же открывшейся странице.
Заключение
Ну, вы сделали это. Другой CMS, но в этот раз в Руби. Эта версия является самой короткой версии все с открытым кодом, созданные в этой серии. Экспериментов с кодом и посмотреть, как можно расширить эту основу.