20210426のRailsに関する記事は21件です。

【Google Maps API】本番環境でGoogle Mapが正しく読み込まれない原因と解決法

本番環境でGoogle Mapを表示できない このページではGoogleマップが正しく読み込まれませんでした。 結論 APIキーを config/credentials.yml.encファイルで管理する (.envファイルでなく) 原因 本番環境にAPIキーを渡せていない 経緯 Google Maps API 導入 ローカル環境でマップ表示できた APIキーを.envファイルで管理していた .envファイルは外部から見えぬよう、Gitの対象から外していた AWSにデプロイ後、ドメイン取得、HTTPS化してアクセスすると、マップ表示できなくなった 本番環境にAPIキーを渡せていないのかも?(仮説) 本番環境からは.envファイルを参照できない! 本番環境にも、APIキーを隠して渡す config.credentials.yml.encファイルにAPIキーを記述 ローカルのターミナル EDITOR=vi rails credentials:edit #vimでファイルを開く config/credentials.yml.enc google_map_api_key: xxxxxxxxxxxxxxxxxxxxxxxx #APIキーを追記 ビューで、config/credentials.yml.encファイルの記述を読み取る map.html.erb <script src="https://maps.googleapis.com/maps/api/js?key=<%= Rails.application.credentials.google_map_api_key %>&callback=initMap" async defer></script> #<%= Rails.application.credentials.google_map_api_key %> #これで「config.credentials.yml.enc」ファイルの「google_map_api_key」を取得できる キーの読み取り方(記述の違い) <%= Rails.application.credentials.google_map_api_key %> #credentials.yml.encファイルからキー取得 <%= ENV['google_map_api_key'] %> #.envファイルからキー取得 Google Mapが表示されない時の確認事項 取得したAPIキーをhtml内に記述しているか 支払い情報を登録しているか 請求先アカウントを未設定の場合、マップが1日1回しか表示できないらしい 表示回数の制限「割り当て」が1回になっていないか 回数制限がかかっているとMapが表示エラーになる可能性がある 請求アカウントとプロジェクトがひも付いているか 参考 GoogleMapが表示されないエラーが出た時の対処法 Rails5.2から追加された credentials.yml.enc のキホン さいごに Google Maps API導入は比較的スムーズに設定できました。(ローカルで) マップがうまく読み込まれない主な原因は、Google Cloud Platformでの設定が多いようですが、 APIキーを本番環境に渡せていないのは盲点でした。 パスワードやキーの管理方法について、セキュリティ面を見直すいい機会とはなりました。 間違っている点がありましたら、ご指摘いただけるとありがたいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Google Maps API】本番環境でGoogle Mapが正しく読み込まれなかった原因と解決法

本番環境でGoogle Mapを表示できない このページではGoogleマップが正しく読み込まれませんでした。 結論 APIキーを config/credentials.yml.encファイルで管理する (.envファイルでなく) 原因 本番環境にAPIキーを渡せていない 新しく取得したキー設定の誤りだと思ったら、キーの管理方法という基礎部分の誤りでした。。 経緯 Google Maps API 導入 ローカル環境でマップ表示できた APIキーを.envファイルで管理していた .envファイルは外部から見えぬよう、Gitの対象から外していた AWSにデプロイ後、ドメイン取得、HTTPS化してアクセスすると、マップ表示できなくなった 本番環境にAPIキーを渡せていないのかも?(仮説) 本番環境からは.envファイルを参照できない! 本番環境にも、APIキーを隠して渡す config.credentials.yml.encファイルにAPIキーを記述 ローカルのターミナル EDITOR=vi rails credentials:edit #vimでファイルを開く config/credentials.yml.enc google_map_api_key: xxxxxxxxxxxxxxxxxxxxxxxx #APIキーを追記 ビューで、config/credentials.yml.encファイルの記述を読み取る map.html.erb <script src="https://maps.googleapis.com/maps/api/js?key=<%= Rails.application.credentials.google_map_api_key %>&callback=initMap" async defer></script> #<%= Rails.application.credentials.google_map_api_key %> #これで「config.credentials.yml.enc」ファイルの「google_map_api_key」を取得できる キーの読み取り方(記述の違い) <%= Rails.application.credentials.google_map_api_key %> #credentials.yml.encファイルからキー取得 <%= ENV['google_map_api_key'] %> #.envファイルからキー取得 Google Mapが表示されない時の確認事項 取得したAPIキーをhtml内に記述しているか 支払い情報を登録しているか 請求先アカウントを未設定の場合、マップが1日1回しか表示できないらしい 表示回数の制限「割り当て」が1回になっていないか 回数制限がかかっているとMapが表示エラーになる可能性がある 請求アカウントとプロジェクトがひも付いているか 参考 GoogleMapが表示されないエラーが出た時の対処法 Rails5.2から追加された credentials.yml.enc のキホン さいごに Google Maps API導入は比較的スムーズに設定できました。(ローカルで) マップがうまく読み込まれない主な原因は、Google Cloud Platformでの設定が多いようですが、 APIキーを本番環境に渡せていないのは盲点でした。 キーやパスワードの管理方法について、セキュリティ面を見直すいい機会とはなりました。 間違っている点がありましたら、ご指摘いただけるとありがたいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsルーティング よく使用するメソッドのまとめ

はじめに ルーティングのメソッドについてふわっとした理解のままでいたので、改めて今まで使ってきたもの、プラスそのメソッド周りのオプションをまとめてみた。 ・resources ・only(オプション) ・except(オプション) ・collection ・member ・on(オプション) ・namespace ・scope ・as(オプション) ・controller resources CRUD機能のルートを一式定義できる resources :blogs :only onlyオプションは特定のルーティングを作成 resources :blogs, only: [:index] :except exceptオプションはonlyの反対で指定したアクションは作成しない resources :blogs, except: [:edit, update] resourcesにブロックを渡す collection, member collection idを必要としないルーティングの生成/blogs/confirm resources :blogs do collection do get 'confirm' end end member idを必要とするルーティングの生成/blogs/1/preview resources :blogs do member do get 'preview' end end on onオプションはcollection, memberのブロック内にルーティングが1つしかない場合は省略して書くことができる。 resources :blogs do get 'confirm', on: :collection end resources :blogs do get 'preview', on: :member end namespaceとscope namespace 多数のコントローラ群をまとめることができる ファイル構成も指定される これによりURL、名前付きルーティングヘルパー(パス)などが生成される controllers/admin/users_controller.rb namespace :admin do resources :users end 例 Helper  HTTP  URI Pattern        Controller#Action admin_users GET /admin/users(.:format) admin/users#index scope URLのパスの指定のみ ファイル構成、ルーティングヘルパーは変わらない controller/articles_controller.rb scope :blogs do resources :articles end 例 Helper  HTTP  URI Pattern        Controller#Action articles GET /blog/articles(.:format) articles#index as scopeにasオプションを使用すると、ルーティングヘルパーに名前をつけることができる (他にも:modle, :pathを指定できる) controller/articles_controller.rb scope :blogs do resources :articles, as: 'hoge_articles' end 例 ヘルパーのみ変更されてる Helper  HTTP  URI Pattern        Controller#Action hoge_articles GET /blogs/articles(.:format) articles#index namespaceスコープは:moduleや:pathプレフィックスに加えて:asも自動的に追加される。 controller コントローラーを指定してコードをまとめることができる。 構造と結びつけて記述することで可読性があがる controller :blogs do scope :blogs do resources :articles, as: 'hoge_articles' end end ネストしたルーティング 親子関係をルーティングで表現できる 親に加えて、ネストされた子のルーティングも生成される resources :admins do resources :users end (namespaceと何が違うの??ということで上記のnamespaceと同じコントローラー名で比較してみた。) URL: /admins/:admin_id/users(.:format)親のidが追加されていた、以上 Helper  HTTP  URI Pattern        Controller#Action admins_users GET /admins/:admin_id/users(.:format) users#index ※ネストは1回にとどめること、2回以上はルーティングが扱いにくくなる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails ルーティングの色々

はじめに ルーティングのメソッドについてふわっとした理解のままでいたので、改めて今まで使ってきたもの、プラスそのメソッド周りのオプションをまとめてみた。 ・resources ・only(オプション) ・except(オプション) ・collection ・member ・on(オプション) ・namespace ・scope ・as(オプション) ・controller resources CRUD機能のルートを一式定義できる resources :blogs :only onlyオプションは特定のルーティングを作成 resources :blogs, only: [:index] :except exceptオプションはonlyの反対で指定したアクションは作成しない resources :blogs, except: [:edit, update] resourcesにブロックを渡す collection, member collection idを必要としないルーティングの生成/blogs/confirm resources :blogs do collection do get 'confirm' end end member idを必要とするルーティングの生成/blogs/1/preview resources :blogs do member do get 'preview' end end on onオプションはcollection, memberのブロック内にルーティングが1つしかない場合は省略して書くことができる。 resources :blogs do get 'confirm', on: :collection end resources :blogs do get 'preview', on: :member end namespaceとscope namespace 多数のコントローラ群をまとめることができる ファイル構成も指定される これによりURL、名前付きルーティングヘルパー(パス)などが生成される controllers/admin/users_controller.rb namespace :admin do resources :users end 例 Helper  HTTP  URI Pattern        Controller#Action admin_users GET /admin/users(.:format) admin/users#index scope URLのパスの指定のみ ファイル構成、ルーティングヘルパーは変わらない controller/articles_controller.rb scope :blogs do resources :articles end 例 Helper  HTTP  URI Pattern        Controller#Action articles GET /blog/articles(.:format) articles#index as scopeにasオプションを使用すると、ルーティングヘルパーに名前をつけることができる (他にも:modle, :pathを指定できる) controller/articles_controller.rb scope :blogs do resources :articles, as: 'hoge_articles' end 例 ヘルパーのみ変更されてる Helper  HTTP  URI Pattern        Controller#Action hoge_articles GET /blogs/articles(.:format) articles#index namespaceスコープは:moduleや:pathプレフィックスに加えて:asも自動的に追加される。 controller コントローラーを指定できる。まとめることができる。 構造と結びつけて記述することで可読性があがる controller :blogs do scope :blogs do resources :articles, as: 'hoge_articles' end end ネストしたルーティング 親子関係をルーティングで表現できる 親に加えて、ネストされた子のルーティングも生成される resources :admins do resources :users end (namespaceと何が違うの??ということで上記のnamespaceと同じコントローラー名で比較してみた。) URL: /admins/:admin_id/users(.:format)親のidが追加されていた、以上 Helper  HTTP  URI Pattern        Controller#Action admins_users GET /admins/:admin_id/users(.:format) users#index ※ネストは1回にとどめること、2回以上はルーティングが扱いにくくなる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[メモ]Dockerの環境構築+Rails APIモードのまとめ

初めに 最近はDockerとRailsのAPIモードを学習してるので、 学んだことをまとめてメモしておきます。 まずはDocker環境構築から Dockerの環境構築は主に以下の記事の参考で作成していきます。 最初はまず作業用のファイルを用意します $ mkdir myapp Dockerfile作成 rubyのバージョンは自分が今使ってる2.7.2に変更してみます。 Dockerfile FROM ruby:2.7.2 RUN apt-get update -qq && apt-get install -y nodejs postgresql-client RUN apt-get update && apt-get install -y curl apt-transport-https wget && \ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \ echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \ apt-get update && apt-get install -y yarn RUN curl -sL https://deb.nodesource.com/setup_7.x | bash - && \ apt-get install nodejs RUN mkdir /myapp WORKDIR /myapp COPY Gemfile /myapp/Gemfile COPY Gemfile.lock /myapp/Gemfile.lock RUN bundle install COPY . /myapp # Add a script to be executed every time the container starts. COPY entrypoint.sh /usr/bin/ RUN chmod +x /usr/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"] EXPOSE 3000 # Start the main process. CMD ["rails", "server", "-b", "0.0.0.0"] P.S.Docker 入門 1項目ずつ理解するでは、Dockerfileの書き方について分かりやすく説明していますので、是非ご参考ください。 Gemfile作成 myapp/Gemfile source 'https://rubygems.org' gem 'rails', '~> 6' Gemfile.lock 作成 $ touch Gemfile.lock 中身は空のままでオッケー entrypoint.sh作成 $ touch entrypoint.sh myapp/entrypoint.sh #!/bin/bash set -e # Remove a potentially pre-existing server.pid for Rails. rm -f /myapp/tmp/pids/server.pid # Then exec the container's main process (what's set as CMD in the Dockerfile). exec "$@" docker-compose.yml 作成 $ touch docker-compose.yml myapp/docker-compose.yml version: "3.9" services: db: image: postgres volumes: - ./tmp/db:/var/lib/postgresql/data environment: POSTGRES_HOST_AUTH_METHOD: 'trust' web: build: . command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" volumes: - .:/myapp ports: - "3000:3000" depends_on: - db P.S.Docker 入門 1項目ずつ理解する これで下準備完了 ファイルの中はこんな感じ: myapp - Dockerfile - Gemfile - Gemfile.lock - entrypoint.sh - docker-compose.yml いよいよRails new 今回RailsのAPI開発モードを利用していますので、--apiオプションを使います。 -Tはデフォルトのテスティングフレームワーク「Minitest」をオフにするオプションです。 APIモード使わない場合は--apiを自由に削除してください。 $ docker-compose run web rails new . --force --no-deps --database=postgresql --api -T ls -lコマンドでみると。。。 $ ls -l total 64 -rw-r--r-- 1 tsuki_ staff 831 Apr 24 23:02 Dockerfile -rw-r--r-- 1 tsuki_ staff 1397 Apr 24 23:48 Gemfile -rw-r--r-- 1 tsuki_ staff 4022 Apr 24 23:48 Gemfile.lock -rw-r--r-- 1 tsuki_ staff 374 Apr 24 23:48 README.md -rw-r--r-- 1 tsuki_ staff 227 Apr 24 23:48 Rakefile drwxr-xr-x 8 tsuki_ staff 256 Apr 24 23:48 app drwxr-xr-x 7 tsuki_ staff 224 Apr 24 23:48 bin drwxr-xr-x 16 tsuki_ staff 512 Apr 24 23:48 config -rw-r--r-- 1 tsuki_ staff 160 Apr 24 23:48 config.ru drwxr-xr-x 3 tsuki_ staff 96 Apr 24 23:48 db -rw-r--r-- 1 tsuki_ staff 366 Apr 24 23:29 docker-compose.yml -rw-r--r-- 1 tsuki_ staff 201 Apr 24 23:23 entrypoint.sh drwxr-xr-x 3 tsuki_ staff 96 Apr 24 23:48 lib drwxr-xr-x 3 tsuki_ staff 96 Apr 24 23:48 log drwxr-xr-x 3 tsuki_ staff 96 Apr 24 23:48 public drwxr-xr-x 3 tsuki_ staff 96 Apr 24 23:48 storage drwxr-xr-x 7 tsuki_ staff 224 Apr 24 23:48 tmp drwxr-xr-x 3 tsuki_ staff 96 Apr 24 23:48 vendor 無事にRailsが生成されてますね! bundle install $ docker-compose build ぬぬ!?エラーが出ました ...[前略] ------ > [ 9/12] RUN bundle install: #14 0.294 /usr/local/lib/ruby/2.7.0/rubygems.rb:277:in `find_spec_for_exe': Could not find 'bundler' (1.17.2) required by your /myapp/Gemfile.lock. (Gem::GemNotFoundException) #14 0.294 To update to the latest version installed on your system, run `bundle update --bundler`. #14 0.294 To install the missing version, run `gem install bundler:1.17.2` #14 0.296 from /usr/local/lib/ruby/2.7.0/rubygems.rb:296:in `activate_bin_path' #14 0.296 from ------ ...[後略] run `gem install bundler:1.17.2` と書いたので、追加してみます。 Dockerfile RUN mkdir /myapp WORKDIR /myapp COPY Gemfile /myapp/Gemfile COPY Gemfile.lock /myapp/Gemfile.lock ########追加########### RUN gem install bundler:1.17.2 ###################### RUN bundle install COPY . /myapp はい、$ docker-compose build ぬぬ!?また新しいエラーが出てきました。 ...[前略] ------ > [10/13] RUN bundle install: #14 0.734 Your Ruby version is 2.7.2, but your Gemfile specified 2.6.5 ------ ...[後略] そっか、GemfileのRubyバージョンは2.6.5だったのか はい、変更! Gemfile #変更前 ruby '2.6.5' #変更後 ruby '2.7.2' 。。。 はい、$ docker-compose build! ...[前略] Successfully built..... ...[後略] 今度こそ無事にできたんですね! DBへの接続設定 Railsのconfig/database.ymlファイルを、下記のように書き換えます。 database.yml default: &default adapter: postgresql encoding: unicode host: db username: postgres password: pool: 5 development: <<: *default database: myapp_development test: <<: *default database: myapp_test Dockerコンテナの起動 では、Dockerを起動してみます。 $ docker-compose up したら、安心する画面ができましたね! ...[前略] web_1 | => Booting Puma web_1 | => Rails 6.1.3.1 application starting in development web_1 | => Run `bin/rails server --help` for more startup options web_1 | Puma starting in single mode... web_1 | * Puma version: 5.2.2 (ruby 2.7.2-p137) ("Fettisdagsbulle") web_1 | * Min threads: 5 web_1 | * Max threads: 5 web_1 | * Environment: development web_1 | * PID: 1 web_1 | * Listening on http://0.0.0.0:3000 web_1 | Use Ctrl-C to stop http://0.0.0.0:3000にアクセスしてみると ぬぬ!?またエラーの画面が出てきました。 ...[前略] web_1 | ActiveRecord::NoDatabaseError (FATAL: database "myapp_development" does not exist web_1 | ): ...[後略] そかそかdatebaseの問題ね! では一旦 $ docker-compose down P.s.筆者はもう一つターミナルを開いて実行していますが、 こちらの【Docker超入門 #7】Docker ComposeでRailsを構築しようの19:55からのところは、 docker-compose upをバックグラウンドで実行してくれることについて説明がありますので、 必要な方は是非参考してみてください。 DB作成 $ docker-compose run web rails db:create そして $ docker-compose up 無事に環境構築できたそうですね!! Dockerの環境でAPIモードでAPIを作ってみよう この記事の参考で、Docker内でAPIを作っていきます。 テスト環境 まずはテスト環境を先に用意していきます。今回はRspecを使います。 Gemfile group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] #⇩追加 gem 'rspec-rails' end そしてdocker-compose buildでbundle install 次はこちらの記事:dockerでよく使うコマンド一覧【Rails】で学んだDockerのコマンドで Rspecのインストールを行います。 $ docker-compose run web bundle exec rails g rspec:install それでspecファイルが生成されます。 モデル.コントローラの作成 では今回は簡単なタスク機能のアプリを作っていきます。 $ docker-compose run web bundle exec rails g model post title:string content:text $ docker-compose run web bundle exec rails g controller posts $ docker-compose run web bundle exec rails db:create $ docker-compose run web bundle exec rails db:migrate ルーティングの設定 config/routes.rb Rails.application.routes.draw do namespace 'api' do namespace 'v1' do resources :posts end end end コントローラの設定 ルートで設定した名前空間に合わせてディレクトリの構成を以下のようにします。 controllers/api/v1/posts_controller.rb module Api module V1 class PostsController < ApplicationController before_action :set_post, only: [:show, :update, :destroy] def index posts = Post.order(created_at: :desc) render json: { status: 'SUCCESS', message: 'Loaded posts', data: posts } end def show render json: { status: 'SUCCESS', message: 'Loaded the post', data: @post } end def create post = Post.new(post_params) if post.save render json: { status: 'SUCCESS', data: post } else render json: { status: 'ERROR', data: post.errors } end end def destroy @post.destroy render json: { status: 'SUCCESS', message: 'Deleted the post', data: @post } end def update if @post.update(post_params) render json: { status: 'SUCCESS', message: 'Updated the post', data: @post } else render json: { status: 'SUCCESS', message: 'Not updated', data: @post.errors } end end private def set_post @post = Post.find(params[:id]) end def post_params params.require(:post).permit(:title,:content) end end end end 初期データ作成 今回はseedで作ってみます。 seedの書き方はこの記事の参考しています→railsのseedの書き方いろいろ config/db/seeds 15.times do |i| Post.create!( title: "title#{i+1}", content: "テキスト#{i+1}" * 10 ) end seed書いたら $ docker-compose run web rails db:seed エラーが出ないので、無事にできたようです。 postmanを使用して動作の確認 データはseedで生成したので Railsのサーバーを動かしてみます。 $ docker-compose up 次はPostmanを開きます。 GET まずはGETの方にhttp://localhost:3000/api/v1/posts 、もしくは http://0.0.0.0:3000/api/v1/posts と記述していきます。 下の方にidの1〜15番のデータを取得することができました。 今度はidを指定します。 POST 次はデータ作成してみましょう。 JSONのデータ形式で渡します。 Sendするとちゃんとid16が表示されました。 PUT 今回はURLをidを16に指定します http://0.0.0.0:3000/api/v1/posts/16 登録したデータをupdate更新してみます。 PUTの選択とJSON形式の設定を忘れないように DELETE 最後に削除(Destroy)してみます。 同じURLをidを16に指定します http://0.0.0.0:3000/api/v1/posts/16 DELETEの選択で実行すれば、 無事に削除できたようですね まとめ 以上、Dockerの環境構築+RailsのAPIモードのまとめでした。 今回Rspecのテストは書かないのですが、 こちらの記事:【Rails】APIテストの書き方がありますので、興味がある方はご参考ください もし内容の誤りがありましたら、ご指摘いただければ幸いです。 最後まで読んでいただき、ありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RSpecで_spec.rbファイルの先頭行にrequire 'rails_helper'を忘れたときのエラー

こんなエラーになりました。 spec/models/address_purchase_spec.rb % bundle exec rspec spec/models/address_purchase_spec.rb An error occurred while loading ./spec/models/address_purchase_spec.rb. Failure/Error: RSpec.describe AddressPurchase, type: :model do describe '商品購入テスト' do before do # @address_purchase = FactoryBot.build(:address_purchase) end it 'すべての値が正しく入力されていれば保存できること' do end it '郵便番号が空だと保存できないこと' do @address_purchase.postal_code = '' NameError: uninitialized constant AddressPurchase # ./spec/models/address_purchase_spec.rb:2:in `<top (required)>' No examples found. ちなみにrequire 'rails_helper'は のrails_helper.rbを読みにいっています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

投稿を検索する機能

今回 ・投稿を検索する機能をつける。 ・テストは後日改めて書きます。 投稿検索について ・ログイン時にホームの画面に投稿検索ができるような形にする。 実装 ・投稿検索用のフォームを付けたいと思います。 app/views/shared/_microposts_search_form.html.erb <%= search_form_for @q, url: @url do |f| %> <%= f.label :content_cont, '投稿検索' %> <div class = "input-group"> <%= f.text_field :content_cont, placeholder: "キーワードで検索...。"%> <%= f.submit '検索' %> </div> <% end %> ・検索条件のcontent_contはMicropostのcontentからの検索です。 ・ユーザー検索フォームと違うのは、url: @urlの部分です。 ・投稿検索時に、urlを指定しなければ、検索実行時にエラーが出てしまいます。(エラーの詳細は割愛) ・検索フォームが完成したので、次にcontrollerの実装に移ります。 ・ホーム画面のフィードを検索するためにstatic_pages_controllerのhomeアクションに処理を加えます。 app/controllers/static_pages_controller def home if logged_in? @micropost = current_user.microposts.build if params[:q] && params[:q].reject { |key,value| value.blank? }.present? @q = current_user.feed.ransack(microposts_search_params) @feed_items = @q.result.paginate(page: params[:page]) else @q = Micropost.none.ransack @feed_items = current_user.feed.paginate(page: params[:page]) end @url = root_path end end ・microposts_search_paramsは、applicationControllerに設置しました。 app/controllers/application_controller #投稿検索機能 def microposts_search_params params.require(:q).permit(:content_cont) end ・micropostControllerのcreateアクションに@qとransackの処理を追加します。 app/controllers/microposts_controller.rb def create @micropost = current_user.microposts.build(micropost_params) if @micropost.save flash[:success] = "投稿完了" redirect_to root_url else @q = Micropost.none.ransack @feed_items = current_user.feed.paginate(page: params[:page]) render'static_pages/home' end end ・追加するのは投稿が失敗した際の処理のみです。 ・失敗した場合だけmicropostContorollerからホームのviewをrender(redirect_toではなく)しているた め、@qが必要になります。 ・viewに戻って最初に作った_microposts_search_form.html.erbをhomeに挿入します。 app/views/static_pages/home.html.erb <% provide(:title,"ホーム") %> <h1>ホームページ</h1> <p>パパっ子</p> <h1>解決した投稿</h1> <% if logged_in? %> <section class = "user_info"> <%= render'shared/user_info' %> </section> <section class = "micropost_form"> <%= render'shared/micropost_form' %> </section> <h3>投稿</h3> <div class="search_form"> <%= render 'shared/microposts_search_form' %>  </div> <%= render 'shared/feed' %> <% end %> ・こんな感じです。終わりです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails6] 共通処理をbefore_actionにまとめる

概要 コントローラのなかで処理を共通化したい場合の方法をしてbefore_actionを使うことを学んだので、 その方法をメモします。 方法 idからDBを一つ取得する処理を共通化する privateに共通化する処理を記述 private def set_question @question = Question.find(params[:id]) end before_actionを記述 class QuestionsController < ApplicationController before_action :set_question end 実行するアクションを指定したい場合はonlyオプションを追加で記述 class QuestionsController < ApplicationController before_action :set_question, only: [:show, :edit, :destroy, :update] end 実行しないアクションを指定する場合はexceptオプションを追加で記述 class QuestionsController < ApplicationController before_action :set_question, except: [:index] end 参考文献 【Rails】before_actionの使い方を徹底解説!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]ActiveHash使用の際のエラー(undefined method `current_scope' )

はじめに とてもしょーもないミスで1時間も浪費しましたので、二度と内容にこちらに備忘としてアウトプットいたします。 発生したエラー 以下のようなエラーが出ました。最初は誤字等を睨んだのですが、、そんなことはなく、、 解決方法 こちらの一文を書くことを忘れていました。 game.rb class Game < ApplicationRecord #この一文 extend ActiveHash::Associations::ActiveRecordExtensions  belongs_to :level; ActiveHashを用いて、belongs_toを設定するには、 extend ActiveHash::Associations::ActiveRecordExtensionsと記述してmoduleを取り込む必要がありました。 終わりに エラーが出たときは、基礎の基礎から立ち返ることをここに誓います(笑) 時間は大切に。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails Dir.tmpdir 一時的なディレクトリを作成する

この記事について Dir.tmpdirを使用して一時的なディレクトリを作成する方法を書いた記事。 https://docs.ruby-lang.org/ja/latest/method/Dir/s/mktmpdir.html どんな時に必要なの? 1度限りの画像を生成したくてtmp/hoge.pngと決め打ちすると、複数セッションだと同じ画像が共有されてしまう可能性がある。そんな時にユーザー別にディレクトリを用意してやって共有できないようにしてやる。そこでDir.tmpdirを使う! 他にも単純に「一時的に欲しい」という時に使えれば便利かも。 作成方法その① ブロックを与える ブロックの評価が終わると作成された一時ディレクトリやその配下にあったファイルをFileUtils.#remove_entryで勝手に削除してくれる。 require 'tmpdir' Dir.mktmpdir{|dir| puts dir #=> /var/folders/11/rq5ty2dj3_v0gybj_pmpl2gr0000gn/T/d20210426-40593-1f65en } puts dir #=> NameError (undefined local variable or method `dir' for main:Object) # ブロックを抜けると作成されたディレクトリ及びその以下のファイルは削除される。 でも、時には自分の好きなタイミングで一時ディレクトリを削除したいよね!!! 作成方法その② ブロックを与えずに生成 ブロックを与えずに作成するにはDir.mktmpdirを使用する。 明示的に削除するためにはFileUtils.remove_entry_secureを使用する。 require 'tmpdir' tmp_dir = Dir.mktmpdir puts tmp_dir #=> /var/folders/11/rq5ty2dj3_v0gybj_pmpl2gr0000gn/T/d20210426-40593-r8sklf # ブロックを与えない場合は、ディレクトリは存在する。 FileUtils.remove_entry_secure tmp_dir # FileUtils.remove_entry_securで明示的に削除するまで、作成されたディレクトリは存在する 変わった使い方(決まった単語を入れたい時) ディレクトリ名の先頭に決まった単語を入れたい場合 require 'tmpdir' tmp_dir = Dir.mktmpdir("foo") Dir.mktmpdir("foo"){|dir| puts dir # 出力例:一時ディレクトリ の名前の先頭に'foo' をつける。 #=> /var/folders/11/rq5ty2dj3_v0gybj_pmpl2gr0000gn/T/foo20210426-40593-bujxit" # ^^^ } ディレクトリ名の先頭と末尾に決まった単語を入れたい場合 require 'tmpdir' Dir.mktmpdir(["foo", "bar"]){|dir| puts dir # 出力例: 一時ディレクトリの名前の先頭に'foo' 、最後に'bar'をつける。 #=> /var/folders/11/rq5ty2dj3_v0gybj_pmpl2gr0000gn/T/foo20210426-40593-bujxitbar" # ^^^ ^^^ } ディレクトリ名の末尾に決まった単語を入れたい場合 require 'tmpdir' Dir.mktmpdir([nil, "bar"]){|dir| puts dir # 出力例: 一時ディレクトリの名前の先頭に'foo' 、最後に'bar'をつける。 #=> /var/folders/11/rq5ty2dj3_v0gybj_pmpl2gr0000gn/T/20210426-40593-bujxitbar" # ^^^ } 補足 入れたい単語を指定しない場合(nil)は、ディレクトリ名の先頭にdが付帯される。 /var/folders/11/rq5ty2dj3_v0gybj_pmpl2gr0000gn/T/d20210426-40593-1f65en #                            ^
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

railsの検索フォームで簡単に入力した値を戻す

概要 検索用フォームは、例えばクエリに付く変数名を短くしたいとか、複数のモデルにまたがるとか、単純にカラムに対応してない値で検索させたいなど、モデルを使わず実装することが多いと思います。 モデルを使わないとinput-textやtextareaならまだしも、select、checkboxなどは入力させた値をHTMLタグに反映させるのに、IF文で分岐して属性値を書き込むので結構結構めんどくさいです。数が増えればなおさら。編集フォームの表にサクッと値を戻したい!というのが今回のテーマです。 ポイント ポイントは3つです。 ActiveModel::ModelとActiveModel::Attributesを使って検索専用のモデルを作る。 ActiveRecord::Type::Valueを継承したカスタムタイプを使う。 パラメータにモデルの名前がつかないようにする。 検索用のモデル # app/models/searches/sales_type.rb module Searches class SalesType include ActiveModel::Model include ActiveModel::Attributes attribute :cid, :integer attribute :itid, :integer attribute :price_type, :string attribute :exclude_percent, :string attribute :user_relation, :string attribute :share, :string attribute :shop_ids_and, :boolean attribute :shop_ids, :integer_array end end 読み込めるところだったらどこでもいいのですが、今回はapp/models/searches/に作ってみました。型に関してですが、booleanなんかは飛んでくるのは'1'、'0'なんですけど、form_withがモデルを想定してるのでtrue、falseでもうまく値を戻してくれます。もちろんここは:stringでも期待通りの動きをします。:integerになってるところも:stringでも大丈夫ですね。まあ、なんとなく実際の型に合わせといたほうが落ち着きがいいかなと思いこうしました。 問題はinteger_arrayになってるところですね。これは後述するカスタムタイプです。ここはフォームを作るところで下記のように生成しています。 <%= form.collection_check_boxes(:shop_ids, Shop.alived.ordered, :id, :name, include_hidden: false) do |cb|%> <div class="form-check form-check-inline"> <%= cb.check_box class: 'form-check-input' %> <%= cb.label class: 'form-check-label' do%> <%= cb.text %> <%end%> </div> <%- end -%> こうするとidはintegerの配列になり、最終的なチェックはinclude?で見ていますので文字列の配列だとチェックが戻りません(ここで結構ハマりました)。 カスタムタイプ # config/initializers/active_model_types.rb class IntegerArray < ActiveRecord::Type::Value def cast_value(value) value.map(&:to_i) end end ActiveModel::Type.register(:integer_array, IntegerArray) カスタムタイプはフルにモデルで使うなら多分もっと色々設定しないとダメだと思いますが、今回は値を戻すためだけなのでこれだけでOKです。 パラメータからモデル名を削除 で、このモデルをViewで使えるようにして def index @for_search = Searches::SalesType.new(params.permit( :cid, :itid, :price_type, :exclude_percent, :user_relation, :share, :shop_ids_and, shop_ids: [] )) <%= form_with model: @for_search, url: sales_type_index_path, local: true, method: :get do |form| %> viewでこんな感じしてやれば値は戻るのですが、パラメターの名前にsearches_sales_type[cid]というながったらしいscope名が付きます。まあ、これでもrequireしてやれば動きますが、項目が多かったり、選択肢の多い配列があるとURLが長くなっちゃいます(URLの長さについては色々な調査をおこっなってる方がいらっしゃりますし、SEO的な観点の意見も散見されます。気になるか方は一度調べてみて下さい。)。 そこでsearches_sales_typeを外せないの?と思ってソースを追ってみたらscopeに空文字を渡すとなくなるのがわかりました。 <%= form_with model: @for_search, scope: '', url: sales_type_index_path, local: true, method: :get do |form| %> ここでscope ||= ...となっているのでnilでなければ空文字になり、名前はcidだけになります。値も無事戻りました。 ただ、ドキュメントにはないのでバージョンでどうなるかわかりません。そこは留意しておいてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby on rails 結合テスト

勉強用に残して置きたかったやつです 初心者なので間違ってる箇所があると思います。 結合テストコード 結合テストコードは、ユーザーがたどる一連の流れを確認するもの。 System Spec System Spec(システムスペック)は結合テストコードを記述するための仕組みのことを言います。大枠の記述はこれまでのRSpecと変わりません。 System Specを記述するためには、CapybaraというGemを用います。これはすでにデフォルトでRuby on Railsに搭載されています。 すなわち導入済みなのでそのままいけます!! テストコードを書くためのファイルを用意しよう 以下のコマンドを実行して、ユーザーに関する結合テストコードを書くためのファイルを生成しましょう。 % rails g rspec:system users 新規登録がうまくいくときのテストコードを書こう visit 〇〇_pathのように記述すると、〇〇のページへ遷移することを表現できます。 it '正しい情報を入力すればユーザー新規登録ができてトップページに移動する' do # トップページに移動する visit root_path # トップページにサインアップページへ遷移するボタンがあることを確認する # 新規登録ページへ移動する # ユーザー情報を入力する # サインアップボタンを押すとユーザーモデルのカウントが1上がることを確認する # トップページへ遷移したことを確認する # カーソルを合わせるとログアウトボタンが表示されることを確認する # サインアップページへ遷移するボタンや、ログインページへ遷移するボタンが表示されていないことを確認する end expect(page).to have_content('X')と記述すると、visitで訪れたpageの中に、Xという文字列があるかどうかを判断する. it '正しい情報を入力すればユーザー新規登録ができてトップページに移動する' do # トップページに移動する visit root_path # トップページにサインアップページへ遷移するボタンがあることを確認する expect(page).to have_content('新規登録') # 新規登録ページへ移動する # ユーザー情報を入力する # サインアップボタンを押すとユーザーモデルのカウントが1上がることを確認する # トップページへ遷移したことを確認する # カーソルを合わせるとログアウトボタンが表示されることを確認する # サインアップページへ遷移するボタンや、ログインページへ遷移するボタンが表示されていないことを確認する end fill_in 'フォームの名前', with: '入力する文字列'のように記述することで、フォームへの入力を行うことができます フォームの名前は検証ツールで調べよう it '正しい情報を入力すればユーザー新規登録ができてトップページに移動する' do # トップページに移動する visit root_path # トップページにサインアップページへ遷移するボタンがあることを確認する expect(page).to have_content('新規登録') # 新規登録ページへ移動する visit new_user_registration_path # ユーザー情報を入力する fill_in 'Nickname', with: @user.nickname fill_in 'Email', with: @user.email fill_in 'Password', with: @user.password fill_in 'Password confirmation', with: @user.password_confirmation # サインアップボタンを押すとユーザーモデルのカウントが1上がることを確認する # トップページへ遷移したことを確認する # カーソルを合わせるとログアウトボタンが表示されることを確認する # サインアップページへ遷移するボタンや、ログインページへ遷移するボタンが表示されていないことを確認する end 続いて、「サインアップボタンを押すとユーザーモデルのカウントが1上がることを確認する」へ取り組みます。まずは「サインアップボタンを押すと」の部分を考えましょう。この時に使用するものがfind().clickです。 find('クリックしたい要素').clickと記述することで、実際にクリックができます find('input[name="commit"]').clickという記述になります。 続いて、「ユーザーモデルのカウントが1上がる」をどうやるかその時に用いるものが、changeマッチャです。 expect{ 何かしらの動作 }.to change { モデル名.count }.by(1)と記述することによって、モデルのレコードの数がいくつ変動するのかを確認できます。 そして、{ 何かしらの動作 }の部分にfind('input[name="commit"]').clickが入ります。 it '正しい情報を入力すればユーザー新規登録ができてトップページに移動する' do # トップページに移動する visit root_path # トップページにサインアップページへ遷移するボタンがあることを確認する expect(page).to have_content('新規登録') # 新規登録ページへ移動する visit new_user_registration_path # ユーザー情報を入力する fill_in 'Nickname', with: @user.nickname fill_in 'Email', with: @user.email fill_in 'Password', with: @user.password fill_in 'Password confirmation', with: @user.password_confirmation # サインアップボタンを押すとユーザーモデルのカウントが1上がることを確認する expect{ find('input[name="commit"]').click }.to change { User.count }.by(1) # トップページへ遷移したことを確認する # カーソルを合わせるとログアウトボタンが表示されることを確認する # サインアップページへ遷移するボタンや、ログインページへ遷移するボタンが表示されていないことを確認する end 次にトップページであることを確かめたいです。その時に使用するものが、current_pathです。 文字通り、現在いるページのパスを示します。expect(current_path).to eq(root_path)と記述すれば、今いるページがroot_pathであることを確認できます。 it '正しい情報を入力すればユーザー新規登録ができてトップページに移動する' do # トップページに移動する visit root_path # トップページにサインアップページへ遷移するボタンがあることを確認する expect(page).to have_content('新規登録') # 新規登録ページへ移動する visit new_user_registration_path # ユーザー情報を入力する fill_in 'Nickname', with: @user.nickname fill_in 'Email', with: @user.email fill_in 'Password', with: @user.password fill_in 'Password confirmation', with: @user.password_confirmation # サインアップボタンを押すとユーザーモデルのカウントが1上がることを確認する expect{ find('input[name="commit"]').click }.to change { User.count }.by(1) # トップページへ遷移したことを確認する expect(current_path).to eq(root_path) # カーソルを合わせるとログアウトボタンが表示されることを確認する # サインアップページへ遷移するボタンや、ログインページへ遷移するボタンが表示されていないことを確認する end find('ブラウザ上の要素').hoverとすることで、特定の要素にカーソルをあわせたときの動作を再現できます。 it '正しい情報を入力すればユーザー新規登録ができてトップページに移動する' do # トップページに移動する visit root_path # トップページにサインアップページへ遷移するボタンがあることを確認する expect(page).to have_content('新規登録') # 新規登録ページへ移動する visit new_user_registration_path # ユーザー情報を入力する fill_in 'Nickname', with: @user.nickname fill_in 'Email', with: @user.email fill_in 'Password', with: @user.password fill_in 'Password confirmation', with: @user.password_confirmation # サインアップボタンを押すとユーザーモデルのカウントが1上がることを確認する expect{ find('input[name="commit"]').click }.to change { User.count }.by(1) # トップページへ遷移したことを確認する expect(current_path).to eq(root_path) # カーソルを合わせるとログアウトボタンが表示されることを確認する expect( find('.user_nav').find('span').hover ).to have_content('ログアウト') # サインアップページへ遷移するボタンや、ログインページへ遷移するボタンが表示されていないことを確認する end spanクラスは他の要素でも使っているので親要素から見つける 最後に、「サインアップページへ遷移するボタンや、ログインページへ遷移するボタンが表示されていないことを確認する」へ取り組みます。 存在しない事を確かめるために、have_no_contentを用います。 it '正しい情報を入力すればユーザー新規登録ができてトップページに移動する' do # トップページに移動する visit root_path # トップページにサインアップページへ遷移するボタンがあることを確認する expect(page).to have_content('新規登録') # 新規登録ページへ移動する visit new_user_registration_path # ユーザー情報を入力する fill_in 'Nickname', with: @user.nickname fill_in 'Email', with: @user.email fill_in 'Password', with: @user.password fill_in 'Password confirmation', with: @user.password_confirmation # サインアップボタンを押すとユーザーモデルのカウントが1上がることを確認する expect{ find('input[name="commit"]').click }.to change { User.count }.by(1) # トップページへ遷移したことを確認する expect(current_path).to eq(root_path) # カーソルを合わせるとログアウトボタンが表示されることを確認する expect( find('.user_nav').find('span').hover ).to have_content('ログアウト') # サインアップページへ遷移するボタンや、ログインページへ遷移するボタンが表示されていないことを確認する expect(page).to have_no_content('新規登録') expect(page).to have_no_content('ログイン') end そして、テストコードを実行。 忘れないように書きました。 間違えてるところもあるかもしれません
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails6】"A server is already running."  

開発環境 Rails 6.1.1 Ruby 2.7.2 mac 0S Big Sur 11.2.3 rails serverが立ち上がらない 以下のエラーが発生しました。 ターミナル $ rails s => Booting Puma => Rails 6.1.1 application starting in development => Run `rails server --help` for more startup options A server is already running. Check /Users/*****/Rails/******/tmp/pids/server.pid. Exiting 発生原因 「すでにRailsサーバーを起動しているor前回のプロセスを正確に終了できていないよ」 とのことでしょうか? 以下のコマンドで確認します。 ターミナル $ ps -a | grep rails ps -a= 現在実行中のプロセス番号(PID)を表示する。 grep rails= 指定された入力(rails)から、指定したパターンにマッチする文字列を検索 ターミナル $ ps -a | grep rails 48976 ttys002 0:00.00 grep --color=auto rails プロセス番号(PID)48976が起動中のようです。 killコマンドで対処 ターミナルの再起動で解決する場合もあるようですが、今回はkillコマンドで対処します。 killコマンド(物騒..)は実行しているプロセスを終了させることができるコマンドです。 こちらのコマンドで先程のプロセス番号(PID)48976に対して、 -9(デフォルトよりも強制的に修正するシグナルID)をつけて強制終了させてみます。 ターミナル $ kill -9 プロセス番号(PID)48976 するとrails serverが立ち上がるようになりました。 その他 エラーログにport 3000 (Errno::EADDRINUSE)と記載されている場合は以下が有効です。 ターミナル $ kill -9 $(lsof -i tcp:3000 -t) 参考 https://eng-entrance.com/linux-command-kill
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】concernsを使ってリファクタリング!太ったコントローラーをスリムにする!

railsのコントローラーなどの記述が肥大化したときにconcernsディレクトリにファイルをつくって処理を切り出す方法を紹介します。 読みやすいコードになっているかは、ポートフォリオにおいて差をつけるポイントでもあるので参考にしてください。 結論 controllers/concernsにファイルをつくって以下のように記述します。 (モデルの処理を切り出す場合は、models/concerns) app/controllers/concerns/hoge.rb module Hoge extend ActiveSupport::Concern def fuga #切り出したい処理 end end 上記のようにコントローラーの処理をモジュールに切り出します。 そして、以下のようにコントローラーのファイルで呼び出せばOKです。 controllers/messages_controller.rb class MessagesController < ApplicationController include Hoge def index fuga end end 上記のようにしてモジュールに切り出した処理を呼び出すことができます。 リファクタリングの実践例 実際にどんな感じでリファクタリングするのかを具体例を出して説明します。 (※趣旨から外れる基礎的なルーティングの記述や保存処理の記述などの説明は省略しています。) 可読性の高いコードを書けるかは中級者以降で差がつくポイントですので、イメージをつかんでみてください。 今回以下のような機能をもったアプリで考えてみます。 ワンクリックで一週間分の運動メニューを保存できる機能をもったアプリです。 どのようにやっているのかコントローラーを確認してみます。 plans_controller.rb #~省略~ def basic 7.times do |x| plan1 = Plan.create(plan: "ウォーキング20分", date: (Date.today + x), user_id: current_user.id) plan2 = Plan.create(plan: "腕立て伏せ15回", date: (Date.today + x), user_id: current_user.id) plan3 = Plan.create(plan: "腹筋15回", date: (Date.today + x), user_id: current_user.id) plans = {plan1: plan1, plan2: plan2, plan3: plan3} end render json: {plans: plans} end 「Basicメニューで保存」を押したら、plans_controller#basicが動きます。 basicアクション内では、timesメソッドをつかって一週間分の運動メニューを保存するように処理を書いています。 (保存した運動メニューを非同期で表示するために、jsにjson形式で情報を送っています。) これでボタン一つで一週間分の運動メニューを保存することができました。 さて、今回ですが「Basicメニュー」「Normalメニュー」「Hardメニュー」と3種類のメニューを用意したいです。 それぞれのボタンで違うのはメニュー内容だけなので、処理としてはさっきの記述をそのまま使えそうです。 plans_controller.rb #~省略~ def basic 7.times do |x| plan1 = Plan.create(plan: "ウォーキング20分", date: (Date.today + x), user_id: current_user.id) plan2 = Plan.create(plan: "腕立て伏せ15回", date: (Date.today + x), user_id: current_user.id) plan3 = Plan.create(plan: "腹筋15回", date: (Date.today + x), user_id: current_user.id) plans = {plan1: plan1, plan2: plan2, plan3: plan3} end render json: {plans: plans} end #追加↓ def normal 7.times do |x| plan1 = Plan.create(plan: "ウォーキング40分", date: (Date.today + x), user_id: current_user.id) plan2 = Plan.create(plan: "腕立て伏せ30回", date: (Date.today + x), user_id: current_user.id) plan3 = Plan.create(plan: "腹筋30回", date: (Date.today + x), user_id: current_user.id) plans = {plan1: plan1, plan2: plan2, plan3: plan3} end render json: {plans: plans} end normalアクションを付け加えました。 これでbasicよりも厳しいメニューの保存もできます。 さらに、hardも付け足しましょう。 plans_controller.rb #~省略~ def basic 7.times do |x| plan1 = Plan.create(plan: "ウォーキング20分", date: (Date.today + x), user_id: current_user.id) plan2 = Plan.create(plan: "腕立て伏せ15回", date: (Date.today + x), user_id: current_user.id) plan3 = Plan.create(plan: "腹筋15回", date: (Date.today + x), user_id: current_user.id) plans = {plan1: plan1, plan2: plan2, plan3: plan3} end render json: {plans: plans} end def normal 7.times do |x| plan1 = Plan.create(plan: "ウォーキング40分", date: (Date.today + x), user_id: current_user.id) plan2 = Plan.create(plan: "腕立て伏せ30回", date: (Date.today + x), user_id: current_user.id) plan3 = Plan.create(plan: "腹筋30回", date: (Date.today + x), user_id: current_user.id) plans = {plan1: plan1, plan2: plan2, plan3: plan3} end render json: {plans: plans} end def hard 7.times do |x| plan1 = Plan.create(plan: "ランニング40分", date: (Date.today + x), user_id: current_user.id) plan2 = Plan.create(plan: "腕立て伏せ60回", date: (Date.today + x), user_id: current_user.id) plan3 = Plan.create(plan: "腹筋60回", date: (Date.today + x), user_id: current_user.id) plans = {plan1: plan1, plan2: plan2, plan3: plan3} end render json: {plans: plans} end これで3種類のメニューを用意できました。 しかし、記述が重複していて、DRYの原則に反しています。 コントローラーが太りまくっています。 このままではまずいので、リファクタリングしていきましょう。 concernsを使って、リファクタリングしていこう! さて、ここからが本題です。 concernsディレクトリにファイルをつくって処理を切り出し、コントローラーをスリムにしていきます。 concernsとは、モジュールを置くディレクトリです。modelやcontrollerで使いたい処理をmodule化して共通で使えるようにできます。 今回はコントローラー内の処理を切り出したいので、app/controllers/concernsにファイルをつくっていきます。(quick_actions.rbとしました。) app/controllers/concerns/quick_actions.rb module QuickActions extend ActiveSupport::Concern end 上記の記述で基本的な準備はできました。 module(モジュール)は、処理のまとまりのことです。クラスと同じような記述で定義できます。クラスとの違いは以下です。 ①インスタンスをもつことができない。 ②継承できない。 ①に関しては、モジュールは「処理」のまとまりと理解すれば何ら問題はないと思います。 ②に関しても、継承はできませんが、include モジュール名とすることでクラスに取り込むことができます。 ActiveSupport::Concernは、Railsにおいて共通した処理を切り出すために使われる機能と理解しておきましょう。includeするためにはこの記述が必要です。 さて、コントローラーから処理を切り出していきましょう。 現状のコントローラーの記述を改めて確認すると以下のようになっています。 plans_controller.rb #~省略~ def basic 7.times do |x| plan1 = Plan.create(plan: "ウォーキング20分", date: (Date.today + x), user_id: current_user.id) plan2 = Plan.create(plan: "腕立て伏せ15回", date: (Date.today + x), user_id: current_user.id) plan3 = Plan.create(plan: "腹筋15回", date: (Date.today + x), user_id: current_user.id) plans = {plan1: plan1, plan2: plan2, plan3: plan3} end render json: {plans: plans} end def normal 7.times do |x| plan1 = Plan.create(plan: "ウォーキング40分", date: (Date.today + x), user_id: current_user.id) plan2 = Plan.create(plan: "腕立て伏せ30回", date: (Date.today + x), user_id: current_user.id) plan3 = Plan.create(plan: "腹筋30回", date: (Date.today + x), user_id: current_user.id) plans = {plan1: plan1, plan2: plan2, plan3: plan3} end render json: {plans: plans} end def hard 7.times do |x| plan1 = Plan.create(plan: "ランニング40分", date: (Date.today + x), user_id: current_user.id) plan2 = Plan.create(plan: "腕立て伏せ60回", date: (Date.today + x), user_id: current_user.id) plan3 = Plan.create(plan: "腹筋60回", date: (Date.today + x), user_id: current_user.id) plans = {plan1: plan1, plan2: plan2, plan3: plan3} end render json: {plans: plans} end メソッドbasic・normal・hardは、ほとんどの記述が共通しています。 違うのは「ウォーキング20分」などのplanキーのバリューとなっている運動メニューの部分だけです。後は全部同じです。 ということは、ここの部分を変数にしてあげれば、ひとつにまとめられそうです! concerns/quick_actions.rbに記述してみます。 concerns/quick_actions.rb module QuickActions extend ActiveSupport::Concern def create_plans #←basic,normal,hardをまとめてcreate_plansというメソッド名にしています。 7.times do |x| plan1 = Plan.create(plan: menu1 ←ここを変数化, date: (Date.today + x), user_id: current_user.id) plan2 = Plan.create(plan: menu2 ←ここを変数化, date: (Date.today + x), user_id: current_user.id) plan3 = Plan.create(plan: menu3 ←ここを変数化, date: (Date.today + x), user_id: current_user.id) plans = {plan1: plan1, plan2: plan2, plan3: plan3} end render json: {plans: plans} end end planキーに対するバリューをmenu1menu2menu3という変数に置き換えました。 あとはこの変数に状況に応じた値が入るようにしてあげます。 concerns/quick_actions.rb module QuickActions extend ActiveSupport::Concern def create_plans #条件によって変数の値を変える if params[:action] == "basic" menu1 = "ウォーキング20分" menu2 = "腕立て伏せ15回" menu3 = "腹筋15回" elsif params[:action] == "normal" menu1 = "ランニング20分" menu2 = "腕立て伏せ30回" menu3 = "腹筋30回" else menu1 = "ランニング40分" menu2 = "腕立て伏せ60回" menu3 = "腹筋60回" end 7.times do |x| plan1 = Plan.create(plan: menu1, date: (Date.today + x), user_id: current_user.id) plan2 = Plan.create(plan: menu2, date: (Date.today + x), user_id: current_user.id) plan3 = Plan.create(plan: menu3, date: (Date.today + x), user_id: current_user.id) plans = {plan1: plan1, plan2: plan2, plan3: plan3} end render json: {plans: plans} end end できました! 3つあった記述をひとつにまとめることができました! 後は、これをコントローラーで呼び出すだけです。 plans_controller.rb class PlansController < ApplicationController include QuickActions before_action :create_plans, only: [:basic, :normal, :hard] def basic end def normal end def hard end end コントローラーが別人のようにスリムになりました!! include QuickActionsこちらの記述で先程つくったモジュールを取り込んでいます。 そして、before_action :create_plansで処理を実行しています。 (さらに、basic・normal・hardと3つのメソッドを1つにまとめることなどもできそうですね。) みなさんも可読性が高く保守性の高い開発を目指しましょう! 最後にビフォーアフターを載せておきます。 before class PlansController < ApplicationController def basic 7.times do |x| plan1 = Plan.create(plan: "ウォーキング20分", date: (Date.today + x), user_id: current_user.id) plan2 = Plan.create(plan: "腕立て伏せ15回", date: (Date.today + x), user_id: current_user.id) plan3 = Plan.create(plan: "腹筋15回", date: (Date.today + x), user_id: current_user.id) plans = {plan1: plan1, plan2: plan2, plan3: plan3} end render json: {plans: plans} end def normal 7.times do |x| plan1 = Plan.create(plan: "ウォーキング40分", date: (Date.today + x), user_id: current_user.id) plan2 = Plan.create(plan: "腕立て伏せ30回", date: (Date.today + x), user_id: current_user.id) plan3 = Plan.create(plan: "腹筋30回", date: (Date.today + x), user_id: current_user.id) plans = {plan1: plan1, plan2: plan2, plan3: plan3} end render json: {plans: plans} end def hard 7.times do |x| plan1 = Plan.create(plan: "ランニング40分", date: (Date.today + x), user_id: current_user.id) plan2 = Plan.create(plan: "腕立て伏せ60回", date: (Date.today + x), user_id: current_user.id) plan3 = Plan.create(plan: "腹筋60回", date: (Date.today + x), user_id: current_user.id) plans = {plan1: plan1, plan2: plan2, plan3: plan3} end render json: {plans: plans} end end after class PlansController < ApplicationController include QuickActions before_action :create_plans, only: [:basic, :normal, :hard] def basic end def normal end def hard end end ファイル数が増えても、1つのファイルの記述を見やすくするということを意識していくといいと思います。 以上です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

form_withのデフォルト設定でハマった話

実装環境 Ruby 2.6.5 Rails 6.0.3.5 発生した不具合 form_withを用いた新規登録ページのバリデーションによるエラー表示ができなくなった。 問題点 # 新規登録のviewでの記述 <%= form_with model: @user, url: user_registration_path, class: "sign-up-form", id: 'form' do |f| %> 解決 早速答えですが、form_withはデフォルトだとAjax処理を行うという仕様で引っ掛かっていました。 これを踏まえてviewの記述を見れば分かる通りlocal: trueが抜けているためAjax処理を行なっています。 そのためterminalでもProcessing ~ as JSとある様にJavaScriptでの処理が行われています。 非同期に対応したコントローラーを作成していないのにAjax処理をしたところでデータは受け取れないし、返ってくる訳無いです。はい。 そしてコントローラーでモデルへアクセスする事も無いため当然バリデーションも動かないということになります。 振り返り viewの記述の確認は何度もしたつもりでしたが、完全に抜け落ちていた様です。思い込みって怖い... それによりterminalのJS表記に気を取られ、Javascriptの実装を片っ端から消してみたり、gitでフロント実装前まで巻き戻してみたり、あっちこっち飛び回る泥沼状態でした。挙句正常に動作するのがフロント実装前のデータだったがためにもう諦めて別のアプリ作成まで考える始末でした。 使用するものは公式ドキュメントでしっかり仕様を把握してから使うことを改めて肝に命じた出来事になりました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【個人開発】ポモドーロテクニックとTodoリストを組み合わせた時間管理アプリを作りました

はじめに ポモドーロテクニックxTodoリストを組み合わせた作業効率化アプリ「pomotto」をリリースしました! 25分集中して5分休憩するサイクルを繰り返すことでメリハリをつけて集中力を高めるポモドーロ・テクニック。 Todoリストと掛け合わせたアプリケーションが意外となかったことがきっかけで作り始めました。 一週間作業時間のログが残るので自分の勉強ペースも確認することができます。 自分が欲しかった機能を詰めて作ったものなので、誰かのもくもく勉強や作業のお供になればとても嬉しいです。 サービス概要 pomotto ターゲット層 自宅などで1人でもくもく勉強や作業をしている方。 ユーザーが抱える問題 長時間作業をしていると休憩時間をとるタイミングを見失う。 休憩するとつい長々と休憩してしまう。 集中力が途切れやすく、作業効率が下がってしまう。 自分がどの作業にどれくらい時間をかけたのか見直したい。 上記の問題を解決するために「pomotto」をリリースしました。 使い方 タスクを作成 「タスクを作成」ボタンからやることを書きます。 作成ボタンをクリック ポモドーロタイマースタート タスクごとにある「▶︎」ボタンをクリックしてタイマーをスタート。 すると25分のタイマーモーダルが表示されます。 25分経過すると音が鳴り、完了ボタンが表示される仕組みになっています。 完了ボタンをクリックするとタスク詳細ページのグラフに25分加算されます。 ポモドーロ完了後は5分休憩タイマーが表示されます。 経過時間を確認 プロフィールページで一週間の作業時間を確認できるようになっています。 タスク詳細ページでも一週間での経過時間を確認できます。 使用技術 rails 6.0.3 ruby 2.7.2 Vue.js vuetify bootstrapvue axios(API) Vuex Vee-validate Vue-router Firebase storage(音楽ファイル管理) 主要gem sorcery(ログイン認証) vue-cahrtkick(グラフ) rspec-rails(テスト) meta-tags(メタタグ) 工夫したところ UI/UX なるべく直感的に操作できるように実装しました。 タイマーモーダルを閉じても小さなタイマーブロックを表示したり、タイマー完了ボタンを押したら休憩タイマーを表示させるなど、なるべく同一ページで動くように実装しています。 実際にユーザーに使っていただき、操作がわかりにくかったところなどを修正しました。 ・チェックリストでタスク完了が分かりづらい →チェックボックスをボタンに改良することで分かりやすくしました。 ・ タスク締め切り日を入力しないとタスクが作成できないということがわかりづらい →デフォルトで入力日を設定しておくことで改善 Vue.jsを導入 タイマーを使ったアプリケーションを実装するにあたって、Railsのみでは実装できる機能に限界を感じたため、Vue.jsを導入しました。 Railsのみに比べてブラウザ上での画面の動きがスムーズになるので導入して本当に良かったと感じています。 アプリで望む未来 自分もどうしても長時間作業を続けていくとだれてしまったり休憩時間とのメリハリがつけられなくなってしまったりしてしまっていました。そのときにポモドーロ・テクニックと出会い、やる気がない時でもメリハリをつけて勉強できるようになりました。 正直自分自身が「pomotto」を一番使っていますが、このアプリで長時間もくもく作業する人の手助けになればいいなと願っております。 ポートフォリオを作る上で気を付ければよかったこと テストは機能ごとに毎回書く 機能をまとめて実装してからテストをまとめて書こうとしたのですが、まとめて書くと実装したことを遡って書かないといけないためかなり大変でした。 テストは機能ごとに一つずつ確実に書いていくことが一番やりやすいです。急がば回れというのはこのことですね。 完璧を求めずにフィードバックを貰うこと(MVP開発) まだ未経験のうちに完璧を求めて1人で長い間試行錯誤してしまっていましたが、最低限の機能を実装して実際に使っていただいた方が様々な意見がもらえる上に自分では思いつかないようなアイデアを頂いたりすることもあったのでMVP開発は今後意識して開発していこうと考えました。 今後の改善に向けて タイマー 現在、ブラウザのローカルストレージにてタイマーの経過時間を管理しています。 こちらのタイマーの経過時間をバックエンド側でDBで管理したいと考えております。 テーブル設計予定図 上記の仕様に変更した上での改善したい点 - タイマーを途中で止められるように設計 - タイマー実行中に実行中のタスクを削除したらタイマーを止められない問題を改善 レスポンシブデザイン 現状、PCでのみ画面デザインが適用されるようになっていますが今後はスマホでも使用できるように画面比率の調整を行っていきたいです。 ブラウザを少し縮小してしまうとレイアウトが崩れてしまうのでその部分も直していきたいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

オリジナルアプリ開発【ユーザー視点で考える】

エンジニア転職をするにあたって、転職先の企業へのアピールポイントになるポートフォリオ作成(オリジナルアプリ開発)をしていきます。 オリジナルアプリのペルソナを考える ペルソナとはマーケティングの用語で、「サービスを使用するユーザー」のことです。 似た言葉に「ターゲット」などがありますが、ペルソナはそれらよりも詳細に「サービスを使用するユーザー」を決めます。 年齢層や、職業。性別や、現在の境遇などまで決めることで、よりリアルに使用者の目線でサービスを考案できます。 設定方法 性別・年齢 職業 趣味 日頃の生活 ペルソナの課題を洗い出し、解決する機能を考える(ユーザーストーリー) ユーザーストーリーとは、「ペルソナの課題に対して、どのような機能で解決していくのか」を明確にしたものです。 ペルソナが感じるであろう課題を、以下のようにテーブル形式で洗い出します。 この作業を行わないと、「思いついた機能をとりあえず実装する」という開発になり非効率です。 解決したい課題 なぜその課題解決が必要なのか 課題を解決する実装の内容 まとめ ペルソナとユーザーストーリーを詳細に考えることで、後々に非効率な開発にならなくて済みますが、 最初はざっくりと考え作っていくうちに修正を加えていく感じで良いと思います。 じゃないと、明確に『こんなアプリを作りたい!!』というイメージがない人は、ペルソナとユーザーストーリーを考える時点で、 時間が掛かり過ぎてしまい早々に挫折なんてこともあるかもしれないので、、、。 次回は、テーブル設計について書きたいと思います。 ではでは!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails 勉強日記 1日目(4/23)

内容 Railsの学習の予定を立てた。 メモ Rails(Ruby on Rails)を段階的に学んでいくための予定を以下でまとめる。 目標 簡単なWebアプリケーションを作れるレベルになる 自分のブログを構築する 現状 HTMLやCSSを読むことはできるが、実際に書いたことはほとんどない Rubyも雰囲気はわかるが、実際に書いたことはほとんどない JavaScriptは全くわからない Railsはチュートリアルの初めの方だけやったことがある 段階 HTML、CSS、Ruby、JavaScript、Railsに慣れる Progateを数回やる 段階的にそれぞれの技術のレベルを上げる Rails Girlsを進める Railsチュートリアルを進める 目標となる物を作る (目標の通り)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails6]Production環境でrails db:reset ,db:dropができない時

今回は下記を参考にさせていただきました。 はじめに Railsアプリの本番環境でrails db:resetを実行した所以下のエラーが出ました。 $ rails db:migrate:reset RAILS_ENV=production rails aborted! ActiveRecord::ProtectedEnvironmentError: You are attempting to run a destructive action against your 'production' database. If you are sure you want to continue, run the same command with the environment variable: DISABLE_DATABASE_ENVIRONMENT_CHECK=1 bin/rails:4:in `<main>' Tasks: TOP => db:migrate:reset => db:drop => db:check_protected_environments (See full trace by running task with --trace) 原因 Rails5からはproduction環境で破壊的処理(内容は以下に記述)をすることができなくなっている。 db:drop db:drop:all db:reset db:purge db:purge:all db:purge:test db:schema:load 解決方法 ヒントはエラーメッセージに記載されている。 $ rails db:migrate:reset RAILS_ENV=production rails aborted! ActiveRecord::ProtectedEnvironmentError: You are attempting to run a destructive action against your 'production' database. If you are sure you want to continue, run the same command with the environment variable: DISABLE_DATABASE_ENVIRONMENT_CHECK=1 ←ここ 環境変数としてDISABLE_DATABASE_ENVIRONMENT_CHECK=1を指定することでコマンドを実行することができます。 $ rails db:migrate:reset RAILS_ENV=production DISABLE_DATABASE_ENVIRONMENT_CHECK=1 Dropped database 'hogehoge_production' Created database 'hogehoge_production' == 20210311185651 DeviseCreateUsers: migrating ================================ 以上です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

草野球の出欠確認Webアプリを作ろう! part.1

これから作っていく簡単なWebアプリの作成メモ(自分の備忘)です。 自分用なのであまり凝りすぎないように書いていきたい。 環境などメモ ・WSLのUbuntu(20.04.2 LTS (Focal Fossa)) ・Ruby on Rails(ruby 3.0.1p64 / Rails 6.1.3.1) ・PostgreSQL(psql (PostgreSQL) 12.6 (Ubuntu 12.6-0ubuntu0.20.04.1)) 今回やったこと 環境構築 DB関連 以下の記事を参考にした。 Windows10のWSL(Ubuntu)にPostgreSQLをインストールしたときのメモ Rails環境構築 以下の記事を参考にした。 [Rails] Windows10 で WSL を使って Rails 環境を構築したときのメモ ※Yarnをインストールしないとエラーになったのでバージョンによっては注意が必要。 Rails6 開発時につまづきそうな webpacker, yarn 関係のエラーと解決方法 Rails初期セットアップ 以下は前述の手順と被りもあるが一応書いておく。 $ bundle install $ rails new SampleApp $ bin/rails db:create $ bin/rails db:migrate $ bin/rails s 機能作成 Userモデルの作成 以下の記事を参考にした。 Rails「ユーザーのモデルを作成する」 Userコントローラーの作成 以下の記事をコントローラー生成時に参考にした。 Rails generate の使い方とコントローラーやモデルの命名規則 $ rails g controller Users index show new create edit update delete ルートファイルを編集した。 routes.rb Rails.application.routes.draw do # ルート root "users#index" # Users resources :users end viewsにユーザー一覧を追加した。 users/index.html.erb <h1>メンバーの一覧</h1> <% if @users.empty? %> <div><%= "表示できるメンバーがいません。" %></div> <% else %> <table align="center"> <thead> <tr> <th>ユーザー名</th> <th>メールアドレス</th> </tr> </thead> <tbody> <% @users.each do |lst| %> <tr> <td><%= lst.name %></td> <td><%= lst.email %></td> </tr> <% end %> </tbody> </table> <% end %> コントローラーのindexアクションにUserモデルの値を渡すよう編集する。 controllers/users_controller.rb def index @users = User.all end ※初回動作確認時にrails serverは再起動しておくといいかも(bycryptのbundleのタイミング次第ではエラーになる) このあと、コンソール経由でユーザーを登録して動作確認した。 $ rails c $ User.new(name:"admin",email:"admin@example.com",password:"administrator",password_confirmation:"administrator").save テーブルのデザインが気に入らなかったので、Webから適当に探してきて試す(以下を参照)。 【コピペOK】CSSだけで実装できるおしゃれテーブルデザイン10つ ↑こんなふうになった。(テーブルサイズは画面幅の7割) 今回はここまで。 次回の記事>>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails & AWS]https化によるNginxの設定

はじめに AWSのEC2で上げているRailsアプリをHTTPS化したのでメモとして残しておきます。 画像なしで説明しますのであしからず。 少しだけrailsのファイルの設定もしていきます。 Nginxとは HTTPおよび、HTTPSでのアクセスに使われる軽量サーバーのことでWebサーバーとしての基本的な機能を持っています。 Nginxの設定 以下のファイルを編集します /etc/nginx/nginx.conf httpでアクセスされたものをhttpsへリダイレクトする記述へ変更します。 EC2サーバー環境 $ sudo vim /etc/nginx/nginx.conf server { listen 80 default_server; listen [::]:80 default_server; server_name www.hogehoge.com; #ドメイン名に変更 root /usr/share/nginx/html; # Load configuration files for the default server block. include /etc/nginx/default.d/*.conf; location / { } error_page 404 /404.html; location = /404.html { } error_page 500 502 503 504 /50x.html; location = /50x.html { } } ドメイン名を記述したら:wqで保存します。 次にRailsアプリに移動します。 そして/config/environments/production.rbファイルを編集します EC2サーバー環境 $ sudo vim /config/environments/production.rb /config/environments/production.rb config.force_ssl = true これをすることで常時SSL化対応となります。 NginxとUnicornの再起動 Nginxの再起動 EC2サーバー環境 $ sudo service nginx restart 次にUnicornを再起動させます。 まずUnicornの起動を確認 EC2サーバー環境 $ ps -ef | grep unicorn | grep -v grep ryogo 11658 1 0 4月24 ? 00:00:02 unicorn_rails master -c /var/www/rails/travelour/config/unicorn.conf.rb -D -E production ryogo 11662 11658 0 4月24 ? 00:00:02 unicorn_rails worker[0] -c /var/www/rails/travelour/config/unicorn.conf.rb -D -E production ryogo 11663 11658 0 4月24 ? 00:00:02 unicorn_rails worker[1] -c /var/www/rails/travelour/config/unicorn.conf.rb -D -E production 表示されたUnicornの番号をkillします。 1行目の11658のみkillすれば良いです EC2サーバー環境 $ kill 11658 Unicornを再度起動させます。 EC2サーバー環境 $ bundle exec unicorn_rails -c /var/www/rails/アプリ名/config/unicorn.conf.rb -D -E production これでhttpsでドメインにアクセスできるようになります。 EC2サーバー環境 https://www.hogehoge.com 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む