end

A instrução que começa com run! é usado para iniciar o aplicativo diretamente, com ruby demo_api.rb, assim como com o aplicativo clássico. Por outro lado, se o aplicativo for implementado com Rack, o conteúdo dos manipuladores de rackup.ru devemos ser:

require './demo_api' run DemoApi

Sequela

Sequel é a segunda ferramenta neste conjunto. Em contraste com ActiveRecord, que faz parte do Ruby on Rails, as dependências do Sequel são muito pequenas. Ao mesmo tempo, é bastante rico em recursos e pode ser usado para todos os tipos de tarefas de manipulação de banco de dados. Com sua linguagem simples de domínio específico, Sequel livra o desenvolvedor de todos os problemas com a manutenção de conexões, construção de consultas SQL, busca de dados (e envio de dados de volta) do banco de dados.

Por exemplo, estabelecer uma conexão com o banco de dados é muito simples:

DB = Sequel.connect(adapter: :postgres, database: 'my_db', host: 'localhost', user: 'db_user')

O método de conexão retorna um objeto de banco de dados, neste caso, Sequel::Postgres::Database, que pode ser usado posteriormente para executar SQL bruto.

DB['select count(*) from players']

Como alternativa, para criar um novo objeto de conjunto de dados:

DB[:players]

Ambas as instruções criam um objeto de conjunto de dados, que é uma entidade básica do Sequel.

Um dos recursos mais importantes do conjunto de dados Sequel é que ele não executa consultas imediatamente. Isso torna possível armazenar conjuntos de dados para uso posterior e, na maioria dos casos, encadea-los.

users = DB[:players].where(sport: 'tennis')

Portanto, se um conjunto de dados não atinge o banco de dados imediatamente, a questão é: quando isso ocorre? O Sequel executa SQL no banco de dados quando os chamados “métodos executáveis” são usados. Esses métodos são, para citar alguns, all, each, map, first e last.

O Sequel é extensível, e sua extensibilidade é resultado de uma decisão arquitetônica fundamental de construir um pequeno núcleo complementado com um sistema de plugins. Os recursos são facilmente adicionados por meio de plug-ins que, na verdade, são módulos Ruby. O plugin mais importante é o Model plugar. É um plugin vazio que não define nenhum método de classe ou instância por si só. Em vez disso, inclui outros plug-ins (submódulos) que definem uma classe, instância ou métodos de conjunto de dados de modelo. O plugin Model permite o uso do Sequel como a ferramenta object-relational-mapping (ORM) e é freqüentemente referido como o “plugin base”.

class Player

O modelo Sequel analisa automaticamente o esquema do banco de dados e configura todos os métodos de acesso necessários para todas as colunas. Ele assume que o nome da tabela está no plural e é uma versão sublinhada do nome do modelo. Caso haja necessidade de trabalhar com bancos de dados que não sigam esta convenção de nomenclatura, o nome da tabela pode ser explicitamente configurado quando o modelo for definido.

class Player

Portanto, agora temos tudo de que precisamos para começar a construir a API back-end.

Construindo a API

Estrutura do Código

Ao contrário do Rails, o Sinatra não impõe nenhuma estrutura de projeto. No entanto, como é sempre uma boa prática organizar o código para facilitar a manutenção e o desenvolvimento, faremos isso aqui também, com a seguinte estrutura de diretório:

project root |-config |-helpers |-models |-routes

A configuração do aplicativo será carregada do arquivo de configuração YAML para o ambiente atual com:

taxa horária w2 vs calculadora de salário
Sinatra::Application.config_file File.join(File.dirname(__FILE__), 'config', '#{Sinatra::Application.settings.environment}_config.yml')

Por padrão, Sinatra::Applicationsettings.environment o valor é development, e é alterado definindo RACK_ENV variável de ambiente.

Além disso, nosso aplicativo deve carregar todos os arquivos dos outros três diretórios. Podemos fazer isso facilmente executando:

%w{helpers models routes}.each {|dir| Dir.glob('#{dir}/*.rb', &method(:require))}

À primeira vista, essa forma de carregamento pode parecer conveniente. No entanto, com essa única linha do código, não podemos pular arquivos facilmente, porque isso carregará todos os arquivos dos diretórios na matriz. É por isso que usaremos uma abordagem de carregamento de arquivo único mais eficiente, que assume que em cada pasta temos um arquivo de manifesto init.rb, que carrega todos os outros arquivos do diretório. Além disso, adicionaremos um diretório de destino ao caminho de carregamento do Ruby:

%w{helpers models routes}.each do |dir| $LOAD_PATH << File.expand_path('.', File.join(File.dirname(__FILE__), dir)) require File.join(dir, 'init') end

Essa abordagem requer um pouco mais de trabalho porque temos que manter as instruções require em cada init.rb arquivo, mas, em troca, obtemos mais controle e podemos facilmente deixar um ou mais arquivos de fora, removendo-os do manifesto init.rb arquivo no diretório de destino.

Autenticação API

A primeira coisa que precisamos em cada API é autenticação. Vamos implementá-lo como um módulo auxiliar. A lógica de autenticação completa estará no helpers/authentication.rb Arquivo.

require 'multi_json' module Sinatra module Authentication def authenticate! client_id = request['client_id'] client_secret = request['client_secret'] # Authenticate client here halt 401, MultiJson.dump({message: 'You are not authorized to access this resource'}) unless authenticated? end def current_client @current_client end def authenticated? !current_client.nil? end end helpers Authentication end

Tudo o que precisamos fazer agora é carregar este arquivo adicionando uma instrução require no arquivo de manifesto auxiliar (helpers/init.rb) e chamar o authenticate! método em Sinatra before gancho que será executado antes de processar qualquer pedido.

atributo de classe vs atributo de instância python
before do authenticate! end

Base de dados

Em seguida, temos que preparar nosso banco de dados para o aplicativo. Existem muitas maneiras de preparar o banco de dados, mas como estamos usando o Sequel, é natural fazê-lo usando migradores. O Sequel vem com dois tipos de migrador - baseado em número inteiro e em carimbo de data / hora. Cada um tem suas vantagens e desvantagens. Em nosso exemplo, decidimos usar o migrador de carimbo de data / hora do Sequel, que exige que os arquivos de migração sejam prefixados com um carimbo de data / hora. O migrador de carimbo de data / hora é muito flexível e pode aceitar vários formatos de carimbo de data / hora, mas usaremos apenas aquele que consiste em ano, mês, dia, hora, minuto e segundo. Aqui estão nossos dois arquivos de migração:

# db/migrations/20160710094000_sports.rb Sequel.migration do change do create_table(:sports) do primary_key :id String :name, :null => false end end end # db/migrations/20160710094100_players.rb Sequel.migration do change do create_table(:players) do primary_key :id String :name, :null => false foreign_key :sport_id, :sports end end end

Agora estamos prontos para criar um banco de dados com todas as tabelas.

bundle exec sequel -m db/migrations sqlite://db/development.sqlite3

Finalmente, temos os arquivos de modelo sport.rb e player.rb no models diretório.

# models/sport.rb class Sport

Aqui estamos empregando uma maneira Sequel de definir relacionamentos de modelo, onde o Sport objeto tem muitos jogadores e Player pode ter apenas um esporte. Além disso, cada modelo define seu to_api método, que retorna um hash com atributos que precisam ser serializados. Esta é uma abordagem geral que podemos usar para vários formatos. No entanto, se usarmos apenas um formato JSON em nossa API, poderíamos usar Ruby’s to_json com only argumento para restringir a serialização aos atributos necessários, ou seja, player.to_json(only: [:id, :name, :sport_i]). Obviamente, também podemos definir um BaseModel que herda de Sequel::Model e define um padrão to_api método, do qual herdar todos os modelos poderiam então herdar.

Agora, podemos começar a implementar os endpoints reais da API.

Endpoints API

Manteremos a definição de todos os pontos de extremidade em arquivos dentro de routes diretório. Como estamos usando arquivos de manifesto para carregar arquivos, agruparemos as rotas por recursos (ou seja, manteremos todas as rotas relacionadas a esportes em sports.rb arquivo, todas as rotas de jogadores em routes.rb e assim por diante).

# routes/sports.rb class DemoApi

As rotas aninhadas, como aquela para obter todos os jogadores de um esporte /sports/:id/players, podem ser definidas colocando-as junto com outras rotas ou criando um arquivo de recurso separado que conterá apenas as rotas aninhadas.

Com as rotas designadas, o aplicativo agora está pronto para aceitar solicitações:

curl -i -XGET 'http://localhost:9292/sports?client_id=&client_secret='

Observe que, conforme exigido pelo sistema de autenticação do aplicativo definido em helpers/authentication.rb arquivo, estamos passando credenciais diretamente nos parâmetros de solicitação.

Relacionado: Tutorial Grape Gem: como construir uma API semelhante a REST em Ruby

Conclusão

Os princípios demonstrados neste aplicativo de exemplo simples se aplicam a qualquer aplicativo back-end da API. Não é baseado na arquitetura model-view-controller (MVC), mas mantém uma separação clara de responsabilidades de maneira semelhante; a lógica de negócios completa é mantida em arquivos de modelo enquanto o tratamento de solicitações é feito nos métodos de rotas de Sinatra. Ao contrário da arquitetura MVC, onde as visualizações são usadas para renderizar respostas, este aplicativo faz isso no mesmo local onde trata as solicitações - nos métodos de rotas. Com novos arquivos auxiliares, o aplicativo pode ser facilmente estendido para enviar paginação ou, se necessário, solicitar informações de limites de volta ao usuário em cabeçalhos de resposta.

No final, construímos uma API completa com um conjunto de ferramentas muito simples e sem perder nenhuma funcionalidade. O número limitado de dependências ajuda a garantir que o aplicativo carregue e inicie muito mais rápido e tenha uma pegada de memória muito menor do que um baseado em Rails teria. Então, da próxima vez que você começar a trabalhar em uma nova API em Rubi , considere usar o Sinatra e o Sequel, pois são ferramentas muito poderosas para esse caso de uso.