Статьи

Создайте Sinatra MVC Framework

В последней главе моей книги, Jump Start Sinatra , я предположил, что код, созданный из книги, может быть преобразован в структуру MVC, похожую на Rails. Я оставил это в качестве упражнения для читателя, но решил написать здесь свою попытку. Если у вас нет книги, то позор вам (!), Но вы должны быть в состоянии следовать вместе с большей частью того, что написано здесь в общем смысле. Отличный Padrino показывает, чего можно достичь, создавая поверх Sinatra, поэтому это должен быть увлекательный проект по созданию микросхемы с нуля, который можно использовать для упрощения организации кода при сохранении простоты Sinatra.

Песни Синатры

В книге мы создаем пример приложения под названием «Песни Синатры». Это сайт, посвященный песням великого Фрэнка Синатры. Это позволяет пользователю, вошедшему в систему, добавлять песни Ol ‘Blue Eyes, включая название, дату, длину и текст песни. Это также позволяет посетителям сайта «любить» эти песни. Живой сайт можно увидеть здесь .

songsbysinatra

Создание структуры файла

Первая задача — создать файловую структуру. Поскольку мы используем структуру MVC, имеет смысл иметь папки «models», «views» и «controllers». Я также решил создать папку «помощников» и папку «lib» для любых расширений и промежуточного программного обеспечения.

В книге мы создали небольшую часть промежуточного программного обеспечения для обработки ресурсов (таких как файлы CoffeeScript и Sass), поэтому у нас есть папка ‘assets’, а также стандартная общая папка для всех общедоступных ресурсов, таких как изображения.

Вот схема структуры моей папки:

рамки-каталог-структура

Один контролер, чтобы управлять ими всеми

Я также предлагаю в книге написать глобальный контроллер под названием ApplicationController. Это должно использовать все представления, макеты и зарегистрировать любые расширения, используемые всем приложением.

$:.unshift(File.expand_path('../../lib', __FILE__)) require 'sinatra/base' require 'slim' require 'sass' require 'coffee-script' require 'v8' require 'sinatra/auth' require 'sinatra/contact' require 'sinatra/flash' require 'asset-handler' class ApplicationController < Sinatra::Base helpers ApplicationHelpers set :views, File.expand_path('../../views', __FILE__) enable :sessions, :method_override register Sinatra::Auth register Sinatra::Contact register Sinatra::Flash use AssetHandler not_found{ slim :not_found } end 

Для этого требуются все необходимые гемы, которые используются, а затем создается класс ApplcationController . Это будет базовый класс для всех контроллеров. Он регистрирует ApplicationHelpers , куда будут отправляться все помощники всего приложения.

Здесь необходимо установить папку views , поскольку она не находится в той же директории, что и наш файл application_controller.rb, чего и ожидает Синатра. Это легко изменить, используя команду set .

Мы также method_override sessions и method_override здесь. Сессии необходимы для использования sinatra-flash, а также для большинства приложений. Параметр method_override используется, чтобы позволить браузерам поддерживать такие методы HTTP, как PUT, PATCH и DELETE, используя метод POST и скрытое поле ввода в форме.

расширения

В книге я рассмотрел создание модуля расширения Auth . Я также объяснил, как написать несколько вспомогательных методов для отправки контактного электронного письма. Я извлек эти и контактные маршруты в их собственное расширение, чтобы его можно было установить отдельно

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

Другие контроллеры

Другие контроллеры теперь наследуются от класса ApplicationController . В этом приложении есть два контроллера — WebsiteController , отвечающий за основную часть сайта, и SongController , отвечающий за все операции CRUD, выполняемые на модели Song .

 class WebsiteController < ApplicationController helpers WebsiteHelpers get '/' do slim :home end get '/about' do @title = "All About This Website" slim :about end end class SongController < ApplicationController helpers SongHelpers get '/' do find_songs slim :songs end get '/new' do protected! find_song slim :new_song end get '/:id' do find_song slim :show_song end post '/songs' do protected! create_song flash[:notice] = "Song successfully added" redirect to("/#{@song.id}") end get '/:id/edit' do protected! find_song slim :edit_song end put '/:id' do protected! update_song flash[:notice] = "Song successfully updated" redirect to("/#{@song.id}") end delete '/:id' do protected! find_song.destroy flash[:notice] = "Song deleted" redirect to('/') end post '/:id/like' do find_song @song.likes = @song.likes.next @song.save redirect to("/#{@song.id}") unless request.xhr? slim :like, :layout => false end end 

модели

В этом случае есть только одна модель — модель Song . В каталоге модели есть только один файл song.rb, который создает класс Song и устанавливает все свойства DataMapper:

 require 'dm-core' require 'dm-migrations' class Song include DataMapper::Resource property :id, Serial property :title, String property :lyrics, Text property :length, Integer property :released_on, Date property :likes, Integer, :default => 0 def released_on=date super Date.strptime(date, '%m/%d/%Y') end DataMapper.finalize end 

Обратите внимание, что я использую DataMapper для этой модели, но могу легко использовать другой ORM. На самом деле, я мог бы даже использовать другой ORM в другой модели.

Помощники

Каждый контроллер имеет свой собственный вспомогательный файл, поэтому есть application-helpers.rb , website-helpers.rb и song-helpers.rb . Они создаются как модуль, а затем каждый контроллер должен явно зарегистрировать соответствующий модуль помощников. Вот файл помощников приложения:

 module ApplicationHelpers def css(*stylesheets) stylesheets.map do |stylesheet| "<link href="/#{stylesheet}.css" media="screen, projection" rel="stylesheet" />" end.join end def current?(path='/') request.path_info==path ? "current": nil end end 

Он зарегистрирован в application_controller.rb со следующей строкой:

 helpers ApplicationHelpers 

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

Rackup

На config.ru все настройки выполнены:

 require 'sinatra/base' Dir.glob('./{models,helpers,controllers}/*.rb').each { |file| require file } SongController.configure :development do DataMapper.setup(:default, "sqlite3://#{Dir.pwd}/development.db") end SongController.configure :production do DataMapper.setup(:default, ENV['DATABASE_URL']) end map('/songs') { run SongController } map('/') { run WebsiteController } 

Прежде всего нам требуется Sinatra::Base (в отличие от Sinatra , так как мы используем модульную структуру). Затем нам потребуются все файлы, хранящиеся в папках моделей, помощников и контроллеров.

После этого мы немного настраиваем класс SongController чтобы настроить базу данных для сред производства и разработки. Это можно было бы поместить в файл song_controller.rb , но config.ru, похоже, был лучшим местом для размещения конфигурации.

И наконец, мы используем удобные методы map которые Rack дает нам для создания пространства имен для каждого контроллера.

Это все люди

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

В дальнейшем я думаю, что мне хотелось бы взглянуть на добавление еще нескольких расширений, использование лучшего обработчика активов и немного более удобную организацию базы данных и ORM. Я поместил весь код на GitHub и назвал его «Jump Start». Название кажется вдвойне подходящим, учитывая, как оно возникло, и потому, что оно помогает вам «запустить» ваши проекты Sinatra. Вы бы использовали Jump Start? Можете ли вы придумать способы улучшить его? Вы создали свой собственный фреймворк с использованием Sinatra? Как обычно, дайте нам знать в комментариях ниже.