Статьи

Синатра Размеры

3sizes_of_sinatra

Одна из замечательных особенностей Синатры — отсутствие мнений. Это позволяет создавать приложения так, как вам нравится. Но эта свобода часто может сбивать с толку — как лучше всего структурировать приложение? Конечно, нет единственно правильного ответа на этот вопрос, но в этом посте я расскажу о трех разных стилях структурирования приложения Sinatra.

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

  • SMALL — весь код в одном файле
  • MEDIUM — тесты в отдельном файле и отдельной папке представлений
  • LARGE — приложение в модульном стиле

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

Приложение — шифр Цезаря Сдвига

Приложение добавит к строковому классу caesar_shift который caesar_shift строку с помощью шифра Цезаря . Для этого приложения понадобятся два обработчика маршрута — один с формой для ввода строки, а другой отображает сообщение в виде открытого текста и зашифрованного текста.

НЕБОЛЬШОЙ

Смотрите пример приложения здесь

Малое приложение — это буквально один файл. Все в одном файле: код, обработчики маршрутов (контроллеры), помощники, представления и даже тесты. Да, все верно, тесты находятся в одном файле с приложением! Некоторым это может показаться совершенно чокнутым, но я думаю, что тот факт, что вы можете создать приложение с полным стеком всего в одном файле, является истинным недостатком Синатры, который часто упускается из виду.

Этот тип структуры имеет следующие преимущества:

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

  • Это прекрасно, когда вам нужно всего несколько обработчиков маршрутов, и виды не слишком сложны.

  • Это также полезно, если вы тестируете идею и хотите быстро получить подтверждение концепции.

Вот первая часть приложения, которая просто сохраняется в файле с именем main.rb (или любым другим именем файла):

 require 'sinatra' class String def caesar_shift(shift=1) letters = ("a".."z").to_a ciphertext = [] self.downcase.scan( /./ ) do |char| if letters.include?(char) ciphertext << letters[(letters.index(char)+shift)%26] else ciphertext << char end end ciphertext.join.upcase end end 

В этом коде нам, как обычно, требуется Sinatra, затем откройте класс String и добавьте метод caesar_shift . Метод ne2w использует scan чтобы scan итерацию по каждому символу в строке и сдвинуть любые буквы на значение аргумента shift , а затем заглавными буквами результат. Любые значения, которые не являются буквами, например знаки пунктуации, просто остаются без изменений.

После этого идут помощники в блоке:

 helpers do def title @title || "Caesar Shift Cipher" end end 

Это простой помощник для создания заголовка страницы. Заголовок может быть установлен в обработчике маршрута с помощью переменной экземпляра @title , в противном случае по умолчанию используется «Цезарь сдвигового шифра».

Далее это обработчики маршрута:

 get '/' do erb :form end post '/' do @title = "Secret Message" @plaintext = params[:plaintext].chomp shift = params[:shift].to_i @ciphertext = @plaintext.caesar_shift(shift) erb :result end 

Первый обработчик маршрута просто использует вспомогательный метод erb для отображения представления формы. Второй маршрут используется, когда форма отправляется через почтовый запрос. Прежде всего, установите заголовок страницы, используя переменную экземпляра @title . Затем получите сообщение из формы (хранится в хэше params ) и сохраните его в переменной экземпляра @plaintext . Затем примените параметр shift который был представлен в форме в качестве аргумента, к методу caesar_shift . Возьмите результат caeser_shift и сохраните его в переменной экземпляра @ciphertext .

Тесты идут дальше. Чтобы иметь возможность запускать тесты из одного и того же файла, они должны идти внутри следующего оператора if :

 if ARGV.include? 'test' # tests go here end 

Это тестирование, чтобы увидеть, есть ли аргумент ‘test’ при запуске программы.

Если указан аргумент, установите для среды «test» и остановите запуск приложения Sinatra:

 set :environment, :test set :run, false 

Также требуются соответствующие гемы тестирования:

 require 'test/unit' require 'rack/test' 

Фактические тесты идут дальше. Я написал пару, которая тестирует метод caesar_shift и другую, которая проверяет, что запрос POST фактически возвращает зашифрованную строку:

 class CaesarCipherTest < Test::Unit::TestCase include Rack::Test::Methods def app Sinatra::Application end def test_it_can_encrypt_strings assert_equal 'JGNNQ','hello'.caesar_shift(2) end def test_it_can_encrypt_with_negative_shifts assert_equal 'GDKKN','hello'.caesar_shift(-1) end def test_it_can_encrypt_from_a_URL post '/', params={plaintext: 'hello', shift: '2'} assert last_response.ok? assert last_response.body.include?('hello'.caesar_shift(2)) end end 

Чтобы запустить тесты, просто введите в терминал следующее:

 ruby main.rb test 

Я обнаружил этот метод добавления тестов в тот же файл, что и приложение, в сообщении в блоге Авди и Дана .

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

 __END__ 

Имя каждого представления затем начинается с двойного амперсанда @@ с кодом, написанным на ERB:

 @@layout <!doctype html> <html> <head> <meta charset="utf-8"> <title>Caesar Cipher</title> </head> <body> <h1> <a href='/''>Caesar Cipher</a> </h1> <%= yield %> </body> </html> @@form <form action='/' method='POST'> <textarea rows=4 cols=50 name='plaintext'>Enter plaintext</textarea> <input type='number' name='shift' value=1 min=1 max=26> <input type='submit' value='Encrypt'> </form> @@result <p>Plaintext:</p> <p><%= @plaintext %></p> <p>Ciphertext:</p> <p><%= @ciphertext %></p> <a href='/''>Write another message</a> 

Среднего размера приложения

Смотрите пример приложения здесь

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

Файл caesar_shift содержит помощники, метод caesar_shift и обработчики маршрутов. Кроме того, тесты были перемещены в собственный файл с именем test.rb, а представления были перемещены в отдельные файлы и помещены в папку представлений.

Имеет следующие преимущества:

  • Он по-прежнему использует классический стиль, поэтому весь код обработки маршрута и помощники все в одном ( main.rb ). В результате преимущество всего кода, находящегося в одном файле, остается.

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

  • Тесты разделены в свой собственный файл. Это убирает их с пути основного кода, но сохраняет их все в одном месте.

Весь код в структуре Medium идентичен коду в структуре Small. Основным отличием является то, что он был организован в разных местах, что облегчает поиск различных фрагментов кода.

БОЛЬШОЙ

Смотрите пример приложения здесь

БОЛЬШОЕ приложение Sinatra использует структуру модульного стиля Sinatra и больше похоже на классическую архитектуру в стиле MVC. В этой структуре мы по-прежнему используем отдельные папки для представлений, но добавляем папки для тестов и помощников.

Другое большое отличие заключается в отделении обработчиков маршрутов от методов String которые реализуют шифр Цезаря. Код cesar cipher должен использоваться как отдельная Ruby-программа, для которой не требуется Sinatra. По этой причине мы помещаем его в каталог lib в файле caesar-cipher.rb .

Обработчики маршрута размещаются внутри класса Controller который наследуется от Sinatra::Base в файле controller.rb :

 require 'sinatra/base' require_relative 'lib/caesar-cipher.rb' require_relative 'helpers/helpers.rb' class Controller < Sinatra::Base helpers TitleHelpers get '/' do erb :form end post '/' do @title = "Secret Message" @plaintext = params[:plaintext].chomp shift = params[:shift].to_i @ciphertext = @plaintext.caesar_shift(shift) erb :result end end 

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

 module TitleHelpers def title @title || "Casaer Shift Cipher" end end 

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

Тесты также помещаются в отдельные файлы в зависимости от того, тестируют ли они веб-приложение или код шифра Цезаря. В этом случае результатом является один файл для caesar_shift метода caesar_shift и другой файл для тестов обработчика маршрута. Чтобы сохранить вещи СУХИМ, мы создаем файл с именем test_helper.rb, который включает в себя весь установочный код для тестов:

 ENV['RACK_ENV'] = 'test' require 'minitest/autorun' require 'rack/test' require_relative '../controller' include Rack::Test::Methods 

Этот файл затем требуется во всех других тестовых файлах. Например, вот файл test_caesar-cipher.rb :

 require_relative 'test_helper.rb' class CaesarCipherTest < MiniTest::Unit::TestCase def test_it_can_encrypt_strings assert_equal 'JGNNQ','hello'.caesar_shift(2) end def test_it_can_encrypt_with_negative_shifts assert_equal 'GDKKN','hello'.caesar_shift(-1) end end 

Для запуска тестов используйте следующий код:

 $ ruby test/test_website.rb $ ruby test/test_caesar-cipher.rb 

Преимущества этого стиля:

  • Код является модульным, что облегчает повторное использование или разработку самостоятельно.
  • Существует четкое разделение проблем. Все обработчики маршрутов хранятся в классах контроллеров, а код приложения хранится в каталоге lib .
  • Тесты разделены в зависимости от того, что они тестируют, что упрощает проведение более целенаправленного тестирования.
  • Помощники хранятся в модулях, что упрощает создание различных типов вспомогательных модулей, которые можно использовать независимо.

XL

Если приложение становится очень большим, оно может перерасти даже БОЛЬШУЮ структуру. Следующим этапом будет разбиение приложения на несколько модулей и, возможно, создание класса Controller который другие классы могли бы создавать подклассами. Некоторые из помощников также могут быть превращены в расширения Синатры. Это создаст структуру, аналогичную структуре MVC, которую я описал здесь .

Многие считают, что Rails больше подходит для приложений такого типа, но Sinatra более чем способна обрабатывать такие сложные приложения.

Резюме

Какой из них самый лучший? Что ж, хорошая новость в том, что вам не нужно выбирать одно или другое. На самом деле, хорошая вещь во всем этом заключается в том, что вы можете легко переходить от одного размера к другому постепенно. Однажды вы можете получить отличную идею и собрать некоторый код в одном файле, используя стиль SMALL, просто чтобы посмотреть, работает ли он. Затем, когда количество обработчиков маршрутов, представлений и тестов начинает расти, разбейте их на отдельные файлы и используйте стиль MEDIUM. Через некоторое время, когда проект становится более сложным, разделите различные части проекта на их собственные отдельные классы и начните организовывать его в БОЛЬШУЮ модульную структуру.

Замечательная вещь в такой работе заключается в том, насколько она органична — структура приложения может изменяться и адаптироваться к размеру приложения по мере его разработки. Если вы создали приложение с нуля, запустив SMALL, у вас будет полное понимание того, как все сочетается друг с другом по мере того, как приложение растет до MEDIUM, а затем до LARGE.

Это все люди

В этом посте я представил три разных размера структуры приложения, которые можно создать с помощью Sinatra, которые, мы надеемся, продемонстрируют, насколько она гибкая. Как вы думаете, эти три размера охватывают все?

Какой «размер» является наиболее близким примером для ваших приложений Sinatra? Может ли Sinatra действительно использоваться для создания приложений размера XL? У вас есть что-нибудь еще, чтобы добавить? Как обычно, оставляйте свои комментарии ниже.