20210729のRailsに関する記事は19件です。

[Rails]基本的なエラーを知ろう

はじめに 本記事では、 私が体験したエラーや、 調べた結果、おそらく基本的なエラーを共有します。 エラーの一覧 Name Error 定義されていない変数名を利用した場合に発生。 このエラーが発生したら、変数名を打ち間違えていないか、 変数を定義し忘れていないか確認する。 No Method Error メソッドを定義していないことで生じるエラー。 メソッドを定義している場所は、コントローラーであるため、 まず、各アクションのコードをみて、メソッドを定義しているかを確認する。 コントローラーにメソッドが定義されていた場合は、 コントローラアクションに関連したデータベース上のテーブルカラムを確認する。 カラムがなければ、カラムを追加する。 Argument Error メソッドの引数の数が合っていないときや、引数が空であるときに発生するエラー。 Argumentは、引数という意味。 Routing Error 「受け取ったURL」と「コントローラー内のアクション」が関係していないためのエラー。 原因として、config/routes.rbにてコードの書き間違いであることが多い印象。 Template is missing コントローラーで設定しているアクションの ビューが設定されていない場合に発生するエラー。 Railsでは、 「コントローラーのアクション名」と「ビューのファイル名」が 同名称になる事が基本で、 コントローラーのアクションとビューのファイル名に違いがある場合は コントローラーで明示的に記載する必要がある。 終わりに 自分が体験し、 おそらく基本的なエラーはこの辺かと思い、 アウトプットとして記事にしました。 エラーが出たらとにかく調べることが大切だと思いますが、 基本的なエラーは知っておく方が対処が早くできるのではないかと思いました。 エラーは出ないのが、一番ですが、 エラーはいつになっても付き纏うものです。 逃げずに積極的にエラーに向き合いましょう!!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【画像手順解説】Auth0 Rails(api) + React SPAでユーザー認証機能を実装しよう 2 (Rails編)

はじめに こんにちは、つよしと申します。転職のために、railsとreactでユーザー認証付きのSPAのポートフォリオを作成しました。 この記事では、Auth0 Rails(api) + React SPAでユーザー認証を実装する方法を解説します 初学者のため、間違っている情報があるかもしれません。 その場合は、ご指摘,もしくは適宜読み替えて勧めていただけたらと思います。 この記事は3部構成になっています。 【画像手順解説】Auth0 Rails(api) + React SPAでユーザー認証機能を実装しよう 2 (Rails編) → 本記事 この記事で最終的に出来ること Rails(API) + React SPAでユーザー認証機能を実装 current_user機能 Authenticate_user機能 新しいユーザーがサインアップした場合は自動でユーザーをクリエイト 個別での使用方法も学べるので、どちらか一方の実装する方も参考になると思います。 本記事で解説すること Rails(api)でのAuth0設定 current_user機能 Authenticate_user機能 新しいユーザーがサインアップした場合は自動でユーザーをクリエイト この記事でしないこと 機能を詳細に説明はせず、手順を丁寧に書いた記事にします。 参考となる記事を随所で貼るので、気になる方はそちらを御覧ください。 目次 Auth0とは? どのような機構? Rails Auth0設定 Rails アプリ設定 Auth0とは? どのような機構 Rails Auth0設定 基本的に以下の記事を参考にして制作します Rails Auth0設定 左のタブでApplication→APIsを選択し、右上のCreate APIをクリックしてください。 NameとIdentifierを入力してください。 Nameはアプリ名で大丈夫です Identifierはhttps://アプリ名-auth-apiにしておきましょう 今回はtestとしました。 signing AlgorithmはRS256のままで大丈夫です。 Identifierの値を控えておいてください。 入力できたらCreateをクリックします。 ドメインを控えます。Testタブに移動し、url(domain)を控えてください。 オレンジ枠の部分のみ控えてください。 → https://[TENANT_NAME].auth0.com/ 以上でAuth0の設定は終わりです。 Rails アプリ設定 Auth0 railsアプリを作成します console rails new api --api Gemfileに以下の3つを記述し、bundle installします gem 'dotenv-rails' gem 'jwt' gem 'rack-cors' console bundle install .envファイルを作成し、identifierとDomainを使用できるようにします。 ※ gitにpushする場合は.gitignoreに.envを記述してください console touch .env AUTH0_DOMAIN=[domain] # 例:https://hogehoge.auth0.com/ AUTH0_IDENTIFIER=[identifier] 例:https://test-auth-api ※ []はいりません。文字列のみ入力してください。 環境変数が正しく出力できるか確認します console irb(main):001:0> ENV['AUTH0_DOMAIN'] => "https://hogehoge.auth0.com/" irb(main):002:0> ENV['AUTH0_IDENTIFIER'] => "https://test-auth-api" ここから少し複雑になるので ディレクトリ構成の確認 コードをコピーしてタイプミス防止 以上の2点に気をつけてください。 各種ファイルについての役割は最後に説明します。 app配下にlibフォルダを作成します。 console mkdir app/lib app/lib/にjson_web_token.rbを作成します。 console touch app/lib/json_web_token.rb json_web_token.rbに以下を記述します。 app/lib/json_web_token.rb # app/lib/json_web_token.rb require 'net/http' require 'uri' class JsonWebToken def self.verify(token) JWT.decode(token, nil, true, # Verify the signature of this token algorithm: 'RS256', iss: ENV['AUTH0_DOMAIN'], verify_iss: true, aud: ENV['AUTH0_IDENTIFIER'], verify_aud: true) do |header| jwks_hash[header['kid']] end end def self.jwks_hash jwks_raw = Net::HTTP.get URI("#{ENV['AUTH0_DOMAIN']}.well-known/jwks.json") jwks_keys = Array(JSON.parse(jwks_raw)['keys']) Hash[ jwks_keys .map do |k| [ k['kid'], OpenSSL::X509::Certificate.new( Base64.decode64(k['x5c'].first) ).public_key ] end ] end end app配下にservicesフォルダを作成します console mkdir app/services app/services/にauthorization_service.rbを作成します console touch app/services/authorization_service.rb authorization_service.rbに以下を記述します app/services/authorization_service.rb class AuthorizationService def initialize(headers = {}) @headers = headers end def current_user @auth_payload, @auth_header = verify_token @user = User.from_token_payload(@auth_payload) end private def http_token @headers['Authorization'].split(' ').last if @headers['Authorization'].present? end def verify_token JsonWebToken.verify(http_token) end end app/controllersにsecured_controllersを作成します console touch app/controllers/secured_controller.rb securedcontroller.rbに以下を記述します app/controllers/secured_controller.rb class SecuredController < ApplicationController before_action :authorize_request private def authorize_request authorize_request = AuthorizationService.new(request.headers) @current_user = authorize_request.current_user rescue JWT::VerificationError, JWT::DecodeError render json: { errors: ['Not Authenticated'] }, status: :unauthorized end end config/initializers/wrap_parameters.rbを以下に変更 config/initializers/wrap_parameters.rb wrap_parameters format: [] 以上でAuth0に必要な設定が終わりました。 これで、以下を実装できています。 - current_user機能 - Authenticate_user機能 - 新規サインアップ時にユーザーを自動でcreate 各種ファイルの役割について軽く説明します。 json_web_token.rb HTTPリクエストのheaderに添付されたtoken情報を解析し、Auth0の情報と照らし合わせて、人間が読めるユーザー情報に変換します。 authorization_service.rb ■ def verify_token json_web_token.rbを実行し、tokenを渡しています。 ■ def current_user User.from_token_payload(@auth_payload)で、ユーザーモデルのメソッドを実行し、ユーザーcreateもしくはユーザー情報を返します。 ユーザーモデルのメソッドは後で実装します。 secured_controller.rb ApplicationControllerを継承し、すべてのコントローラー実行前にauthorize_requestを実行します。 tokenを解析した結果、ユーザー認証出来なかった場合は errors: ['Not Authenticated'] }, status: :unauthorized を返します。 これで、Authenticate_user機能(deviseとは逆ですが...) が実装できました。 Rails アプリ設定 通信確認 それでは実際にモデルを作成して、通信をしてみます。 ユーザーモデルとポストモデルを作成します。 ユーザーとポストは 1:多で構成します console rails g model User sub:string rails g model Post user:references title:string caption:text rails db:migrate user.rbに以下を記述してください。 user.rb class User < ApplicationRecord has_many :posts, dependent: :destroy def self.from_token_payload(payload) find_by(sub: payload['sub']) || create!(sub: payload['sub']) end end ■ def self.from_token_payload(payload) token情報を参照し、userが存在する場合はuserを返す、存在しない場合はuserをcreateします。 posts_controllerを作成します console rails g controller api/v1/posts posts_controller.rbを以下の様に編集します posts_controller.rb class Api::V1::PostsController < SecuredController #SecuredContollerを継承する skip_before_action :authorize_request, only: [:index,:show] def index posts = Post.all render json: posts end def show post = Post.find(params[:id]) render json: post end def create post = @current_user.posts.build(post_params) if post.save render json: post else render json: post.errors, status: :unprocessable_entity end end def destroy post = Post.find(params[:id]) post.delete end private def post_params params.permit(:title,:caption) end end ■ skip_before_action ユーザー認証をスキップするアクションを記入します。 今回はindex,showアクションのみユーザー認証をスキップします。 routes.rbを以下の様に記述します routes.rb Rails.application.routes.draw do namespace :api do namespace :v1 do resources :posts end end end いよいよAPI通信確認です!! auth0のサイトで、テスト用のtokenをコピーしましょう オレンジ枠のaccess_tokenの値のみをコピーしてください。 railsサーバーを立ち上げます console rails s 別のターミナルで立ち上げて、以下のGETリクエストを実行します。 console curl http://localhost:3000/api/v1/posts → [] postsにはまだなにも入っていないので、空のデータが帰ってきます。 以下のPOSTリクエストを実行します。 console curl -H "Content-Type: application/json" -d '{"title":"タイトル1", "caption":"説明1"}' -X POST http://localhost:3000/api/v1/posts → {"errors":["Not Authenticated"]} headerにtokenを載せていないので、エラーが帰ってきます!! ちゃんとユーザー認証出来ていますね。 それでは最後にtokenを載せてPOSTリクエストを実行します。 [ACCESS_TOKEN]のところにコピーしたtokenを挿入してください。 console curl -H "Content-Type: application/json" -H "Authorization: bearer [ACCESS_TOKEN]" -d '{"title":"タイトル1", "caption":"説明1"}' -X POST http://localhost:3000/api/v1/posts 以下の様なレスポンスが返ってくれば成功です!! rails_log User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."sub" = ? LIMIT ? [["sub", "ZGLom4PI88yF3FvKd4tctFgKldVNDxHZ@clients"], ["LIMIT", 1]] ↳ app/models/user.rb:4:in `from_token_payload' (0.1ms) begin transaction ↳ app/models/user.rb:4:in `from_token_payload' User Create (0.6ms) INSERT INTO "users" ("sub", "created_at", "updated_at") VALUES (?, ?, ?) [["sub", "ZGLom4PI88yF3FvKd4tctFgKldVNDxHZ@clients"], ["created_at", "2021-07-29 08:50:47.913221"], ["updated_at", "2021-07-29 08:50:47.913221"]] ↳ app/models/user.rb:4:in `from_token_payload' (1.5ms) commit transaction # UserがCreateされている ↳ app/models/user.rb:4:in `from_token_payload' (0.1ms) begin transaction ↳ app/controllers/api/v1/posts_controller.rb:17:in `create' Post Create (0.5ms) INSERT INTO "posts" ("user_id", "title", "caption", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?) [["user_id", 3], ["title", "タイトル1"], ["caption", "説明1"], ["created_at", "2021-07-29 08:50:47.930796"], ["updated_at", "2021-07-29 08:50:47.930796"]] ↳ app/controllers/api/v1/posts_controller.rb:17:in `create' (1.0ms) commit transaction ↳ app/controllers/api/v1/posts_controller.rb:17:in `create' Completed 200 OK in 563ms (Views: 0.4ms | ActiveRecord: 6.5ms | Allocations: 13691 最初にUserがCreateされている PostがCreateされている 以上2点を確認してください!! もう一度GETリクエストしてみましょう。 console curl http://localhost:3000/api/v1/posts → [{"id":1,"user_id":1,"title":"タイトル1","caption":"説明1","created_at":"2021-07-29T08:57:09.782Z","updated_at":"2021-07-29T08:57:09.782Z"}] 以上の様に返ってくれば成功です!! お疲れ様でした!! Rails(api)でのAuth0を使用したユーザー認証を機能を実装することが出来ました。 次は を御覧ください~
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【画像手順解説】Auth0 Rails(api) + React SPAでユーザー認証機能を実装しよう 3 (Rails(api) + React編)

はじめに こんにちは、つよしと申します。転職のために、railsとreactでユーザー認証付きのSPAのポートフォリオを作成しました。 この記事では、Auth0 Rails(api) + React SPAでユーザー認証を実装する方法を解説します 初学者のため、間違っている情報があるかもしれません。 その場合は、ご指摘,もしくは適宜読み替えて勧めていただけたらと思います。 この記事は3部構成になっています。 【画像手順解説】Auth0 Rails(api) + React SPAでユーザー認証機能を実装しよう 3 (Rails(api) + React編) → 本記事 この記事で最終的に出来ること Rails(API) + React SPAでユーザー認証機能を実装 current_user機能 Authenticate_user機能 新しいユーザーがサインアップした場合は自動でユーザーをクリエイト 個別での使用方法も学べるので、どちらか一方の実装する方も参考になると思います。 本記事で解説すること Rails(api) + Reactでユーザー認証を実装する 簡単なRails + ReactのCRUD機能 注:part1とpart2を読んでから本記事に取り組んでください。 この記事でしないこと 機能を詳細に説明はせず、手順を丁寧に書いた記事にします。 参考となる記事を随所で貼るので、気になる方はそちらを御覧ください。 目次 Auth0とは? どのような機構? Rails アプリ設定 React アプリ設定 Auth0とは? どのような機構 Rails アプリ設定 cors.rbに以下を記述します config/initializers/cors.rb Rails.application.config.middleware.insert_before 0, Rack::Cors do allow do origins 'http://localhost:3001' resource '*', headers: :any, methods: %i[get post put patch delete options head] end end React アプリ設定 .envファイルにAUDIENCEを追加します AUDIENCEの値はpart2で控えたIdentifierです。 本記事の場合は https://test-auth-apiとなります。 /.env PORT=3001 REACT_APP_AUTH0_DOMAIN=[Domain] REACT_APP_AUTH0_CLIENT_ID=[Client ID] REACT_APP_AUTH0_AUDIENCE=[Identifier] // 追加 例: https://test-auth-api REACT_APP_REST_URL="http://localhost:3000/api/v1" ※ []はいりません。文字列のみ入力してください。 index.tsxにaudienceを追加します。 src/index.tsx ... const domain: any = process.env.REACT_APP_AUTH0_DOMAIN const clientId: any = process.env.REACT_APP_AUTH0_CLIENT_ID const audience: any = process.env.REACT_APP_AUTH0_AUDIENCE // 追加 ReactDOM.render( <Auth0Provider domain={domain} clientId={clientId} audience={audience} // 追加 redirectUri={window.location.origin} > <Provider store={store}> <App /> </Provider> </Auth0Provider>, document.getElementById('root') ... 以上でAuth0の設定は終わりです。 次からはrailsとreactの通信に入っていきます。 通信の流れ 1.React側でtokenを取得 2.Rails(api)にtoken付きでリクエストを投げる 3.Rails(api)でcreate 以上のような流れで行います。 通信をするために、axiosをインストールします。 console npm install axios --save React側でtokenを取得 ここからはログインした状態で進めてください。 /App.tsx import React, { useEffect, useState } from 'react'; import './App.css'; import { useAuth0 } from "@auth0/auth0-react"; function App() { // getAccessTokenSilentlyを追加 const { isAuthenticated,loginWithRedirect,logout,getAccessTokenSilently } = useAuth0(); //追加 const [token,setToken] = useState<string>('') // 追加 useEffect(() => { const getToken = async () => { try { const accessToken = await getAccessTokenSilently({}) setToken(accessToken) } catch (e) { console.log(e.message) } } getToken() }, []) ■ getAccessTokenSilently Auth0の関数で、ログインしている場合にtokenを取得することが出来ます。 取得したtokenはuseStateのtokenに格納しています。 index railsとreactの通信がちゃんと出来ているか確認するために、postsの投稿を取得してみます。 indexアクションはskip_before_actionで指定しているので、tokenは必要ありません。 今回は簡単に、ボタンで取得する形にします。 /App.tsx import React, { useEffect, useState } from 'react'; import './App.css'; import { useAuth0 } from "@auth0/auth0-react"; import axios from 'axios'; function App() { const { isAuthenticated,loginWithRedirect,logout,getAccessTokenSilently } = useAuth0(); const [token,setToken] = useState<string>('') // 追加 const [posts,setPosts] = useState<any>() // 追加 const fetchPosts = () => { axios.get('http://localhost:3000/api/v1/posts') .then((res) => { setPosts(res.data) }) } ... return ( <div className="App"> ... //追加 <h2>投稿一覧</h2> <button onClick={fetchPosts}>投稿取得</button> {posts?.map((post :any,index:number) => <div key={index}> <p>{post.title}</p> <p>{post.caption}</p> </div> )} </div> </div> ); } export default App; ボタンをクリックして、タイトル1,説明1が表示されれば成功です。 表示されない場合は検証ツールでconsole.logやNewWork,railsのログをみて解消してください。 create それでは最後にtoken付きのリクエストを送って投稿を作成したいと思います。 /App.tsx ... // 追加 const createPosts = () => { const headers = { headers: { Authorization: token, 'Content-Type': 'application/json', } } const data = { title: 'タイトル2', caption: '説明2' } axios.post('http://localhost:3000/api/v1/posts',data,headers) } ... return ( ... //追加 <h2>投稿作成</h2> <button onClick={createPosts}>投稿作成</button> <h2>投稿一覧</h2> <button onClick={fetchPosts}>投稿取得</button> {posts?.map((post :any,index:number) => <div key={index}> <p>{post.title}</p> <p>{post.caption}</p> </div> )} </div> </div> ); } export default App; axiosに投稿データとtoken付きheaderを載せてリクエストしています。 rails.log User Create (1.7ms) INSERT INTO "users" ("sub", "created_at", "updated_at") VALUES (?, ?, ?) [["sub", "auth0|60c833837c1b260072d36e1a"], ["created_at", "2021-07-29 13:11:44.946926"], ["updated_at", "2021-07-29 13:11:44.946926"]] ↳ app/models/user.rb:4:in `from_token_payload' (1.4ms) commit transaction ↳ app/models/user.rb:4:in `from_token_payload' (0.1ms) begin transaction ↳ app/controllers/api/v1/posts_controller.rb:17:in `create' Post Create (1.1ms) INSERT INTO "posts" ("user_id", "title", "caption", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?) [["user_id", 5], ["title", "タイトル2"], ["caption", "説明2"], ["created_at", "2021-07-29 13:11:44.968824"], ["updated_at", "2021-07-29 13:11:44.968824"]] ↳ app/controllers/api/v1/posts_controller.rb:17:in `create' 新規のユーザーなのでCreateされていますね!! もう一度投稿取得ボタンを押して、タイトル2,説明2が追加されていたら成功です!! ログアウトした状態で投稿作成するとエラーになることも確認してください。 App.tsxの全体です App.tsx import React, { useEffect, useState } from 'react'; import './App.css'; import { useAuth0 } from "@auth0/auth0-react"; import axios from 'axios'; function App() { const { isAuthenticated,loginWithRedirect,logout,getAccessTokenSilently } = useAuth0(); const [token,setToken] = useState<string>('') const [posts,setPosts] = useState<any>() const fetchPosts = () => { axios.get('http://localhost:3000/api/v1/posts') .then((res) => { setPosts(res.data) }) } const createPosts = () => { const headers = { headers: { Authorization: token, 'Content-Type': 'application/json', } } const data = { title: 'タイトル2', caption: '説明2' } axios.post('http://localhost:3000/api/v1/posts',data,headers) } useEffect(() => { const getToken = async () => { try { const accessToken = await getAccessTokenSilently({}) setToken(accessToken) } catch (e) { console.log(e.message) } } getToken() }, []) console.log(posts) return ( <div className="App"> <div style={{padding:'20px'}}> <h2>ログインボタン</h2> <button onClick={() => loginWithRedirect()}>ログイン</button> <h2>ログアウトボタン</h2> <button onClick={() => logout()}>ログアウト</button> <h2>ログイン状態</h2> { isAuthenticated ? <p>ログイン</p> : <p>ログアウト</p> } <h2>投稿作成</h2> <button onClick={createPosts}>投稿作成</button> <h2>投稿一覧</h2> <button onClick={fetchPosts}>投稿取得</button> {posts?.map((post :any,index:number) => <div key={index}> <p>{post.title}</p> <p>{post.caption}</p> </div> )} </div> </div> ); } export default App; すべてApp.tsxに記述していますが、実際にアプリにするときは - token付きheaderをreduxで管理 - 通信処理をreactQueryで管理 以上の様にするとすっきりしたコードになります。 ここまでお読み頂きありがとうございました!! Auth0を使用し、ユーザー認証付きでSPAを作成する方法について解説させていただきました。 挙動や詳細な機能については、ぜひ自分で調べてみて、色々試してみてください~
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【画像手順解説】Auth0 Rails(api) + React SPAでユーザー認証機能を実装しよう 1 (React編)

はじめに こんにちは、つよしと申します。転職のために、railsとreactでユーザー認証付きのSPAのポートフォリオを作成しました。 この記事では、Auth0 Rails(api) + React SPAでユーザー認証を実装する方法を解説します 初学者のため、間違っている情報があるかもしれません。 その場合は、ご指摘,もしくは適宜読み替えて勧めていただけたらと思います。 この記事は3部構成になっています。 【画像手順解説】Auth0 Rails(api) + React SPAでユーザー認証機能を実装しよう 1 (React編) → 本記事 この記事で最終的に出来ること Rails(API) + React SPAでユーザー認証機能を実装 current_user機能 Authenticate_user機能 新しいユーザーがサインアップした場合は自動でユーザーをクリエイト 個別での使用方法も学べるので、どちらか一方の実装する方も参考になると思います。 本記事で解説すること ReactでのAuth0導入の仕方 この記事でしないこと 機能を詳細に説明はせず、手順を丁寧に書いた記事にします。 参考となる記事を随所で貼るので、気になる方はそちらを御覧ください。 目次 Auth0とは? どのような機構? React Auth0設定 React アプリ設定 Auth0とは? どのような機構 React Auth0設定 Auth0にログインしましょう。 右上の + Create Applicationをクリックします Single Page Web Applicationsを選択します。 名前は自分の好きなものをつけてください。(今回はTest Appにします) 設定出来たらCreateをクリックします。 Settingsタブに移り、 - Domain - Client ID 以上2つを控えておいてください。 そのまま下にスクロールし、 Application URIの項目を埋めます。 Allowed Callback URLs Allowed Logout URLs Allowed Web Origins Allowed Origins (CORS) 以上の4項目に http://localhost:3001/, http://localhost:3000/ を入力。 入力完了後は、下にスクロールし、Save Changesボタンをクリックして保存してください。 以上でreactのAuth0側の設定は終わりです。 次からアプリケーションに入ります。 Quick Startタブに移り、Reactをクリックしましょう。 ReactのQuickStartがでるのでこれを参考にして制作していきます。 Reactアプリ設定 reactのアプリを作成します(reduxは任意です) console npx create-react-app frontend --template redux-typescript もしくは npx create-react-app frontend --template typescript npm startをし、ちゃんとreactが動くか確認します。 console npm start envファイルを作成し、Settingsタブで控えたDomainとClient IDを使用できるようにします。 ※ gitにpushする場合は.gitignoreに.envを記述してください console touch .env ついでにポート番号とapiのURLも記入します。 /.env PORT=3001 REACT_APP_AUTH0_DOMAIN=[Domain] REACT_APP_AUTH0_CLIENT_ID=[Client ID] REACT_APP_REST_URL="http://localhost:3000/api/v1" ※ []はいりません。文字列のみ入力してください。 reactでauth0を簡単に使うために、以下のSDKを導入します npm install @auth0/auth0-react index.tsxを編集します src/index.tsx import { Auth0Provider } from '@auth0/auth0-react' import React from 'react' import ReactDOM from 'react-dom' import { Provider } from 'react-redux' import App from './App' import { store } from './app/store' import './index.css' import * as serviceWorker from './serviceWorker' const domain: any = process.env.REACT_APP_AUTH0_DOMAIN const clientId: any = process.env.REACT_APP_AUTH0_CLIENT_ID ReactDOM.render( <Auth0Provider domain={domain} clientId={clientId} redirectUri={window.location.origin} > <Provider store={store}> <App /> </Provider> </Auth0Provider>, document.getElementById('root') ) お疲れ様です! ここまで設定は完了です!! あとはログイン機能,ログアウトを実装しましょう!! ログイン,ログアウト機能実装 App.tsxを以下のように編集します /src/App.tsx import React from 'react'; import './App.css'; import { useAuth0 } from "@auth0/auth0-react"; // Auth0の機能をインポート function App() { const { isAuthenticated,loginWithRedirect,logout } = useAuth0(); // 必要な機能をインポート return ( <div className="App"> <div style={{padding:'20px'}}> <h2>ログインボタン</h2> <button onClick={() => loginWithRedirect()}>ログイン</button> <h2>ログアウトボタン</h2> <button onClick={() => logout()}>ログアウト</button> <h2>ログイン状態</h2> { isAuthenticated ? <p>ログイン</p> : <p>ログアウト</p> } </div> </div> ); } export default App; 基本的な使い方は以下の通りです。 import { useAuth0 } from "@auth0/auth0-react"; // auth0の機能をインポート const { isAuthenticated,loginWithRedirect,logout } = useAuth0(); // 必要な機能をインポート <button onClick={() => loginWithRedirect()}>ログイン</button> // 関数を実行する npm startをして確認してみましょう。 ログインボタンを押してみます。 ※初期設定だと英語だと思います。 自分のメールアドレスとパスワードを入力して、サインアップボタンを押してください。(Googleログインでも大丈夫です) サインアップ後自動でリダイレクトします。 ログイン状態が「ログイン」になっていれば成功です。 ログアウトもできるか試してみてください。 お疲れ様です!! 以上でReact側の設定は終了です。 次は をよかったら御覧ください~
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails] Capistranoを使って自動デプロイできるようにする

はじめに Capistranoを使って、アプリを改修や変更を加えたときに、コマンド一つで自動でデプロイできるようにしましたので、行った方法を残しておきます。 前回の続きとして捉えていただければ大丈夫です。 よろしくお願いします。 Capistrano導入 まずはCapistranoを導入するために必要なGemを追加します。 Gemfileのgroup :development, :test do ~ endの中に下記の記述を記入します。 Gemfile (省略) group :development, :test do gem 'capistrano' gem 'capistrano-rbenv' gem 'capistrano-bundler' gem 'capistrano-rails' gem 'capistrano3-unicorn' end (省略) 記述できたら、bundle install その後、下記のコマンドも実行します。 ターミナル # アプリケーションのディレクトリで実行しましょう % bundle exec cap install するといくつかのファイルが自動で生成されます。 Capfileを編集 Capfileとは先程のコマンドで生成されたファイルのうちの一つです。 capistranoは複数のライブラリ(Gem)から成り立っているので、このCapfileでどのライブラリを読み込むかの指定をします。 Capfileを下記のように編集します。 Capfile require "capistrano/setup" require "capistrano/deploy" require 'capistrano/rbenv' require 'capistrano/bundler' require 'capistrano/rails/assets' require 'capistrano/rails/migrations' require 'capistrano3/unicorn' Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r } production.rbを編集 続いて、コマンドで生成されたファイルの一つであるproduction.rbを編集します。 このファイルはconfig/deploy/production.rbにあります。 このファイルの一番下に下記の記述を追記します。 config/deploy/production.rb server '自分のElastic IP', user: 'ec2-user', roles: %w{app db web} Elastic IPは自分のものに置き換えてください。 deploy.rbを編集 config/deploy.rbの記述を全て削除し、下記を貼り付け、さらに自分の使用している各々のツールのバージョンや情報を置き換えます。 config/deploy.rb # capistranoのバージョンを記載。固定のバージョンを利用し続け、バージョン変更によるトラブルを防止する lock 'Capistranoのバージョン' # Capistranoのログの表示に利用する set :application, 'ご自身のアプリケーション名' # どのリポジトリからアプリをpullするかを指定する set :repo_url, 'git@github.com:Githubのユーザー名/レポジトリ名.git' # バージョンが変わっても共通で参照するディレクトリを指定 set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system', 'public/uploads') set :rbenv_type, :user set :rbenv_ruby, 'このアプリで使用しているrubyのバージョン' #カリキュラム通りに進めた場合、’2.6.5’ です # どの公開鍵を利用してデプロイするか set :ssh_options, auth_methods: ['publickey'], keys: ['~/.ssh/ご自身のキーペア名.pem'] # プロセス番号を記載したファイルの場所 set :unicorn_pid, -> { "#{shared_path}/tmp/pids/unicorn.pid" } # Unicornの設定ファイルの場所 set :unicorn_config_path, -> { "#{current_path}/config/unicorn.rb" } set :keep_releases, 5 # デプロイ処理が終わった後、Unicornを再起動するための記述 after 'deploy:publishing', 'deploy:restart' namespace :deploy do task :restart do invoke 'unicorn:restart' end end 置き換える箇所は、2, 5, 8, 14, 18行目です。 二行目のCapistranoのバージョンはGemfile.lockに記載されています。 例↓ Gemfile.lock (省略) capistrano (3.11.0) (省略) 上記のように書かれていた場合は、2行目に「3.11.0」と記載します。 unicorn.rbを編集する 続いて、自動デプロイの場合Railsアプリのディレクトリが一段階深くなるため、少しunicorn.rbの記述を編集します。 config/unicorn.rb #サーバ上でのアプリケーションコードが設置されているディレクトリを変数に入れておく app_path = File.expand_path('../../../', __FILE__) # 「../」が一つ増えている #アプリケーションサーバの性能を決定する worker_processes 1 #アプリケーションの設置されているディレクトリを指定 working_directory "#{app_path}/current" # 「current」を指定 #Unicornの起動に必要なファイルの設置場所を指定 pid "#{app_path}/shared/tmp/pids/unicorn.pid" # 「shared」の中を参照するよう変更 #ポート番号を指定 listen "#{app_path}/shared/tmp/sockets/unicorn.sock" # 「shared」の中を参照するよう変更 #エラーのログを記録するファイルを指定 stderr_path "#{app_path}/shared/log/unicorn.stderr.log" # 「shared」の中を参照するよう変更 #通常のログを記録するファイルを指定 stdout_path "#{app_path}/shared/log/unicorn.stdout.log" # 「shared」の中を参照するよう変更 (省略) Nginxの設定ファイルを編集 unicornの設定ファイルを編集した理由と同じ理由でNginxのファイルも編集します。 まずはEC2にログインした状態のターミナルで下記のコマンドを実行してエディタを立ち上げます。 ターミナル $ sudo vim /etc/nginx/conf.d/rails.conf その後「iキー」で入力できるよにしたら、下記のように変更します。 /etc/nginx/conf.d/rails.conf upstream app_server { # Unicornと連携させるための設定 server unix:/var/www/リポジトリ名/shared/tmp/sockets/unicorn.sock; } # {}で囲った部分をブロックと呼ぶ。サーバの設定ができる server { # このプログラムが接続を受け付けるポート番号 listen 80; # 接続を受け付けるリクエストURL ここに書いていないURLではアクセスできない server_name Elastic IP; # クライアントからアップロードされてくるファイルの容量の上限を2ギガに設定。デフォルトは1メガなので大きめにしておく client_max_body_size 2g; # 接続が来た際のrootディレクトリ root /var/www/リポジトリ名/current/public; # assetsファイル(CSSやJavaScriptのファイルなど)にアクセスが来た際に適用される設定 location ^~ /assets/ { gzip_static on; expires max; add_header Cache-Control public; root /var/www/リポジトリ名/current/public; } try_files $uri/index.html $uri @unicorn; location @unicorn { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; proxy_pass http://app_server; } error_page 500 502 503 504 /500.html; } 3行目の「リポジトリ名」は自分のものに置き換えます。 17, 24行目は完全に新しい記述のため、気をつけてください。 入力を終えたら「escキー」→「:wq」の順で実行し、保存します。 Nginxの設定を変更したら、再読み込み、再起動を下記のコマンドで行います。 ターミナル [ec2-user@ip-172-31-25-189 ~]$ sudo systemctl reload nginx [ec2-user@ip-172-31-25-189 ~]$ sudo systemctl restart nginx これでNginxの編集は以上です。 データベースの起動の確認 下記コマンドでデータベースが立ち上がっているか確認します。 ターミナル [ec2-user@ip-172-31-25-189 ~]$ sudo systemctl status mariadb 緑色で「active」の文字が表示されていれば正常に起動しています。 もしactiveになっていない場合はsudo systemctl start mariadbを実行します。 Unicornのプロセスをkill 最後に自動デプロイをする前に、二重にサーバーを立ち上げることを防ぐために、プロセスをkillしておきます。 まずはプロセスを確認します。 ターミナル [ec2-user@ip-172-31-23-189 <リポジトリ名>]$ ps aux | grep unicorn ec2-user 17877 0.4 18.1 588472 182840 ? Sl 01:55 0:02 unicorn_rails master -c config/unicorn.rb -E production -D ec2-user 17881 0.0 17.3 589088 175164 ? Sl 01:55 0:00 unicorn_rails worker[0] -c config/unicorn.rb -E production -D ec2-user 17911 0.0 0.2 110532 2180 pts/0 S+ 02:05 0:00 grep --color=auto unicorn 続いてプロセスをkill ターミナル # 上記の例だと「17877」 [ec2-user@ip-172-31-23-189 <リポジトリ名>]$ kill プロセス番号 完了したらローカルで修正を全てmasterにプッシュします。 自動デプロイ ローカルのターミナルで自動デプロイは行います。 下記のコマンドをアプリのディレクトリで行います。 ターミナル # アプリケーションのディレクトリで実行しましょう % bundle exec cap production deploy これで自動デプロイ完了です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.js プロジェクトを作成

はじめに 初めてVue.jsを使用したアプリを開発しているので、 そこで構築手順をアウトプットとして記載して行きたいと思います。^^ 流れ ・vue-cliのコマンドでVue.jsのプロジェクトを作成する    ・プロジェクトの設定を聞かれるので一つずつ答えていく ・Vue.jsのサーバを起動してブラウザでウェルカム画面を表示する プロジェクトの作成 ターミナル上で次のコマンドを実装 ターミナル vue create  フォルダ名 するとターミナル上に、プリセットを利用するかどうかの質問が表示されます。 ? Please pick a preset: memo (babel, eslint) default (babel, eslint) ❯ Manually select features 今回は自分で設定を選択していきたいので「Manually select features」を選択してEnterを押します。 次に、プロジェクトで使用するライブラリを聞かれます。 ? Please pick a preset: Manually select features ? Check the features needed for your project: ◯ Choose Vue version ◯ Babel ◯ TypeScript ◯ Progressive Web App (PWA) Support ◯ Router ◉ Vuex ◯ CSS Pre-processors ❯◉ Linter / Formatter ◯ Unit Testing ◯ E2E Testing 今回は「Vuex」と「Linter / Formatter」を選択します。 スペースキーで選択/解除できますので、その2つを選択した状態でEnterを押します。 次に、linter / formatterの設定を聞かれます。 ? Please pick a preset: Manually select features ? Check the features needed for your project: Vuex, Linter ? Pick a linter / formatter config: ESLint with error prevention only ESLint + Airbnb config ESLint + Standard config ❯ ESLint + Prettier 今回は「ESLint + Prettier」を選択してEnterを押します。 次に、Lintを実行するタイミングを聞かれます。 ? Please pick a preset: Manually select features ? Check the features needed for your project: Vuex, Linter ? Pick a linter / formatter config: Prettier ? Pick additional lint features: ❯◉ Lint on save ◯ Lint and fix on commit 今回は「Lint on save」を選択してEnterを押します。 次に、ESLintなどの設定ファイルを集約するか各設定ファイルで分けて書くかを聞かれます。 ? Please pick a preset: Manually select features ? Check the features needed for your project: Vuex, Linter ? Pick a linter / formatter config: Prettier ? Pick additional lint features: Lint on save ? Where do you prefer placing config for Babel, ESLint, etc.? ❯ In dedicated config files In package.json 今回は「In dedicated config files」を選択してEnterを押します。 次に、パッケージ管理をnpmコマンドかyarnコマンドのどちらを使うかを聞かれます。 ? Pick the package manager to use when installing dependencies: (Use arrow keys) Use Yarn ❯ Use NPM 今回は「Use NPM」を選択してEnterを押します。 最後に、今回選択した設定をプリセットとして保存しておくかどうかを聞かれます。 ? Please pick a preset: Manually select features ? Check the features needed for your project: Vuex, Linter ? Pick a linter / formatter config: Prettier ? Pick additional lint features: Lint on save ? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files ? Pick the package manager to use when installing dependencies: Use NPM ? Save this as a preset for future projects? (y/N) 今回は「N」を入力してEnterを押します。 これで設定の選択は完了です。 その設定に沿ってプロジェクトの作成処理が走ります。 作成が完了すると次のように表示されます。 Vue CLI v4.5.3 ✨ Creating project in /Users/username/workspace/vue-calendar/frontend. ? Initializing git repository... ⚙️ Installing CLI plugins. This might take a while... > fsevents@1.2.13 install /Users/username/workspace/vue-calendar/frontend/node_modules/watchpack-chokidar2/node_modules/fsevents > node install.js SOLINK_MODULE(target) Release/.node CXX(target) Release/obj.target/fse/fsevents.o SOLINK_MODULE(target) Release/fse.node ... ⚓ Running completion hooks... ? Generating README.md... ? Successfully created project frontend. ? Get started with the following commands: 作成されたディレクトリに移動します。 $ cd 作成したフォルダ名 nodeのバージョンを改めて設定します。 次のコマンドを実行して、今のディレクトリで使用するnodeのバージョンを指定します。 $ nodenv local 14.8.0 nodeのバージョンが14.8.0になっているかを確認します。 $ node -v v14.8.0 次のコマンドでサーバーを起動します。 $ npm run serve 処理が完了すると次のように表示されます。 DONE Compiled successfully in 1977ms 21:29:28 App running at: - Local: http://localhost:8080/ - Network: http://192.168.2.110:8080/ Note that the development build is not optimized. To create a production build, run npm run build. そうしたら、ブラウザでhttp://localhost:8080/ を開いてください。 次の画面が表示されれば成功です。 ここまでで詰まったポイント 全ての設定を終えて作成処理が始まった後に、、 Vue CLI v4.5.3 ✨ Creating project in /Users/username/workspace/vue-calendar/frontend. ⚙️ Installing CLI plugins. This might take a while... added 1193 packages in 2m ? Invoking generators... ? Installing additional dependencies... npm ERR! code ERESOLVE npm ERR! ERESOLVE unable to resolve dependency tree npm ERR! npm ERR! Found: vue@2.6.14 npm ERR! node_modules/vue npm ERR! vue@"^2.6.11" from the root project npm ERR! peer vue@"^2.0.0" from vuex@3.6.2 npm ERR! node_modules/vuex npm ERR! vuex@"^3.4.0" from the root project npm ERR! npm ERR! Could not resolve dependency: npm ERR! peer vue@"3.1.5" from @vue/compiler-sfc@3.1.5 { npm ERR! node_modules/@vue/compiler-sfc npm ERR! peer @vue/compiler-sfc@"^3.0.0-beta.14" from @vue/cli-service@4.5.13 npm ERR! node_modules/@vue/cli-service npm ERR! dev @vue/cli-service@"~4.5.0" from the root project npm ERR! 3 more (@vue/cli-plugin-eslint, @vue/cli-plugin-router, @vue/cli-plugin-vuex) npm ERR! peer @vue/compiler-sfc@"^3.0.8" from vue-loader-v16@16.3.3 { npm ERR! node_modules/vue-loader-v16 npm ERR! optional vue-loader-v16@"npm:vue-loader@^16.1.0" from @vue/cli-service@4.5.13 npm ERR! node_modules/@vue/cli-service npm ERR! dev @vue/cli-service@"~4.5.0" from the root project npm ERR! 3 more (@vue/cli-plugin-eslint, @vue/cli-plugin-router, @vue/cli-plugin-vuex) npm ERR! . . ERROR command failed: npm install --loglevel error 無数のエラーが発生してしまいました^^; エラー内容で調べると この記事にたどり着きなんとか作成できました! https://qiita.com/saken649/items/ccabb2f34cdac784b383 正直内容は理解できていませんが、 解決に至った流れを記載しておきます? $ vi ~/.vuerc { "useTaobaoRegistry": true, ⇦ false 変更 "latestVersion": "4.5.13", "lastChecked": 1627541270361, "packageManager": "npm" } trueをfalseに変更してあげる "useTaobaoRegistry": false 再度実行するといけました! vue create フォルダ名 おわりに Vue.jsやりたてほやほやです。 感じたこと大事だと思ったことはしっかりアウトプットして行きたいと思います^^
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails→Vue]一対多の関係にある親子オブジェクトをネストさせた状態で取得する

[Vue→Rails]ネストされた状態の親子孫オブジェクト(一対多の関係あり)を全て同時にデータベースに保存するでは、Vue側で作ったネストされたデータを複数のテーブルに保存した。 この記事では、別々のテーブルに保存されたそれらのデータを再びネストされた形でVueコンポーネント側で取得する。 前提条件 モデル 親:School 子:Student school.rb # 親 class School has_many :students student.rb # 子 class Student belongs_to :school 保存されているレコード School { id: 1, school_name: 'first' } { id: 2, school_name: 'second'} Student { id: 1, student_name: 'Suzuki', age: 13, school_id: 1 } { id: 2, student_name: 'Hirano', age: 14, school_id: 1 } { id: 3, student_name: 'Nagai', age: 15, school_id: 1 } { id: 4, student_name: 'Sato', age: 13, school_id: 2 } { id: 5, student_name: 'Iguchi', age: 14, school_id: 2 } { id: 6, student_name: 'Arai', age: 15, school_id: 2 } axios.getによるデータの取得 $axiosはインスタンスプロパティに追加済み schools#indexにgetリクエストを送りデータを取得する <script> ... this.$axios.get('schools') .then(res => { console.log(res) }) .catch(err => { console.log(err) }) ... </script> コントローラ to_jsonメソッドの使用 includeオプションでstudentsを指定する。school.studentsで取得されるstudentの配列をschoolにネストさせることができる。 schools_controller.rb def index schools = School.all string = schools.map.to_json(include: :students) render json: string end 取得できるデータ to_jsonのincludeオプションで指定した'students'がそのままキー名になる。 data: [ { schoolName: 'first', students: [ { studentName: 'Suzuki', age: 13 }, { studentName: 'Hirano', age: 14 }, { studentName: 'Nagai', age: 15 } ] }, { schoolName: 'second', students: [ { studentName: 'Sato', age: 13 }, { studentName: 'Iguchi', age: 14 }, { studentName: 'Arai', age: 15 } ] } ] 【追記】 キー名を変えたいとき students以外のキー名にしたいとき(例:studentDataなど) モデル アソシエーションを取得するメソッドを親側に新しく定義し、目的のキー名をメソッド名にする。 school.rb class School < ApplicationRecord has_many :students def studentData # 目的のキー名 self.students end end コントローラ to_jsonメソッドのmethodsオプションでそのメソッド名を指定する。 メソッド名がそのままキー名になる。 schools_controller.rb def index schools = School.all string = schools.map.to_json(methods: :studentData) render json: string end 取得できるデータ data: [ { schoolName: 'first', studentData: [ { studentName: 'Suzuki', age: 13 }, { studentName: 'Hirano', age: 14 }, { studentName: 'Nagai', age: 15 } ] }, { schoolName: 'second', studentData: [ { studentName: 'Sato', age: 13 }, { studentName: 'Iguchi', age: 14 }, { studentName: 'Arai', age: 15 } ] } ] 参考文献
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【既卒】【未経験】テックキャンプ受講から内定までの感想

 「テックキャンプ」は日本最大級のプログラミングスクールです。今受講すべきかどうか悩んでいる方もいらっしゃることでしょう。またプログラミング教育に携わる方にとっても、テックキャンプの実態は気になるのではないでしょうか。  そこでテックキャンプ卒業生の生の声を記事にしたいと思います。  先に簡潔な感想を述べるなら、テックキャンプは、アプリ開発の流れを短期間で効率的に学べる点で優れた環境です。一方で受講生の立場によっては受講料が割に合わない部分もあると言えます。  ではその理由を以下の流れでこれから掘り下げていきたいと思います。 僕の経歴 テックキャンプの学習内容 テックキャンプカリキュラムの難易度 テックキャンプのメリット テックキャンプのデメリット 僕の実績 まとめ  まずどういう人物の生の声なのかを明確にしないと、情報の受け取り方が変わってくると思いますので、僕の経歴を書いておきましょう。  気になる方以外は読み飛ばしてください。 1. 僕の経歴 ・この記事の目線  僕は社会人経験なし(既卒)、学歴なし、プログラミング未経験という立場からの受講となります。ということで僕の意見は、学力が優秀ではない若い人物の目線になります。 ・プログラミングを始めた理由  そんな僕がプログラミングを始めることにした理由は、食いっぱぐれにくいスキルを保有しておきたいと考えるようになったからです。  学歴なしということですが、僕は最も受験難易度の易しい公立大学に通っていました。高校時代の僕は受験勉強と並行しつつ、複数のやりたいことに時間を割いていました。やりたいことが複数あったことから専門大学へ行く決断はできず、受験勉強にあまり時間を割かなかったことから難関大学に行けるラインにも到達できず、ひとまず学費のかからない公立大学へ通うことにした形です。  そしてやりたいことの1つが「漫画」であり、在学中に2度、週刊少年ジャンプに自作の漫画を投稿しました。その時に痛感したのが、漫画で経済的に自立するには10年はかかるということでした。  よく考えてみれば、Amazonが消費者にとってコスパの良いサービスを提供できるのは、AWSという利益率の高い事業を手がけているからであり、サイバーエージェントが赤字のABEMAに先行投資し続けられるのも、インターネット広告事業やゲーム事業といった収益基盤があるからです。  だとすれば僕もリスクを取る前に、確実に経済的に自立した方が賢明だと考えるようになり、漫画制作と類似しているプログラミングを始めることにしました。プログラマーも年々希少価値は無くなっていくと感じていたからこそ、複数の分野に手を出しておいた方が、人生100年時代を楽しめるかなとも考えていました。  これが正直な理由です。しかしこんな甘い考えでは企業の採用担当者からは見切りをつけられます。エンジニアとして食っていくならば、それ相応の覚悟と本気度が必要だと言えます。 ・テックキャンプを受講した理由  プログラミングを始めるにあたってテックキャンプを受講することにした理由は以下の通りです。 将来的に教育事業にも携わりたいと考えていた 「何を学ぶべきか」を考える手間を省きたかった マコなり社長の正義感を信用していた 確実に就職したかった  わざわざ高い受講料を払ってまでテックキャンプを受講した訳は、「教育」に高い関心があったからです。社会課題の根本は「教育」に帰結するため、最終的には僕は教育事業に携わりたいと考えています。将来的に教育事業に携わるのであれば、ただ教育体系を頭で考えるだけでなく、実際に高水準の教育環境を体感することが必要だと思っていました。だとすればマコなり社長が仕組み化した日本最大級のプログラミングスクールに通うことは、僕にとって貴重な経験財になるはずだと考えたのです。  また独学ではなくスクールに通うことの1番のメリットは、「何を学ぶべきか」を考える手間が省けるということです。独学だと自分のやっている勉強が間違った方向に進んでいないか不安になりますが、スクールに通えば、その不安を効率的に解消できます。  あとエンジニア転職できなければ受講料全額返金というのがテックキャンプの特徴ですが、マコなり社長の使命感や正義感に偽りは全く感じられなかったため、テックキャンプを信用していたというのも大きいです。 2. テックキャンプの学習内容  ではテックキャンプでどんなことを学ぶのかをまとめておきたいと思います。  テックキャンプの特徴は、アプリ開発の流れを短期間で効率的に掴めるということです。「Ruby on Rails」というフレームの使い方を手を動かしながら学ぶのがメインです。サーバーサイドの実装が受講期間の大半を占めると言えます。  僕の中では、「プログラミング」というと、自分でアルゴリズムを考えて数学の問題を解くといったイメージがあり、アプリ開発というとホームページのデザインを考えて形にするイメージがありました。  しかしテックキャンプでは、アルゴリズム問題を解くことや、ホームページのデザインを考えるクライアントサイドの実装はメインではありません。  「いかにしてデータをHTMLに埋め込むか」を考えることに多くの時間を使います。HTML&CSS、JavaScript、AWSなど、アプリ開発に関する様々なことを広く学びますが、「Ruby on Rails」を学んだという感触が大きいカリキュラムとなっています。 3. テックキャンプカリキュラムの難易度  テックキャンプのカリキュラムに関して、「ゲーム感覚で学べた」という意見もあれば、「しんどかった」という意見もあります。  このことに関して僕の意見を述べるとするならば、カリキュラム終了時点では、「易しい難易度だった」と感じる場合が多いと考えられます。というのも、一度特定のスキルを習得して自動化できるようになった場合、大変だった頃の記憶は思い出しにくいからです。自転車に乗れるようになるまでの過程は大変だったはずですが、乗れるようになっている今では、あの時なぜ乗れなかったのかが分からないのと同じです。  またテックキャンプのカリキュラムが終わったとしても、その後自分で学ばなければならないことが膨大であるため、テックキャンプのカリキュラムは序の口だったと感じることでしょう。  プログラミング未経験者にとって、インプットの時間が長い序盤はしんどいと感じることもあるかと思いますが、アウトプットの時間が長くなる後半では、しんどさを感じにくいと言えます。  特に学力が優秀ではない人物の目線から言わせるならば、序盤は「1文あたりの横文字が多い」、「詰め込みすぎ」と感じることもあるかと思われます。カリキュラムを終えてみればそれで問題はないですけどね。 4. テックキャンプのメリット  そんなテックキャンプカリキュラムを受講してから就職活動を終了させるまでの過程を終えた上で感じた、テックキャンプのメリットを説明したいと思います。  ただし僕はメリットを感じにくい立場であったとも言えます。プログラミング未経験かつ社会人未経験であったため、そもそもプログラミングに挫折した経験もなければ、態度の悪い社会人の教育者も知らないからです。受講する世代が若くなるにつれて、テックキャンプの教育レベルが当たり前の水準になっていくとしたら恐ろしいですね。(笑)  そんな僕が感じたメリットは主に以下の4点です。 メンターのおかげで学習進捗が遅れない 最適化されたカリキュラム カリキュラム終了後も勉強しやすくなる 同じ課題を共有できる同期の存在  やはり土日も含めて大体10:00~22:00までいつでもすぐに繋がってくれるメンターの存在は大きいのではないでしょうか。考えても分からない課題に直面したとしても、メンターに質問すれば理解度を高めた上で先に進めます。  高い受講料というのは、テックキャンプ社員に対するプレッシャーになっている部分もあるはずです。そのため、どのメンターさんも責任を全うしようとする姿勢が強いです。ただもちろん受講生サイドも、仮説を立てて要点を絞った質問をすることが求められます。  このメンター制度に加えて、アプリ開発の流れを掴める最適化されたカリキュラムがあることによって、独学では到達できないスキルをたった10週間で身につけられると言えます。  またアプリ開発に関する知識を幅広く学べるため、テックキャンプのカリキュラムが終了したとしても次のステップに進みやすいです。  多くの企業はテックキャンプで学んだ「Ruby」ではなく「Java」をはじめとした他のプログラミング言語を扱っています。しかし基本的な考え方は同じであるため、新しい言語を学ぶ際の吸収力は上がります。  そしてなんといっても同期の存在は大きかったです。テックキャンプでは、1日に複数回、同期受講生5~7人でアウトプットをします。僕はオンラインでの受講でしたが、同じ課題を共有できる仲間がいることによって、孤独を感じることは全くなかったです。  また比較できる相手がいることによってグループ内での学習進捗率が大幅に上がります。1時期、学習進捗率の上位1位〜7位までが全員自分たちのグループだったなんてこともありました。 5. テックキャンプのデメリット  テックキャンプにはもちろんデメリットもあります。しかし僕が感じたデメリットはテックキャンプ側に問題があるわけではなく、受講生の立場に依存する問題です。受講生の立場次第では、「テックキャンプ以外でプログラミングを学ぶ方が最適かもしれないよ」という気づきになります。  僕が感じたテックキャンプ唯一にして最大のデメリットはやはり「受講料の高さ」です。なぜ受講料が高いのかを切り分けて考えれば、他のプログラミングの学び方も選択できるはずです。  少なくとも「既卒」や「学生」向けではないと僕個人は感じました。そもそもテックキャンプのターゲット層ではないでしょうし、僕個人に問題がありますけどね。(笑)  なぜ受講料が高いのかを考えた時に挙げられるのはやはり「人件費」ではないでしょうか。いつでも質問に答えてくれる「メンターさん」がいて、学習面での不安を聞いてくれる「ライフコーチ」がいて、転職活動を支援してくれる「キャリアアドバイザーさん」がいて、テックキャンプ経由で求人を提供するための「営業担当」もいます。これだけ万全の体制が整っていれば受講料が高くて当然です。  しかしこれらに高いお金を払う必要がない立場の方もいらっしゃると思います。メンターさん、ライフコーチ、キャリアアドバイザーさんは全員良い人でしたが、受講生の立場によっては、受講料が割高だと感じるかもしれません。  僕の場合は学習面で不安を感じることがなかったため、ライフコーチのサポートは全く利用しませんでした。  またテックキャンプ経由では既卒の求人はほとんどないと言われたため、就職活動も自己応募がメインとなりました。  さらに言えば完全未経験者とプログラミングスクール卒業生を区別した求人はなかなか見当たらず、未経験者を採用する自社内開発企業があったとしても、テックキャンプではほとんど学ばないインフラを扱うエンジニアの求人ばかりでした。アプリ開発のフレームワークよりもアルゴリズム問題を解くスキルやITの基礎的な知識を重視する企業からは好まれない傾向もあります。  メンター制度に関して言えば、カリキュラム外のことは質問できないという特徴もあります。僕の場合は、受講期間の1ヶ月前倒しで最終課題を提出しましたが、もっと学習進捗が速い方からすればほとんどの期間メンターさんが使えない状態となる可能性もあります。 6. 僕の実績  そんな僕はテックキャンプ受講生の中では苦戦しなかった方だと思います。受講期間の終了時点では、以下のように、フリマアプリに10個程度機能を追加し、10個程度機能を実装したオリジナルアプリを完成させました。 追加実装したフリマアプリ オリジナルの大喜利投稿アプリ  その中でも最も複雑だった実装は、以下のように記事にしています。  就職活動では、テックキャンプ経由で、実力主義の受託開発企業の最終選考までは進むことができました。既卒向けの求人はほとんどないと言いましたが、この企業に関しては、プログラミングテストやアプリ課題提出などの成果で判断してくださったため、選考に通過することができました。ただ実力及ばず最終面接で落ちてしまいました。  また応募条件に該当する自社内開発企業はいずれもインフラ系であり、面接ではどの採用担当者もテックキャンプカリキュラムと実務内容に齟齬があることを懸念されている印象でした。こちらも実力及ばず面接で落ちてしまいました。  結果的にはアプリ開発系のSES企業に内定をいただいた形となります。 7. まとめ  以上までがテックキャンプ受講から内定までの感想となります。受講料が高いことだけが唯一の懸念材料であり、カリキュラム内容や体制自体に不満は全くありません。僕の満足度は高く、社会人の方にはぜひお勧めしたいです。  仮に合わないと感じたとしても、受講から14日の間に辞めれば受講料は返済されるので、辞めない決断をした場合は文句の言いようもないと思います。  ただ実際に受講した立場として、学生や既卒の方は、テックキャンプ以外の選択肢で勉強した方が合理的な場合があると感じました。その辺を考えて慎重に比較検討してもいいかもしれません。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【個人開発】リモートワーク時代なので、デスク周りに特化した写真投稿サービスを作りました

こんにちは、Barry(ばりゅー)と申します。 いきなりですが言わせてください。 このサービスは今読んでいるあなたのために作ったサービスです! はじめに リモートワークが当たり前になった今、みなさんはどんなデスクで作業してますか? 「もっと快適にしたい。」「もっとオシャレにしたい。」 デスクワーカーなら一度、いや、二度三度思ったことがあると思います。 そんなとき、他人のデスクが参考になったりしますよね。 そこでこんなサービスを開発しました。 どんなサービス? デスク周りに特化した写真投稿サービス「Buildesk(ビルデスク)」です。 【サービスURL】https://buildesk.app 【GitHub】https://github.com/en-Barry/buildesk Buildeskの一番の特徴は、使ってるアイテムも登録できること。投稿者がどんなアイテムを使っているのか瞬時に分かるようになっています。 投稿デモ 投稿詳細ページ 投稿詳細ページ 参考にしたい投稿を保存しておくことも可能です。気になったデスク、アイテムの型番や名前を忘れる心配がありません。 投稿保存ページ シェアするときもいい感じで表示されるようになってます。 どうして作った? プログラミングの勉強を始めたときに抱えた悩み。 それは、「身体が痛くて集中できない!」ということでした。 デスクワークに慣れていなかった自分は作業環境を整える必要があり、暇さえあればTwitterやインスタで参考になるデスク写真を探す日々。 ただ、Twitterやインスタで見かけるのは写真のみの投稿が多く、見た目しか参考になりません。 何のアイテムを使ってるんだろう?使い心地はどんなもんだろう? 実際に知りたいことが分からないもどかしさがありました。 そこで、みんながデスク周りに使ってるアイテムを瞬時に分かるサービスを開発しました。 使用技術 Rails '6.0.3.5' Ruby '2.6.6' 主なGem rakuten_web_service 商品価格ナビ製品検索API sorcery carrierwave ransack enum_help meta-tags hanmoto フロント jQuery Swiper Bulma インフラ AWS EC2, S3, CloudFront, RDS puma Nginx 工夫 ・ 苦労した点 jQuery x FormObject x 楽天APIで実装したアイテム登録機能 サービスの肝であり、一番苦労したのもこの部分。 スクールの講師にも実装が難しいと判断され、「Amazonのリンクを貼ればいいんじゃない?」と提案を受けていました。 ただ、世に出ているサービスとの違いはここ!といったこだわりがあったため、どうしても提案を受け入れられず。 現にjsonファイルの扱い、jQueryのDOM操作、複数モデルのデータ保存など分からないことが次々に待ち受けており実装にも1~2ヶ月かかってしまいましたが、理想に近い機能を実装できました。 デザイン 一度使ったら終わりのアプリではなく、ユーザーが長く滞在するのを想定しているアプリなので見た目にはこだわりました。 ただ、デザイン知識やCSSの知識が乏しかったためCSSフレームワークのBulmaに頼って何とか仕上げました。フレームワーク様様です。 また、投稿するとなるとスマホからの利用が多いと思ったので、スマホで操作しやすいように画面下にナビゲーションバーを自作で作りました。 TOPページ 投稿一覧ページ マネタイズ マネタイズと言えるほどでもないですが、一応収益が出る仕組みになっています。 楽天APIの利用ではアフィリエイトIDが必要になり、商品情報取得時点で自動的にアフィリンクが作成されます。 これを利用して、アイテム詳細ページにアフィリンクを設置し、自然な形で収益につながる設計ができました。 今読んでるあなたに使ってもらいたい Qiitaを読んでいるあなたこそBuildeskの対象者です。 デスクワーカーである、あなたのために作ったサービスと言っても過言ではありません! 「自慢できるほどのデスクじゃないしな。。」 そんな方も現時点のデスクを登録するメリットがあります。 デスク遍歴がひと目で分かるので、エモさを感じれる デスク周りでオススメを聞かれたときに、使ってるアイテムを思い出す手間が省ける 他人にデスクを見られると思うと、自然に片付け始める などなど.. もちろん、ガチガチにこだわってる人も大いに自慢してください! 今後のアップデートで人気の投稿はトップページで紹介する予定です。 イケてるデスクを投稿した際にはぜひTwitterでシェアしてくださいね。必ずいいねしに行きます。 記事は以上となります、ここまで読んでくださってありがとうございました!! Special Thanks プログラミングに関しての知識が全くの0だった自分が開発、リリースできるまでになったのはRUNTEQのサポートがあったから他なりません。 また@aiandroxさんをはじめとした@sho171さんや@krpk1900さんなど、RUNTEQコミュニティで出会えた方々のサポートもすごく大きかったです。 この場を借りて御礼申し上げます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsで架空のCafeのHPを作ってみよう!【13日目】『f.text_areaの値を改行、空白含めそのまま反映』編

概要 基本Railsの記法に則り書いていきます! 1から全ての説明ではなく その中であれ?どうやるの?と 疑問に思った点や実装に困った箇所を ピックアップして紹介していきます♩ 設定と準備 ・Rails ・HTML ・CSS ・Javascript(jQuery) ↑上記の言語とフレームワークを使い 架空(自分で考えたテキトーなもの)のCafeの HPを作っていこうと思います! 13日目の作業内容 ・ビューの作成(一覧ページ) 13日目の気になった箇所 f.text_areaで入力された値をそのまま反映したいが どのようにすれば良いだろうか。 仮説 調べたらrailsのヘルパーメソッドである simple_formatや safe_joinなどを使うと 表示できるらしい。 testsテーブルのsampleというカラムに保存させて反映させていくとします。 tests/new.html.erb <%= f.text_area :sample %> まずは入力フォームを作成。 入力例) sample sample sample 上記のように文字の前に空白や改行を適当に入力したとします。 tests_controller.erb def show @test = Test.find(params[:id]) end コントローラでインスタンス変数に格納し使えるようにします。 tests/show.html.erb <%= @test.sample %> これで反映はされれるが sample sample sample このように表示されてしまいうまく表示できない。 ではここにヘルパーメソッドを使い検証していきます。 tests/show.html.erb <%= simple_fomat(@test.sample) %> tests/show.html.erb <%= safe_join(@test.sample) %> どちらもエラーが出てしまいうまくいかなかった。 結論 上記で検証したsimple_fomatやsafe_joinでも うまく反映させることが可能だと思いますが 今回は私がうまくいった方法を紹介します。 上記と同じものを使うと仮定するので入力フォームなどは割愛させていただきます。 では、同じようにように表示させていきます。 tests/show.html.erb <p><%= @test.sample %></p> 表示させたい値をpタグで囲っていきます。 今回はpタグで囲いましたがクラス名を付与でも可能です。↓ tests/show.html.erb 例) <%= @test.sample, class:"text" %> 次にこのpタグ(もしくは付与したクラス名)に CSSを当てていきたいと思います。 show.css p { white-space: pre-wrap; } /* クラス名の場合 */ .text { white-space: pre-wrap; } たったこれだけで空白、改行全て反映してくれます! このwhite_spaceというプロパティは要素内の空白をどのように 扱うかを指定するものです。 今回はpre-wrapを指定しましたが、他にも 要素を詰めて空白を作らない 改行はするが最初の空白は作らない など 用途に合わせて使えると思いますので、気になったら調べてみてください!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Vue→Rails]一対多の関係にある親子孫のデータがネストされたオブジェクトを全て同時にデータベースに保存する

前提条件 モデル 親:City 子:School 孫:Student 一つの市に複数の学校があり、それぞれの学校に複数の生徒がいるという想定 city.rb # 親 class City has_many :school school.rb # 子 class School has_many :students belongs_to :city student.rb # 孫 class Student belongs_to :school Vue(フロントエンド側)で生成されるオブジェクト // the city has 2 schools // each school has 3 students { city: { cityName: 'tokyo', schools: [ { schoolName: 'first', students: [ { studentName: 'Suzuki', age: 13 }, { studentName: 'Hirano', age: 14 }, { studentName: 'Nagai', age: 15 } ] }, { schoolName: 'second', students: [ { studentName: 'Sato', age: 13 }, { studentName: 'Iguchi', age: 14 }, { studentName: 'Arai', age: 15 } ] }, ] } } axiosのpostメソッドでバックエンドに送る。 コントローラ cities,schools,studentsコントローラで個別にレコードを保存するのではなく、citiesコントローラで一括で保存を行う。 ストロングパラメータの記述 cityの一つの属性としてschoolsを指定する。配列データであるschoolsは[]で表し、中にschoolsのパラメータを記述する。 さらに、schoolsの一つのパラメータとしてstudentsを指定する。同様に配列データであるstudentsを[]で表し、中にstudentsのパラメータを記述する。 createアクションの記述 CityObjectインスタンス(次章で解説)をビルドし、パラメータを渡す(2行目)。 CityObjectのインスタンスメソッド'save'(次章で解説:ActiveRecordのsaveメソッドではない)内で親子孫それぞれのモデルにおいてレコードの保存を行う(4行目)。 cities_controller.rb def create city = CityObject.new(city_params) if city.save render json: city else render json: city.errors.full_messages, status: :unprocessable_entity end end private def city_params params.require(:city).permit(:cityName, schools: [:schoolName, students: [:studentName, :age]]) end 子孫オブジェクトを包含したインスタンスの利用 City,School,Studentのパラメータを持つ非ActiveRecordクラス'CityObject'を定義する。 コントローラでCityObjectのインスタンスにパラメータが渡されているので、インスタンスメソッド'save'内でそのパラメータにアクセスし、City,School,Studentそれぞれのインスタンスに渡して保存を行う。 ActiveModelモジュールの利用 CityObjectクラスを定義し、ActiveModel::ModelをインクルードすることでActiveRecordモデルと同様の処理を可能にする(1,2行目)。 それとともにActiveModel::Attributesをインクルードし、attributeメソッドによりCityObjectに渡されたパラメータを参照できるようにする(3-5行目)。親オブジェクトの各属性を指定する。 CityObjectのインスタンスメソッド'save'の定義 CityObjectの属性値'cityName'にアクセスし、新しいCityインスタンスの属性値'city_name'としてセットし、それを保存する(10-11行目)。 schools(selfは省略可)でschoolオブジェクトの配列を取得し、一つ一つのオブジェクトについて以下のループ処理を行う(14行目)。 schoolオブジェクトの'schoolName'の値をSchoolインスタンスの属性値'school_name'としてセットし、先程保存したCityインスタンスの子オブジェクトとして保存する(15行目)。 sc[:students]でschoolオブジェクトが持つstudentオブジェクトの配列を取得し、一つ一つのオブジェクトについて以下のループ処理を行う(16行目)。 studentオブジェクトの'studentName','age'の値をStudentインスタンスの属性値'student_name','age'として渡し、先程保存したSchoolインスタンスの子として保存する(18行目)。 app/models/city_object.rb class CityObject include ActiveModel::Model include ActiveModel::Attributes attribute :cityName attribute :schools def save # Cityインスタンスの保存 city = City.new(city_name: self.cityName) city.save # Schoolインスタンスの保存 self.schools.each do |sc| school = city.schools.create(school_name: sc[:schoolName]) sc[:students].each do |st| # Studentインスタンスの保存 school.students.create(student_name: st[:studentName], age: st[:age]) end end end end 保存されるレコード City { id: 1, city_name: 'tokyo' } School { id: 1, school_name: 'first', city_id: 1 } { id: 2, school_name: 'second', city_id: 1 } Student { id: 1, student_name: 'Suzuki', age: 13, school_id: 1 } { id: 2, student_name: 'Hirano', age: 14, school_id: 1 } { id: 3, student_name: 'Nagai', age: 15, school_id: 1 } { id: 4, student_name: 'Sato', age: 13, school_id: 2 } { id: 5, student_name: 'Iguchi', age: 14, school_id: 2 } { id: 6, student_name: 'Arai', age: 15, school_id: 2 }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Vue→Rails]ネストされた状態の親子孫オブジェクト(一対多の関係あり)を全て同時にデータベースに保存する

前提条件 モデル 親:City 子:School 孫:Student 一つの市に複数の学校があり、それぞれの学校に複数の生徒がいるという想定 city.rb # 親 class City has_many :school school.rb # 子 class School has_many :students belongs_to :city student.rb # 孫 class Student belongs_to :school Vue(フロントエンド側)で生成されるオブジェクト // the city has 2 schools // each school has 3 students { city: { cityName: 'tokyo', schools: [ { schoolName: 'first', students: [ { studentName: 'Suzuki', age: 13 }, { studentName: 'Hirano', age: 14 }, { studentName: 'Nagai', age: 15 } ] }, { schoolName: 'second', students: [ { studentName: 'Sato', age: 13 }, { studentName: 'Iguchi', age: 14 }, { studentName: 'Arai', age: 15 } ] }, ] } } axiosのpostメソッドでバックエンドに送る。 コントローラ cities,schools,studentsコントローラで個別にレコードを保存するのではなく、citiesコントローラで一括で保存を行う。 ストロングパラメータの記述 cityの一つの属性としてschoolsを指定する。配列データであるschoolsは[]で表し、中にschoolsのパラメータを記述する。 さらに、schoolsの一つのパラメータとしてstudentsを指定する。同様に配列データであるstudentsを[]で表し、中にstudentsのパラメータを記述する。 createアクションの記述 CityObjectインスタンス(次章で解説)をビルドし、パラメータを渡す(2行目)。 CityObjectのインスタンスメソッド'save'(次章で解説:ActiveRecordのsaveメソッドではない)内で親子孫それぞれのモデルにおいてレコードの保存を行う(4行目)。 cities_controller.rb def create city = CityObject.new(city_params) if city.save render json: city else render json: city.errors.full_messages, status: :unprocessable_entity end end private def city_params params.require(:city).permit(:cityName, schools: [:schoolName, students: [:studentName, :age]]) end 子孫オブジェクトを包含したインスタンスの利用 City,School,Studentのパラメータを持つ非ActiveRecordクラス'CityObject'を定義する。 コントローラでCityObjectのインスタンスにパラメータが渡されているので、インスタンスメソッド'save'内でそのパラメータにアクセスし、City,School,Studentそれぞれのインスタンスに渡して保存を行う。 ActiveModelモジュールの利用 CityObjectクラスを定義し、ActiveModel::ModelをインクルードすることでActiveRecordモデルと同様の処理を可能にする(1,2行目)。 それとともにActiveModel::Attributesをインクルードし、attributeメソッドによりCityObjectに渡されたパラメータを参照できるようにする(3-5行目)。親オブジェクトの各属性を指定する。 CityObjectのインスタンスメソッド'save'の定義 CityObjectの属性値'cityName'にアクセスし、新しいCityインスタンスの属性値'city_name'としてセットし、それを保存する(10-11行目)。 schools(selfは省略可)でschoolオブジェクトの配列を取得し、一つ一つのオブジェクトについて以下のループ処理を行う(14行目)。 schoolオブジェクトの'schoolName'の値をSchoolインスタンスの属性値'school_name'としてセットし、先程保存したCityインスタンスの子オブジェクトとして保存する(15行目)。 sc[:students]でschoolオブジェクトが持つstudentオブジェクトの配列を取得し、一つ一つのオブジェクトについて以下のループ処理を行う(16行目)。 studentオブジェクトの'studentName','age'の値をStudentインスタンスの属性値'student_name','age'として渡し、先程保存したSchoolインスタンスの子として保存する(18行目)。 app/models/city_object.rb class CityObject include ActiveModel::Model include ActiveModel::Attributes attribute :cityName attribute :schools def save # Cityインスタンスの保存 city = City.new(city_name: self.cityName) city.save # Schoolインスタンスの保存 self.schools.each do |sc| school = city.schools.create(school_name: sc[:schoolName]) sc[:students].each do |st| # Studentインスタンスの保存 school.students.create(student_name: st[:studentName], age: st[:age]) end end end end 保存されるレコード City { id: 1, city_name: 'tokyo' } School { id: 1, school_name: 'first', city_id: 1 } { id: 2, school_name: 'second', city_id: 1 } Student { id: 1, student_name: 'Suzuki', age: 13, school_id: 1 } { id: 2, student_name: 'Hirano', age: 14, school_id: 1 } { id: 3, student_name: 'Nagai', age: 15, school_id: 1 } { id: 4, student_name: 'Sato', age: 13, school_id: 2 } { id: 5, student_name: 'Iguchi', age: 14, school_id: 2 } { id: 6, student_name: 'Arai', age: 15, school_id: 2 }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Vue→Rails]一対多の関係にある親、子、孫オブジェクトを全て同時にデータベースに保存する

前提条件 モデル 親:City 子:School 孫:Student 一つの市に複数の学校があり、それぞれの学校に複数の生徒がいるという想定 city.rb # 親 class City has_many :school school.rb # 子 class School has_many :students belongs_to :city student.rb # 孫 class Student belongs_to :school Vue(フロントエンド側)で生成されるオブジェクト // the city has 2 schools // each school has 3 students { city: { cityName: 'tokyo', schools: [ { schoolName: 'first', students: [ { studentName: 'Suzuki', age: 13 }, { studentName: 'Hirano', age: 14 }, { studentName: 'Nagai', age: 15 } ] }, { schoolName: 'second', students: [ { studentName: 'Sato', age: 13 }, { studentName: 'Iguchi', age: 14 }, { studentName: 'Arai', age: 15 } ] }, ] } } axiosのpostメソッドでバックエンドに送る。 コントローラ cities,schools,studentsコントローラで個別にレコードを保存するのではなく、citiesコントローラで一括で保存を行う。 ストロングパラメータの記述 cityの一つの属性としてschoolsを指定する。配列データであるschoolsは[]で表し、中にschoolsのパラメータを記述する。 さらに、schoolsの一つのパラメータとしてstudentsを指定する。同様に配列データであるstudentsを[]で表し、中にstudentsのパラメータを記述する。 createアクションの記述 CityObjectインスタンス(次章で解説)をビルドし、パラメータを渡す(2行目)。 CityObjectのインスタンスメソッド'save'(次章で解説:ActiveRecordのsaveメソッドではない)内で親子孫それぞれのモデルにおいてレコードの保存を行う(4行目)。 cities_controller.rb def create city = CityObject.new(city_params) if city.save render json: city else render json: city.errors.full_messages, status: :unprocessable_entity end end private def city_params params.require(:city).permit(:cityName, schools: [:schoolName, students: [:studentName, :age]]) end 子孫オブジェクトを包含したインスタンスの利用 City,School,Studentのパラメータを持つ非ActiveRecordクラス'CityObject'を定義する。 コントローラでCityObjectのインスタンスにパラメータが渡されているので、インスタンスメソッド'save'内でそのパラメータにアクセスし、City,School,Studentそれぞれのインスタンスに渡して保存を行う。 ActiveModelモジュールの利用 CityObjectクラスを定義し、ActiveModel::ModelをインクルードすることでActiveRecordモデルと同様の処理を可能にする(1,2行目)。 それとともにActiveModel::Attributesをインクルードし、attributeメソッドによりCityObjectに渡されたパラメータを参照できるようにする(3-5行目)。親オブジェクトの各属性を指定する。 CityObjectのインスタンスメソッド'save'の定義 CityObjectの属性値'cityName'にアクセスし、新しいCityインスタンスの属性値'city_name'としてセットし、それを保存する(10-11行目)。 schools(selfは省略可)でschoolオブジェクトの配列を取得し、一つ一つのオブジェクトについて以下のループ処理を行う(14行目)。 schoolオブジェクトの'schoolName'の値をSchoolインスタンスの属性値'school_name'としてセットし、先程保存したCityインスタンスの子オブジェクトとして保存する(15行目)。 sc[:students]でschoolオブジェクトが持つstudentオブジェクトの配列を取得し、一つ一つのオブジェクトについて以下のループ処理を行う(16行目)。 studentオブジェクトの'studentName','age'の値をStudentインスタンスの属性値'student_name','age'として渡し、先程保存したSchoolインスタンスの子として保存する(18行目)。 app/models/city_object.rb class CityObject include ActiveModel::Model include ActiveModel::Attributes attribute :cityName attribute :schools def save # Cityインスタンスの保存 city = City.new(city_name: self.cityName) city.save # Schoolインスタンスの保存 self.schools.each do |sc| school = city.schools.create(school_name: sc[:schoolName]) sc[:students].each do |st| # Studentインスタンスの保存 school.students.create(student_name: st[:studentName], age: st[:age]) end end end end 保存されるレコード City { id: 1, city_name: 'tokyo' } School { id: 1, school_name: 'first', city_id: 1 } { id: 2, school_name: 'second', city_id: 1 } Student { id: 1, student_name: 'Suzuki', age: 13, school_id: 1 } { id: 2, student_name: 'Hirano', age: 14, school_id: 1 } { id: 3, student_name: 'Nagai', age: 15, school_id: 1 } { id: 4, student_name: 'Sato', age: 13, school_id: 2 } { id: 5, student_name: 'Iguchi', age: 14, school_id: 2 } { id: 6, student_name: 'Arai', age: 15, school_id: 2 }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】MySQLからPostgresqlに変更してHerokuにデプロイする

経緯 開発環境ではMySQLを使っていたが、HerokuではPostgresqlがデフォルトでMySQLを使うにはクレジットカードが必要だったがクレジットカードを持っていなかったため開発環境はPostgresqlに変更してHerokuにデプロイすることになった。 目標 development, testのデータベースはMySQLのままでProductionのみPostgresqlに変更しHerokuにデプロイする。 環境 Windows10 Docker Rails MySQL Postgresql 実行内容 Gemfileの変更 Dockerfileの変更 docker-compose.ymlの変更 database.ymlの変更 Herokuの設定 Herokuの接続先情報の追加 Heroku上で公開 1. Gemfileの変更 Gemfile group :production do gem 'pg' end production用にpgを追加する。また、mysql2に関してはproductionでは使わないためgroup :development :testに封印しておく。その後 docker-compose run web bundle install 2. Dockerfileの変更 dockerfile FROM ruby:2.5 RUN apt-get update -qq && apt-get install -y nodejs yarnpkg RUN ln -s /usr/bin/yarnpkg /usr/bin/yarn RUN mkdir /app WORKDIR /app COPY Gemfile /app/Gemfile COPY Gemfile.lock /app/Gemfile.lock RUN bundle install COPY . /app COPY entrypoint.sh /usr/bin/ RUN chmod +x /usr/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"] EXPOSE 3000 ENV RAILS_ENV production   #これを追加する CMD ["rails", "server", "-b", "0.0.0.0"] productionで動かせるようにDockerfileにENV RAILS_ENV productionを追加する。 3. docker-compose.ymlの変更 docker-compose.yml version: '3' services: db: image: postgres environment: POSTGRES_USER: <%= ENV['APP_USERNAME'] %> POSTGRES_PASSWORD: <%= ENV['APP_PASSWORD'] %> PGDATA: /var/lib/postgresql/data/pgdata volumes: - ./tmp/db:/var/lib/postgresql/data web: build: . command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" environment: RAILS_ENV: production volumes: - .:/app - gem_data:/usr/local/bundle ports: - "3000:3000" depends_on: - db volumes: gem_data: POSTGRES_USER, POSTGRES_PASSWORDにdatabase.ymlにも指定する環境変数を指定しておく。RAILS_ENV: productionも設定しておく。 4. database.ymlの変更 database.yml production: <<: *default adapter: postgresql encoding: unicode pool: 5 database: <%= ENV['APP_DATABASE'] %> username: <%= ENV['APP_USERNAME'] %> password: <%= ENV['APP_PASSWORD'] %> host: <%= ENV['APP_HOST'] %> database, username, password, hostには任意の環境変数を設定する。(後からHerokuに接続先情報を追加する) 5. Herokuの設定 heroku login    ↓ heroku container:login    ↓ heroku create -a application_name    ↓  heroku addons:create heroku-postgresql:hobby-dev heroku createでHerokuアプリケーションを作成する。-a によってアプリケーションに任意の名前を付けることができる。 heroku用のデータベースをアドオンとして追加する。お金がないのでタダのheroku-postgresql:hobby-devを用いる。 6. Herokuの接続先情報の追加 database.ymlでは環境変数を使っているため、Herokuに環境変数を設定しておく必要があり、まずはHerokuのデータベース情報を取得する。 heroku config -a application_name これを実行するとアプリケーションに関する情報が表示されるため、そのなかにあるDATABA_URLを確認する。DATABASE_URLは次のようになっている。 DATABASE_URL:postgres://<username>:<password>@<host>/<database> これらを用いて環境変数を設定する。heroku config:add ~で環境変数は設定することができ、APP_USERNAMEを設定する場合は次のよう heroku config:add APP_USERNAME='<username>' -a application_name APP_DATABASE, APP_PASSWORD, APP_HOSTも同様に設定する。 ※APP_USERNAME = ''など=の間に半角スペースを入れるとエラーになるため気を付けてね^^ 設定できているかを確認したい場合は heroku config -a application_nameで確認することができる。 7. Heroku上で公開する いろいろ終わったらあとは公開するだけ プロジェクトディレクトリでコンテナにログインした状態で heroku container:push web -a application_name    ↓ heroku container:release web -a application_name    ↓ heroku run rails db:migrate ↓ heroku run rails assets:precompile    ↓ heroku open へいお待ち 8.補足 Herokuはデフォルトではlogの量が少なくerrorの原因がわからないことがあるためlogが多く出されるように設定しておく heroku config:add RAILS_LOG_TO_STDOUT='true' -a application_name 参考にしたサイト  DockerでRailsの環境構築してHerokuへデプロイする  Container Registry および Runtime (Docker デプロイ) まとめ 改善点などあれば指摘していただけたら嬉しいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

REST APIとは

REST API とは   1 情報の操作は全てHTTPメソッド(CRUD処理)で行われる CRUD 処理の種類 ​controller_methods Create create Read indexとshow Update update Delete destroy 2 URI(URL)を読むだけで『何のリソース』に『 どのような処理』をするのかわかる *ここではURLはarticlesにする *『何のリソース』は/articlesかarticles/:id *どのような処理はHTTP method CRUD 処理の種類     HTTP method URL Pattern ​ controller_methods Create POST POST /articles create Read GET GET /articles index Read GET GET /articles/:id show Update PUT PATCH PUT or PATCH/articles/:id update Delete DELETE DELETE/articles/:id destroy
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

投稿にタグ、カテゴリー付け タグで検索できるようにする(単数タグ)ジャンル検索 タグ検索

やりたいこと ①投稿時にカテゴリータグ追加。 ②カテゴリーのリンク押すと、同じカテゴリーの投稿を取り出せる ("プログラミング"押すと、同じくプログラミングというタグで投稿されてる記事一覧を見れる) まずはカテゴリーを追加できるようにする マイグレーションファイル記述 カテゴリーカラムを追加しました。 class CreateBooks < ActiveRecord::Migration[5.2] def change create_table :books do |t| t.text :title t.text :body t.string :category t.references :user,foreign_key:true t.timestamps end end end Viewファイル form_withで受け取る <%= form_with model:@book,url:books_path,local:true do |f| %> <label>Title</label> <p><%= f.text_field:title %></p> <label>Opinion</label> <p><%= f.text_area:body %></p> <label>カテゴリー</label> <p><%= f.text_field:category %></p> コントローラー修正 カテゴリーも保存できるように修正。 private def book_params params.require(:book).permit(:title,:body,:category) end View 一覧画面にカテゴリー表示する。 <% @books.each do |book| %> <td><%= link_to user_path(book.user.id) do %> <%= attachment_image_tag book.user, :profile_image,format: 'jpeg',size: "40x40", fallback: "no_image.jpg" %> <% end %></td> <td><%= link_to book_path(book.id) do %><%= book.title %></td> <td><%= book.body %></td> <td><%= book.category%></td> ここまではカラムを追加するだけなので、なんてこともなくできました。 タグのリンクで検索できるようにする コントローラー/ルーティング記述 books_controller.rb def search_book @book=Book.new @books = Book.search(params[:keyword]) end routes.rb get "search_book" => "books#search_book" Viewファイル記載 検索ワードを受け取る viewを以下の通りに書き換えます。 <td> <%= link_to book.category, search_book_path(keyword: book.category) %> </td> book.categoryを、search_book_pathにkeywordとして送っています。 そのキーワードをコントローラーで受け取っています。 モデルファイルに検索方法記述 book.rb def self.search(search_word) Book.where(['category LIKE ?', "#{search_word}"]) end 完全一致で検索しています。 self.searchは、Book.searchですね。これはコントローラーから、(search_word)に入る値を送っています。 Viewの記述:検索結果の表示 views/books/search_book.html.erb 省略 <tr> <% @books.each do |book| %> <td><%= link_to user_path(book.user.id) do %> <%= attachment_image_tag book.user, :profile_image,format: 'jpeg',size: "40x40", fallback: "no_image.jpg" %> <% end %></td> <td><%= link_to book_path(book.id) do %><%= book.title %></td> <td><%= book.body %></td> <td><%= link_to book.category, search_book_path(keyword: book.category) %></td> 省略 次回は、複数タグ付けれるようにする!!!!!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rails_admin管理者画面の日本語化

今回は、下記記事で解説した管理者画面が英語表記なので、日本語化していきます。 rails_adminとcancancanを使った実装に関しては、当記事では一切触れませんので、下記記事参照ください。 rails_admin+cancancanで管理者機能実装を5ステップで解説! 手順 gemをインストール デフォルトの言語を日本語に設定 ymlに日本語の設定をする gemをインストール 日本語化するのにまずは、rails-i18というgemをインストールします。 他のエラーメッセージやdeviseの日本語化をしたときに、インストールしている場合は、飛ばしてください。 gem 'rails-i18n' で、 $ bundle install デフォルトの言語を日本語に設定 次に、デフォルトの言語を日本語に設定します。 config/application.rbに以下を追加します。 追加する場所に注意してください。 class Application < Rails::Applicationの中に記述 config/application.rb : module SampleApp class Application < Rails::Application : # 以下1行を追加 config.i18n.default_locale = :ja end end これを記述することで、デフォルトの言語を日本語にできました。 ymlに日本語の設定をする railsで日本語化したいときには、ja.ymlファイルに設定を書いていけばいいので、記述していきます。 config/locales直下にja.ymlファイルを作成してください。 以下、記述をコピペしていきます。 だいたい、テンプレートがあるので、そのままコピーしてきます。(今回のはどこからコピーしたか覚えていないので、コピー先は省略します) config/locales/ja.yml # 日本語化テンプレ ja: activerecord: # 全てのモデル記載 models: user: ユーザー post: 投稿 comment: コメント like: いいね contact: お問い合わせ attributes: # 各モデルのカラム名を記載 user: name: 名前 email: メールアドレス password: パスワード post: content: 説明 image: 画像 comment: comment: コメント内容 contact: name: 名前 email: メールアドレス content: お問い合わせ内容 errors: messages: record_invalid: 'バリデーションに失敗しました: %{errors}' restrict_dependent_destroy: has_one: "%{record}が存在しているので削除できません" has_many: "%{record}が存在しているので削除できません" date: abbr_day_names: - 日 - 月 - 火 - 水 - 木 - 金 - 土 abbr_month_names: - - 1月 - 2月 - 3月 - 4月 - 5月 - 6月 - 7月 - 8月 - 9月 - 10月 - 11月 - 12月 day_names: - 日曜日 - 月曜日 - 火曜日 - 水曜日 - 木曜日 - 金曜日 - 土曜日 formats: default: "%Y/%m/%d" long: "%Y年%m月%d日(%a)" short: "%m/%d" month_names: - - 1月 - 2月 - 3月 - 4月 - 5月 - 6月 - 7月 - 8月 - 9月 - 10月 - 11月 - 12月 order: - :year - :month - :day datetime: distance_in_words: about_x_hours: one: 約1時間 other: 約%{count}時間 about_x_months: one: 約1ヶ月 other: 約%{count}ヶ月 about_x_years: one: 約1年 other: 約%{count}年 almost_x_years: one: 1年弱 other: "%{count}年弱" half_a_minute: 30秒前後 less_than_x_seconds: one: 1秒以内 other: "%{count}秒未満" less_than_x_minutes: one: 1分以内 other: "%{count}分未満" over_x_years: one: 1年以上 other: "%{count}年以上" x_seconds: one: 1秒 other: "%{count}秒" x_minutes: one: 1分 other: "%{count}分" x_days: one: 1日 other: "%{count}日" x_months: one: 1ヶ月 other: "%{count}ヶ月" x_years: one: 1年 other: "%{count}年" prompts: second: 秒 minute: 分 hour: 時 day: 日 month: 月 year: 年 errors: format: "%{attribute}%{message}" messages: accepted: を受諾してください blank: を入力してください confirmation: と%{attribute}の入力が一致しません empty: を入力してください equal_to: は%{count}にしてください even: は偶数にしてください exclusion: は予約されています greater_than: は%{count}より大きい値にしてください greater_than_or_equal_to: は%{count}以上の値にしてください inclusion: は一覧にありません invalid: は不正な値です less_than: は%{count}より小さい値にしてください less_than_or_equal_to: は%{count}以下の値にしてください model_invalid: 'バリデーションに失敗しました: %{errors}' not_a_number: は数値で入力してください not_an_integer: は整数で入力してください odd: は奇数にしてください other_than: は%{count}以外の値にしてください present: は入力しないでください required: を入力してください taken: はすでに存在します too_long: は%{count}文字以内で入力してください too_short: は%{count}文字以上で入力してください wrong_length: は%{count}文字で入力してください template: body: 次の項目を確認してください header: one: "%{model}にエラーが発生しました" other: "%{model}に%{count}個のエラーが発生しました" helpers: select: prompt: 選択してください submit: create: 登録する submit: 保存する update: 更新する number: currency: format: delimiter: "," format: "%n%u" precision: 0 separator: "." significant: false strip_insignificant_zeros: false unit: 円 format: delimiter: "," precision: 3 separator: "." significant: false strip_insignificant_zeros: false human: decimal_units: format: "%n %u" units: billion: 十億 million: 百万 quadrillion: 千兆 thousand: 千 trillion: 兆 unit: '' format: delimiter: '' precision: 3 significant: true strip_insignificant_zeros: true storage_units: format: "%n%u" units: byte: バイト eb: EB gb: GB kb: KB mb: MB pb: PB tb: TB percentage: format: delimiter: '' format: "%n%" precision: format: delimiter: '' support: array: last_word_connector: "、" two_words_connector: "、" words_connector: "、" time: am: 午前 formats: default: "%Y年%m月%d日(%a) %H時%M分%S秒 %z" long: "%Y/%m/%d %H:%M" short: "%m/%d %H:%M" pm: 午後 # rails_admin日本語化 admin: js: true: True false: False is_present: 存在する is_blank: 空白 date: 日付 ... between_and_: ... から ... today: 今日 yesterday: 昨日 this_week: 今週 last_week: 先週 number: 数字 ... contains: 含む is_exactly: 完全に一致 starts_with: で始まる ends_with: で終わる too_many_objects: "対象が多すぎます、上の検索ボックスを使用してください" no_objects: "対象が見つかりません" loading: "読み込み中..." toggle_navigation: ナビゲーション切り替え home: name: "ホーム" pagination: previous: "&laquo; 前" next: "次 &raquo;" truncate: "…" misc: search: "検索" filter: "検索" refresh: "更新" show_all: "すべて表示" add_filter: "絞り込む..." bulk_menu_title: "選択項目を..." remove: "削除" add_new: "新規作成" chosen: "選択された%{name}" chose_all: "すべて選択" clear_all: "選択解除" up: "上" down: "下" navigation: "ナビゲーション" root_navigation: "アクション" navigation_static_label: "リンク" log_out: "ログアウト" time_ago: "%{time}前" ago: "前" more: "さらに %{count} 個以上の %{models_name}" flash: successful: "%{name}の%{action}に成功しました" error: "%{name}の%{action}に失敗しました" noaction: "操作を取り消しました" model_not_found: "モデル'%{model}'はありません" object_not_found: "'ID:%{id}'の%{model}はありません" table_headers: model_name: "モデル名" last_used: "最終アクセス日" records: "レコード数" username: "ユーザ" changes: "変更" created_at: "日時" item: "アイテム" message: "メッセージ" actions: dashboard: title: "サイト管理" menu: "ダッシュボード" breadcrumb: "ダッシュボード" index: title: "%{model_label_plural}の一覧" menu: "一覧" breadcrumb: "%{model_label_plural}" show: title: "%{model_label} '%{object_label}'の詳細" menu: "詳細" breadcrumb: "%{object_label}" show_in_app: menu: "表示" new: title: "新規%{model_label}" menu: "新規作成" breadcrumb: "新規" link: "新規%{model_label}追加" done: "作成" edit: title: "%{model_label} '%{object_label}'を編集" menu: "編集" breadcrumb: "編集" link: "この%{model_label}を編集" done: "更新" delete: title: "%{model_label} '%{object_label}'を削除" menu: "削除" breadcrumb: "削除" link: "'%{object_label}'削除" done: "削除" bulk_delete: title: "%{model_label_plural}を一括削除" menu: "一括削除" breadcrumb: "一括削除" bulk_link: "選択した%{model_label_plural}を削除" export: title: "%{model_label}をエクスポート" menu: "エクスポート" breadcrumb: "エクスポート" link: "%{model_label_plural}をエクスポート" bulk_link: "選択した%{model_label_plural}をエクスポート" done: "エクスポート" history_index: title: "%{model_label_plural}の更新履歴" menu: "履歴" breadcrumb: "履歴" history_show: title: "%{model_label} '%{object_label}'の履歴" menu: "履歴" breadcrumb: "履歴" form: cancel: "キャンセル" basic_info: "基本情報" required: "必須" optional: "オプション" one_char: "文字" char_length_up_to: "最大文字数:" char_length_of: "文字数:" save: "保存" save_and_add_another: "保存して次へ" save_and_edit: "保存して編集" all_of_the_following_related_items_will_be_deleted: "を削除してよろしいですか? 以下の項目が削除され、もしくは関係を失います:" are_you_sure_you_want_to_delete_the_object: "%{model_name}の項目" confirmation: "実行する" bulk_delete: "以下の項目が削除され、それによって関連する項目が削除され、もしくは関係を失います:" new_model: "%{name} (新規)" export: confirmation: "%{name}としてエクスポート" select: "エクスポートするフィールドを選択してください" select_all_fields: "全フィールドを選択" fields_from: "%{name}のフィールド" fields_from_associated: "関連する%{name}のフィールド" display: "カラム:%{name} 型:%{type}" options_for: "%{name}の出力オプション" empty_value_for_associated_objects: "<empty>" click_to_reverse_selection: 'クリックで選択を反転します' csv: header_for_root_methods: "%{name}" # 'model' is available header_for_association_methods: "%{name} [%{association}]" encoding_to: "文字コード" encoding_to_help: "空白の場合は%{name}になります" skip_header: "ヘッダなし" skip_header_help: "チェックすると出力にヘッダ行を含めません" default_col_sep: "," col_sep: "区切り文字" col_sep_help: "空白の場合は'%{value}'区切りです" # value is default_col_sep あ、、めっちゃ長いですねww だいたいはコピペのままでいいのですが、上の方のモデルのところだけ各自変更してください。 コメントアウトで「全てのモデル記載」と書いてあるところに、全てのモデルをモデル名と日本語変換両方書いて、 「各モデルのカラム名を記載」と書いてあるところに、モデル名、カラム名、日本語変換を記載してください。 自由に好きな日本語変換を設定できます。 たとえば、userを「最高のユーザー」なんかにも設定できますw 以上で日本語化できているはずなので、管理者画面に遷移して確認してみてください。 まとめ 日本語化手順は3ステップです! gemをインストール デフォルトの言語を日本語に設定 ymlに日本語の設定をする これで、rails_adminとcancancanを使った管理者機能が完成しました。 rails_adminはカスタマイズ性があまりないので、ここからは特にいじることもありません。 実際に使ってみて、かなりシンプルで直感的に使えるので、操作感になれていきましょう! こちらで実装方法については、5ステップでかんたんに解説してますので、こちらもどうぞ! rails_admin+cancancanで管理者機能実装を5ステップで解説!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】Bootstrapを導入する方法

My Profile プログラミング学習歴②ヶ月目のアカウントです! プログラミングスクールで学んだ内容や自分が躓いた箇所等のアウトプットの為に発信しています。 また、プログラミング初学者の方にわかりやすく、簡潔にまとめて情報共有できればと考えています。 もし、投稿した記事の中に誤り等ございましたら、コメント欄でご教授いただけると幸いです。  対象者 Bootstrapを導入してレイアウトを整えたい方 目的 bootstrapを導入して、使える状態にする 実際の手順と実例 1.Bootstrapとは 「Bootstrap:ブートストラップ」は、Twitter社が開発したCSSの「フレームワーク」 RailsにはCSSのデザインテンプレートが準備されていないので、ページデザインも各自で設計しなければなりません。 Bootstrapを利用することで、整ったデザインを効率的に作れるようになります。 2.Bootstrap導入方法 Gemfileを編集 gemを入れていきます。 下記の「jquery-rails」はBootstrapがjQueryというライブラリに依存しているため、導入します。 最終行に追記 Gemfile : : gem 'bootstrap', '~> 4.5' gem 'jquery-rails' 2.bootstrapファイルを読み込む 直接編集、もしくは下記のコマンドでファイル名を変更します。 $ mv app/assets/stylesheets/application.css app/assets/stylesheets/application.scss 3.bootstrapをSCSSに読み込ませる こちらも最終行に追記 app/assets/stylesheets/application.scss : : @import "bootstrap"; 4.application.jsファイルを編集する app/assets/javascripts/application.js //= require jquery3 //= require popper //= require bootstrap-sprockets これで準備完了です。 3.実際にBootstrapを使ってみる 良く下記のサイトを参考にBootstrapを使っています。 Bootstrap4なので少し古いですが 使えるものばかりなのでぜひ参考にしてみてください
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails, Docker, Postgresql, Production】本番環境で起動させる

目標 本番環境(production)でdockerを用いてrailsを動かす 実行内容 docker-compose.prd.ymlの作成 database.ymlの変更 DBの作成 アセットのプリコンパイル ビルド、起動 1. docker-compose.prd.ymlの作成 docker-compose.prd.yml version: '3' services: db: image: postgres environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_HOST_AUTH_METHOD: 'trust' #実際にデプロイする場合は使わない PGDATA: /var/lib/postgresql/data/pgdata volumes: - ./tmp/db:/var/lib/postgresql/data web: build: . command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" environment: RAILS_ENV: production  #これを追加する。 volumes: - .:/app - gem_data:/usr/local/bundle ports: - "3000:3000" depends_on: - db volumes: gem_data: development環境で動かしていたdocker-compose.ymlとは別で、docker-compose.prd.ymlを作成しwebのenvironmentにはRAILS_ENV=productionを指定する。 また、productionとtestではdatabase.ymlのhost: dbを指定することができるがproductionではできないためとりあえずPOSTGRES_HOST_AUTH_METHOD: 'trust'を設定する。デプロイする際には環境変数を使って設定する。 2. database.ymlの変更 database.yml production: <<: *default adapter: postgresql encoding: unicode pool: 5 database: app_production username: postgres password: postgres username, passwordにはdocker-compose.prd.ymlのPOSTGRES_USER,POSTGRES_PASSWORDの値を設定する。(デプロイの際は環境変数を用いる)hostはdocker-compose.prd.ymlでPOSTGRES_HOST_AUTH_METHOD: 'trust'を設定しているため特に設定しない。 3. DBの作成 docker-compose -f docker-compose.prd.yml run web rails db:create RAILS_ENV=production -f docker-compose.prd.ymlをつけ忘れないように気を付ける。 docker-compose -f docker-compose.prd.yml run web rails db:migrate RAILS_ENV=production 4. アセットのプリコンパイル docker-compose run web rails assets:precompile RAILS_ENV=production 本番環境の場合、アセットのプリコンパイルをしておかないと起動しない。 5. ビルドして起動 docker-compose -f docker-compose.prd.yml build docker-compose -f docker-compose.prd.yml up 補足 development環境では読み込まれていた画像が表示されなかったりcssが適用されない,httpのメソッドがDELETEで指定しているのにGETでアクセスされているなどがあった場合 config/environments/production.rbで次のように変更する。 production.rb config.public_file_server.enabled = true 参考にしたサイト  Dockerでコンテナ化したrailsアプリをproductionモードで起動する  【初めての環境構築】Windows10にRails6+MySQL8.0+Dockerな環境を作ってみた まとめ 改善点や間違っているところがあればぜひ意見をお願いします!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む