Пару месяцев назад я написал статью о том, как я быстро создал свой личный сайт с помощью Sinatra . Во время создания сайта я начал думать о том, как лучше всего добавить файлы JavaScript на страницы. Поработав с ним некоторое время, я использовал некоторые вспомогательные методы для добавления файлов JavaScript на любую страницу.
На самом деле очень легко добавлять файлы javascript на страницы в Sinatra с помощью файла макета. Например, если вы хотите включить JQuery и пользовательский файл JavaScript (называемый application.js
) на всех страницах, то все, что вам нужно сделать, это поместить следующие строки кода в файл макета:
<script src=»https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js«></script> | |
<script src=»/javascripts/application.js«></script> |
(Предполагается, что файлы Javascript находятся в общей папке).
Размещение этого кода в файле макета означает, что они будут включены на каждой странице. Однако некоторые файлы JavaScript могут понадобиться только на определенных страницах. Например, вы можете захотеть, чтобы файл email.js
загружался только на странице контактов. Оказывается, есть простой способ динамически добавлять вещи в файлы макетов — переменные экземпляра. Они могут быть установлены в маршрутах и затем сослаться на в представлениях. Я использую переменную экземпляра @js
для добавления пользовательского файла JavaScript в маршрут:
get ‘/’ do | |
@js = «custom.js» | |
slim :index | |
end |
Затем я могу добавить в свой файл макета дополнительную строку, которая будет включать соответствующий тег сценария, если была @js
переменная @js
.
<script src=»https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js«></script> | |
<script src=»/application.js«></script> | |
<%= «<script src=\«/#{@js}\»></script>» if @js %> |
Это большое улучшение. Это означает, что вы можете добавить файл javascript на основе маршрута. Единственная проблема заключается в том, что вы можете добавить только один файл на маршрут. Чтобы обойти это, вы можете изменить переменную экземпляра на массив файлов JavaScript:
get ‘/’ do | |
@js = [«custom.js»,«sorter.js»,«colorpicker.js»] | |
slim :index | |
end |
Для этого также необходимо изменить строку в файле макета, чтобы она перебирала каждую строку в массиве и добавляла тег сценария для каждого файла JavaScript:
<% if @js %> | |
<% @js.each do |script| %> | |
<%= «<script src=\»/javascripts/#{script}.js\»></script>» %> | |
<% end %> | |
<% end %> |
Пока все хорошо — теперь мы можем вставлять пользовательские файлы JavaScript в схему отдельных маршрутов. Поскольку мы перебираем все пользовательские файлы JavaScript в массиве @js
, почему бы просто не добавить в массив сценарии, которые появятся на всех страницах (application.js и JQuery)? Это может быть достигнуто путем создания другого массива, используя метод settings
Sinatra, называемый settings.javascripts
. Этот массив может быть размещен в любом месте вашего файла ruby (мне нравится держать его в верхней части) и содержит все файлы JavaScript, которые должны быть включены на всех страницах:
settings.javascripts = [ «https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js», «application.js» ] |
Эти два массива необходимо сложить вместе, чтобы создать один большой массив для повторения. Метод uniq используется, чтобы избежать повторения в случае добавления файла в массив @js
, который уже был помещен в массив settings.javascripts
:
<% javascripts = (@js ? @js + settings.javascripts : settings.javascripts).uniq %> | |
<% javascripts.each do |script| %> | |
<%= «<script src=\»/javascripts/#{script}.js\»></script>» %> | |
<% end %> |
Такое количество логики в представлении выглядит грязно, что всегда является плохим признаком. Лучший способ справиться с большим количеством логики представления — это абстрагировать его во вспомогательный метод. Помощники легко настраиваются в Синатре, вы просто открываете блок и размещаете методы внутри. Эти методы затем доступны внутри маршрутов и видов. Вот помощник, который можно использовать для очистки макета:
helpers do | |
def javascripts | |
javascripts = (@js ? @js + settings.javascripts : settings.javascripts).uniq | |
javascripts.each do |script| | |
html << «<script src=\»/#{script}\»></script>» | |
end.join | |
end | |
end |
Теперь все, что нужно в файле макета, это строка:
<%= javascripts %> |
Гораздо чище.
Поскольку мы сейчас используем вспомогательный метод, почему бы не разрешить ему иметь аргументы? Это позволило бы нам добавлять файлы JavaScript из файла макета, как и раньше. Это не требует особых усилий — добавление звездочки перед именем параметра метода позволяет методу принимать столько аргументов, сколько необходимо. Это означает, что мы можем добавить столько файлов JavaScript, сколько нам нужно для каждого маршрута. Каждый аргумент является частью массива args
, который теперь может быть просто добавлен в большой массив файлов JavaScript, который повторяется в макете (в данном случае бонус заключается в том, что нам не нужно проверять, существует ли он, поскольку по умолчанию пустой массив, если аргументы не заданы):
helpers do | |
def javascripts *scripts | |
javascripts = (@js ? @js + settings.javascripts + args : settings.javascripts + args).uniq | |
javascripts.each do |script| | |
html << «<script src=\»/#{script}\»></script>» | |
end.join | |
end | |
end |
Теперь у нас есть три способа добавить файлы JavaScript на страницу:
- В настройках (это для файлов JavaScript, которые являются глобальными для всего сайта)
- В файле макета используется помощник
javascripts
. Это используется для файлов JavaScript, которые являются общими для всех страниц, использующих этот макет. - В обработчике используется переменная экземпляра
@js
. Это используется для добавления файлов JavaScript по маршруту на основе маршрута.
Сначала может показаться, что методы 1 и 2 — это практически одно и то же — способ добавления глобальных файлов JavaScript. Но весьма часто иметь разные макеты для сайта, и вполне вероятно, что каждый макет потребует определенных файлов JavaScript. Массив settings.javascripts
должен использоваться только для действительно глобального JavaScript, который используется по всему сайту. Помощник javascripts
может быть использован для добавления файлов JavaScript на основе макета.
Мы можем улучшить это дальше, используя вспомогательный метод для установки метода @js
instansce. Вместо того, чтобы устанавливать его непосредственно в обработчике, мы можем использовать вспомогательный метод js
который можно использовать для добавления любых пользовательских js-файлов, необходимых для этого маршрута. Это также хорошая возможность очистить вспомогательный метод javascripts
как он выглядит немного хрупким (предполагается, что на данный момент существует settings.javascripts
).
helpers do | |
def js *scripts | |
@js ||= [] | |
@js = scripts | |
end | |
def javascripts(*args) | |
js = [] | |
js << settings.javascripts if settings.respond_to?(‘javascripts’) | |
js << args | |
js << @js if @js | |
js.flatten.uniq.map do |script| | |
«<script src=\»#{path_to script}\»></script>» | |
end.join | |
end | |
end |
Наш новый вспомогательный метод js
означает, что при вызове в обработчике все выглядит немного лучше. Это особенно верно, так как файлы больше не должны помещаться в массив:
get ‘/’ do | |
js «custom.js»,«sorter.js»,«colorpicker.js» | |
slim :index | |
end |
В заключение давайте упростим добавление JQuery и других популярных библиотек, когда они требуются. Это включает в себя написание другого вспомогательного метода, который использует символы быстрого доступа вместо полного URL:
helpers do | |
def js *scripts | |
@js ||= [] | |
@js = scripts | |
end | |
def javascripts(*args) | |
js = [] | |
js << settings.javascripts if settings.respond_to?(‘javascripts’) | |
js << args | |
js << @js if @js | |
js.flatten.uniq.map do |script| | |
«<script src=\»#{path_to script}\»></script>» | |
end.join | |
end | |
def path_to script | |
case script | |
when :jquery then ‘https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js’ | |
when :rightjs then ‘http://cdn.rightjs.org/right-2.3.0.js’ | |
when :backbone then ‘http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.0/backbone-min.js’ | |
when :underscore then ‘http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.3.1/underscore-min.js’ | |
else script.to_s + ‘.js’ | |
end | |
end | |
end |
Теперь, если вы хотите добавить определенную библиотеку, все, что вам нужно, это следующий код внутри обработчика маршрута:
get ‘/’ do | |
js :backbone, :custom | |
end |
Обратите внимание, что пользовательские файлы JavaScript теперь можно вводить в виде символов и не нужно path_to
в конец «.js», поскольку все это делается с path_to
вспомогательного метода path_to
.
С вспомогательным методом, работающим так, как мы хотим, пришло время перейти к модульному и переместить его в отдельный файл. Сначала нам нужно создать файл с именем javascripts.rb
и сохранить его в папке с именем «sinatra» в корневом каталоге приложения. Весь код внутри блока помощников затем помещается в модуль под названием JavaScripts
. Этот модуль затем помещается в модуль под названием Sinatra
(что считается наилучшей практикой при расширении Sinatra модулями). Последнее, что нужно сделать, это указать Sinatra::Base
в верхней части файла.
require ‘sinatra/base’ | |
module Sinatra | |
module JavaScripts | |
def js *scripts | |
@js ||= [] | |
@js = scripts | |
end | |
def javascripts(*args) | |
js = [] | |
js << settings.javascripts if settings.respond_to?(‘javascripts’) | |
js << args | |
js << @js if @js | |
js.flatten.uniq.map do |script| | |
«<script src=\»#{path_to script}\»></script>» | |
end.join | |
end | |
def path_to script | |
case script | |
when :jquery then ‘https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js’ | |
when :rightjs then ‘http://cdn.rightjs.org/right-2.3.0.js’ | |
when :backbone then ‘http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.0/backbone-min.js’ | |
when :underscore then ‘http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.3.1/underscore-min.js’ | |
else script.to_s + ‘.js’ | |
end | |
end | |
end | |
helpers JavaScripts | |
end |
Строка helpers JavaScripts
прямо в конце модуля Sinatra регистрирует вспомогательные методы, чтобы они были доступны для использования в приложении.
Чтобы использовать эти вспомогательные методы, все, что вам нужно, это следующая строка кода в главном файле приложения ( main.rb
):
require ‘./sinatra/helpers’ |
Теперь у нас есть удобный маленький помощник, который позволяет нам легко добавлять файлы JavaScript именно там, где они нужны. Приятно отметить, что файлы JavaScript также могут быть добавлены на условной основе в обработчики маршрутов, например:
get ‘/’ | |
js :backbone, :application | |
js :admin if logged_in? | |
slim :index | |
end |
Больше вспомогательных методов
Метод, описанный выше, не ограничивается файлами JavaScript. Подобный метод может быть использован для добавления CSS в мелкозернистом виде:
module Sinatra | |
module JavaScripts | |
def js *scripts | |
@js ||= [] | |
@js = scripts | |
end | |
def javascripts(*args) | |
js = [] | |
js << settings.javascripts if settings.respond_to?(‘javascripts’) | |
js << args | |
js << @js if @js | |
js.flatten.uniq.map do |script| | |
«<script src=\»#{path_to script}\»></script>» | |
end.join | |
end | |
def path_to script | |
case script | |
when :jquery then ‘https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js’ | |
when :rightjs then ‘http://cdn.rightjs.org/right-2.3.0.js’ | |
when :backbone then ‘http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.0/backbone-min.js’ | |
when :underscore then ‘http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.3.1/underscore-min.js’ | |
else script.to_s + ‘.js’ | |
end | |
end | |
end | |
module StyleSheets | |
def css *files | |
@css ||= [] | |
@css = files | |
end | |
def styles(*args) | |
css = [] | |
css << settings.css if settings.respond_to?(‘css’) | |
css << args | |
css << @css if @css | |
css.flatten.uniq.map do |stylesheet| | |
«<link href=\»/#{stylesheet}.css\» media=\»screen, projection\» rel=\»stylesheet\» />» | |
end.join | |
end | |
end | |
helpers JavaScripts, StyleSheets | |
end |
Я HeadCleaner
всю концепцию дальше и создал модуль под названием HeadCleaner
который имеет вспомогательный метод для всех общих элементов, находящихся внутри title
html-страницы, таких как meta
, title
, webfont
s и т. Д. Это означает, что вы можете просто использовать метод вызывает создание очень миниатюрного раздела заголовка файла макета, такого как показан ниже:
<head> | |
<%= meta %> | |
<%= title_tag %> | |
<%= favicon %> | |
<%= ie_shim %> | |
<%= webfonts %> | |
<%= javascripts %> | |
<%= styles :mobile,:general,:ie %> | |
</head> |
Вы можете увидеть код здесь .
Это ни в коем случае не идеально, и я буду признателен за любые отзывы или помощь в дальнейшей разработке (разветвите код на github!). У меня есть идеи для развития этого:
- Разрешить пользователям устанавливать путь к javascripts
- Иметь отдельный файл yaml с ярлыками для внешних библиотек, чтобы его можно было легко редактировать
- Интегрируйте с загрузчиком JavaScript вместо того, чтобы иметь отдельный тег сценария для каждого файла JavaScript.
Я надеюсь, что эти помощники, ну, в общем, полезны. Если вы разработали каких-либо помощников в разработке Синатры, сообщите об этом в комментариях. Ура!
(Главное изображение любезно предоставлено Shutterstock )