Airbnb-clone by ruby and rails part2
Airbnb-clone video4-6 setting users API
- icon
<div class="relative>
<div class="absolute inset-0 flex item-center" aria-hidden="true">
<div class="w-full border-gray-300"></div>
</div>
<div class="relative flex justify-center">
<span class="px-2 bg-white text-sm text-gray-500">Continue</span>
</div>
</div>
<div class="border border-gray-300 rounded-md px-3 py-2 shadow-sm focus-within:ring-1 focus-within:ring-indigo-600 focus-within:border-indigo-600">
<label for="name" class="block text-xs font-medium text-gray-900">Name</label>
<input type="text" name="name" id="name" class="block w-full border-0 p-0 text-gray-900 placeholder-gray-500 focus:ring-0 sm:text-sm" placeholder="Jan
</div>
Input Group
<!--
This example requires some changes to your config:
// tailwind.config.js module.exports = { // ... plugins: [ // ... require('@tailwindcss/forms'), ], }
-->
<div>
<label for="price" class="block text-sm font-medium leading-6 text-gray-900">Price</label>
<div class="relative mt-2 rounded-md shadow-sm">
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<span class="text-gray-500 sm:text-sm">$</span>
</div>
<input type="text" name="price" id="price" class="block w-full rounded-md border-0 py-1.5 pl-7 pr-20 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6" placeholder="0.00">
<div class="absolute inset-y-0 right-0 flex items-center">
<label for="currency" class="sr-only">Currency</label>
<select id="currency" name="currency" class="h-full rounded-md border-0 bg-transparent py-0 pl-2 pr-7 text-gray-500 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm">
<option>USD</option>
<option>CAD</option>
<option>EUR</option>
</select>
</div>
</div>
</div>
https://www.w3.org/TR/SVG/
https://heroicons.com/
6. Setting up the Users API
https://www.youtube.com/watch?v=8v202u1i1dg&list=PLCawOXF4xaJK1_-KVgXyREULRVy_W_1pe&index=6
- api directoryとuser controllerを作成
┌─(~/dev/monchifc)──────────────┐
└─(18:49:54 on feature-modal)──> mkdir app/controllers/api ──(Wed,Aug02)─┘
┌─(~/dev/monchifc)──────────────┐
└─(10:47:19 on feature-modal)──> touch app/controllers/api/users_controller.rb
config/routes.rb
Rails.application.routes.draw do
devise_for :users
root "home#index"
namespace :api do
resources :users, only: :show
end
end
app/controllers/api/users_controller.rb
module Api
class UsersController < ApplicationController
def show
end
end
end
- user_specでapiの動きをdefine
- RSpecでは、テストコードのことをスペック(仕様)と言う
┌─(~/dev/monchifc)──────────────┐
└─(11:29:09 on feature-modal ✭)──> mkdir spec/requests/api ──(Thu,Aug03)─┘
┌─(~/dev/monchifc)──────────────┐
└─(11:29:41 on feature-modal ✭)──> touch spec/requests/api/user_spec.rb
spec/requests/api/user_spec.rb
require 'rails_helper'
RSpec.describe "Api::Users", type: :request do
describe "GET show" do
context "user exists" do
it "is successful" do
end
end
context "user does not exist" do
it "is not found" do
end
end
end
end
- API用のcontroller routeを作成
bundle exec rails routes -c api/users
└─(12:04:50 on feature-modal ✹ ✭)──> bundle exec rails routes -c api/users
Prefix Verb URI Pattern Controller#Action
api_user GET /api/users/:id(.:format) api/users#show
modelを作ろうとしたらerror
└─(13:14:12 on feature-modal ✹ ✭)──> bundle exec rails g model user
invoke active_record
The name 'User' is either already used in your application or reserved by Ruby on Rails. Please choose an alternative or use --skip-collision-check or --force to skip this check and run this generator again.
- deviseで認証するためのモデルを作成
deviseとはrailsで作ったwebアプリケーションに簡単に認証機能を実装できるgem
┌─(~/dev/monchifc)──────────────┐
└─(09:51:25 on feature-api ✹)──> bundle exec rails g devise user
invoke active_record
create db/migrate/20230806055559_add_devise_to_users.rb
File unchanged! Either the supplied flag value not found or the content has already been inserted! app/models/user.rb
route devise_for :users
- DBはremoveする
└─(09:55:59 on feature-api ✹ ✭)──> rm db/migrate/20230806055559_add_devise_to_users.rb
- factory_bot_railsをsettingする
- RSpecのテストコードを効率化できるツール「FactoryBot」
spec/factories のdirectoryを作成。その下にemailやpasswordなどのuser sample dataをdescribeする
実際のテストコードではFactoryで定義したデータの内、テストしたい部分だけを上書きしていく
テストのたびにテストデータを用意する必要がないのでとても便利
┌─(~/dev/monchifc)──────────────┐
└─(14:35:43 on feature-modal ✹ ✭)──> mkdir spec/factories ──(Thu,Aug03)─┘
┌─(~/dev/monchifc)──────────────┐
└─(14:36:48 on feature-modal ✹ ✭)──> touch spec/factories/users.rb
https://github.com/thoughtbot/factory_bot_rails
- user_specのFactoryBotという文言を省略するための構造をhelper file上につくる
spec/rails_helper.rb
RSpec.configure do |config|
config.include FactoryBot::Syntax::Methods
- この雛形にsample dataを記載していく https://qiita.com/mmaumtjgj/items/39144c2e4314a798a0e6
spec/factories/users.rb
FactoryBot.define do
factory :user do
sequence(:email) { |i| "foo_#{i}@example.com" }
#↑これが呼び出されるたびに、iの部分に数字が一つずつ増えて入るため、一意性が保たれる
password { "password" }
end
end
- matcher sample
matcherは「期待値と実際の値を比較して、一致した(もしくは一致しなかった)という結果を返すオブジェクト」のこと。
・空であることを望んでいる
expect(user).to be_empty
# user.empty? が true になればパスする
・存在することを望んでいる
expect(user).to be_valid
# user.valid? が true になればパスする
・存在しないことを望んでいる/not_to(to_not)を使わない記入法
expect(user).to be_invalid
# user.invalid? が true になればパスする
spec/requests/api/users_spec.rb
require 'rails_helper'
RSpec.describe "Api::Users", type: :request do
describe "GET show" do
context "user exists" do
it "is successful" do
user = create(:user)
get api_user_path(user), headers:{ 'ACCEPT' => 'application/json' }
expect(response).to be_successful
end
end
context "user does not exist" do
it "is not found" do
end
end
end
end
- Test
└─(10:25:38 on feature-api ✖ ✹ ✭)──> bundle exec rspec spec/requests/api/users_spec.rb
//sample
user = FactoryBot.build(:user, nickname: "")
↓
user = build(:user, nickname: "")
- set rspec default get request format to json
https://stackoverflow.com/questions/11022839/set-rspec-default-get-request-format-to-json
let(:headers) do
{'ACCEPT' => 'application/json'}
headers: headers の追加が特徴的。
spec/requests/api/users_spec.rb
require 'rails_helper'
RSpec.describe "Api::Users", type: :request do
describe "GET show" do
let(:headers) do
{'ACCEPT' => 'application/json'}
end
context "user exists" do
it "is successful" do
user = create(:user)
get api_user_path(user), headers: headers
expect(response).to be_successful
end
end
context "user does not exist" do
it "is not found" do
end
end
end
end
- Test
└─(10:30:25 on feature-api ✖ ✹ ✭)──> bundle exec rspec spec/requests/api/users_spec.rb
app/controllers/api/users_controller.rb
module Api
class UsersController < ApplicationController
def show
user = User.find(params[:id])
respond_to do |format|
format.json do
render json: user.to_json, status: ok
end
end
end
end
└─(10:30:25 on feature-api ✖ ✹ ✭)──> bundle exec rspec spec/requests/api/users_spec.rb
spec/requests/api/user_spec.rb
require 'rails_helper'
RSpec.describe "Api::Users", type: :request do
describe "GET show" do
let(:headers) do
{'ACCEPT' => 'application/json'}
end
context "user exists" do
it "is successful" do
user = create(:user)
get api_user_path(user), headers: headers
expect(response).to be_successful
end
end
context "user does not exist" do
it "is not found" do
get api_user_path, id: "junk", headers: headers
expect(response).to be_not_found
end
end
end
end
https://stackoverflow.com/questions/17890419/when-to-display-record-not-found-page-or-return-http-404
require 'rails_helper'
RSpec.describe "Api::Users", type: :request do
describe "GET show" do
let(:headers) do
{'ACCEPT' => 'application/json'}
end
context "user exists" do
it "is successful" do
user = create(:user)
get api_user_path(user), headers: headers
expect(response).to be_successful
end
end
context "user does not exist" do
it "is not found" do
get api_user_path(id: "junk"), params: { id: "junk" }, headers: headers
expect(response).to eq 404
end
end
end
end
require 'rails_helper'
RSpec.describe "Api::Users", type: :request do
describe "GET show" do
let(:headers) do
{"ACCEPT" => "application/json"}
end
context "user exists" do
it "is successful" do
user = create(:user)
get api_user_path(user), headers: headers
expect(response).to be_successful
end
end
context "user does not exist" do
it "is not found" do
get api_user_path(id: "junk"), headers: headers
expect(response).to eq 404
end
end
end
end
- Test
└─(10:30:25 on feature-api ✖ ✹ ✭)──> bundle exec rspec spec/requests/api/users_spec.rb
Failure/Error: user = User.find(params[:id])
idのjunkは存在しないので Couldn't find User with id = junk
-
rescueメソッドを追加
app/controllers/api/users_controller.rb module Api class UsersController < ApplicationController def show user = User.find(params[:id]) respond_to do |format| format.json do render json: user.to_json, status: :ok end end rescue ActiveRecord::RecordNotFound => e binding.pry respond_to do |format| format.json do render json: { error: e.message }.to_json, status: 404 end end end end end
*** stringであるok に :ok の形でdefineしないとerrorになるので注意
└─(10:57:20 on feature-api ✖ ✹ ✭)──> bundle exec rspec spec/requests/api/users_spec.rb
F.
Failures:
1) Api::Users GET show user exists is successful
Failure/Error: render json: user.to_json, status: ok
NameError:
undefined local variable or method `ok' for #<Api::UsersController:0x0000000000cf30>
- Errorがなぜ起こるのか確認のためpry デバックツール 追加
- binding.pryはコード上にbinding.pryを記述することでbinding.pryの書かれている箇所までの処理を実行し、
binding.pryの書かれている箇所で処理を一時的に止めることができる。
https://github.com/pry/pry-rails
└─(17:06:26 on feature-modal ✹ ✭)──> bundle 1 ↵ ──(Thu,Aug03)─┘
Fetching gem metadata from https://rubygems.org/..........
Resolving dependencies...
Fetching coderay 1.1.3
Installing coderay 1.1.3
Fetching pry 0.14.2
Installing pry 0.14.2
Fetching pry-rails 0.3.9
Installing pry-rails 0.3.9
Bundle complete! 21 Gemfile dependencies, 93 gems now installed.
Bundled gems are installed into `./.bundle`
- rspecを実行するとpryのdebug consoleが立ち上がる
rescueをdebugしてErrorの原因確認
└─(10:45:13 on feature-api ✖ ✹ ✭)──> bundle exec rspec spec/requests/api/users_spec.rb
F
From: /Users/$HOME/dev/$PROJECTNAME/app/controllers/api/users_controller.rb:12 Api::UsersController#show:
3: def show
4: user = User.find(params[:id])
5:
6: respond_to do |format|
7: format.json do
8: render json: user.to_json, status: :ok
9: end
10: end
11: rescue ActiveRecord::RecordNotFound => e
=> 12: binding.pry
13:
14: respond_to do |format|
15: format.json do
16: render json: { error: e.message }.to_json, status: 404
17: end
18: end
19: end
[1] pry(#<Api::UsersController>)> e
=> #<ActiveRecord::RecordNotFound: Couldn't find User with 'id'=junk>
[2] pry(#<Api::UsersController>)> e.message
=> "Couldn't find User with 'id'=junk"
[3] pry(#<Api::UsersController>)> exit
.
debugしてもちゃんと動作している
- add status argument
require 'rails_helper'
RSpec.describe "Api::Users", type: :request do
describe "GET show" do
let(:headers) do
{"ACCEPT" => "application/json"}
end
context "user exists" do
it "is successful" do
user = create(:user)
get api_user_path(user), headers: headers
expect(response).to be_successful
end
end
context "user does not exist" do
it "is not found" do
get api_user_path(id: "junk"), headers: headers
expect(response.status).to eq 404
end
end
end
end
- Test success!!
└─(11:03:35 on feature-api ✖ ✹ ✭)──> bundle exec rspec spec/requests/api/users_spec.rb
..
Finished in 0.14728 seconds (files took 1.45 seconds to load)
2 examples, 0 failures
Got some error in Rails on sprockets precompilation step after tailwind build file generated
This is to prevent error caused by the sassc-rails gem when running-test
//config/environments/test.rb
#add
config.assets.css_compressor = nil
- solved