Као што Програмери Руби Он Раилс , често морамо да проширимо наше апликације АПИ-јевим крајњим тачкама како би подржавали ЈаваСцрипт тежак Богати Интернет клијентима или изворним иПхоне и Андроид апликацијама. Постоје и неки случајеви када је једина сврха апликације да опслужује иПхоне / Андроид апликације путем а ЈСОН ВАТРА.
У овом упутству ћу показати како се користи Грожђа - РЕСТ-сличан АПИ микро-оквир за Руби - за изградњу позадинске подршке у Раилс-у за ЈСОН АПИ. Грожђе је дизајнирано да ради као а мотор са сталком за уградњу до допуна наше веб апликације, без мешајући се са њима.
Случај примене на који ћемо се фокусирати за овај водич је апликација способна да ухвати и прегледа сесије програмирања у пару. Сама апликација биће написана за иОС у ОбјецтивеЦ-у и мораће да комуницира са позадинском услугом за чување и преузимање података. Наш фокус у овом упутству је на стварању робусне и сигурне позадинске услуге која подржава ЈСОН АПИ.
АПИ ће подржати методе за:
БЕЛЕШКА: Поред пружања могућности испитивања прегледа програмских сесија у пару, стварни АПИ би такође требало да обезбеди могућност подношења рецензија парног програмирања ради укључивања у базу података. Будући да подршка преко АПИ-ја превазилази опсег овог водича, једноставно ћемо претпоставити да је база података попуњена узорком скупа прегледа програма у пару.
Кључни технички захтеви укључују:
Такође, с обзиром да ће наша апликација морати да служи спољним клијентима, мораћемо да се бринемо о сигурности. Ка том циљу:
Користићемо Тест Дривен Девелопмент (ТДД) као наш приступ развоју софтвера који помаже у обезбеђивању детерминистичког понашања нашег АПИ-ја.
У сврху тестирања користићемо РСпец , добро познати оквир за тестирање у заједници РубиОнРаилс. Стога ћу се у овом чланку позвати на „спецификације“, а не на „тестове“.
ДО свеобухватна методологија испитивања састоји се и од „позитивних“ и „негативних“ тестова. Негативне спецификације ће одредити, на пример, како се понаша АПИ крајња тачка ако неки параметри недостају или су нетачни. Позитивне спецификације покривају случајеве када се АПИ исправно позива.
Спустимо темеље за наш позадински АПИ. Прво, морамо створити нову апликацију шина:
rails new toptal_grape_blog
Затим ћемо инсталирати РСпец додавањем rspec-rails
у наш гемфиле:
group :development, :test do gem 'rspec-rails', '~> 3.2' end
Затим из наше командне линије морамо покренути:
rails generate rspec:install
Такође можемо да искористимо неки постојећи софтвер отвореног кода за наш оквир за тестирање. Конкретно:
Корак 1: Додајте их у наш гемфиле:
... gem 'devise' ... group :development, :test do ... gem 'factory_girl_rails', '~> 4.5' ... end
Корак 2: Генеришите кориснички модел, иницијализујте devise
драгуљ и додајте га корисничком моделу (ово омогућава да се корисничка класа користи за потврду идентитета).
rails g model user rails generate devise:install rails generate devise user
Корак 3: Укључите factory_girl
метода синтаксе у нашем rails_helper.rb
датотеку како бисмо користили скраћену верзију креирања корисника у нашим спецификацијама:
RSpec.configure do |config| config.include FactoryGirl::Syntax::Methods
Корак 4: Додајте драгуљ грожђа у наш ДСЛ и инсталирајте га:
gem 'grape' bundle
Наша позадина ће морати да подржи основну могућност пријављивања. Створимо костур за нашег login_spec
, под претпоставком да се важећи захтев за пријављивање састоји од регистроване адресе е-поште и пара лозинке:
require 'rails_helper' describe '/api/login' do context 'negative tests' do context 'missing params' do context 'password' do end context 'email' do end end context 'invalid params' do context 'incorrect password' do end context 'with a non-existent login' do end end end context 'positive tests' do context 'valid params' do end end end
Ако недостаје било који од параметара, клијент треба да добије ХТТП повратни статусни код од 400 (тј. Лош захтев), заједно са поруком о грешци „недостаје е-пошта“ или „недостаје лозинка“.
За наш тест ћемо створити важећег корисника и поставити е-пошту и лозинку корисника као оригиналне параметре за овај тестни пакет. Тада ћемо прилагодити хеш овог параметра за сваку специфичност тако што ћемо изоставити лозинку / е-пошту или је заменити.
Креирајмо корисника и хеш параметара на почетку спецификације. Овај код ћемо ставити након блока опис:
describe '/api/login' do let(:email) { user.email } let(:password) { user.password } let!(:user) { create :user } let(:original_params) { { email: email, password: password } } let(:params) { original_params } ...
Затим можемо проширити контекст „недостајућих параметара“ / „лозинке“ на следећи начин:
let(:params) { original_params.except(:password) } it_behaves_like '400' it_behaves_like 'json result' it_behaves_like 'contains error msg', 'password is missing'
Али уместо да поновимо очекивања у контексту „е-поште“ и „лозинке“, можемо користити исте заједничке примере као и очекивања. Због тога морамо да коментаришемо ову линију у нашим rails_helper.rb
датотека:
Dir[Rails.root.join('spec/support/**/*.rb')].each
Затим морамо додати 3 РСпец дељена примера у spec/support/shared.rb
:
RSpec.shared_examples 'json result' do specify 'returns JSON' do api_call params expect { JSON.parse(response.body) }.not_to raise_error end end RSpec.shared_examples '400' do specify 'returns 400' do api_call params expect(response.status).to eq(400) end end RSpec.shared_examples 'contains error msg' do |msg| specify 'error msg is #{msg}' do api_call params json = JSON.parse(response.body) expect(json['error_msg']).to eq(msg) end end
Ови дељени примери позивају api_call
метода која нам омогућава да дефинишемо АПИ крајњу тачку само једном у нашој спецификацији (у складу са СУВИ принцип ). Ову методу дефинишемо на следећи начин:
describe '/api/login' do ... def api_call *params post '/api/login', *params end ...
Такође ћемо морати да прилагодимо фабрику за нашег корисника:
FactoryGirl.define do factory :user do password 'Passw0rd' password_confirmation sequence(:email) { |n| 'test#{n}@example.com' } end end
И на крају, пре покретања наших спецификација морамо покренути наше миграције:
rake db:migrate
Међутим, имајте на уму да спецификације у овом тренутку и даље неће успети, јер још увек нисмо применили крајњу тачку АПИ-ја. То је следеће.
За почетак ћемо написати празан скелет за наш АПИ за пријаву (app/api/login.rb
):
class Login Затим ћемо написати класу агрегатора која агрегира АПИ крајње тачке (app/api/api.rb
):
class API У реду, сада можемо да монтирамо наш АПИ у руте:
Rails.application.routes.draw do ... mount API => '/' ... end
Сада додајмо код да бисмо проверили да ли недостају параметри. Тај код можемо додати у api.rb
спасавањем из Grape::Exceptions::ValidationErrors
.
rescue_from Grape::Exceptions::ValidationErrors do |e| rack_response({ status: e.status, error_msg: e.message, }.to_json, 400) end
За неважећу лозинку проверићемо да ли је хттп одговор код 401 што значи неовлашћен приступ. Додајмо ово у контекст „нетачне лозинке“:
let(:params) { original_params.merge(password: 'invalid') } it_behaves_like '401' it_behaves_like 'json result' it_behaves_like 'contains error msg', 'Bad Authentication Parameters'
Затим се иста логика додаје и у контекст „са непостојећом пријавом“.
Затим имплементирамо логику која обрађује неважеће покушаје аутентификације у наш login.rb
као што следи:
post do user = User.find_by_email params[:email] if user.present? && user.valid_password?(params[:password]) else error_msg = 'Bad Authentication Parameters' error!({ 'error_msg' => error_msg }, 401) end end
У овом тренутку ће се све негативне спецификације за АПИ за пријављивање понашати исправно, али и даље морамо подржати позитивне спецификације за наш АПИ за пријаву. Наша позитивна спецификација очекиваће да ће крајња тачка вратити код ХТТП одговора од 200 (тј. Успех) са важећим ЈСОН-ом и важећим токеном:
it_behaves_like '200' it_behaves_like 'json result' specify 'returns the token as part of the response' do api_call params expect(JSON.parse(response.body)['token']).to be_present end
Додајмо и очекивање за код одговора 200 на spec/support/shared.rb
:
RSpec.shared_examples '200' do specify 'returns 200' do api_call params expect(response.status).to eq(200) end end
У случају успешне пријаве вратићемо прву важећу потврду идентитета_токен заједно са корисниковом е-поштом у овом формату:
{‘email’:, ‘token’:}
Ако још нема тог токена, креираћемо га за тренутног корисника:
... if user.present? && user.valid_password?(params[:password]) token = user.authentication_tokens.valid.first || AuthenticationToken.generate(user) status 200 else ...
Да би ово функционисало, требаће нам AuthenticationToken
класа која припада кориснику. Генерираћемо овај модел, а затим покренути одговарајућу миграцију:
rails g model authentication_token token user:references expires_at:datetime rake db:migrate
Такође морамо додати одговарајућу асоцијацију на наш кориснички модел:
class User Тада ћемо додати важећи опсег на AuthenticationToken
класа:
class AuthenticationToken { where (expires_at > Time.zone.now) } end
Имајте на уму да смо користили Руби синтаксу у where
изјава. То је омогућено нашом употребом squeel
драгуљ што омогућава подршку за Руби синтаксу у активним записима.
За потврђеног корисника креираћемо ентитет који ћемо називати „корисник са ентитетом токена“, користећи карактеристике grape-entity
драгуљ .
Напишимо спецификацију за наш ентитет и ставимо је у user_with_token_entity_spec.rb
датотека:
require 'rails_helper' describe Entities::UserWithTokenEntity do describe 'fields' do subject(:subject) { Entities::UserWithTokenEntity } specify { expect(subject).to represent(:email)} let!(:token) { create :authentication_token } specify 'presents the first available token' do json = Entities::UserWithTokenEntity.new(token.user).as_json expect(json[:token]).to be_present end end end
Следеће да додамо ентитете у user_entity.rb
:
module Entities class UserEntity И на крају, додајте још једну класу у user_with_token_entity.rb
:
module Entities class UserWithTokenEntity Будући да не желимо да токени остају на снази неограничено, истичемо након једног дана:
FactoryGirl.define do factory :authentication_token do token 'MyString' expires_at 1.day.from_now user end end
Након свега овога, сада можемо вратити очекивани ЈСОН формат са нашим ново написаним UserWithTokenEntity
:
... user = User.find_by_email params[:email] if user.present? && user.valid_password?(params[:password]) token = user.authentication_tokens.valid.first || AuthenticationToken.generate(user) status 200 present token.user, with: Entities::UserWithTokenEntity else ...
Хладан. Сада све наше спецификације пролазе и подржани су функционални захтеви основне крајње тачке апи за пријављивање.
Крајња тачка АПИ-ја за преглед сесије програмирања у пару: Први кораци
Наш бацкенд ће морати да дозволи овлашћен програмери који су се пријавили за упите у парове прегледа програмских сесија.
Наша нова АПИ крајња тачка биће монтирана на /api/pair_programming_session
и вратиће критике које припадају пројекту. Почнимо са писањем основног скелета за ову спецификацију:
require 'rails_helper' describe '/api' do describe '/pair_programming_session' do def api_call *params get '/api/pair_programming_sessions', *params end context 'invalid params' do end context 'valid params' do end end end
Написаћемо и одговарајућу празну крајњу тачку АПИ-ја (app/api/pair_programming_sessions.rb
):
class PairProgrammingSessions Затим монтирајмо наш нови апи (app/api/api.rb
):
... mount Login mount PairProgrammingSessions end
Проширимо спецификације и АПИ крајњу тачку према захтевима један по један.
Крајња тачка АПИ-ја за преглед сесије програмирања у пару: Провера
Један од наших најважнијих нефункционалних сигурносних захтева био је ограничити приступ АПИ-ју малом подскупу програмера које пратимо, па хајде да одредимо следеће:
... def api_call *params get '/api/pair_programming_sessions', *params end let(:token) { create :authentication_token } let(:original_params) { { token: token.token} } let(:params) { original_params } it_behaves_like 'restricted for developers' context 'invalid params' do ...
Тада ћемо створити shared_example
у нашем shared.rb
да потврдимо да захтев долази од једног од наших регистрованих програмера:
RSpec.shared_examples 'restricted for developers' do context 'without developer key' do specify 'should be an unauthorized call' do api_call params expect(response.status).to eq(401) end specify 'error code is 1001' do api_call params json = JSON.parse(response.body) expect(json['error_code']).to eq(ErrorCodes::DEVELOPER_KEY_MISSING) end end end
Такође ћемо морати да креирамо ErrorCodes
класа (у app/models/error_codes.rb
):
module ErrorCodes DEVELOPER_KEY_MISSING = 1001 end
Будући да очекујемо да ће се наш АПИ проширити у будућности, применићемо authorization_helper
који се могу поново користити на свим АПИ тачкама у апликацији да би се ограничио приступ само регистрованим програмерима:
class PairProgrammingSessions Дефинисаћемо методу restrict_access_to_developers
у ApiHelpers::AuthenticationHerlper
модул (app/api/api_helpers/authentication_helper.rb
). Ова метода ће једноставно проверити да ли је кључ Authorization
испод заглавља садржи важећи ApiKey
. (Сваки програмер који жели приступ АПИ-ју захтеваће важећу ApiKey
. То може да обезбеди администратор система или путем неког аутоматског поступка регистрације, али тај механизам превазилази опсег овог чланка.)
module ApiHelpers module AuthenticationHelper def restrict_access_to_developers header_token = headers['Authorization'] key = ApiKey.where{ token == my{ header_token } } Rails.logger.info 'API call: #{headers} With params: #{params.inspect}' if ENV['DEBUG'] if key.blank? error_code = ErrorCodes::DEVELOPER_KEY_MISSING error_msg = 'please aquire a developer key' error!({ :error_msg => error_msg, :error_code => error_code }, 401) # LogAudit.new({env:env}).execute end end end end
Затим треба генерисати АпиКеи модел и покренути миграције: раилс г модел апи_кеи токен раке дб: миграте
Након овога, у нашем spec/api/pair_programming_spec.rb
можемо да проверимо да ли је корисник потврђен идентитет:
... it_behaves_like 'restricted for developers' it_behaves_like 'unauthenticated' ...
Хајде да дефинишемо и unauthenticated
дељени пример који се може поново користити у свим спецификацијама (spec/support/shared.rb
):
RSpec.shared_examples 'unauthenticated' do context 'unauthenticated' do specify 'returns 401 without token' do api_call params.except(:token), developer_header expect(response.status).to eq(401) end specify 'returns JSON' do api_call params.except(:token), developer_header json = JSON.parse(response.body) end end end
Овом дељеном примеру потребан је токен у заглављу програмера, па додајмо то нашим спецификацијама (spec/api/pair_programming_spec.rb
):
... describe '/api' do let(:api_key) { create :api_key } let(:developer_header) { {'Authorization' => api_key.token} } ...
Сада, у нашем app/api/pair_programming_session.rb
, покушајмо да аутентификујемо корисника:
... class PairProgrammingSessions Применимо authenticate!
метода у AuthenticationHelper
(app/api/api_helpers/authentication_helper.rb
):
... module ApiHelpers module AuthenticationHelper TOKEN_PARAM_NAME = :token def token_value_from_request(token_param = TOKEN_PARAM_NAME) params[token_param] end def current_user token = AuthenticationToken.find_by_token(token_value_from_request) return nil unless token.present? @current_user ||= token.user end def signed_in? !!current_user end def authenticate! unless signed_in? AuditLog.create data: 'unauthenticated user access' error!({ :error_msg => 'authentication_error', :error_code => ErrorCodes::BAD_AUTHENTICATION_PARAMS }, 401) end end ...
(Имајте на уму да морамо да додамо код грешке BAD_AUTHENTICATION_PARAMS
у нашу класу ErrorCodes
.)
Даље, хајде да специфицирамо шта ће се догодити ако програмер позове АПИ са неважећим токеном. У том случају повратни код ће бити 401 који означава „неовлашћени приступ“. Резултат треба да буде ЈСОН и треба створити могућност ревизије. Дакле, додајемо ово у spec/api/pair_programming_spec.rb
:
... context 'invalid params' do context 'incorrect token' do let(:params) { original_params.merge(token: 'invalid') } it_behaves_like '401' it_behaves_like 'json result' it_behaves_like 'auditable created' it_behaves_like 'contains error msg', 'authentication_error' it_behaves_like 'contains error code', ErrorCodes::BAD_AUTHENTICATION_PARAMS end end ...
У spec/support/shared.rb
ћемо додати дељене примере „направљено за проверу“, „садржи код грешке“ и „садржи грешку о грешци“: |
... RSpec.shared_examples 'contains error code' do |code| specify 'error code is #{code}' do api_call params, developer_header json = JSON.parse(response.body) expect(json['error_code']).to eq(code) end end RSpec.shared_examples 'contains error msg' do |msg| specify 'error msg is #{msg}' do api_call params, developer_header json = JSON.parse(response.body) expect(json['error_msg']).to eq(msg) end end RSpec.shared_examples 'auditable created' do specify 'creates an api call audit' do expect do api_call params, developer_header end.to change{ AuditLog.count }.by(1) end end ...
Такође морамо створити модел аудит_лог:
rails g model audit_log backtrace data user:references rake db:migrate
Крајња тачка АПИ-ја за преглед сесије програмирања у пару: Враћање резултата
За аутентификованог и овлашћеног корисника, позив на ову АПИ крајњу тачку треба да врати скуп прегледа сесија програмирања у пару груписаних по пројекту. Изменимо наш spec/api/pair_programming_spec.rb
према томе:
... context 'valid params' do it_behaves_like '200' it_behaves_like 'json result' end ...
Ово прецизира да је захтев поднет са важећим api_key
и важећи параметри враћају ХТТП код од 200 (тј. успех) и да се резултат враћа у облику важећег ЈСОН-а.
Упитаћемо и вратити у ЈСОН формату оне сесије програмирања у пару где је било који од учесника тренутни корисник (app/api/pair_programming_sessions.rb
):
... get do sessions = PairProgrammingSession.where{(host_user == my{current_user}) | (visitor_user == my{current_user})} sessions = sessions.includes(:project, :host_user, :visitor_user, reviews: [:code_samples, :user] ) present sessions, with: Entities::PairProgrammingSessionsEntity end ...
Сесије програмирања парова моделиране су на следећи начин у бази података:
- Однос 1 према много између пројеката и програмских сесија у пару
- Однос 1 према многим између сесија програмирања у пару и прегледа
- Однос 1 према много између рецензија и узорака кода
Генеришимо моделе у складу са тим, а затим покренимо миграције:
rails g model project name rails g model pair_programming_session project:references host_user:references visitor_user:references rails g model review pair_programming_session:references user:references comment rails g model code_sample review:references code:text rake db:migrate
Тада треба да изменимо наш PairProgrammingSession
и Review
класе које садрже has_many
удружења:
class Review БЕЛЕШКА: У нормалним околностима генерирао бих ове класе тако што бих прво написао спецификације за њих, али пошто је то изван делокруга овог чланка, прескочићу тај корак.
Сада морамо да напишемо оне класе које ће трансформисати наше моделе у њихове ЈСОН репрезентације (које се називају ентитети грожђа терминологијом грожђа). Ради једноставности, користићемо мапирање 1 на 1 између модела и ентитета грожђа.
Почињемо излагањем code
поље са CodeSampleEntity
(у api/entities/code_sample_entity.rb
):
module Entities class CodeSampleEntity Затим излажемо user
и придружени code_samples
поновном употребом већ дефинисаног UserEntity
и CodeSampleEntity
:
module Entities class ReviewEntity Такође излажемо name
поље са ProjectEntity
:
module Entities class ProjectEntity Коначно, ентитет окупљамо у нови PairProgrammingSessionsEntity
где излажемо project
, host_user
, visitor_user
и reviews
:
module Entities class PairProgrammingSessionsEntity А тиме је и наш АПИ у потпуности примењен!
Генерисање података о испитивању
За потребе тестирања створићемо неке узорке података у db/seeds.rb
. Ова датотека треба да садржи све записе потребне за постављање базе података са њеним подразумеваним вредностима. Подаци се тада могу учитати са rake db:seed
(или креирано са дб када се позове db:setup
). Ево примера шта би ово могло да укључује:
user_1 = User.create email: ' [email protected] ', password: 'password', password_confirmation: 'password' user_2 = User.create email: ' [email protected] ', password: 'password', password_confirmation: 'password' user_3 = User.create email: ' [email protected] ', password: 'password', password_confirmation: 'password' ApiKey.create token: '12345654321' project_1 = Project.create name: 'Time Sheets' project_2 = Project.create name: 'ApeeScape Blog' project_3 = Project.create name: 'Hobby Project' session_1 = PairProgrammingSession.create project: project_1, host_user: user_1, visitor_user: user_2 session_2 = PairProgrammingSession.create project: project_2, host_user: user_1, visitor_user: user_3 session_3 = PairProgrammingSession.create project: project_3, host_user: user_2, visitor_user: user_3 review_1 = session_1.reviews.create user: user_1, comment: 'Please DRY a bit your code' review_2 = session_1.reviews.create user: user_1, comment: 'Please DRY a bit your specs' review_3 = session_2.reviews.create user: user_1, comment: 'Please DRY your view templates' review_4 = session_2.reviews.create user: user_1, comment: 'Please clean your N+1 queries' review_1.code_samples.create code: 'Lorem Ipsum' review_1.code_samples.create code: 'Do not abuse the single responsibility principle' review_2.code_samples.create code: 'Use some shared examples' review_2.code_samples.create code: 'Use at the beginning of specs'
Сада је наша апликација спремна за употребу и можемо да покренемо наш раилс сервер.
Тестирање АПИ-ја
Користићемо Сваггер да извршимо неко ручно тестирање нашег АПИ-ја засновано на прегледачу. Неколико корака за подешавање је потребно да бисмо могли да користимо Сваггер.
Прво, морамо додати неколико драгуља у наш гемфиле:
... gem 'grape-swagger' gem 'grape-swagger-ui' ...
Затим покрећемо bundle
да инсталирате ове драгуље.
Такође их морамо додати у имовину у наш цевовод имовине (у config/initializers/assets.rb
):
Rails.application.config.assets.precompile += %w( swagger_ui.js ) Rails.application.config.assets.precompile += %w( swagger_ui.css )
Коначно, у app/api/api.rb
морамо да монтирамо генератор разметања:
... add_swagger_documentation end ...
Сада можемо да искористимо Сваггеров леп кориснички интерфејс да истражимо наш АПИ једноставним одласком на http://localhost:3000/api/swagger
Сваггер представља наше АПИ крајње тачке на лепо истраживан начин. Ако кликнемо на крајњу тачку, Сваггер ће навести њене операције. Ако кликнемо на операцију, Сваггер приказује потребне и необавезне параметре и њихове типове података.
Преостали детаљ пре него што наставимо: Будући да смо ограничили употребу програмера за АПИ важећим api_key
, нећемо моћи приступити АПИ завршној тачки директно из прегледача, јер ће сервер захтевати важећу api_key
у заглављу ХТТП. То можемо постићи у сврху тестирања у Гоогле Цхроме-у коришћењем Измените заглавља за Гоогле Цхроме додатак . Овај додатак ће нам омогућити да изменимо ХТТП заглавље и додамо важећи api_key
(користићемо лажну api_key
од 12345654321
коју смо уврстили у нашу почетну датотеку базе података).
У реду, сада смо спремни за тестирање!
Да бисте позвали pair_programming_sessions
АПИ крајњу тачку, прво се морамо пријавити. Користићемо само комбинацију е-поште и лозинке из наше почетне датотеке базе података и послати је путем Сваггер-а до крајње тачке АПИ-ја за пријављивање, као што је приказано у наставку.

Као што видите горе, враћа се токен који припада том кориснику, што указује на то да АПИ за пријаву ради исправно како је предвиђено. Сада можемо да користимо тај токен за успешно извођење GET /api/pair_programming_sessions.json
операција.

Као што је приказано, резултат се враћа као правилно форматирани хијерархијски ЈСОН објекат. Приметите да ЈСОН структура одражава две угнежђене асоцијације 1 на више, јер пројекат има више прегледа, а преглед има више узорака кода. Ако не бисмо вратили структуру на овај начин, тада би позивалац нашег АПИ-ја морао засебно да захтева прегледе за сваки пројекат који би захтевао слање Н упита на нашу крајњу тачку АПИ-ја. Овом структуром решавамо проблем перформанси упита Н + 1.
Упаковати
Као што је овде приказано, свеобухватне спецификације за ваш АПИ помажу да се осигура да се имплементирани АПИ правилно и адекватно бави предвиђеним (и ненамерним!) Случајевима употребе.
Иако су примери АПИ-ја представљени у овом упутству прилично основни, приступ и технике које смо показали могу послужити као основа за софистицираније АПИ-је произвољне сложености помоћу Грожђа драгуљ. Надамо се да је ово упутство показало да је Грапе користан и флексибилан драгуљ који може помоћи у олакшавању примене ЈСОН АПИ-ја у вашим Раилс апликацијама. Уживати!