Aula 1: Back-end com Ruby On Rails – SpotCode | OneBitCode
• Planejamento – 00:02:39
• Criando o Projeto – 00:08:51
• Configurando o Devise – 00:15:25
• Gerando os Models – 00:20:41
• Preparando os Seeds – 00:32:16
• Criando o Endpoint de Discovery – 00:40:49
• Criando os Endpoints de Buscas – 01:01:40
• Criando os Endpoints de Categorias – 01:12:14
• Criando os Endpoints de Álbuns – 01:20:30
• Criando os Endpoints de Favoritos – 01:31:57
Preparando seu ambiente
Para conseguir executar com perfeição cada passo dado nesta série você precisa de alguns softwares instalados na sua máquina, eu recomendo que você siga este tutorial completo do GoRails para realizar a instalação deles: https://gorails.com/setup
- Escolha seu sistema operacional
- Instale as seguintes dependências apresentadas lá:
- Ruby
- Ruby On Rails
- Git
- NodeJs
- Yarn
Obs: Não é necessário instalar o PostgreSQL nem o MySQL
Obs2: Caso você tenha algum problema por não possuir o SQLite3 basta instalar seguindo: https://www.servermania.com/kb/articles/install-sqlite
Criando o Projeto
1. Instale a versão 6 do Rails
1 |
gem install rails -v '6.0.2.1' |
2. Crie o app
1 |
rails _6.0.2.1_ new spotcode |
3. Crie o controller Home
1 |
rails g controller home index |
4. Deixe o arquivo de rotas (_config/routes.rb_) com o seguinte conteúdo
1 2 3 |
Rails.application.routes.draw do root to: "home#index" end |
Estamos criando uma rota raiz para a action index do controller Home
5. Agora vamos iniciar o servidor
1 |
rails s |
6. E ir ao browser testar nossa rota raiz
Abrir o Browser e testar a URL localhost:3000
Configurando o Devise
1. Adicione no Gemfile
1 |
gem 'devise' |
2. Instale as gems com o comando
1 |
bundle install |
3. Vamos agora gerar os arquivos de configuração do Devise
1 |
rails g devise:install |
4. Vamos gerar o model que terá a autenticação rodando
1 |
rails generate devise User |
5. Execute as migrations
1 |
rails db:migrate |
6. Adicione o Hook do Devise em HomeController
1 2 3 4 5 |
class HomeController < ApplicationController before_action :authenticate_user! ... end |
7. Vamos iniciar o servidor e testar com o Postman
1 |
rails s |
Tentar acessar a página Home, que deve retornar erro
Cadastrar um usuário com a rota de SignUp
Gerando os Models
1. Execute o seguinte comando para instalar o Active Storage
1 |
rails active_storage:install |
Falar sobre os services que são configurados no config/storage.yml e como ele são escolhidos nas configurações de ambiente (config/enviroments)
2. Execute os seguintes comandos para gerar os outros models
1 2 3 4 5 6 |
rails g model artist name rails g model category name rails g model album title date:datetime category:references artist:references rails g model song title played_count:integer album:references rails g model recently_heard user:references album:references rails g model favorite user:references favoritable:references{polymorphic} |
3. Na migration de songs (_db/migrate/\_create_songs.rb), faça o seguinte:
Substitua a linha:
1 |
t.integer :played_count |
Por:
1 |
t.integer :played_count, default: 0 |
4. Rode as migration com o comando
1 |
rails db:migrate |
5. O model Album (_app/models/album.rb_) deve ficar com o seguinte conteúdo
1 2 3 4 5 6 7 8 9 10 |
class Album < ApplicationRecord belongs_to :category belongs_to :artist has_many :songs validates :title, presence: true validates :date, presence: true has_one_attached :cover end |
6. O model Artist (_app/models/artist.rb_) deve ficar com o seguinte conteúdo
1 2 3 4 5 6 |
class Artist < ApplicationRecord has_many :albums validates :name, presence: true has_one_attached :photo end |
7. O model Category (_app/models/category.rb_) deve ficar com o conteúdo
1 2 3 4 5 |
class Category < ApplicationRecord has_many :albums validates :name, presence: true has_one_attached :image end |
8. O model Song (_app/models/song.rb_) deve ficar com o conteúdo
1 2 3 4 5 |
class Song < ApplicationRecord belongs_to :album validates :title, presence: true has_one_attached :file end |
9. Também vamos adicionar o campo para o nome do usuário
1 |
rails g migration add_name_to_users name:string |
10. E o model User (_app/models/user.rb_) deve ficar com o conteúdo
1 2 3 4 5 6 7 8 9 |
class User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable has_many :favorites has_many :recently_heards validates :name, presence: true end |
Preparando os Seeds
1. Baixe este pacote de arquivos contendo as músicas e as imagens utilizadas no app
2. Após baixa, coloque dentro da pasta _tmp_ como nome _seed_files_. Ou seja, o caminho da pasta contendo os arquivos ficará _tmp/seeds_files_
3. Acrescente o seguinte conteúdo em _db/seeds.rb_
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
#======================== # SONGS #======================== tmp_dir = Rails.root.join("tmp") rock = Category.create(name: "Rock") rock.image.attach(io: File.open("#{tmp_dir}/seed_files/rock.png"), filename: "rock.png") blues = Category.create(name: "Blues") blues.image.attach(io: File.open("#{tmp_dir}/seed_files/blues.png"), filename: "blues.png") country = Category.create(name: "Country") country.image.attach(io: File.open("#{tmp_dir}/seed_files/country.png"), filename: "country.png") jazz = Category.create(name: "Jazz") jazz.image.attach(io: File.open("#{tmp_dir}/seed_files/jazz.png"), filename: "jazz.png") # ANDREW HOWS andrew_howes = Artist.create(name: "Andrew Howes") andrew_howes.photo.attach(io: File.open("#{tmp_dir}/seed_files/andrew_howes.jpg"), filename: "andrew_howes.jpg") gubernator = andrew_howes.albums.create(title: "Gubernator", date: Time.strptime("18/05/2015", "%d/%m/%Y"), category: rock) gubernator.cover.attach(io: File.open("#{tmp_dir}/seed_files/gubernator.jpg"), filename: "gubernator.jpg") dumb_luck = gubernator.songs.create(title: "Dumb Luck", played_count: Random.rand(1000)) dumb_luck.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/dumb_luck.mp3"), filename: "dumb_luck.mp3") helmsman = gubernator.songs.create(title: "Helmsman", played_count: Random.rand(1000)) helmsman.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/helmsman.mp3"), filename: "helmsman.mp3") crimea = gubernator.songs.create(title: "Crimea", played_count: Random.rand(1000)) crimea.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/crimea.mp3"), filename: "crimea.mp3") traitors_gate = gubernator.songs.create(title: "Traitors Gate", played_count: Random.rand(1000)) traitors_gate.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/traitors_gate.mp3"), filename: "traitors_gate.mp3") the_great_bear = andrew_howes.albums.create(title: "The Great Bear", date: Time.strptime("19/05/2015", "%d/%m/%Y"), category: rock) the_great_bear.cover.attach(io: File.open("#{tmp_dir}/seed_files/the_great_bear.jpg"), filename: "the_great_bear.jpg") big_drop = the_great_bear.songs.create(title: "Big Drop", played_count: Random.rand(1000)) big_drop.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/big_drop.mp3"), filename: "big_drop.mp3") four_am = the_great_bear.songs.create(title: "4am", played_count: Random.rand(1000)) four_am.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/4_am.mp3"), filename: "4_am.mp3") waiting = the_great_bear.songs.create(title: "Waiting", played_count: Random.rand(1000)) waiting.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/waiting.mp3"), filename: "waiting.mp3") # YELLOW CHAIR yellow_chair = Artist.create(name: "Yellow Chair") yellow_chair.photo.attach(io: File.open("#{tmp_dir}/seed_files/yellow_chair.jpg"), filename: "yellow_chair.jpg") barcelona = yellow_chair.albums.create(title: "Barcelona", date: Time.strptime("07/01/2008", "%d/%m/%Y"), category: rock) barcelona.cover.attach(io: File.open("#{tmp_dir}/seed_files/barcelona.jpg"), filename: "barcelona.jpg") via_laietana = barcelona.songs.create(title: "Via Laietana", played_count: Random.rand(1000)) via_laietana.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/via_laietana.mp3"), filename: "via_laietana.mp3") passeig_de_gracia = barcelona.songs.create(title: "Passeig de Gràcia", played_count: Random.rand(1000)) passeig_de_gracia.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/passeig_de_gracia.mp3"), filename: "passeig_de_gracia.mp3") itaca = barcelona.songs.create(title: "Itaca", played_count: Random.rand(1000)) itaca.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/itaca.mp3"), filename: "itaca.mp3") everything_zen = yellow_chair.albums.create(title: "Everything Zen", date: Time.strptime("01/07/2013", "%d/%m/%Y"), category: rock) everything_zen.cover.attach(io: File.open("#{tmp_dir}/seed_files/everything_zen.jpg"), filename: "everything_zen.jpg") everything_zen_music = everything_zen.songs.create(title: "Everything Zen", played_count: Random.rand(1000)) everything_zen_music.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/everything_zen.mp3"), filename: "everything_zen.mp3") meng_jia = yellow_chair.albums.create(title: "Meng Jia", date: Time.strptime("07/03/2014", "%d/%m/%Y"), category: rock) meng_jia.cover.attach(io: File.open("#{tmp_dir}/seed_files/meng_jia.jpg"), filename: "meng_jia.jpg") malvinas_go_go = meng_jia.songs.create(title: "Malvinas Go-Go", played_count: Random.rand(1000)) malvinas_go_go.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/malvinas_go_go.mp3"), filename: "malvinas_go_go.mp3") coronation_pope_francis = meng_jia.songs.create(title: "Coronation of Pope Francis", played_count: Random.rand(1000)) coronation_pope_francis.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/coronation_pope_francis.mp3"), filename: "coronation_pope_francis.mp3") # WAYLON THORNTON waylon_thornton = Artist.create(name: "Waylon Thornton") waylon_thornton.photo.attach(io: File.open("#{tmp_dir}/seed_files/waylon_thornton.jpg"), filename: "waylon_thornton.jpg") mystery_club = waylon_thornton.albums.create(title: "Mystery Club", date: Time.strptime("29/06/2011", "%d/%m/%Y"), category: rock) mystery_club.cover.attach(io: File.open("#{tmp_dir}/seed_files/mystery_club.jpg"), filename: "mystery_club.jpg") wobbly_way = mystery_club.songs.create(title: "Wobbly Way", played_count: Random.rand(1000)) wobbly_way.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/wobbly_way.mp3"), filename: "wobbly_way.mp3") very_hazel = mystery_club.songs.create(title: "Very Hazel", played_count: Random.rand(1000)) very_hazel.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/very_hazel.mp3"), filename: "very_hazel.mp3") favorite_secrets = mystery_club.songs.create(title: "Favorite Secrets", played_count: Random.rand(1000)) favorite_secrets.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/favorite_secrets.mp3"), filename: "favorite_secrets.mp3") # BREAK THE BANS break_the_bans = Artist.create(name: "Break the Bans") break_the_bans.photo.attach(io: File.open("#{tmp_dir}/seed_files/break_the_bans.jpg"), filename: "break_the_bans.jpg") covers_besides = break_the_bans.albums.create(title: "Covers & B-sides", date: Time.strptime("29/07/2013", "%d/%m/%Y"), category: blues) covers_besides.cover.attach(io: File.open("#{tmp_dir}/seed_files/covers_besides.jpg"), filename: "covers_besides.jpg") how_can_i_love_her = covers_besides.songs.create(title: "How can I love her", played_count: Random.rand(1000)) how_can_i_love_her.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/how_can_i_love_her.mp3"), filename: "how_can_i_love_her.mp3") # CULLAH cullah = Artist.create(name: "Cullah") cullah.photo.attach(io: File.open("#{tmp_dir}/seed_files/cullah.jpg"), filename: "cullah.jpg") be_love_not_fear = cullah.albums.create(title: "Be Love Not Fear", date: Time.strptime("29/07/2013", "%d/%m/%Y"), category: blues) be_love_not_fear.cover.attach(io: File.open("#{tmp_dir}/seed_files/be_love_not_fear.jpg"), filename: "be_love_not_fear.jpg") save_my_soul = be_love_not_fear.songs.create(title: "Save my Soul", played_count: Random.rand(1000)) save_my_soul.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/save_my_soul.mp3"), filename: "save_my_soul.mp3") who_am_i = be_love_not_fear.songs.create(title: "Who am I?", played_count: Random.rand(1000)) who_am_i.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/who_am_i.mp3"), filename: "who_am_i.mp3") jane_the_ripper = be_love_not_fear.songs.create(title: "Jane the Ripper", played_count: Random.rand(1000)) jane_the_ripper.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/jane_the_ripper.mp3"), filename: "jane_the_ripper.mp3") trinity = cullah.albums.create(title: "Trinity", date: Time.strptime("27/04/2016", "%d/%m/%Y"), category: blues) trinity.cover.attach(io: File.open("#{tmp_dir}/seed_files/trinity.jpg"), filename: "trinity.jpg") freed_from_greed = trinity.songs.create(title: "Freed from Greed", played_count: Random.rand(1000)) freed_from_greed.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/freed_from_greed.mp3"), filename: "freed_from_greed.mp3") aisling = trinity.songs.create(title: "Aisling", played_count: Random.rand(1000)) aisling.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/aisling.mp3"), filename: "aisling.mp3") # HANDMADE MOMENTS handmade_moments = Artist.create(name: "Handmade Moments") handmade_moments.photo.attach(io: File.open("#{tmp_dir}/seed_files/handmade_moments.jpg"), filename: "handmade_moments.jpg") paw_paw_tree = handmade_moments.albums.create(title: "Paw Paw Tree", date: Time.strptime("29/07/2013", "%d/%m/%Y"), category: country) paw_paw_tree.cover.attach(io: File.open("#{tmp_dir}/seed_files/paw_paw_tree.jpg"), filename: "paw_paw_tree.jpg") junkie = paw_paw_tree.songs.create(title: "Junkie", played_count: Random.rand(1000)) junkie.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/junkie.mp3"), filename: "junkie.mp3") fighting_a_mountain = paw_paw_tree.songs.create(title: "Fighting a Mountain", played_count: Random.rand(1000)) fighting_a_mountain.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/fighting_a_mountain.mp3"), filename: "fighting_a_mountain.mp3") wanderin_eyes = paw_paw_tree.songs.create(title: "Wanderin' Eyes", played_count: Random.rand(1000)) wanderin_eyes.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/wanderin_eyes.mp3"), filename: "wanderin_eyes.mp3") human_hands = paw_paw_tree.songs.create(title: "Human Hands", played_count: Random.rand(1000)) human_hands.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/human_hands.mp3"), filename: "human_hands.mp3") coffee_chocolate_earth = paw_paw_tree.songs.create(title: "Coffee, Chocolate, Earth", played_count: Random.rand(1000)) coffee_chocolate_earth.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/coffee_chocolate_earth.mp3"), filename: "coffee_chocolate_earth.mp3") # DEE YAN-KEY dee_yan_key = Artist.create(name: "Dee Yan-Key") dee_yan_key.photo.attach(io: File.open("#{tmp_dir}/seed_files/dee_yan_key.jpg"), filename: "dee_yan_key.jpg") years_and_years_ago = dee_yan_key.albums.create(title: "Years and Years Ago", date: Time.strptime("17/05/2011", "%d/%m/%Y"), category: jazz) years_and_years_ago.cover.attach(io: File.open("#{tmp_dir}/seed_files/years_and_years_ago.jpg"), filename: "years_and_years_ago.jpg") lazy = years_and_years_ago.songs.create(title: "Lazy", played_count: Random.rand(1000)) lazy.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/lazy.mp3"), filename: "lazy.mp3") snow = years_and_years_ago.songs.create(title: "Snow", played_count: Random.rand(1000)) snow.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/snow.mp3"), filename: "snow.mp3") grief = years_and_years_ago.songs.create(title: "Grief", played_count: Random.rand(1000)) grief.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/grief.mp3"), filename: "grief.mp3") clowns = years_and_years_ago.songs.create(title: "Clowns", played_count: Random.rand(1000)) clowns.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/clowns.mp3"), filename: "clowns.mp3") mans_life = dee_yan_key.albums.create(title: "A Man's Life", date: Time.strptime("17/09/2012", "%d/%m/%Y"), category: jazz) mans_life.cover.attach(io: File.open("#{tmp_dir}/seed_files/mans_life.jpg"), filename: "mans_life.jpg") life = mans_life.songs.create(title: "Life", played_count: Random.rand(1000)) life.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/life.mp3"), filename: "life.mp3") death_redemption = mans_life.songs.create(title: "Death & Redemption", played_count: Random.rand(1000)) death_redemption.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/death_redemption.mp3"), filename: "death_redemption.mp3") aldebaran = dee_yan_key.albums.create(title: "Aldebaran", date: Time.strptime("30/01/2012", "%d/%m/%Y"), category: jazz) aldebaran.cover.attach(io: File.open("#{tmp_dir}/seed_files/aldebaran.jpg"), filename: "aldebaran.jpg") antares, = aldebaran.songs.create(title: "Antares", played_count: Random.rand(1000)) antares.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/antares.mp3"), filename: "antares.mp3") # KING IMAGINE king_imagine = Artist.create(name: "King Imagine") king_imagine.photo.attach(io: File.open("#{tmp_dir}/seed_files/king_imagine.jpg"), filename: "king_imagine.jpg") inside = king_imagine.albums.create(title: "Inside", date: Time.strptime("30/09/2012", "%d/%m/%Y"), category: jazz) inside.cover.attach(io: File.open("#{tmp_dir}/seed_files/inside.jpg"), filename: "inside.jpg") ivy = inside.songs.create(title: "Ivy", played_count: Random.rand(1000)) ivy.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/ivy.mp3"), filename: "ivy.mp3") escape = inside.songs.create(title: "Escape", played_count: Random.rand(1000)) escape.file.attach(io: File.open("#{tmp_dir}/seed_files/musics/escape.mp3"), filename: "escape.mp3") #======================== # USERS #======================== freddie_mercury = User.create(name: "Freddie Mercury", email: "freddie@mercury.com", password: "123456", password_confirmation: "123456") freddie_mercury.favorites.create(favoritable: trinity) freddie_mercury.favorites.create(favoritable: paw_paw_tree) freddie_mercury.favorites.create(favoritable: everything_zen) freddie_mercury.favorites.create(favoritable: gubernator) freddie_mercury.recently_heards.create(album: inside) freddie_mercury.recently_heards.create(album: trinity) freddie_mercury.recently_heards.create(album: gubernator) freddie_mercury.recently_heards.create(album: the_great_bear) diana_ross = User.create(name: "Diana Ross", email: "diana@ross.com", password: "123456", password_confirmation: "123456") diana_ross.favorites.create(favoritable: trinity) diana_ross.recently_heards.create(album: the_great_bear) diana_ross.recently_heards.create(album: paw_paw_tree) diana_ross.recently_heards.create(album: aldebaran) michael_jackson = User.create(name: "Michael Jackson", email: "michael@jackson.com", password: "123456", password_confirmation: "123456") michael_jackson.favorites.create(favoritable: trinity) michael_jackson.favorites.create(favoritable: paw_paw_tree) celine_dion = User.create(name: "Celine Dion", email: "celine@dion.com", password: "123456", password_confirmation: "123456") celine_dion.favorites.create(favoritable: trinity) celine_dion.favorites.create(favoritable: everything_zen) |
4. Agora execute o comando para rodar os seeds
1 |
rails db:seed |
Criando o Endpoint de Discovery
1. Vamos criar o controller para o dashboard
1 |
rails g controller api/v1/dashboard |
Perceba que ele criou na estrutura e pastas api/v1 junto com os módulos de cada um
2. Para este controller, vamos criar uma action index para carregar as músicas. Adicione em app/controllers/api/v1/dashboard_controller.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
class Api::V1::DashboardController < ApplicationController def index load_recent_heard load_recommendations end private def load_recent_heard @recent_albums = current_user.recently_heards.order("created_at DESC").limit(4).map(&:album) end def load_recommendations heard_categories = @recent_albums.map(&:category) if heard_categories.present? @recommended_albums = Album.joins(:category, :songs).where(category: heard_categories).order("songs.played_count") .select("distinct albums.*").limit(12) else @recommended_albums = Album.all.limit(12) end end end |
Estamos carregando para o dashboard os álbuns ouvidos recentemente e as recomendações com base nas últimas categorias que o usuário ouviu
3. Com o controller criado, podemos adicionar as rotas. Adicione em config/routes.rb
1 2 3 4 5 6 7 8 9 |
Rails.application.routes.draw do ... namespace :api, defaults: { format: :json } do namespace :v1 do resources :dashboard, only: :index end end end |
Rotas foram adicionadas com o namespace api e v1 para que possamos isolar num contexto à parte rotas HTML
4. Para fazer a renderização JSON, utilizaremos o JBuilder. Para isso, vamos criar um arquivo de view com a extensão dele
Crie um arquivo app/views/api/v1/dashboard/index.json.jbuilder
5. Acrescente o seguinte conteúdo em app/views/api/v1/dashboard/index.json.jbuilder
1 2 3 4 5 6 7 8 9 10 11 12 13 |
json.recent_albums @recent_albums.each do |album| json.id album.id json.title album.title json.artist_name album.artist.name json.cover_url url_for(album.cover) end json.recommended_albums @recommended_albums.each do |album| json.id album.id json.title album.title json.artist_name album.artist.name json.cover_url url_for(album.cover) end |
6. Testar o endpoint
Criando os Endpoints de Buscas
1. Primeiro vamos analisar a página de busca em si. Ela precisará da lista de categorias. Então vamos começar a criar um endpoint proprio para isso. Digite no terminal
1 |
rails g controller api/v1/categories |
2. Acrescente o seguinte código em app/controllers/api/v1/categories_controller.rb
1 2 3 4 5 |
class Api::V1::CategoriesController < ApplicationController def index @categories = Category.all end end |
3. Para renderizar o JSON deste enpoint vamos criar o arquivo do JBuilder
Crie um arquivo app/views/api/v1/categories/index.json.jbuilder
4. Acrescente o seguinte conteúdo em app/views/api/v1/categories/index.json.jbuilder
1 2 3 4 5 |
json.categories @categories.each do |category| json.id category.id json.name category.name json.image_url url_for(category.image) end |
5. Para a busca, vamos criar um controller específico para ela. Digite no terminal
1 |
rails g controller api/v1/search |
6. Acrescente o seguinte em app/controllers/api/v1/search_controller.rb
1 2 3 4 5 6 7 |
class Api::V1::SearchController < ApplicationController def index @songs = Song.where("title LIKE ?", "%#{params[:query]}%") @albums = Album.where("title LIKE ?", "%#{params[:query]}%") @artists = Artist.where("name LIKE ?", "%#{params[:query]}%") end end |
7. Para renderizar o JSON deste enpoint de busca vamos criar o arquivo do JBuilder
Crie um arquivo app/views/api/v1/search/index.json.jbuilder
8. Acrescente o seguinte conteúdo em app/views/api/v1/search/index.json.jbuilder
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
json.songs @songs.each do |song| json.id song.id json.title song.title json.artist_name song.album.artist.name json.file_url url_for(song.file) end json.artists @artists.each do |artist| json.id artist.id json.name artist.name json.photo_url url_for(artist.photo) end json.albums @albums.each do |album| json.id album.id json.title album.title json.artist_name album.artist.name json.cover_url url_for(album.cover) end |
9. E por último, vamos acrescentar as rotas para listar as categorias e fazer a busca. Acrescente em config/routes.rb
1 2 3 4 5 6 7 8 9 10 11 |
Rails.application.routes.draw do ... namespace :api, defaults: { format: :json } do namespace :v1 do resources :dashboard, only: :index resources :search, only: :index resources :categories, only: :index end end end |
Criando os Endpoints de Categorias
1. Vamos começar adicionando alguns método no model Category. Adicione em app/models/category.rb
1 2 3 4 5 6 7 8 9 10 11 12 |
class Category < ApplicationRecord ... def artists Artist.joins(:albums).where(albums: { id: self.albums.ids }).distinct end def songs Song.joins(:album).where(songs: { id: self.albums.ids }).distinct end end |
Estamos adicionando dois método para retornar os artistas e as músicas de um determinada categoria.
2. Com o métodos em categoria, podemos adicionar uma action show no controller de categorias. Adicione em app/controllers/api/v1/categories_controller.rb
1 2 3 4 5 6 7 |
class Api::V1::CategoriesController < ApplicationController ... def show @category = Category.find(params[:id]) end end |
Estamos apenas buscando uma categoria por ID
3. Para retornar um objeto JSON que precisamos com o resultado, vamos criar um JBuilder para isso
Crie um arquivo app/views/api/v1/categories/show.json.jbuilder
4. Adicione o seguinte em app/views/api/v1/categories/show.json.jbuilder
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
json.name @category.name json.image_url url_for(@category.image) json.songs @category.songs.each do |song| json.id song.id json.title song.title json.artist_name song.album.artist.name json.file_url url_for(song.file) end json.artists @category.artists.each do |artist| json.id artist.id json.name artist.name json.photo_url url_for(artist.photo) end json.albums @category.albums.each do |album| json.id album.id json.title album.title json.artist_name album.artist.name json.cover_url url_for(album.cover) end |
5. Por último, vamos acrescentar rota para detalhar uma categoria. Em config/routes.rb:
Substitua:
1 |
resources :categories, only: :index |
Por:
1 |
resources :categories, only: %i[index show] |
Criando os Endpoints de Álbuns
1. Vamos começar criando um controller para o Álbum
1 |
rails g controller api/v1/albums |
2. Acrescente em app/controllers/api/v1/albums
1 2 3 4 5 |
class Api::V1::AlbumsController < ApplicationController def show @album = Album.find(params[:id]) end end |
3. Para renderizar os detalhes, vamos criar uma view JBuilder para isso
Crie uma view app/views/api/v1/albums/show.json.jbuilder
4. Adicione em app/views/api/v1/albums/show.json.jbuilder
1 2 3 4 5 6 7 8 |
json.title @album.title json.cover_url url_for(@album.cover) json.songs @album.songs.each do |song| json.id song.id json.title song.title json.file_url url_for(song.file) end |
5. Para o álbum tocado recentemente, criaremos um controller para que isso seja feito. Digite no terminal
1 |
rails g controller api/v1/recently_heards |
6. Crie um action :create em app/controllers/api/v1/recently_heads_controller.rb
1 2 3 4 5 6 7 |
class Api::V1::RecentlyHeardsController < ApplicationController def create @recently_heard = current_user.recently_heards.new(album_id: params[:album_id]) @recently_heard.save head :ok end end |
7. Para que o POST não falhe numa requisição JSON, acrescente o seguinte em ApplicationController
1 2 3 4 |
class ApplicationController < ActionController::Base before_action :authenticate_user! protect_from_forgery unless: -> { request.format.json? } end |
8. E para finalizar, vamos acrescentas as rotas para estas duas actions que criamos. Acrescente em config/routes.rb
1 2 3 4 5 6 7 8 9 10 11 12 |
Rails.application.routes.draw do ... namespace :api, defaults: { format: :json } do namespace :v1 do ... resources :albums, only: :show do resources :recently_heards, only: :create end end end end |
Criando os Endpoints de Favoritos
1. Vamos começar criando o controller para os favoritos. Digite no terminal
1 |
rails g controller api/v1/favorites |
2. Nos favoritos, vamos criar as actions para listar, adicionar e remover. Adicione em app/controllers/api/v1/favorites_controller.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
class Api::V1::FavoritesController < ApplicationController def index @favorite_albums = current_user.favorites.where(favoritable_type: "Album").map(&:favoritable) @favorite_songs = current_user.favorites.where(favoritable_type: "Song").map(&:favoritable) @favorite_artists = current_user.favorites.where(favoritable_type: "Artist").map(&:favoritable) end def create @favoritable = current_user.favorites.new(favoritable_type: params[:favoritable_type], favoritable_id: params[:id]) @favoritable.save head :ok end def destroy @favoritable = current_user.favorites.find_by(favoritable_type: params[:favoritable_type], favoritable_id: params[:id]) @favoritable.destroy head :ok end end |
Perceba que estamos utilizando bastante os recursos que a entidade polimórfica do Rails já nos traz. Na busca, estamos buscando tanto os Albuns, Música e Artistas favoritas utilizando como filtro o campo favoritable_type.
Para adicionar e remover não faremos direrente. Como queremos utilizar uma rota aninhada para favoritar, utilizamos tanto o id da entidade com a qual será aninhada quanto o favoritable_type que colocaremos como parâmetro fixo.
3. Agora nas rotas, utilizaremos um recurso chamado concern onde podemos criar um grupo de rotas que queremos aplicar em diversos contextos. Para isso, acrescente o seguinte com config/routes.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Rails.application.routes.draw do devise_for :users root to: "home#index" concern :favoritable do |options| shallow do post "/favorite", { to: "favorites#create", on: :member }.merge(options) delete "/favorite", { to: "favorites#destroy", on: :member }.merge(options) end end ... end |
4. Para podermos utilizar este recurso, faremos o seguinte também nas rotas. Acrescente em config/routes.rb
1 2 3 4 5 6 7 8 9 10 11 12 |
Rails.application.routes.draw do ... namespace :api, defaults: { format: :json } do namespace :v1 do ... resources :songs, only: [] do concerns :favoritable, favoritable_type: 'Song' end end end end |
Aquele concern que criamos anteriormente, estamos aninhando com uma rota songs. Ou seja, ela ficará como POST api/v1/songs/:id/favorite e DELETE api/v1/songs/:id/favorite e estas duas rotas receberão um parâmetro favoritable_type que terá o valor Song
Com isso temos as rotas para favoritar e desfavoritar uma música
5. Agora precisamos de mais uma rota que é para listar os favoritos, já que temos esta action lá no controller. Adicione em config/routes.rb
1 2 3 4 5 6 7 8 9 10 |
Rails.application.routes.draw do ... namespace :api, defaults: { format: :json } do namespace :v1 do ... resources :favorites, only: :index end end end |
6. E por último, vamos criar a view JSON
Crie um arquivo app/views/api/v1/favorites/index.json.jbuilder
7. Acrescente em app/views/api/v1/favorites/index.json.jbuilder
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
json.songs @favorite_songs.each do |song| json.id song.id json.title song.title json.artist_name song.album.artist.name json.file_url url_for(song.file) end json.artists @favorite_artists.each do |artist| json.id artist.id json.name artist.name json.photo_url url_for(artist.photo) end json.albums @favorite_albums.each do |album| json.id album.id json.title album.title json.artist_name album.artist.name json.cover_url url_for(album.cover) end |