Grape: не рельсами едиными

в 15:21, , рубрики: api, ruby, ruby on rails, Веб-разработка

Grape: не рельсами едиными
В этом посте я хотел бы познакомить вас с Grape — веб-фреймворком, написанным на ruby, предназначенным для быстрой и удобной разработки API, а также немного порассуждать о судьбе Rails в свете последних тенденций в веб-разработке.

Ruby = Ruby On Rails

Как-то так сложилось, что при упоминании ruby в качестве средства веб-разработки (да и просто в качестве скриптового ЯП) передо многими людьми, имеющими некоторое отношение к этой самой веб-разработке, в голове возникает если не логотип с пресловутыми белыми рельсами на красном фоне, то магическое словосочетание Ruby On Rails уж точно.
Я не берусь спорить, хорошо это или плохо — эта статья не для спора. Одно могу сказать с уверенностью — RoR оказал огромное влияние на развитие веб-фреймворков в целом, а этот вклад переоценить крайне трудно.

НО

Но жизнь не стоит на месте.
Веб становится динамичным, все большее и большее значение приобретают мобильные приложения, пользователю нужно потреблять контент «не отходя от кассы», то есть с экрана своего айфона, гугл нексуса, хуайвэя, впиши_название_своего_телефона.
Да и сами сайты нуждаются в качественно новых подходах к организации пользовательских взаимодействий и подаче контента.
AngularJS, Ember, Meteor.js, Derby.js — технологии, предвосхищающие очередной прорыв в сайтостроении, который можно сравнить с «изобретением» AJAX в старые добрые времена.
Ruby-разработчикам нужно мощное и в то же время легкое для освоения средство для разработки API, которым когда-то стали RoR для обычных сайтов.

Давайте уже к делу!

Действительно, хватит рассуждений. Встречайте — Grape
Это фреймворк, заточенный под разработку API, никаких швейцарских ножей.
Но надо отдать должное, он умеет делать API очень неплохо.
Попробую перечислить основные его достоинства:

  1. DSL, заточенный под описание API
  2. версионирование API из коробки
  3. параметризация методов со встроенной валидацией
  4. автоматическая генерация OPTIONS (кто встречался с CORS — оценит)
  5. прозрачная работа с форматами API
  6. встроенный DSL для документирования

вот далеко не полный перечень инструментов, которые облегчают жизнь разработчика API, когда он использует Grape.

Code time

Для начала приведу пример простого приложения, которое по адресу /hello/world.json вернет нам {hello: 'world'}

Gemfile

source 'https://rubygems.org'

gem 'grape', github: 'intridea/grape'
gem 'rack', '~> 1.5.2'
gem 'thin', '~> 1.6.2'

hello_world.rb

require 'grape'

class HelloWorld < Grape::API
  format :json
  namespace :hello do
    get :world do
      {hello: 'world'}
    end
  end
end

config.ru

require_relative 'hello_world'

run HelloWorld

На моем i5 c 16 Гб памяти и HDD это приложение стартует где-то за 400-700 мс. Вот список используемых гемов:

Using i18n 0.6.11
Using json 1.8.1
Using minitest 5.4.1
Using thread_safe 0.3.4
Using tzinfo 1.2.2
Using activesupport 4.1.6
Using descendants_tracker 0.0.4
Using ice_nine 0.11.0
Using axiom-types 0.1.1
Using builder 3.2.2
Using coercible 1.0.0
Using daemons 1.1.9
Using equalizer 0.0.9
Using eventmachine 1.0.3
Using hashie 3.3.1
Using multi_json 1.10.1
Using multi_xml 0.5.5
Using rack 1.5.2
Using rack-accept 0.4.5
Using rack-mount 0.8.3
Using virtus 1.0.3
Using grape 0.9.1 from git://github.com/intridea/grape.git (at master)
Using thin 1.6.2

Как вы могли заметить, в Grape есть чудесная штука, которая называется namespace. Она же group, resource, resources, segment — все для удобства чтения кода.
При этом она может использоваться без параметров. Казалось бы, зачем? А вот вам пример:

namespace :authorized do
    before { authorize! }
    get :some_secret_data ...
end
group do
    before { authorize! }
    get :some_secret_data ...
end

Это как в фильме — «все что случилось в Лас-Вегасе — остается в Лас-Вегасе».
Внутри групп, равно как нэймспейсов, вы можете определять before и after блоки, которые будут выполняться только для роутов, указанных в данных группах (и глубже).

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

params do
    requires :first_name, type: String
    requires :last_name, type: String
    optional :birth_date, type: DateTime 
end
post :register do
...
end

Как по мне, так понятно без слов. Управление в роут даже не попадет, если с запросом не будут переданы параметры, которые удовлетворяют описанным условиям. Самое чудесное, что это все с минимальными модификациями можно использовать для документирования API. Например:

desc 'User signup'
params do
    requires :first_name, type: String, desc: 'First name'
    requires :last_name, type: String, desc: 'Last name' 
    optional :birth_date, type: DateTime, desc: 'Date of birth'
end
post :register do
...
end

Убиваем сразу целую охапку зайцев — код документирован на месте, подключив grape-swagger получаем swagger-совместимую документацию. Изменили код — изменилась документация.

Одной из многих чудесных штук, которые меня покорили в Grape, является mount. Позволяет примонтировать ранее описанный API в новое место:

mount.rb

class Mount < Grape::API
  get :mounted do
    {mounted: true}
  end
end

mount.rb

require 'grape'
require_relative 'mount'
class HelloWorld < Grape::API
  format :json
  namespace :hello do
    mount Mount
    get :world do
      {hello: 'world'}
    end
  end
end

Как мы все уже поняли, наш роут из класса Mount станет доступен по адресу /hello/mounted.json

«Меня терзают смутные сомнения...»

Само собой, объема среднестатистической статьи, которая не вызовет у хабражителя стойкого зевотного рефлекса, вряд ли хватит, чтобы рассказать о всех плюсах и минусах фреймворка. Моей задачей в первую очередь было вызвать в вас интерес — документация у проекта неплохая, трудностей с дальнейшим изучением возникнуть не должно.
Также на гитхаб-страничке можно найти перечень гемов, которые можно использовать совместно с grape.

Эпилог

До недавнего времени у проекта была небольшая проблема, связанная с автоматической перезагрузкой измененного кода в dev-режиме. Все Rails-разработчики к этому привыкли и на мой взгляд это must have feature. В issues на гитхабе эта проблема была озвучена несколько раз и вроде предлагались какие-то решения на Rack::Reloader и для случаев использования совместно с Rails.
Я позволю себе упомянуть свое собственное решение, которое увидело свет буквально пару недель назад, а именно гем grape-reload, предназначенный для использования в plain-rack стеках.
Для grape версии 0.9.0 и ранее можно использовать версию гема 0.0.3, для более поздних и master-ветки фреймворка используйте master-ветку репозитория гема.

Если вам будут интересны дальнейшие статьи, посвященные данному фреймворку — не забудьте упомянуть об этом в комемнтариях. Всем ruby, посоны!

Автор: AMar4enko

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js