OneBitFood - Aula 1 - Planejamento e API Rails - OneBitCode
OneBitFood - Aula 1 - Planejamento e API Rails - OneBitCode
Sobre
Contato
Cursos
Login
Sign Up
OneBitCode
OneBitCode
Sobre
Contato
Cursos
Home
OneBitFood • Aula 1: Planejamento e API Rails
1. 1. Planejamento • 00:02:07
2. 2. Criando o Projeto • 00:15:23
3. 3. Gerando os Models • 00:18:47
4. 4. Relacionamentos • 00:24:18
5. 5. Melhorando os Models • 00:28:24
6. 6. Gerando os Controllers e Rotas • 00:34:05
7. 7. Upload de Imagens • 00:37:15
8. 8. Serialização • 00:40:28
9. 9. Geolocalização • 00:50:51
10. 10. Controllers • 00:54:59
11. 11. Buscas • 01:01:41
12. 12. Controller Order • 01:04:07
13. 13. Cadastrando Dados de Teste • 01:10:35
14. 14. Testando a API • 01:13:59
Planejamento
A ideia inicial
Criar um APP inspirado no iFood usando Ruby On Rails como API e React como cliente Web
O Planejamento
Mockups
1. Ferramenta: Pencil
2. Material: https://drive.google.com/drive/folders/1Hlziu-vIbg476kLajGXrbOYpMFkUjmWp?usp=sharing
Endpoints
1. Ferramenta: Postman
2. Material: https://documenter.getpostman.com/view/5748264/S1EWQFdD
Dependências
• Ruby 2.5
• Ruby on Rails 5.2
• SQLite 3
Instalação
Criando o Projeto
Default
Default
1 cd onebitfood
Default
1 gem 'ransack'
2 gem 'geocoder', '~> 1.5', '>= 1.5.1'
3 gem 'active_model_serializers', '~> 0.10.2'
Default
1 bundle install
Banco de dados
Default
1 rails db:create
Gerando os Models
Default
Default
rails g model Restaurant name description:text status:integer delivery_tax:float state city street neighborhood number complement reference cep latitude:float
1
longitude:float category:references
Referências:
• Categoria
3. Para que os restaurantes possam gerenciar suas categorias de produtos, crie o model ProductCategory
Default
Referências:
• Restaurante
Default
Referências:
• Categoria do Produto
Default
1 rails g model Review value:integer restaurant:references
Referências:
• Restaurante
Default
Referências:
• Restaurante
Default
Referências:
• Pedido
• Produto
Migration
1. Vá até a Migration de criação da tabela Order e adicione um valor padrão para o status de um pedido
Default
2. Execute as migrations
Default
1 rails db:migrate
Relacionamentos
Default
1 has_many :restaurants
2. Um restaurante pode ter ‘N’ Categorias de Produtos, ‘N’ Pedidos e ‘N’ Avaliações
Faça o relcionamento estre essas tabelas adicionando ao model restaurant.rb
Default
1 has_many :product_categories
2 has_many :orders
3 has_many :reviews
Default
1 has_many :products
Default
1 has_many :order_products
Default
1 has_many :order_products
Melhorando os Models
Nesta aula você irá configurar as validações e enum para os seus Models.
Validações
Default
1 validates_associated :category
2 validates :name, presence: true
3 validates :status, presence: true
4 validates :delivery_tax, presence: true
5 validates :city, presence: true
6 validates :street, presence: true
Default
Default
1 validates :name, presence: true
2 validates :phone_number, presence: true
3 validates :total_value, presence: true
Default
1 validates_associated :order
2 validates_associated :product
3 validates :quantity, presence: true
Default
1 validates_associated :restaurant
2 validates :title, presence: true
Default
1 validates_associated :product_category
2 validates :name, presence: true
3 validates :price, presence: true
Default
1 validates_associated :restaurant
2 validates :value, inclusion: 1..5
Enums
1. Um restaurante pode estar Fechado ou Aberto. Para fazer este controle vá ao model Restaurante e adicione o seguinte enum
em app/models/restaurant.rb
Default
2. Um Pedido pode estar em andamento ou entregue. Para fazer este controle vá ao model Pedido e adicione o seguinte enum
Default
Default
Default
Default
Rotas
Default
Upload de Imagens
Nesta aula você utilizará o Active Storage para permitir que Categorias, Restaurantes, e Produtos tenham uma imagem
de apresentação.
Active Storage
Default
1 rails active_storage:install
Default
1 config.active_storage.service = :local
2 Rails.application.routes.default_url_options[:host] = 'localhost:3000'
Default
1 rails db:migrate
Models
Default
1 has_one_attached :image
Default
1 has_one_attached :image
Default
1 has_one_attached :image
Serialização
Nesta aula vamos configurar a Serialização de nossos Models no formato JSON. Dessa forma seremos capazes de enviar eles
de forma organizada.
Configuração
Default
1 touch config/initializers/ams.rb
Default
1 ActiveModelSerializers.config.adapter = :json
2 ActiveModel::Serializer.config.default_includes = '**'
Gerando os Arquivos
Default
Default
Default
Default
Default
Atributos da Exibição
Default
1 include Rails.application.routes.url_helpers
2 attributes :id, :title, :image_url
3
4 def image_url
5 rails_blob_url(object.image)
6 end
Default
1 class RestaurantSerializer < ActiveModel::Serializer
2 include Rails.application.routes.url_helpers
3
4 attributes :id, :name, :description, :review, :status, :delivery_tax, :state,
5 :city, :street, :neighborhood, :number, :complement,
6 :reference, :cep, :image_url, :category_title
7
8 has_many :product_categories, if: -> { @instance_options[:product_categories]}
9
10 def image_url
11 rails_blob_url(object.image)
12 end
13
14 def review
15 object.reviews&.average(:value)
16 end
17
18 def category_title
19 "cozinha #{object.category&.title}"
20 end
21 end
Obs: Foi acrescentado o category_title que não está na aula em vídeo, ele é responsável por devolver o título de
categoria do restaurante que será exibido para o usuário
Default
Default
1 include Rails.application.routes.url_helpers
2
3 attributes :id, :name, :description, :price, :image_url
4
5 def image_url
6 rails_blob_url(object.image)
7 end
Default
Geolocalização
Default
1 geocoded_by :address
2
3 after_validation :geocode
4
5 def address
6 [street, number, city, state].compact.join(', ')
7 end
Controllers
Default
1 @categories = Category.all.order(:title)
2 render json: @categories
Default
Default
1 def filter_by_category
2 @restaurants = @restaurants.select do |r|
3 r.category.title == params[:category]
4 end
5 end
Default
Default
Default
1 def set_restaurant
2 @restaurant = Restaurant.find_by(id: params[:id])
3 end
Buscas
Default
1 @restaurants = Restaurant.search(
2 name_or_description_cont: params[:q]
3 ).result
4 @restaurants = @restaurants.near(params[:city]) if params[:city]
5 render json: @restaurants
Controller Order
1. Adicione um callback para salvar o valor total do pedido. Vá até o model de Pedido e adicione o seguninte código em
app/models/order.rb
Default
1 before_validation :set_price
2
3 private
4
5 def set_price
6 @final_price = 0
7 order_products.each do |order_product|
8 product = Product.find order_product.product_id
9 @final_price += order_product.quantity * product.price
10 end
11
12 self.total_value = @final_price
13 end
2. Para permitir que os Itens de um Pedido sejam salvos junto ao Pedido utilize Nested Attributes. Vá até o model order.rb e
adicione a linha a seguir
Default
Default
1 class OrdersController < ApplicationController
2 before_action :set_order, only: :show
3
4 def create
5 order = Order.new(order_params)
6 if order.save
7 render json: @order, status: :created
8 else
9 render json: order.errors, status: :unprocessable_entity
10 end
11 end
12
13 def show
14 render json: @order
15 end
16
17 private
18
19 def set_order
20 @order = Order.find(params[:id])
21 end
22
23 def order_params
24 params.require(:order).permit(
25 :name, :phone_number, :restaurant_id,
26 order_products_attributes: %i[quantity comment product_id]
27 )
28 end
29 end
Nessa aula você incluira os dados que serão usados para teste
Default
Product.destroy_all
ProductCategory.destroy_all
Restaurant.destroy_all
1 Category.destroy_all
2
3 puts 'Criando Categorias'
4
5 path_image = 'public/images/categories/mexican.jpg'
6 c = Category.create(id: 1, title: 'mexicana')
7 c.image.attach(io: File.open(path_image), filename: 'mexican.jpg')
8
9 path_image = 'public/images/categories/italian.jpeg'
10 c = Category.create(id: 2, title: 'italiana')
11 c.image.attach(io: File.open(path_image), filename: 'italian.jpeg')
12
13 path_image = 'public/images/categories/japonese.jpeg'
14 c = Category.create(id: 3, title: 'japonesa')
15 c.image.attach(io: File.open(path_image), filename: 'japonese.jpeg')
16
17 path_image = 'public/images/categories/vegan.jpeg'
18 c = Category.create(id: 4, title: 'vegana')
19 c.image.attach(io: File.open(path_image), filename: 'vegan.jpeg')
20
21
22 path_image = 'public/images/categories/peruvian.jpg'
23 c = Category.create(id: 5, title: 'vegana')
24 c.image.attach(io: File.open(path_image), filename: 'peruana.jpg')
25
26
27 puts 'Cadastrando Restaurantes'
28
29 # Mexican Restaurants
30 path_image = 'public/images/restaurants/1.jpeg'
31 r = Restaurant.create!(
32 name: 'Los Sombreros',
33 description: 'Nossa missão tem sido ajudar as pessoas a alcançar seus objetivos de saúde e bem-estar. Embora tenhamos mudado ao longo dos anos, nossos
34 valores permaneceram os mesmos.',
35 status: 'open', delivery_tax: 5.50,
36 state: 'SP', city: 'São Paulo', street: 'Melo Barreto',
37 number: '1393', neighborhood: 'Brás', category_id: 1
38 )
39 r.image.attach(io: File.open(path_image), filename: '1.jpg')
40 pc = ProductCategory.create!(title: 'Pratos Mexicanos', restaurant: r)
41 prod = Product.create!(name: 'Nacho Guacamole', price: 19, description: 'Tortilhas com Guacamole', product_category: pc)
42 prod.image.attach(io: File.open('public/images/products/nachosg.jpg'), filename: 'nachosg.jpg')
43 prod = Product.create!(name: 'Nacho', price: 19, description: 'Tortilhas com milho', product_category: pc)
44 prod.image.attach(io: File.open('public/images/products/nachosg2.jpeg'), filename: 'nachosg2.jpeg')
45
46 path_image = 'public/images/restaurants/2.jpeg'
47 r = Restaurant.create!(
48 name: 'Ola Que Tal',
49 description: 'Para alcançar e manter essa distinção em comida e vinho, serviço, ambiente e ambiente, o restaurante ganha reputação de primeira classe por
50 gastronomia, hospitalidade graciosa e informada, conforto e beleza que atraem clientes novos e repetidos ano após ano.',
51 status: 'open', delivery_tax: 5.50,
52 state: 'SP', city: 'São Paulo', street: 'Viela Eugênio Monteiro Junior',
53 number: '659', neighborhood: 'Paraíso', category_id: 1
54 )
55 r.image.attach(io: File.open(path_image), filename: '2.jpg')
56 pc = ProductCategory.create!(title: 'Pratos Mexicanos', restaurant: r)
57 prod = Product.create!(name: 'Burrito', price: 19, description: 'Tortilhas com Guacamole', product_category: pc)
58 prod.image.attach(io: File.open('public/images/products/bt.jpg'), filename: 'bt.jpg')
59 prod = Product.create!(name: 'Quesadilha', price: 25, description: 'Tortilhas de queijo', product_category: pc)
60 prod.image.attach(io: File.open('public/images/products/quesa.jpeg'), filename: 'quesa.jpeg')
61
62 # Italian Restaurants
63 path_image = 'public/images/restaurants/3.jpeg'
64 r = Restaurant.create!(
65 name: 'Bravo',
66 description: 'Estamos empenhados em usar os melhores ingredientes em nossas receitas. Nenhum alimento deixa a nossa cozinha que nós mesmos não
67 comeríamos.',
68 status: 'open', delivery_tax: 3.50,
69 state: 'SP', city: 'São Paulo', street: 'Rua Coperema',
70 number: '250', neighborhood: 'Jardim Jaú (Zona Leste)', category_id: 2
71 )
72 r.image.attach(io: File.open(path_image), filename: '3.jpg')
73 pc = ProductCategory.create!(title: 'Porções', restaurant: r)
74 prod = Product.create!(name: 'Berinjela à parmegiana', price: 78, description: 'Com arroz e fritas', product_category: pc)
75 prod.image.attach(io: File.open('public/images/products/berinjela.jpg'), filename: 'berinjela.jpg')
76 prod = Product.create!(name: 'Fritas', price: 35, description: 'Bata frita com bacon', product_category: pc)
77 prod.image.attach(io: File.open('public/images/products/fritas.jpg'), filename: 'fritas.jpg')
78
79 path_image = 'public/images/restaurants/4.jpeg'
80 r = Restaurant.create!(
81 name: 'La Pergoletti',
82 description: 'Nossa missão é estabelecer relações comerciais benéficas com diversos fornecedores que compartilham nosso compromisso com o atendimento ao
83 cliente, qualidade e preços competitivos.',
84 status: 'open', delivery_tax: 6.70,
85 state: 'SP', city: 'São Paulo', street: 'Rua Joaquim Pinto',
86 number: '929', neighborhood: 'Vila Gomes Cardim', category_id: 2
87 )
88 r.image.attach(io: File.open(path_image), filename: '4.jpg')
89 pc = ProductCategory.create!(title: 'Fogazzas (Individuais)', restaurant: r)
90 prod = Product.create!(name: 'Fogazza Bacon', price: 12, description: 'Bacon, parmesão e mussarela.', product_category: pc)
91 prod.image.attach(io: File.open('public/images/products/fogazza.jpg'), filename: 'fogazza.jpg')
92 prod = Product.create!(name: 'Fogazza A moda da Casa', price: 12, description: 'Calabresa, bacon, palmito e mussarela.', product_category: pc)
93 prod.image.attach(io: File.open('public/images/products/fogazza.jpg'), filename: 'fogazza.jpg')
94
95 # Japonese Restaurants
96 path_image = 'public/images/restaurants/5.jpeg'
97 r = Restaurant.create!(
98 name: 'Sushi Eterno',
99 description: 'Existimos para garantir que cada hóspede receba um serviço rápido, profissional, amigável e cortês.',
100 status: 'open', delivery_tax: 7.50,
101 state: 'SP', city: 'São Paulo', street: 'Avenida Manoel Domingos Pinto',
102 number: '507', neighborhood: 'Parque Anhangüera', category_id: 3
103 )
104 r.image.attach(io: File.open(path_image), filename: '5.jpg')
105 pc = ProductCategory.create!(title: 'Entrada', restaurant: r)
106 prod = Product.create!(name: 'Temaki', price: 19.99, description: 'Enrolado de arroz com alga marinha em forma de cone', product_category: pc)
107 prod.image.attach(io: File.open('public/images/products/temaki.jpeg'), filename: 'temaki.jpeg')
108 prod = Product.create!(name: 'Sashimi', price: 30.90, description: 'Peixe cru fatiado, salmao, atum e peixe prego', product_category: pc)
109 prod.image.attach(io: File.open('public/images/products/sashimi.jpg'), filename: 'sashimi.jpg')
110
111 path_image = 'public/images/restaurants/6.jpeg'
112 r = Restaurant.create!(
113 name: 'Okuyamah',
114 description: 'Restaurante conceituado, vencedor por 5 vezes como melhor restaurante Japones de São Paulo.',
115 status: 'open', delivery_tax: 8.30,
116 state: 'SP', city: 'São Paulo', street: 'Rua Francisco Artassio',
117 number: '134', neighborhood: 'Jardim das Laranjeiras', category_id: 3
118 )
119 r.image.attach(io: File.open(path_image), filename: '6.jpg')
120 pc = ProductCategory.create!(title: 'Entrada', restaurant: r)
121 prod = Product.create!(name: 'Hossomaki 16 unidades', price: 20.90, description: 'Enrolado fino com folha de alga marinha por fora.', product_category: pc)
122 prod.image.attach(io: File.open('public/images/products/hosomaki.jpg'), filename: 'hosomaki.jpg')
123 prod = Product.create!(name: 'Hot roll - 10 unidades', price: 12, description: '10 unidades.', product_category: pc)
124 prod.image.attach(io: File.open('public/images/products/hot-holl.jpg'), filename: 'hot-holl.jpg')
125
126 # Vegan Restaurants
127 path_image = 'public/images/restaurants/7.jpeg'
128 r = Restaurant.create!(
129 name: 'Club Life',
130 description: 'NOSSA ESPECIALIDADE. pratos vegetais de alta qualidade,com opções de alimentos integrais, sem glúten e sem lactose.',
131 status: 'open', delivery_tax: 5.70,
132 state: 'SP', city: 'São Paulo', street: 'Alameda dos Uapês',
133 number: '933', neighborhood: 'Planalto Paulista', category_id: 4
134 )
135 r.image.attach(io: File.open(path_image), filename: '7.jpg')
136 pc = ProductCategory.create!(title: 'Saladas, molhos e wraps', restaurant: r)
137 prod = Product.create!(name: 'Coleslaw', price: 8.99, description: 'Repolho roxo, couve, cenoura, cebola, maionese de castanha e xylitol', product_category: pc)
138 prod.image.attach(io: File.open('public/images/products/coleslaw.jpg'), filename: 'coleslaw.jpg')
139 prod = Product.create!(name: 'Side salad', price: 9.90, description: 'Mix de folhas com cenoura ralada, tomatinho sweet e semente de girassol.', product_category:
140 pc)
141 prod.image.attach(io: File.open('public/images/products/side-salad.jpeg'), filename: 'side-salad.jpeg')
142
143 path_image = 'public/images/restaurants/8.jpeg'
144 r = Restaurant.create!(
145 name: 'Casa Natural',
146 description: 'Oferecemos, desde 1981, refeições ovo-lacto-vegetarianas, leves, saudáveis, balanceadas e principalmente saborosas, procurando aliar o sabor, a
147 qualidade de vida e o bem-estar dos clientes.',
148 status: 'open', delivery_tax: 8.30,
149 state: 'SP', city: 'São Paulo', street: 'Rua Natal',
150 number: '938', neighborhood: 'Cantinho do Céu', category_id: 4
151 )
152 r.image.attach(io: File.open(path_image), filename: '8.jpg')
153 pc = ProductCategory.create!(title: 'Saladas, molhos e wraps', restaurant: r)
154 prod = Product.create!(name: 'Salada de quinoa', price: 20.90, description: 'Alface americana, roxa, frisee, quinoa cozida, cenoura, tomate, damasco dessecado,
amendoa crua.', product_category: pc)
prod.image.attach(io: File.open('public/images/products/salada-de-quinoa.jpg'), filename: 'salada-de-quinoa.jpg')
prod = Product.create!(name: 'Coleslaw', price: 11, description: 'Repolho roxo, couve, cenoura, cebola, maionese de castanha e xylitol', product_category: pc)
prod.image.attach(io: File.open('public/images/products/coleslaw2.jpeg'), filename: 'coleslaw2.jpeg')
Default
1 rails db:seed
Testando a API
Conteúdo na videoaula.
OneBitCode
Contato
Cursos
Slack
WhatsApp
Parceiros
Vídeos de TI
Lucas Caton
Programathor
Negocie APP
Currículo Gráfico
Diário de um Back-End
Vagas para Programadores