20210516のRailsに関する記事は20件です。

【rails】gemを使って簡易的なハッシュタグ機能を実装する

はじめに act_as_taggrable_onというgemを使って簡易的なハッシュタグ機能を実装しようと思います。 エラーがでたりして複数の記事をみながら実装し時間がかかったので、1つにまとめて見やすいように備忘録としてこの記事を執筆します。 用意するもの シンプルな投稿機能は用意しておいてください。 環境 windows10 ruby 2.6.6 rails 6.0 実装 1.gemをインストールする Gemfile. gem 'acts-as-taggable-on', '~> 7.0' $ bundle install 2.テーブルを作成する $ rails acts_as_taggable_on_engine:install:migrations 次にrails db:migrateをする前に、作成された6つのマイグレーションファイルのうち○○○○_add_missing_unique_indices.acts_as_taggable_on_engine.rbの以下の行をコメントアウトします。 db/migrate/○○○○_add_missing_unique_indices.acts_as_taggable_on_engine.rb AddMissingUniqueIndices.class_eval do def self.up add_index ActsAsTaggableOn.tags_table, :name, unique: true #remove_index ActsAsTaggableOn.taggings_table, :tag_id if index_exists?(ActsAsTaggableOn.taggings_table, :tag_id) # ↑上記をコメントアウト remove_index ActsAsTaggableOn.taggings_table, name: 'taggings_taggable_context_idx' add_index ActsAsTaggableOn.taggings_table, [:tag_id, :taggable_id, :taggable_type, :context, :tagger_id, :tagger_type], unique: true, name: 'taggings_idx' end def self.down remove_index ActsAsTaggableOn.tags_table, :name remove_index ActsAsTaggableOn.taggings_table, name: 'taggings_idx' add_index ActsAsTaggableOn.taggings_table, :tag_id unless index_exists?(ActsAsTaggableOn.taggings_table, :tag_id) add_index ActsAsTaggableOn.taggings_table, [:taggable_id, :taggable_type, :context], name: 'taggings_taggable_context_idx' end end そして、マイグレーションを行います。 $ rails db:migrate ※マイグレーションファイルを編集する前にrails db:migrateを行うと、エラーがでてきます。 3.モデルとコントローラーに追加 post.rb class Post < ApplicationRecord acts_as_taggable #追加 end posts_controller.rb class PostsController < ApplicationController def index @posts = Post.all #ここから追加 if params[:tag_name] @posts = Post.tagged_with("#{params[:tag_name]}") end #ここまで end #省略 private def post_params params.require(:post).permit(:body, :tag_list) #tag_list を追加 end end 4.ビューの編集 投稿ページ new.html.erb <%= form_for @post do |f| %> <div class="field"> <%= f.label :投稿内容 %> <br> <%= f.text_area :body %> </div> #ここから追加  <div class="field"> <%= f.label :タグ %> <%= f.text_field :tag_list, value: @post.tag_list %>   </div>   #ここまで <%= f.submit "投稿する" %> <% end %> 投稿一覧ページ index.html.erb <% @posts.each do |p| %> #省略 <%= render 'posts/tag_list', tag_list: p.tag_list %> #追加 <% end %> _tag_list.html.erbファイルを作成 _tag_list.html.erb <% tag_list.each do |tag| %> <%= link_to tag, posts_path(tag_name: tag) %> <% end %> これで完成です! 複数のタグを作成するためにはタグとタグをカンマで区切ります。 区切り方をカンマから半角スペースにする方法 半角スペースなどで区切りたい場合は、config/initializers に acts_as_taggable_on.rb を作成して、以下のように記述します。 acts_as_taggable_on.rb ActsAsTaggableOn.delimiter = ' ' その後、一度サーバー再起動すると半角スペースでタグを区切れるようになります。 参考記事 Rails | acts-as-taggable-on を使ったタグ機能の実装 | 備忘録 Railsにタグ機能をつける。acts-as-taggable-on使用 【Rails】acts-as-taggable-onの区切り文字の変更 acts-as-taggable-on導入時のエラー
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【rails】gemを使って簡易的なタグ機能を実装する

はじめに act_as_taggrable_onというgemを使って簡易的なタグ機能を実装しようと思います。 エラーがでたりして複数の記事をみながら実装し時間がかかったので、1つにまとめて見やすいように備忘録としてこの記事を執筆します。 用意するもの シンプルな投稿機能は用意しておいてください。 環境 windows10 ruby 2.6.6 rails 6.0 実装 1.gemをインストールする Gemfile. gem 'acts-as-taggable-on', '~> 7.0' $ bundle install 2.テーブルを作成する $ rails acts_as_taggable_on_engine:install:migrations 次にrails db:migrateをする前に、作成された6つのマイグレーションファイルのうち○○○○_add_missing_unique_indices.acts_as_taggable_on_engine.rbの以下の行をコメントアウトします。 db/migrate/○○○○_add_missing_unique_indices.acts_as_taggable_on_engine.rb AddMissingUniqueIndices.class_eval do def self.up add_index ActsAsTaggableOn.tags_table, :name, unique: true #remove_index ActsAsTaggableOn.taggings_table, :tag_id if index_exists?(ActsAsTaggableOn.taggings_table, :tag_id) # ↑上記をコメントアウト remove_index ActsAsTaggableOn.taggings_table, name: 'taggings_taggable_context_idx' add_index ActsAsTaggableOn.taggings_table, [:tag_id, :taggable_id, :taggable_type, :context, :tagger_id, :tagger_type], unique: true, name: 'taggings_idx' end def self.down remove_index ActsAsTaggableOn.tags_table, :name remove_index ActsAsTaggableOn.taggings_table, name: 'taggings_idx' add_index ActsAsTaggableOn.taggings_table, :tag_id unless index_exists?(ActsAsTaggableOn.taggings_table, :tag_id) add_index ActsAsTaggableOn.taggings_table, [:taggable_id, :taggable_type, :context], name: 'taggings_taggable_context_idx' end end そして、マイグレーションを行います。 $ rails db:migrate ※マイグレーションファイルを編集する前にrails db:migrateを行うと、エラーがでてきます。 3.モデルとコントローラーに追加 post.rb class Post < ApplicationRecord acts_as_taggable #追加 end posts_controller.rb class PostsController < ApplicationController def index @posts = Post.all #ここから追加 if params[:tag_name] @posts = Post.tagged_with("#{params[:tag_name]}") end #ここまで end #省略 private def post_params params.require(:post).permit(:body, :tag_list) #tag_list を追加 end end 4.ビューの編集 投稿ページ new.html.erb <%= form_for @post do |f| %> <div class="field"> <%= f.label :投稿内容 %> <br> <%= f.text_area :body %> </div> #ここから追加  <div class="field"> <%= f.label :タグ %> <%= f.text_field :tag_list, value: @post.tag_list %>   </div>   #ここまで <%= f.submit "投稿する" %> <% end %> 投稿一覧ページ index.html.erb <% @posts.each do |p| %> #省略 <%= render 'posts/tag_list', tag_list: p.tag_list %> #追加 <% end %> _tag_list.html.erbファイルを作成 _tag_list.html.erb <% tag_list.each do |tag| %> <%= link_to tag, posts_path(tag_name: tag) %> <% end %> これで完成です! 複数のタグを作成するためにはタグとタグをカンマで区切ります。 区切り方をカンマから半角スペースにする方法 半角スペースなどで区切りたい場合は、config/initializers に acts_as_taggable_on.rb を作成して、以下のように記述します。 acts_as_taggable_on.rb ActsAsTaggableOn.delimiter = ' ' その後、一度サーバー再起動すると半角スペースでタグを区切れるようになります。 参考記事 Rails | acts-as-taggable-on を使ったタグ機能の実装 | 備忘録 Railsにタグ機能をつける。acts-as-taggable-on使用 【Rails】acts-as-taggable-onの区切り文字の変更 acts-as-taggable-on導入時のエラー
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Next.js+Rails製アプリケーションでCloudinaryへ画像をアップロードする方法

概要 フロントエンド(Next.js)からバックエンド(Rails APIモード)にAPIを投げ、Cloudinaryへ画像をアップロードする。 公開サイト フロントエンド用リポジトリ バックエンド用リポジトリ フロントエンド側(Next.js) ImageUploadコンポーネント import { useState } from "react"; export default function ImageUpload({ postId }) { const [image, setImage] = useState(null); const handleSubmit = async (e) => { e.preventDefault(); const formData = new FormData(); formData.append("files", image); formData.append("refId", postId); //Rails側でpostとeyecatchを紐づけるため formData.append("field", "image"); const res = await fetch(`http://localhost:3000/api/v1/eyecatches/`, { method: "POST", body: formData, }); if (res.ok) { alert("Image uploaded"); } }; const handleFileChange = (e) => { setImage(e.target.files[0]); }; return ( <form onSubmit={handleSubmit}> <input type="file" onChange={handleFileChange} /> <input type="submit" value="Upload" /> </form> ); } バックエンド(Rails) gemをインストール gem 'cloudinary' gem 'carrierwave' bundle install コントローラー 今回はeyecatches_controllerを用意した。 class Api::V1::EyecatchesController < ApplicationController def create res = Cloudinary::Uploader.upload( params[:files], :folder => "travellog", :public_id => "eyecatch_" + params[:refId], /* 画像に名前をつけている */ ) /* res['secure_url']には画像のパスが入っている */ eyecatch = Eyecatch.new(post_id: params[:refId], image: res['secure_url']) if eyecatch.save render json: { status: :ok, data: eyecatch } else render json: { status: 500, data: eyecatch.errors } end end end routes.rb ルーティングを設定 resources :eyecatches, only: [:create] モデル 今回は、PostとEyecatchを1対1で紐づけている。 rails g model Eyecatch image:text post_id:integer アップローダー EyecatchUploderを作成する。 rails g uploder EyecatchUploder class EyecatchUploader < CarrierWave::Uploader::Base include Cloudinary::CarrierWave CarrierWave.configure do |config| config.cache_storage = :file end end config/cloudinary.yml config/cloudinary.ymlでcloudinaryの必要情報を読み込む development: cloud_name: <%= ENV[:CLOUDINARY_CLOUD_NAME] %> api_key: <%= ENV[:CLOUDINARY_API_KEY] %> api_secret: <%= ENV[:CLOUDINARY_API_SECRET] %> enhance_image_tag: true static_file_support: false production: cloud_name: <%= ENV[:CLOUDINARY_CLOUD_NAME] %> api_key: <%= ENV[:CLOUDINARY_API_KEY] %> api_secret: <%= ENV[:CLOUDINARY_API_SECRET] %> enhance_image_tag: true static_file_support: true test: cloud_name: <%= ENV[:CLOUDINARY_CLOUD_NAME] %> api_key: <%= ENV[:CLOUDINARY_API_KEY] %> api_secret: <%= ENV[:CLOUDINARY_API_SECRET] %> enhance_image_tag: true static_file_support: false 環境変数 今回は.envファイルを用意して環境変数を設定しました。 gem 'dotenv-rails' bundle install // クオーテーションは不要 CLOUDINARY_CLOUD_NAME = *********** CLOUDINARY_API_KEY = *********** CLOUDINARY_API_SECRET = ***********
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Hotwire(Turbo)を試す その3: Trubo Streamsでのブロードキャスト

環境: Rails 6.1、Turbo 7.0.0-beta.5 Hotwire(Turbo)を試す その1: 導入、作成・更新フォーム Hotwire(Turbo)を試す その2: Trubo Streamsでページの一部の差替・追加 の続きです。 Trubo Streamsには、HTML片を通常のHTTPレスポンスで送るほかに、Action Cableを使ってブラウザーに一斉送信(ブロードキャスト)する機能があります。 ブロードキャストをとりあえず試す その1とその2で作ったサンプルにこの機能を導入して、記事を作成・更新・削除するたびに記事一覧が自動更新されるようにしてみます。各メソッドの詳しい説明は後回しにします。 Action Cable経由で更新したいページには、turbo_stream_fromメソッドでストリーム名を指定します。ここでは 'top_entries' としています。 app/views/entries/index.html.erb <%= turbo_stream_from 'top_entries' %> このメソッドは<turbo-cable-stream-source>要素を埋め込みます。TurboのJavaScriptはこれを見てAction Cableへの接続を開始するようです。 <turbo-cable-stream-source channel="Turbo::StreamsChannel" signed-stream-name="InRvcF9lbnRyaWVzIg==--0088422a300e71f502a839bd9f5940479f3fe7b18578309a3313619862ef4118"></turbo-cable-stream-source> 一覧中の記事が個別に更新されるように、記事一つ一つを<turbo-frame>で囲みます。フレーム名に文字列の代わりにモデルオブジェクトentryを指定していますが、これは 'entry_12' のように「モデル名_id」の文字列になります。entryの代わりにdom_id(entry)としても同じです。 その2で一覧を囲む<turbo-frame>に付けていたtarget: '_top'をここに移します。 app/views/entries/_entry.html.erb <%= turbo_frame_tag entry, target: '_top' do %> <div class="entry"> <%= link_to entry.title, entry %> | <%= entry.created_at.strftime('%m/%d %H:%M') %> | <%= link_to 'Edit', edit_entry_path(entry) %> | <%= link_to 'Destroy', entry, method: :delete, data: { confirm: 'Are you sure?' } %> </div> <% end %> モデルクラスでは、クラスメソッドbroadcasts_toを呼び出して、作成・更新・削除のコールバックで自動的にAction Cableによるブロードキャストが行われるようにします。ラムダ内の 'top_entries' は、ストリーム名です。inserts_by: 'prepend', target: 'entries'は作成のときだけ使われるもので、後ろではなく前に追加、追加先のフレーム名は 'entries' 、という指示です。 app/models/entry.rb class Entry < ApplicationRecord validates :title, :body, presence: true broadcasts_to ->(e){ 'top_entries' }, inserts_by: 'prepend', target: 'entries' end ブラウザーのウィンドウを2つ並べ、記事の新規作成・更新・削除を行うと、もう片方の表示に反映されます。 ブロードキャストのためのメソッド 上記のサンプルでは、モデルのコールバックで一律にブロードキャストしていますが、実際のアプリケーションでは、ブロードキャストのためのメソッドを必要に応じて呼び出すことになるかと思います。 Turbo Streamsによるブロードキャストの機能は、Turbo::StreamsChannelクラスで「broadcast_アクション名_to」という名前のメソッドに実装されています。また、同名のメソッドがモデルクラスにも用意されています。 アクションには、append(後ろに追加)、prepend(前に追加)、replace(差し替え)、remove(削除)があります。 Turboのソースコードを見るのが手っ取り早いかもしれません。 Turbo::Streams::Broadcasts Turbo::Broadcastable 追加 ある記事のHTML片を送信してリストの上に追加するには、broadcast_prepend_toメソッドを使います。第1引数はストリーム名です。targetオプションは対象になるフレーム名です。ストリーム名とフレーム名を混同しないようにしましょう。 partialとlocalsはそれぞれ、レンダリングに使う部分テンプレートと部分テンプレートで使うローカル変数です。 Turbo::StreamsChannel.broadcast_prepend_to 'top_entries', target: 'entries', partial: "entries/entry", locals: { entry: @entry } これは、次のようなHTML片をストリーム 'top_entries' に送信します。 <turbo-stream action="prepend" target="entries"> <template> app/views/entries/_entry.html.erb のレンダリング結果 </template> </turbo-stream> 次のようにモデルのメソッドを呼び出しても同じことになります。 @entry.broadcast_prepend_to 'top_entries', target: 'entries', partial: "entries/entry", locals: { entry: @entry } モデルのメソッドの場合、オプションは省略できます。targetを省略すると、モデル名の複数形(ここでは 'entries' )が使われます。partialとlocalsを省略すると、Railsのrenderメソッドでrender partial: 'モデル名の複数形/モデル名', locals: { モデル名: モデルオブジェクト }としたときと同じになります。 @entry.broadcast_prepend_to 'top_entries' なお、HTML片を下に追加するときは、broadcast_prepend_toの代わりにbroadcast_append_toを使います。 差し替え HTML片を送信してリスト中の項目を差し替えるには、broadcast_replace_toメソッドを使います。targetオプションにモデルオブジェクトを指定すると、「entry_123」のようなid付きのフレーム名が対象になります。 Turbo::StreamsChannel.broadcast_replace_to 'top_entries', target: @entry, partial: "entries/entry", locals: { entry: @entry } モデルにも同名のメソッドが用意されています。 @entry.broadcast_replace_to 'top_entries', target: @entry, partial: "entries/entry", locals: { entry: @entry } broadcast_prepend_toと同様にオプションを省略できます。デフォルトのtargetは、target: @entryとなります。 @entry.broadcast_replace_to 'top_entries' 削除 削除用のメソッドは、broadcast_remove_toです。差し替えと同じく、targetオプションにはモデルオブジェクトを指定できます。 Turbo::StreamsChannel.broadcast_remove_to 'top_entries', target: @entry モデルのメソッドを使うと次のようになります。targetオプションは省略できます。 @entry.broadcast_remove_to 'top_entries' ストリームにモデルオブジェクトを指定した場合 turbo_stream_fromメソッドに指定するストリーム名には、文字列のほかにモデルオブジェクトを指定できます。一覧ページではなく詳細ページで自動更新するための書き方でしょうか。 <%= turbo_stream_from @entry %> この場合は、ブロードキャストの際にストリーム名にモデルオブジェクトを指定できます。 @entry.broadcast_replace_to @entry, target: @entry, partial: "entries/entry", locals: { entry: @entry } 「_to」を抜いたメソッドを使うと、ストリーム名はモデルオブジェクトと見なされます。オプションのtarget、partial、localsは省略可能です。 @entry.broadcast_replace ところで、ストリーム名には複数のオブジェクトを配列で指定できます。特定のユーザーにだけ送信したい、という場合に使えそうです。 <%= turbo_stream_from [current_user, @entry] %> ブロードキャスト用のメソッドでもストリーム名に配列を指定します。 @entry.broadcast_replace_to [current_user, @entry] ジョブを使った非同期送信 ブロードキャストのメソッドには、「broadcast_アクション名_later_to」という名前のものも用意されています。「later」付きのメソッドを使うと、Active JobのクラスTurbo::Streams::ActionBroadcastJobのジョブが起動して、ジョブがAction Cableを使って送信する、という流れになります。 @entry.broadcast_prepend_later_to 'top_entries', target: 'entries' 「later」付きのメソッドで非同期のブロードキャストを実験するには、Active Jobを普通に設定すればOKです。アダプターのGemを加え(ここではSidekiq)、 Gemfile gem 'sidekiq' 設定でアダプター名を指定し、 config/environments/development.rb config.active_job.queue_adapter = :sidekiq Sidekiqを起動しておきます。 % bundle exec sidekiq モデルのコールバック用のメソッド 上記の「ブロードキャストをとりあえず試す」では、broadcasts_toメソッドを使いました。 broadcasts_to ->(e){ 'top_entries' }, inserts_by: 'prepend', target: 'entries' これは、次のように指定するのと同じことです。モデルのコールバックを使ってブロードキャスト用のメソッドを呼び出します。 after_create_commit { broadcast_prepend_later_to 'top_entries', target: 'entries' } after_update_commit { broadcast_replace_later_to 'top_entries' } after_destroy_commit { broadcast_remove_to 'top_entries' } 作成と更新のときは「later」付きのジョブを使ったブロードキャストで、削除のときは「later」なしです。なぜそうなのかは分かりません。 broadcasts_toの第1引数には、belongs_toなどで関連付けたモデルオブジェクトを指定することが想定されているようです。次のようにすると、送信先のストリーム名はsend(:blog)になります。上の例のようにストリーム名を文字列で指定したい場合は、ラムダを使うしかありません。 belongs_to :blog broadcasts_to :blog, inserts_by: 'prepend', target: 'entries' これは、次のように指定するのと同じです。 after_create_commit { broadcast_prepend_later_to blog, target: 'entries' } after_update_commit { broadcast_replace_later_to blog } after_destroy_commit { broadcast_remove_to blog } コールバック用のメソッドにはもう1つ、「_to」の付かないbroadcastsメソッドがあります。 broadcasts inserts_by: 'prepend' これは、次のように書くのと同じになります。送信先のストリームは、モデルオブジェクト(@entry.broadcast_replace_later_to @entryなどと同じ)となります。 after_create_commit { broadcast_prepend_later } after_update_commit { broadcast_replace_later } after_destroy_commit { broadcast_remove } ここまでの感想 Action Cableを使うときに、プログラムを自分であれこれ工夫しなくてもよいので楽になりそう。 当面はその1とその3の機能だけ使って、その2は使わないかなあ。 おしまい
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails Tutorial Gimei で行った変更が反映されなかったときしたこと-memo-

ユーザーのサンプルデータを外国人の名前から日本人の名前にしようと思ったとき seeds.rb からサンプルデータをGimei gem を使って変更したが rails db:seed rails aborted! ActiveRecord::RecordInvalid: Validation failed: Email has already been taken このようなエラーが出たので rails db:reset rails db:seed rails db:migrate の順にやったが変わらず $ rails db:drop $ rails db:create $ rails db:schema:load $ rails db:seed $ rails db:migrate でうまくいった。 参考 https://qiita.com/suzuki-koya/items/02d5e349bff179c44454
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

redirect_back(fallback_location: root_url)で常にエラーが発生していた話

前提 rspecでmod操作をチェックすることができるようになったので、確認ダイアログが表示される操作の統合テストを追加した。(data: { confirm: "削除してよろしいですか?" }の部分) しかしその際に、エラーが発生したので、その修正に軌跡をここに記しておくこととする。 ちなみに、RSpecで確認ダイアログを操作するコードは以下を参考にしました。ありがとうございました!! 結論 遷移の前のページが削除される操作の場合、redirect_backという指示は適切ではない。 エラーの原因とその修正 テストした箇所と、エラーが発生していた部分、その修正。 app/views/works/show.html.erb : <% if current_user?(@work.user) %> <div class="work--settings"> <span class="delete_btn field__negative"> <%= link_to "削除する", work_path(@work), method: :delete, data: { confirm: "削除してよろしいですか?" } %> </span> </div> <% end %> : app/controllers/works_controller.rb class WorksController < ApplicationController : def destroy @work.destroy flash[:success] = "削除しました。" - redirect_back(fallback_location: root_url) + redirect_to root_url end : spec/system/works/create_work_spec.rb require "rails_helper" RSpec.describe "Create or delete work", type: :system, js: true do let(:user) { create(:user) } let(:work) { create(:work, user_id: user.id) } it "delete work" do sign_in user visit work_path work within(".work--settings") do expect(page).to have_selector "span.delete_btn" page.accept_confirm do click_link "削除する" end end end end エラーコード Create or delete work delete work (FAILED - 1) Failures: 1) Create or delete work delete work Failure/Error: @work = Work.includes( [ :user, comments: :user, image_attachment: :blob, illustrations: [photo_attachment: :blob], ] ).find(params[:id]) ActiveRecord::RecordNotFound: Couldn't find Work with 'id'=3313 # ./app/controllers/works_controller.rb:15:in `show' : # ------------------ # --- Caused by: --- # Capybara::CapybaraError: # Your application server raised an error - It has been raised in your test code because Capybara.raise_server_errors == true # /usr/local/bundle/gems/capybara-3.35.3/lib/capybara/session.rb:160:in `raise_server_error!' Finished in 5.17 seconds (files took 3.93 seconds to load) 1 example, 1 failure 原因 原因は削除操作にて、redirect_back(fallback_location: root_url)という、redirect_backの操作を行っていた事に由来します。 app/controllers/works_controller.rb class WorksController < ApplicationController : def destroy @work.destroy flash[:success] = "削除しました。" - redirect_back(fallback_location: root_url) + redirect_to root_url end : fallback_locationというオプションは遷移前のページのURL取得する操作でした。しかし、遷移の前のページ(今回でいうworks/show.html.erb)が削除される操作の場合は、そもそもredirect_backという指示は適切なはずがなく、エラーが発生していました。 ちなみにこの修正は、以下の記事を参考にしました。ありがとうございました! つまり、自分のアプリケーションにはエラーが発生するコードが、常に内在していた、と言うことになります。情けない、、 改めてテストの重要性に気付かされました。 今回の記事は以上です、最後まで読んでいただきありがとうございました!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【rails×js】簡単実装できるJavaScript/jQuery小技集

実装すること PFでJavascriptを使用して実装した便利?な機能を紹介します。 実装はとても簡単ですので試してみてください。 ①トップページに戻る機能 ②タイピング風に文字が表示される機能 ③文字数をカウントしてくれる機能 ④入力したpasswordを表示する機能 ⑤データを円グラフで表示してくれる機能(Chart.jsの実装: https://qiita.com/tani__san929/items/cb6640173ff801acc203) 完成形 ①トップページに戻る機能 * 下に進むと右下にボタンが表示されます。 * 押すとトップページに戻ります。 ②タイピング風に文字が表示される機能 * 特定の文字がタイピングされているように順番に表示されます。 * リロードすると再び同じ動きをします。 ③文字数をカウントしてくれる機能 * 特定のフォームに文字が打ち込まれるとカウントされる。 * 上限を設定することで●文字が、赤色に変わるように設定している。 ④入力したpasswordを表示する機能 * devise機能を使用しており、passwordは暗号化(見れないように)されている。 * パスワードを表示するをチェックすることで打ち込んだパスワードが見れるようになります。 ⑤データを円グラフで表示してくれる機能 * Qiita記事で実装方法を書いてます。よかったら。 Char.jsの実装: https://qiita.com/tani__san929/items/cb6640173ff801acc203 実装 ①トップページに戻る機能 ますは、viewにID等を記載します。 どのページでもボタンを表示したいので、layout/application.html.erbファイルに下記を記述します。 <p id="pageTop"><a href="", {"data-turbolinks"= "false"}><i class="fa fa-chevron-up"></i></a></p> pageTopのidをjsファイルで指定することで、その箇所、タグに機能を持たせる事ができます。 layouts/application.html.erb <body> <header> <%= render "layouts/header" %> </header> <main> <%= yield %> </main> <p id="pageTop"><a href="", {"data-turbolinks"= "false"}><i class="fa fa-chevron-up"></i></a></p> <footer> <%= render "layouts/footer" %> </footer> </body> 次は、jsファイルの作成です。 assets/javascripts/application.js $(function(){ var topBtn=$('#pageTop'); topBtn.hide(); //◇ボタンの表示設定 $(window).scroll(function(){ if($(this).scrollTop()>80){ //---- 画面を80pxスクロールしたら、ボタンを表示する topBtn.fadeIn(); }else{ //---- 画面が80pxより上なら、ボタンを表示しない topBtn.fadeOut(); } }); // ◇ボタンをクリックしたら、スクロールして上に戻る topBtn.click(function(){ $('body,html').animate({ scrollTop: 0},500); return false; }); }); 最後にCSSでボタンを装飾します。 assets/stylesheets/application.scss #pageTop { position: fixed; bottom: 20px; right: 20px; } #pageTop i { padding-top: 6px } #pageTop a { display: block; z-index: 999; padding: 15px 10px; border-radius: 30px; width: 60px; height: 60px; background-color: #9FD6D2; color: #fff; font-weight: bold; text-decoration: none; text-align: center; } 完成。 ②タイピング風に文字が表示される機能 まずは、viewへ記述します。 bootstarapやdivなど不必要な箇所はなるべく省略してあります。 タイピング風に表示したい文字(タグ)にclass="typ"を記述します。 私は下記の2行に追加してます。 ページのトップにjquerを使用するための記述も追加します。 views/homes/top.html.erb <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script> ---省略--- <p class="typ">Answer.ly(アンサリー)は、留学に関する質問と回答を通して、</p> <p class="typ">留学に対する不安を無くし、有意義な留学・海外体験を支援しています。</p> 次は、jsファイルの作成です。 assets/javascripts/application.js $('.typ').children().andSelf().contents().each(function() { if (this.nodeType == 3) { $(this).replaceWith($(this).text().replace(/(\S)/g, '<span>$1</span>')); } }); $('.typ').css({'opacity':1}); for (var i = 0; i <= $('.typ').children().size(); i++) { $('.typ').children('span:eq('+i+')').delay(100*i).animate({'opacity':1},50); }; }); 完成。 ③文字数をカウントしてくれる機能 まずは、viewファイルにid=countUp記述。 text_area内ではなく、別途divタグなどので場所を作成してください。 views/contacts/new.html.erb <div class="form-group"> <%= f.label :お問い合わせ詳細 %> <div><span id=countUp>0</span>文字</div> <%= f.text_area :message, rows:8, placeholder:"お問い合わせ内容をご記入ください。(500文字以内)", class:"form-control" %> </div> 次に、jsファイルの作成です。。 assets/javascripts/application.js $(function () { $("textarea").keyup(function(){ var counter = $(this).val().length; $("#countUp").text(counter); if(counter == 0){ $("#countUp").text("0"); } if(counter >= 500){ $("#countUp").css("color","red"); } else { $("#countUp").css("color","#666"); } }); }); 500の部分が自分で設定した文字数の上限です。 それ以下だと、色はグレー色。 500文字に達する(以上だ)と数字は赤色に変わるように設定します。 if(counter >= 500){ 完成。 ④入力したpasswordを表示する機能 まずは、viewの記述です。 * passwarod_fied内にid:"js-password"を記載。 * その下に、チェックボタンを設置。こちらには、id="js-passcheck"を記載。 views/members/registrations/new.html.erb <div class="form-group"> <%= f.label :パスワード %> <% if @minimum_password_length %> <span class="small"></span> <% end %> <%= f.password_field :password, id:"js-password", size:"40%", placeholder:"半角英数記号#{ @minimum_password_length}文字以上", autocomplete: "new-password", class:"form-control password" %> </div> <label for="js-passcheck">パスワードを表示する</label> <input type="checkbox" id="js-passcheck"/></p> 次にjsファイルを作成。 passwordで見えなくなってる部分を☑︎にチェックが入ってるとtextで表示する記述です。 assets/javascripts/application.js $(function() { var password = '#js-password'; var passcheck = '#js-passcheck'; changeInputtype(password, passcheck); }); function changeInputtype(password, passcheck) { $(passcheck).change(function() { if ($(this).prop('checked')) { $(password).attr('type','text'); } else { $(password).attr('type','password'); } }); } 以上です。 最後に 復習も兼ねてまとめてみました。 PFでどう活きてくるかわかりませんが、ユーザビリティを考えた際に思い追加ものです。 また、振り返ってみるとわかってない記述もあったりして知識不足だなと実感してます。 JavaScriptは本格的に勉強していないので、しっかり勉強しないとなーと。 参考書も買ってたので近々そちらで学習予。ありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

モダンな技術を使ってRailsアプリを開発、デプロイするまでのロードマップ2021

はじめに  この記事はモダンな技術(後述)を使用してRailsアプリケーションを開発し、AWS上にデプロイするまでの学習手順を詳細にまとめたものです。基本的に参考になる記事、書籍を編纂し、自分がわかりにくかったと感じた部分は自分で記事を書いています。リンクの記事を書いてくださった方々には本当に感謝です。 この記事の目的  新しいことを独学しようとすると一つ大きな問題があります。それは計画通りに実行するのが困難であるということです。自分のすべき作業が明確化されていないことによって、 1. どのくらいで終わるのかを数値化することが困難 2. エラーを予測できない 3. そもそも次になにをすべきかわからず、それを決定するのに時間を要する などの問題が発生します。私自身インフラの学習からアプリ開発、デプロイ完了までの期間が予定よりも2ヶ月近く伸びしまいました。  この記事は次にやるべきことを明確化することによって、以上のような計画錯誤を減らすことを目的としています。計画術についてはDaiGoさんの書籍が参考になるかと。 この記事で紹介する言語、フレームワーク、その他技術 フロント関連 HTML CSS Sass JavaScript jQuery Vue.js Ruby on Rails MySQL nginx Docker AWS IAM VPC ELB Route 53 RDS S3 ECR ECS CloudWatch Systems Manager Certificate Manager Terraform CircleCI 留意事項 前提知識は特に必要ありません。必要ないところは読み飛ばしてください。 環境はWSL(Windows Subsystem for Linux)です。Macを使用している方は導入に関する部分はご自身で調べてください。 技術の独学について  新しい技術を独学するには以下のような方法が効率的です(経験則ですが)。 基礎知識を身に着けます。具体的には"〇〇 入門"とかで検索すると入門者向けの記事が見つかるのでそれを読むとよいかと。 次にハンズオン学習を行います。言い換えればチュートリアルです。実際に触って基本操作を身に着けます。 チュートリアルをクリアしたら書籍を一冊読むなどして体系的知識を身に着けます。 5. 公式ドキュメントをみながら実践を行います。 Column 「学んだことはすぐに忘れてしまう!だから技術を学ぶにはとにかくスピードが大事なのだ!」  それは短期記憶向きの学習法しか実践していないからだと思います。長期的に記憶を維持したい場合には検索練習を行いましょう。検索練習に関してはパレオさんのブログを参照。個人的にはエクセルなんかに検索練習しておくと忘れたときにチートシートして使えるのでおすすめ。 「完全に理解しようとするのは効率が悪い!だから理解せずともどんどん先に進めるべきだ!」  確かにRailsチュートリアルとかちゃんと理解しながら進めようとするとめちゃくちゃ時間かかるんですよね。かといってこの言葉を鵜呑みにしてもエラーばっかでやる気が削がれてしまうのかなと。なのでハンズオン系の学習に関しては適切な難易度設定を意識してみるとよいかと思います。こちらもパレオさんのブログを参照下さい。 ロードマップ 1. ITに関する基礎知識を学ぶ  ようやくここから本題です。まずITに関する基礎知識を勉強しますが、正直ここはしっかり勉強しようとしなくていいです。なんせ情報が多いので。キタミ式等の書籍をざっくり読んで、後々わからない言葉等が出てきたときにその都度「そういえばそんなことあの本に書いてあったな...」で読み返せばいいと思います。 2. HTML, CSS, Sassの基礎知識、チュートリアル  Webページをつくるための言語です。Progateを利用して学習しましょう。 3. JavaScript, jQueryの基礎知識、チュートリアル  Webページを動的にするために使用します。Progateで。 4. エディターの導入、ショートカット、マルチカーソルの利用  ローカル(自分のPC)にエディターを導入します。色々ありますが迷ったらVSCodeを入れておけばよいかと。 またショートカット、マルチカーソルを利用するとコーディングを効率よく行えます。 Visual Studio Code キーボードショートカット一覧(チートシート) VSCodeのマルチカーソル練習帳 5. HTML、CSSを使ってモダンなコーディングができるようになる  "HTML5/CSS3モダンコーディング"という書籍をクリアすると基本的につくりたいようなページは自分で制作できるようになると思います。 6. CSSの設計方法を学ぶ  CSSはシンプル故にコードが煩雑になりやすいです。"CSS設計の教科書"という書籍を読みましょう。特にBEMとFLOCSSを抑えておくとよいかと思います。 7. Flexbox  FlexboxはCSSのレイアウト方法です。"HTML5/CSS3モダンコーディング"ではfloatプロパティを使って要素の横並びを行っていますが、Flexboxを使えば簡単に実装できます。実際にページを作る際に利用す際にチートシートをみて利用するとよいでしょう。 8. SQLの基礎知識、チュートリアル  データベースを操作するためのクエリ(一連の問い合わせ)です。Progateで。 9. Rubyの基礎知識、チュートリアル  Webアプリケーションのロジックをつくるための言語です。Progateで。 10. Ruby on Railsの超基礎知識、チュートリアル  Rubyで書かれたWebアプリケーションフレームワークです。Progateで。 11. コマンドライン操作の基礎知識、チュートリアル  コマンドを使ってファイル操作等を行います。ProgateのCommand Lineコースを利用して学習しましょう。 12. Gitの基礎知識、チュートリアル  ソースコードのバージョン管理システムです。Progateだけだと心許ないのでドットインストールのgit入門もクリアしておくとよいかと思います。 13. Ruby on Railsチュートリアル  最初の山場です。Ruby on Railsチュートリアルをクリアしましょう。わからなくなっても調べれば有益な情報がいくらでも出てくるので挫折の心配はないかと思います。 14. 開発環境を構築する  ローカル環境を構築します。WSLの基本については以下を参照。 【WSL入門】第1回 Windows 10標準Linux環境WSLを始めよう Ubuntuは無印版を推奨します。LTS版(20.04)ではVSCodeのいくつかの拡張機能をインストールできませんでした。 WSL でマウントしたファイルシステムでもパーミッションを扱えるようにする  WSLはlinuxがWindowsの全てのファイルを含むCドライブをマウントする形を取っていますが、デフォルトではCドライブのファイルにパーミッションが付与されていません(これによって度々エラーが発生します。因みにこれはつまりlinux上で開発を行えばよいのですが、これをすると高負荷な処理をした場合にWSLがフリーズしたりします)。 WSL2によるホストのメモリ枯渇を防ぐための暫定対処  またDocker等を使用して開発するとWSLのVmmemというプロセスがメモリ使用量が肥大化し続け、ホストメモリが枯渇するという問題があります。 WSLでRails環境を構築する場合は以下を参照。 [Rails] Windows10 で WSL を使って Rails 環境を構築したときのメモ パッケージマネージャについては以下を参照。 パッケージ管理ツールをまとめてみる 15. linuxの基礎知識  "linux標準教科書"というのが無料でダウンロードできるのでそちらをさくっと読んでしまうのがよいと思います。後は今後パーミッション、ソケット通信等を扱うのでそちらの知識やコマンドも合わせて抑えておくとよいです。 Linuxの権限確認と変更(chmod)(超初心者向け) インフラエンジニアじゃなくても押さえておきたいSSHの基礎知識 Unixドメインソケット PATHを通すために環境変数の設定を理解する (Mac OS X) Linuxディレクトリ構造 まとまった記事 “応用力”をつけるためのLinux再入門 16. Web技術の基礎知識  個人的には"イラスト図解式 この一冊で全部わかるWeb技術の基本"という書籍がよくまとまっていてわかりやすいと思いました。しかも安い。  上のような入門者向けの本を読んだら、次はWeb系エンジニア必読書と言われる"Webを支える技術"という書籍を読みましょう。 17. Ruby on Railsを体系的に学ぶ  "パーフェクトRuby on Rails"はRuby on Railsガイドをハンズオン形式で学習できるような内容となっています。この書籍を読むことでRailsの基本的な概念に対する理解を深め、Rails付属の機能を扱えるようになります。コンテナやCIに関する内容も参考になりますが、この段階ではまだ未学習(のはず)なので、後々読んでみるとよいかと思います。 18. JavaScript本格入門  "JavaScript本格入門"を読めばES6でJavaScriptをそれなりに書けるようになると思います。手元にあるとどうやって書くんだったかわからなくなったときに便利。 19. JavaScriptスタック JavaScriptのツール全般(Node, NPM, Yarn等)についての知識です。 JavaScript Stack from Scratch 日本語訳は以下 ゼロから始めるJavaScript生活 Webpackについては"webpack 実践入門"という書籍が参考になります。 20. Vue.jsの基礎知識、チュートリアル  SPAを構築するために使用します。あくまでRailsアプリにSPAを実装することが目的なので、基本のオプションとライフサイクル、コンポーネント間のデータフローを抑えておけば問題ないかと思います。学習の仕方としては"21Stepで体得 Vue.jsハンズオン"の1章、Step6, 11, 16を読んでやってみるとよいかと。 Railsへの導入方法は以下を参照。 Vue.js チュートリアル for Rails エンジニア(Vue3 version) またaxiosに関しては以下を参照。 [フロントエンド] axiosライブラリを使って、柔軟にHTTP通信を行う 21. ローカルで一度アプリケーションを作ってみる  ここで一旦Railsアプリケーションをつくってみましょう。この作業の目的は 1. 自分でRailsのコードが書けるようになること 2. アプリケーションの要項の洗い出し です。なのでこの段階ではアプリはガバでいいです。次つくるときにテスト駆動開発しましょう。  因みに要項を洗い出す場合にはマインドマップが役立ちます。 22. Dockerの基礎知識、チュートリアル  コンテナ仮想化を用いてアプリを開発するためのソフトです。私は"自宅ではじめる Docker入門"という書籍をやりました。Dockerfile, Docker Composeまでよくまとまっています。また"Docker/Kubernetes実践コンテナ開発入門"という書籍が有名?ですが入門者向きじゃないです。ただ(本番環境においても)コンテナ技術を使用するメリットを理解できるので、最初の章だけ読んでおけばいいんじゃないかと。 23. nginx、pumaの基礎知識  Railsアプリの前段にリバースプロキシとして使用するWebサーバです。pumaとソケット通信させて使用します。"nginx実践ガイド"という本を読みましょう。pumaの使い方に関しては以下を参照。 Pumaの使い方 まとめ 24. MySQL入門  RDBMSです。"MySQL徹底入門"という本を読みましょう。Docker Composeで扱う場合、主に関心事になるのは接続の仕方なのでユーザー管理の章を読んでおくといいと思います。因みに"基礎からのMySQL"という本もありますが、これは主にクエリについてまとめた本です(知らずに買いました)。  またRailsチュートリアルではサブクエリをそのまま文字列として記述していますが、ActiveRecord::Relationオブジェクトは最終的にサブクエリとして展開されるので、基本的にSQL文をそのまま書くことはないと思います(多分)。 参考 railsチュートリアル サブセレクトでなぜ高速化するのか 25. Dockerを使用した開発環境を構築する  ここが鬼門です。なんせRails, nginx, MySQL, Dockerの知識をここで集約する必要があるので。後々自分でも解説記事を書こうと思います。 参考 Docker + Rails + Puma + Nginx + MySQL 26. AWSインフラ構築入門  ここからは本番環境での運用を目的としたインフラを構築するための学習になってきます。"Amazon Web Services 基礎からのネットワーク&サーバー構築"か"AWSではじめるインフラ構築入門"またはその両方をクリアしておくとよいと思います。正直何も考えなくても手さえ動かせばできてしまうのでしっかり検索練習しときましょう。 初期設定まわりは以下を参照。 AWS-CLIの初期設定のメモ AWSアカウントを取得したら速攻でやっておくべき初期設定まとめ リソースを削除する場合は以下を参考に。 全リージョンから全リソースを一括検索する方法【タグエディター】 全部消したと思って放っておくと一部のリソースが残っていてキャッシュから毎月数千円とかしょっぴかれてる場合があるのでご注意下さい(経験談)。 27. ECS, ECR基礎知識、チュートリアル 上で紹介した書籍ではコンテナ技術を使っていないのでECS, ECRも触っておきましょう。 初心者でもできる! ECS × ECR × CircleCIでRailsアプリケーションをコンテナデプロイ 28. Terraform入門、実践  TerraformとはIaC(Infrastructure as Code)を実現するためのものです。"実践Terraform"という書籍をクリアしましょう。正直Terraformを使用しなくてもデプロイはできるのですが、なぜTerraformを学習するとよいのかというと 1. 冪等性を利用して、破壊、再構築を何度でもできる。 2. 必要な情報のみをコード化して扱うのでロジックを理解しやすい。 3. 充実の公式ドキュメント の理由からです。  まず1について。インフラを運用する場合当然お金がかかります。因みにステージング環境と本番環境をESC、RDSのみ2つ用意し、それ以外の構成をホストベースルーティングで共有する環境を構築したとして、最低スペック、ほぼ未稼働で24時間あたりだいたい9ドル以上、日本円で1000円前後かかります。ただこれをコンソールで管理した場合、依存関係を認識しないとろくに削除もできないし、そもそもコンソールをいじっている時間だけ無駄です。  次に2について。これもすごく大きいです。コンソールだと必要な情報が視覚的にまとまってないし、エラーもキャッチしにくいのでロジックを理解するのが難しいんですよ。対してコードで扱えば情報を自分でまとめられるし、構築時にエラーがちゃんとでるし、何よりコードを書いている間に理解が進みます。  最後に3について。 公式ドキュメントがユースケース付きでまとまってて、すごく見やすいしわかりやすい。コード自体も別に難しくないので学習コストはそんなかかりません。それよりも必要な知識はAWSです。よってAWSに関しては以下を参照。 AWS IAM IAMロールに関しては以下を参照。 【図解/AWS】初心者にも分かりやすいIAM入門~ロールとグループとポリシーの違い,設計・設定手順について~ AWS IAMポリシーを理解する IAM ロールの PassRole と AssumeRole をもう二度と忘れないために絵を描いてみた JSONポリシーに関しては以下を参照。 IAM JSON ポリシーの要素のリファレンス Fargate ECS Fargateに関しては以下を参照。 基礎から応用までじっくり学ぶECS Fargateを利用したコンテナ環境構築 #Fargate "実践Terraform"ではFargate PV1.3.0を使用していますが、最新版は1.4.0で最新版を使用するにはエンドポイントが必要です。以下を参照。 【衝撃に備えろ】Fargate PV1.4のVPCエンドポイント変更点について ECRにpushしたコンテナをECSFargateで使うVPCエンドポイントTerraform例 Amazon ECS インターフェイス VPC エンドポイント (AWS PrivateLink) 秘匿情報 秘匿情報に関しては以下を参考に Terraform × パラメータストアでRDSの機密情報をセキュアに扱う AWS Systems Manager パラメータストア ホストベースルーティング ステージング環境については以下を参照。 まともなステージング環境"を考える リスナールールでサブドメインとターゲットグループを紐付けられます。ホストベースルーティングについては以下を参照。 【新機能】ALBのHost-based routingを試してみた TerraformでAWS上にHTTPS化したサブドメインを定義する タスク定義 ECSのタスク定義に関しては以下。 タスク定義パラメータ Terraform またTerraformに関しては以下を参照。 DRY Rails学習後にTerraformを学習してなんだこれ全然DRYじゃないじゃないかと思った方はいるはず。TerraformをDRYに書く方法に関しては以下を参照。 Terraformで複数台のEC2インスタンスを構築する場合のTIPS ただこの記事はやや古いので最新の構文に関しては公式ドキュメントを参照。 他にもDynamicやforを使用することでDRYに書くことができます。 ディレクトリ設計 ディレクトリ設計に関しては以下を参照。 Terraformのディレクトリパターン集 Code structure JSON TerraformにはJSONオブジェクトなるものがあり、Terraform内の変数使ってJSONを記述できます。 JSON Configuration Syntax 29. Dockerを使用してテスト及び本番環境の構築 本番用のDocker環境を構築する上で必要になってくる知識は、 1. Credentials 2. アセットパイプライン の2つです。 またテスト用の場合にはブラウザテストの知識が必要です。 Credentials Railsで環境毎に秘匿情報を扱うためのものです。詳細は以下を参照。 利用環境のセキュリティ Rails 6よりサポートされたMulti Environment Credentialsをプロジェクトに導入する Rails 5 から 6 にかけての secretes / credentials 周りの変遷 アセットパイプライン アセットパイプラインとはjsやcssのアセットを集約するためのフレームワークです。詳細はRailsガイドを参照。特に"production環境の場合"という項目が重要です。 またFargateを利用する場合、開発環境のようにDockerボリュームを利用できないのでnginxとRails間で同じボリュームをマウントしアセットを共有するということができません。よってAssetSyncというgemを使用し、s3からアセットを配布します。AssetSyncに関しては以下を参照。 Asset Sync Asset Sync を Webpacker と連携させる 【Rails】asset_syncを利用してCloudFront + S3からアセットファイルを配信する またCORSに関しては以下を参照。 オリジン間リソース共有 (CORS) Using CORS ブラウザテスト ブラウザを利用したシステムテストに関しては以下が参考になります。 2020年のRailsでブラウザテストを「正しく」行う方法(翻訳) Rails用Dockerfileを構築する Dockerfileの構築に関しては以下が参考になります。 効率的に安全な Dockerfile を作るには クジラに乗ったRuby: Evil Martians流Docker+Ruby/Rails開発環境構築(翻訳) マルチステージビルドに関しては以下。 マルチステージビルドの利用 Multi-stage build でNode.jsのインストールをちょっぴり効率化する .dockerignoreに関しては以下。 .dockerignore file .dockerignoreが効かない?.gitignoreとは書き方が違うよ! またブラウザテストはdocker-seleniumを使用せずRailsと同じコンテナにchrome用のパッケージを入れることでも行えます。以下が参考になります。 Rails on Docker(alpine)でdocker-seleniumを使わないでSelenium+RSpec+Capybaraでテスト自動化してみる また本番環境用のDockerfile構築には以下が参考になります。 【Dockerfile全解説】Rails本番環境のための一番シンプルなDockerイメージを作る 30. CircleCIの基礎知識、チュートリアル CircleCI自体は難しくありません。ECS, ECRの場合、CircleCIのOrbsという機能を使えば簡単にデプロイできます。"CircleCI実践入門"という書籍を読みましょう。特に"Obrsを使った継続的デプロイの実践例"という項目をやっておくとよいと思います。 31. テスト(RSpec, Capybara) RSpec, Capybaraの書き方に関しては以下が参考になります。 使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」 sleepやリトライに頼らない安定した feature spec を書こう Capybaraチートシート 公式ドキュメントは以下です。 RSpec Expectations 3.10 Capybara 32. アプリ開発開始 ここから実際にアプリをつくっていきます。新規プロジェクトを作成するときには以下の記事を参考に。 新規Railsプロジェクトの作成手順まとめ 33. 自動デプロイ Railsのディレクトリ内に.circleci/config.ymlをつくりCircleCIで自動テスト、自動デプロイを行えるようにします。以下の記事が参考になります。 【実践: 詳しくわかる】TerraformでCircleCIを通してAWSにECS環境を自動構築する方法 【CircleCI】【Terraform】【Rails】【AWS】Orbsを利用してECSでdb:migrateする方法 また公式のObrsは以下を参照。 circleci/aws-ecs circleci/aws-ecr 34. テスト駆動開発 あとはテスト駆動開発を進めていきます。 おわりに このロードマップは割とDeveloper Roadmapに沿ったものになっているかなと思います。改めてDeveloper Roadmapを見るとその有用性がわかります。ロードマップというのは順を追ってみていくのが普通ですが、このロードマップが真に有用である点は知識の依存関係がわかることです。目的の知識から逆順にたどっていくことで、それを理解するにはどんな知識が必要なのかある程度的を絞ることができます。独学をしてつまったときには一度、それにはどんな知識、技術が関わっているのかを考えてみるとよいかもしれません。  一から何かを独学しようなどというのはなかなか酔狂なことだと思いますが、この記事がそのような方の役に立てば幸いです。最後に改めて参考リンクの記事を書いてくださった方々に感謝です。有難うございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby on Railsのform_withでのインスタンス変数(model)とname属性の関係

※プログラミング学習中の私がアップロードしてます。理解が浅いです。 この記事は厳密な仕様に関するものではなく、考え方理解のまとめとして受け取ってください。 Ruby on Railsのform_withとは form_withとは、Railでフォームタグを簡単に作成するためのヘルパーです。使い方をマスターすれば、コーディングがずっと楽になります。RailsでWebアプリケーションを作成する際に、ほぼ必須のヘルパーです。 input 要素 では, フォームのデータが送信されるときに,name 属性 の値が付加されており,対象とする要素のデータに簡単にアクセスできます。 <%= form_with model: @user do |f| %> <%= f.label :name, '名前' %> <%= f.text_field :name %> <%= f.submit "送信" %> <% end %> 生成タグ <form action="/users" accept-charset="UTF-8" data-remote="true" method="post"> <label for="user_name">名前</label> <input type="text" name="user[name]" id="user_name" /> <input type="submit" name="commit" value="送信" data-disable-with="送信" /> </form> name=>user_name name=>user[name] モデル名のuserが追加されます
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails初学者によるRailsチュートリアル学習記録⑨ 第8章

目次 1. はじめに 2. 第8章の概要 3. 学習内容 4. 終わりに 1. はじめに この記事は、Rails初学者の工業大学三年生がRailsチュートリアルの学習記録をつけるための記事です。 筆者自体がRailsやWebについて知識が少ないので、内容の解釈などに間違いがある可能性があります。(その時はコメントで指摘してくださると助かります!) Railsチュートリアル内ではRailsの内容以外にも、gitでのバージョン管理やHerokuを使ったデプロイも学習しますが、gitに関しては既に私が学習済みのため学習記録には記述しません。 演習の記録も省略します。 2. 第7章の概要 前回の章でユーザー登録が可能になったので、この章ではユーザーがログイン・ログアウトを行えるようにします。 ここで実装するログインの仕組みは、ユーザーがログインしているという状態をブラウザが保持し、 ブラウザが閉じられたときにその状態を破棄するという認証システムです。 他にも、ログインしているかしていないかによって、ヘッダーの内容を変えるという認可モデルの一部を実装していきます。 セッションについて セッションとは セッションによってログイン機能を実現する ログインフォームの作成 フォームの処理 ログイン機能の実装 フラッシュによるエラーの表示 ログアウト機能の実装 ログインしている時としていない時でヘッダーの内容を変える 3. 学習内容 1. セッションについて 1-1. セッションとは セッションとはブラウザ上でユーザーが入力した情報などを、他のページに移動したときに保持しておける通信のことです。 普段利用しているHTTPプロトコルは「ステートレス」なプロトコルであり、 リクエスト1つ1つがそれ以前のリクエストの情報を全く利用しないという性質があります。 実装するログイン機能は、ユーザーのIDをブラウザが保持する必要があります。 よって、ログイン機能にはセッションを利用した通信が必要です。 Railsでセッションを実装する一般的な方法はcookiesを使う方法です。 この章ではsessionメソッドで一時セッションを作成します。 この一時セッションは、ブラウザが閉じられたときに自動的に終了するものです。 半永続的に続くセッションは9章でcookiesメソッドを使用して作成します。 1-2. セッションによってログイン機能を実現する ログイン機能を実装するために、コマンドを使用してSessionsコントローラーを作成します。 ログイン・ログアウトの処理はこのコントローラーのRESTアクションに対応付けます。 具体的には以下の表のようにアクションと処理の対応付けを行います。 実装する処理 対応するアクション ログインフォーム new ログイン create ログアウト delete この時点で、この3つのアクションのルーティングを行います。 2. ログインフォームの作成 2-1. フォームの処理 ルーティングが設定できたらログインフォームから作成していくのですが、 前回(第7章)で作成した、ユーザー登録用のフォームとほとんど同じなので、 ここでは前回作成したフォームとの違いを記述します。 ①入力フィールドの数 ユーザー登録時には名前、メールアドレス、パスワード、パスワードの確認の4つの入力フィールドが あったのに対して、ログインにはメールアドレスとパスワードの2つに減っています。 ②エラーメッセージの表示方法 ユーザー登録時に入力した情報が、文字数などの条件を満たしていなかったときは、エラーメッセージを表示しました。 今回は入力した情報に誤りがあった場合にエラーメッセージを表示させたいのですが、その時に使用する方法が前回と異なります。 前回はユーザー登録時にUserモデルを使用して@userというオブジェクトを生成しており、 そのオブジェクトに値を渡していました。 この場合、モデルの属性に文字数などの条件が設定されており、それを満たしていない場合はActive Recordによって エラーメッセージが生成されていました。 今回はSessionモデルというものを使用していないため、Active Recordによるエラーメッセージの生成が行われません。 よって、フラッシュメッセージによってエラーを表示します。 ③form_withヘルパーに渡す情報 先述したSessionモデルを使用していないという違いは、form_withヘルパーを使用する場面でも影響してきます。 ユーザー登録フォームでは以下のようにform_withヘルパーを使用していました。 <%= form_with(model: @user, local: true) do |f| %> Railsでは上記のように書くとフォームは「/usersというURLへのPOSTリクエスト」と自動で判定します。 しかし、モデルを使用してない場合はこのように書くことができないので、 ログインフォームでは以下のようにフォームの送信先のURLを指定する必要があります。 <%= form_with(url: login_path, scope: :session, local: true) do |f| %> また、socpeに指定されている:sessionというシンボルは、 フォームの値を渡す際に使用されるハッシュの名前になります。 2-2. ログイン機能の実装 ログイン機能の処理は以下のように行われます。 ①ユーザーがメールアドレスとパスワードを入力する ↓ ②メールアドレスを受け取って該当するユーザーを検索する ↓ ③検索で取り出されたユーザーのパスワードと入力されたパスワードが一致するかを判断する ↓ ④パスワードが正しければユーザーを使用してセッションを作成する パスワードが間違っていた場合、ログインフォームを再表示する ①を行うためのログインフォームはこれまでで実装できたので、②以降の処理を実装していきます。 ②の処理は、メールアドレスを使用したユーザーの検索なので、find_byメソッドで実装できます。 入力されたメールアドレスはユーザー登録フォームの時のように、入れ子になったハッシュとして渡されます。 form_withヘルパーで指定したscope: :sessionから入力された情報はsessionというハッシュに入ることが分かります。 このハッシュは以下のように表すことができます。 session: { passsword: "入力されたパスワード" email: "入力されたメールアドレス" } そしてこのハッシュはparamsというハッシュの中に入っているので下のように表します。 params[:session] よってデータには以下のように書いてアクセスします。 params[:session][:email] 以上のことから検索の処理は user = User.find_by(email: params[:session][:email].downcase)のように書きます。 ③は検索したユーザーのパスワードと入力されたパスワードが一致しているかを調べるため、 authenticateメソッドを使用します。 このメソッドでは引数に渡した値がユーザーのパスワードと一致したらtrueを返し、一致しなければfalseを返すので if user && user.authenticate(params[:session][:password])という条件式を作れます。 この条件式でなぜuserを使用しているかというと、その前のユーザーの検索でユーザーが存在したかどうかを確かめるためです。 ようやく④でログイン処理が登場します。 とはいってもコード自体は短く、session[:user_id] = user.idという一行で済んでしまいます。 このログイン処理はSessionヘルパーにlog_inという名前のメソッドとして定義しておくと便利です。 パスワードが誤っていた時のページの再表示はrenderメソッドで実装できます。 ここまでの②から④までをまとめるとcreateアクションは以下のようになります app/controlles/sessions_controller.rb def create user = User.find_by(email: params[:session][:email].downcase) if user && user.authenticate(params[:session][:password]) log_in user redirect_to user else render 'new' end end 2-3. フラッシュによるエラーの表示 フラッシュは、前回ウェルカムメッセージを表示するために使用しました。 その時のコードが↓です app/controlles/users_controller.rb def create @user = User.new(user_params) if @user.save # 保存に成功したときの処理 log_in @user flash[:success] = "Welcome to the Sample App!" # フラッシュを使用したウェルカムメッセージ redirect_to @user else # 保存が失敗したときの処理 render 'new' end end 今回もこのようにflashハッシュにメッセージを代入するのですが、前回のコードに倣って先ほどのログイン機能のコードに flash[:danger] = "メールアドレスとパスワードの組み合わせに誤りがあります" と記述すると正しく動作しません。 具体的にどのような不具合が発生するかというと、フラッシュメッセージが表示されたまま消えなくなってしまいます。 フラッシュメッセージは別のページに遷移したり、リロードするはずなのですがそうならないのです。 原因を探すためにユーザー登録機能のコードとログイン機能のコードを比較しましょう。 ユーザー登録機能のcreateアクションの一部 if @user.save # 保存に成功したときの処理 log_in @user flash[:success] = "Welcome to the Sample App!" # フラッシュを使用したウェルカムメッセージ redirect_to @user ログイン機能のcreateアクションの一部 if user && user.authenticate(params[:session][:password]) log_in user redirect_to user else flash[:danger] = "メールアドレスとパスワードの組み合わせに誤りがあります" render 'new' end 上記の2つのコードを比較するとflashハッシュに値を代入した後のビューの表示方法が違うことが分かります。 ユーザー登録機能はredirect_toメソッドを使用しているのに対して、ログイン機能ではrenderメソッドを使用しています。 これが先ほどの不具合の原因でrenderメソッドはリクエストとみなされないため、リクエスト時のメッセージがきえないのです。 この不具合を解消するにはflash.now[:danger:]と書いて値を代入します。 flash.nowによってその後のリクエストが発生したときにメッセージが消えるようになります。 2-4. ログアウト機能の実装 ログアウト機能はSessionsコントローラのdestroyアクションで実装します。 ログアウト処理はセッションからユーザーIDを削除します。 そのためにsession.delete(:user_id)と記述します。 この処理もログインと同じように使いまわすためにlog_outメソッドとしてsessions_helper.rbに定義しておきます。 2. ログインしている時としていない時でヘッダーの内容を変える ここからはユーザーがログインしている時としていない時でヘッダーの内容を変えていきます。 この機能を実現するには、現在ユーザーがログイン中かどうかを判別するメソッドが必要です。 そのメソッドの前に、現在ログイン中のユーザーの情報を扱えるようにするメソッドを説明します。 そのメソッド名はcurrent_userとして、以下のように書きます。 app/helpers/sessions_helper.rb # 現在ログイン中のユーザーを返す(いる場合) def current_user if session[:user_id] @current_user ||= User.find_by(id: session[:user_id]) end end このメソッドでは 「||=」 というRuby特有の演算子を使用しています。 上記のコードの条件式を書き直すと、 @current_user = @current_user || User.find_by(id: session[:user_id])となります。 意味としては@current_userが存在するときはそのままの値、存在しないときはユーザーを検索して代入するという意味です。 このメソッドにより、現在ログイン中のユーザーを@current_userというインスタンス変数で扱うことができます。 上記のメソッドが定義できたら、本題のユーザーがログイン中かどうかを判別するメソッドを定義します。 そのメソッドはlogged_in?メソッドという名前で以下のように書きます。 app/helpers/sessions_helper.rb # ユーザーがログインしていれば true、その他なら false を返す def logged_in? !current_user.nil? end このメソッドは単純で、current_userがnilの時はfalse、それ以外はtrueを返します。 このメソッドによってユーザーがログインしているかを判別できるようになったので、 ヘッダーの内容をログイン状態に応じて変更できるようになりました。 以下に変更後のヘッダーの変更部分を示しておきます。 app/views/layouts/_header.html.erb <li><%= link_to "Home", root_path %></li> <li><%= link_to "Help", help_path %></li> <% if logged_in? %> <li><%= link_to "Users", '#' %></li> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown"> Account <b class="caret"></b> </a> <ul class="dropdown-menu"> <li><%= link_to "Profile", current_user %></li> <li><%= link_to "Settings", '#' %></li> <li class="divider"></li> <li> <%= link_to "Log out", logout_path, method: :delete %> </li> </ul> </li> <% else %> <li><%= link_to "Log in", login_path %></li> <% end %> 4. 終わりに この章はログイン機能を作成してユーザー登録したユーザーがアプリケーションを使えるようになりました。 Railsでは今回実装したような認証機能を簡単に実装できるgemがありますが、 Railsチュートリアルではそのようなgemを使えば簡単に実装できる機能を1から自分で実装するので、 その分機能の仕組みがよく理解できていると思います。 内容が徐々に難しくなってきていますが、今のところ1週間に2章分というペースを維持できているので、 できる限りこのペースのまますすめていこうと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Host 'IPアドレス' is not allowed to connect to this MySQL serverの対処

DockerfileにImageMagicをインストールする記述を入れてコンテナイメージをビルドしました。 そこでDocker-composeを再起動してリロードをしてみた所、以下のエラーが出てきてしまいました。 Host 'IPアドレス' is not allowed to connect to this MySQL server そこで色々と考えてみたのですが、原因はおそらくいままでDocker、Docker−composeを使ってきた時に出てきたキャッシュが影響してしまっているのではないかと考えました。 幸いdocker-compose は起動することができていたので、以下のコマンドをやってみました。 docker-compose down --rmi all --volumes --remove-orphans というコマンドをやってみて、コンテナイメージとコンテナとネットワークなどをすべて削除しました。 そして、もう一度 docker-compose build をしてみました。 そうしたら無事起動しました。 キャッシュが影響することってよくあるんですね。 知らなかったです。 指摘や間違っているところがありましたらご指摘お願いします。 出典
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Action Textで画像が投稿されない問題を解決した話。

本番環境でActionTextで投稿された画像をAWSのEC2煮上げた画像をS3のバケットの中に保存しておくようにしたのですが、そこで本番環境でS3の方に保存されないというもんだいがおきました。 慌てて検証ツールを見てみるとそこでCROSの処理の問題であるというエラーメッセージが出てきました。 私その時CROSについて全く知らなかったので改めてググって調べてみました。 CROSとはなにか ブラウザがオリジン(HTMLを読み込んだサーバのこと)以外のサーバからデータを取得する仕組みです。 今回で言うところの、EC2でHTMLLを読み込まれていますが、画像に関してはS3にあるのでS3からHTMLとして画像を持ってくるといったことが必要になってきます。 その時に役に立つのがCROS処理です。 出典 今回の失敗 S3のCROS処理のポリシーを書いておくべき所が空欄だったので、保存ができませんでした。 #解決策 CROS処理を許可できるようにポリシーを書いていった所保存することができました。 私は以下のようにして書きました。 [ { "AllowedHeaders": [ "*" ], "AllowedMethods": [ "PUT", "POST", "DELETE" ], "AllowedOrigins": [ "*" ], "ExposeHeaders": [] } ] そうした所、無事保存ができました。 いやー良かったよかった。 出典 お役に立てれば幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Active Storageで投稿した画像をS3に入れようとしたら権限関係で詰まった話。

AWSのS3に投稿された画像を保存できるようにしよとした時にかなり苦戦したのでそのお話をさせていただければと思います。 S3の権限については学習したはずなのにそのことについてすっかり忘れてしまっていたのでそのお話をさせていただければと思います。 まず、どうやってアクセス権限を得ることができるのかというと、 1つ目として、 EDITOR="vi" bin/rails credentials:edit コマンドを使用してシークレットアクセスキーとアクセスキーを入れて環境変数を有効にするコマンドである source ~/.zshrc コマンドを打って環境変数を有効化します。 2つめは、S3のコンソールに行って バケットからアクセス許可の項目に行っていただいて、そこにあるバケットポリシーにJSON形式でコードを書いて許可するやり方です。 私は以下のようにしてアクセスを許可しました。 それは、 { "Version": "2012-10-17", "Id": "Policy1544152951996", "Statement": [ { "Sid": "Stmt1544152948221", "Effect": "Allow", "Principal": { "AWS": "IAMユーザーのARN" }, "Action": [ "s3:*", "s3:PutObjectAcl", "s3:PutObject", "s3:GetObject", "s3:DeleteObject" ], "Resource": "バケットのARN/*" } ] といった感じです。 ひとつひとつ見ていくと、Actionの項目に、PutとGetとDeleteの項目を許可するようにアクセス制限が組まれています。 これをしてあげることで、S3に対して投稿やその投稿されている画像等のファイルをWeb上に表示できる仕組みができました。 私はこれでつまずいていました。 なので、このようなことがないように備忘録として保存しておきます。 ありがとうございました。今回AWSがより一層好きになりました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

クラスの定義

クラスを使用して以下の表示結果を出力するコードを記述します。 表示結果 ワンワン わたしは犬です わたしの名前はポチで種類はトイプードルです 仕様 クラス変数 値 type 犬 インスタンス変数 値 name ポチ dog_type トイプードル クラスメソッド メソッド名 処理 say ワンワン インスタンスメソッド メソッド名 処理 say_type "私は{クラス変数type}です" self_introduction わたしの名前は{インスタンス変数name}で種類は{インスタンス変数dog_type}です 雛形 class Dog @@type = "犬" def initialize @name = "マロン" @dog_type = "トイプードル" end def self.say puts "ワンワン" end def say_type puts "わたしは#{@@type}です" end def self_introduction puts "わたしの名前は#{@name}で種類は#{@dog_type}です" end end dog = Dog.new Dog.say dog.say_type dog.self_introduction 1)クラスの定義はclass クラス名 〜 endでした。まずはクラス変数であるtypeを定義します。 2)クラス変数は変数名の始めに@@(アットマーク2つ)をつけます。 3)インスタンス変数です。インスタンス変数は変数名の始めに@(アットマーク1つ)をつけます。 4)インスタンスごとに定義する必要があるのでインスタンスメソッド内で定義します。 5)クラスメソッドsayの定義をします。クラスメソッドはメソッドの前に自身のメソッドであることを示すself.をつけます。 6)インスタンスメソッドであるsay_typeとself_introductionを定義します。インスタンスメソッドの中ではクラス変数とインスタンス変数の両方が使えます。インスタンスメソッドの書き方は通常のメソッドと同じです。 7)定義したメソッドをクラスの外から呼び出します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsポートフォリオ #15 READMEの作成

こんにちは 今回はReadmeの作成を行いました。 (前回記事 #14 カテゴリーソート機能) ポートフォリオの顔とも言える超重要なReadme 今回はポートフォリオにおいて最も重要なものの一つであるReadmeの作成に取り組みました。 Readmeとアプリの最初の画面がポートフォリオにおいて最も大切、もはやそこだけで判断される、なんていう話は皆さんもご存知だと思います。 私が作っているアプリが業務改善系ということもあり、多くの方が作られるSNS系のアプリに比べると使い方がなかなか伝わりにくいので、Readmeでの説明には特に力を入れようと考えていました。 まずは先人たちのものを見せてもらい、何を書けばいいのかを把握するところから始めました。 作成時意識したこと 最初にわかりやすい概要を書くことで、そこだけしかみてもらえない場合にも雰囲気を掴んでもらえるようにすること 画像も上の方に一枚貼ることで、少しのスクロールでも目に入るようにし、視覚的にも伝えられるようにすること アプリ内にも簡単な説明書きがあるので、Readmeは丁寧な説明を記述すること 読んでもらえるかはわからないけど、各機能の説明をしっかり書きました。 書く順番 概要 URL 技術一覧 ER図 インフラ構成図 開発背景 意識したこと 機能一覧 機能の詳細 今後の改良計画 どの順番がベストなのかはわかりませんが、レビューをしてもらった際にもらったご指摘も踏まえ、最終的にこの順番で書きました。 ポートフォリオ作りもいよいよゴールが近づいてきているなという感じがします。 この調子で頑張るぞー!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

structure.sqlを使いたい!

はじめに Railsではdb:migrateすると、デフォルトでdb/schema.rbを生成されて、このファイルでスキーマ管理をしてくれます。 しかし、ユニークインデックスにwhereを付けるようなRDB固有の命令については、Rails標準のschema.rbで管理し続けるのは限界があるのではないかという問題点があります。 それを解決してくれるのがstructure.sqlで、SQLでスキーマ情報を管理することも可能です。 structure.sql生成方法 config/application.rbに以下を記述して、rails db:migrateを実行することで生成されます。 config/application.rb config.active_record.schema_format = :sql # デフォルトでは「:ruby」となっています structure.sqlをロードする 生成されたstructure.sqlファイルは、下記のコマンドでデータベースへロードできます。 $ rails db:structure:load SQLファイルだけ欲しい場合 SQLダンプファイルのみが欲しい場合は、下記コマンドでstructure.sqlのみを生成することができます。 $ rails db:structure:dump 注意点 migrationファイルに基づいてstructure.sqlは更新されていくので、中身を変更したい時は手動では無くrails db:migrate:resetなどでmigrationを実行し直す。 終わりに 最近では、structure.sqlでスキーマ管理することも増えてきている?っぽいので、どういったものか?ということくらいは掴んでおいた方がいいかもしれないですね!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

バリデーションの種類

こんにちは! 今回の記事はバリデーションについて あれこれアウトプットしていこうと思います! 僕の理解した範囲のバリデーションについて 記述していこうと思います! バリデーションとは? バリデーションとはデータを登録する際に 一定の制約をかけることを言います。 *新規登録ユーザー名が空のデータで登録されるのを防ぐ。 *新規パスワード登録の際に最低6文字以上は入力を強制させる。 といった例が挙げられます。 バリデーションを設定する際はvalidatesメソッドを使用します。 記述方法は以下のとおりです。 validates :カラムの種類, バリデーションの種類 validates :name, presence: true 基本的にデータの登録などテーブルとの やり取りに関する情報は モデルを使用します。 コントローラーはモデルの機能を利用して処理を 呼ぶだけの簡単な処理を記述します。 バリデーションの種類 validates :name, presence: true presence: tureは名前が空欄だとデータの登録ができない というバリデーションです。空欄のまま登録を試みるとエラーが起こります。 validates :name, absence: true absence: trueはpresence: trueの逆で 名前が空欄であるというバリデーションです。 validates :name, acceptance: true, acceptance: trueはモデルにチェックボックスをチェックしたかを 確認させるバリデーションです。 利用規約の同意などでこの記述が用いられます。 validates :name, presence: true length: { maximum: 6 } lengthを指定することで指定した属性の文字数制限をチェックすることができます。 length: { maximum: 6 }の通り、6文字以内の文字制限があるため、 7文字以上記述しようとするとエラーが発生します。 validates :name, presence: true length: { minimum: 6 } 逆にこちらは7文字以上は記述しなければいけません。 validates :email, uniqueness: true emailは一意となるようにRails側でバリデーションをかけることができます。 バリデーションの共通オプション validates :name, presence: true on: :create onオプションはバリデーション実行のタイミングを設定できます。 on: :createを指定することでレコードに新規作成時に バリデーションが行われます。 validates :name, length: { maximum: 6 }, allow_blank: true allow_blank: trueは値が空の場合にバリデーションを実行します。 この記述で、値が空の場合と7文字以内でデータを登録しようとすると エラーを返されます。 validates :email, format: { with: /\A[a-zA-Z]+\z/, message: "英文字のみが使用できます" } messageオプションを使用するとバリデーション失敗時にエラ〜メッセージを 表示することができます。 また、formatはwithオプションを指定した正規表現と一致するかどうかを調べます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RSpec(model)の読みやすい書き方

Rubyでテストを書くにあたって userモデル 今回はこちらのメソッドへのテストの書き方について! user.rb class User def fullname [name, surname].join(' ') if middle_name.nil? [name, middle_name, surname].join(' ') if middle_name.nil? end end userモデルRspec userの名前が定義されている状況に応じて異なるフルネームを返す。 user_spec.rb describe 'fullname' do context 'with surname and name' do let(:user) { build(:user, surname: 'delicious', name: 'banana') } it 'shows full name without middle name' do expect(user.fullname).to eq('banana delicious') end end context 'with surname, name and middle name' do let(:user) { build(:user, surname: 'delicious', name: 'banana', middle_name: 'super') } it 'shows full name with middle name' do expect(user.fullname).to eq('banana super delicious') end end end どこに何を書けば良い? describe: メソッド名 context: 状況設定 it: returnされる結果 改善のアドバイス等ある方はコメントいただけると嬉しいです!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】Herokuにデプロイ後、一部のページで500エラー(Internal Server Error)

はじめに 本番環境にて500エラーが発生し 1週間ほど悩み通してようやく改善したのでこの機会にまとめておきたいと思います。 発生している問題 ローカル環境では問題なく動くが、(PostgreSQLへの対応などは済ませてある) Herokuにデプロイすると、一部のページを開いたときに500エラーが出て「We're sorry, but something went wrong.」となる。 heroku logs -tでログを見ても500と書いてるだけで何が悪いのかよくわからない 2021-05-14T16:33:21.741002+00:00 heroku[router]: at=info method=GET path="/recipes" host=myapp.herokuapp.com request_id=58d9aaaf-b1c3-46ab-a97c-51d54f102341 fwd="14.10.86.33" dyno=web.1 connect=1ms service=23ms status=500 bytes=1827 protocol=https 対処 マイグレーションの実行 DBのマイグレーションがうまくいっていない場合が多いみたいなので、heroku run rails db:migrateを試してみるが、特にエラーも出ず実行できた。だがそれでも改善しない。 設定などあちこち見ても悪いとこはなさそう・・・ 結論 もう一度Herokuのログを見直してみると何やら書いてあった。 FATAL -- : ActiveRecord::StatementInvalid (PG::UndefinedTable: ERROR: relation "recipes" does not exist WHERE a.attrelid = '"recipes"'::regclass 調べてみるとどうやら「テーブルが無いよ」という意味らしい。 そこでheroku run rails cでコンソール起動しモデル名.column_namesでカラム名確認するとテーブルが無いとエラーが出た。 原因はこれだったみたい。 db:migrateで上手くいかなかったのでDBリセットで対処することに。 heroku pg:reset DATABASE_URLコマンド実行 WARNING: Destructive action ▸ データベース名 will lose all of its data ▸ ▸ To proceed, type アプリ名 or re-run this command with --confirm こういったメッセージが出るので、アプリ名を入力して実行すればリセット完了。 もう一度heroku run rails db:migrate実行し、コンソールでテーブル確認すると今度はきちんとテーブルができてる。 デプロイしたアプリも正常に動くようになった。 最後に 今回学んだのはログってすごく重要だということ。 何がダメかちゃんと書いてあるし、これからはしっかり見るようにしよう。 500エラーで悩んでる人結構いると思うので この記事がお役に立てれば幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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

これから作っていく簡単なWebアプリの作成メモ(自分の備忘)です。 自分用なのであまり凝りすぎないように書いていきたい。 <<前回の記事 今回やったこと 前回作ったスケジュールの編集機能の修正点について 前回記事にて、一覧画面からテーブルの一行をクリックすることで、そのスケジュールの編集画面に遷移できるようにする変更をした。しかし、この時は眠くて画面遷移の動作確認を「一覧画面」⇒「編集画面」(⇒既設linkにて「一覧画面」)と、一度しか実施しなかった。 今日、改めて触ってみると、ページリロード後の「一覧画面」⇒「編集画面」⇒「一覧画面」は移動できるが、その後もう一度同じ動きで(或いはテーブルにある別のスケジュール行で)編集画面に遷移しようとしても、何も起こらなかった(!?)。 上記一覧画面からスケジュールをクリックして... 赤枠のリンクをクリックすることで一覧画面に戻って... 戻ってきた一覧画面からもう一度同じ遷移をしようとしても、何も起きない!? なぜうまくいかないのか、だいぶ考えてしまいました。。。 そして出した結論が、 「もしかして、JQuery側でレンダリングしてる処理が最初(一覧画面の一番はじめの表示時)しか動いてない??」 です。 ではなぜ、そのような現象が起こってしまうのか。 いろいろとWeb記事を見回って、やっとこれだというものを発見(以下記事です)。 【Rails】turbolinksやAjaxの影響でjQueryが正常に動作しないとき turbolinksがページ遷移時の動作を軽くしてくれていますが、これによってJQueryのイベント発火のタイミングが少し考慮しなければいけなくなり、その部分にまるで気が付いていなかったので、今回のようなダメな実装例が出来上がってしまったということです。 反省しつつ、前回書いたJQueryの処理を上記記事の通りに修正。 app/javascript/packs/schedules.js jQuery(document).on('turbolinks:load', function() { $(".clickable-row").css("cursor","pointer").on('click', function() { location.href = $(this).data("href"); }); }); turbolinksを使う際には、このあたりの考慮をしないといけなかったんですね。 無知故致し方なし...。 スケジュールの編集に対する画面遷移の変更 修正をしていて思ったこと。 「一覧からクリックしていきなり編集画面になるの、なんか変では?」 最初に作った時にはまるで気が付かなかったけど、一般的には以下のようになっていることが多い。 一覧画面⇔詳細表示画面⇔編集画面 ようやくそのことに気が付いたので、これも修正する。 まずは一覧画面からテーブルの行をクリックしたときの遷移先を詳細表示画面にする。 app/views/schedules/index.html.erb (略) <tbody> <% @schedules.each do |lst| %> <tr class="clickable-row" data-href="<%= schedule_path lst.id %>"> (略) さらに、詳細表示画面から編集画面へのリンクを用意する。 app/views/schedules/show.html.erb (略) <div class="row_line"> <%= link_to '編集', edit_schedule_path(@schedules.id), class: 'btn primary-btn' %> </div> 動作も確認できた。 スケジュールの削除 スケジュールを編集できても、削除できないと不便(というか最低限出来ないと困る)。 なので、スケジュールを削除できるようにします。 以下の記事は参考に読んだ。 [Rails]deleteメソッドとdestroyメソッドの違いについて!![初心者] まずはコントローラーの手入れから。 app/controllers/schedules_controller.rb (略) def destroy @schedules = Schedule.find(params[:id]) destroyed_title = @schedules.title @schedules.destroy redirect_to schedules_url, notice: "予定『#{destroyed_title}』を削除しました。" end (略) ※これまで、しれっとdestroyアクションのことをdeleteって書いてしまっていたことに気が付いて、これも修正(Userコントローラーのほうもアクション名を修正した)。 次に、詳細表示画面に削除ボタンを配置する。 app/views/schedules/show.html.erb (略) <div class="row_line"> <%= link_to '編集', edit_schedule_path(@schedules.id), class: 'btn primary-btn' %> <%= link_to '削除', schedule_path(@schedules.id), method: :delete, class: 'btn btn-danger' %> </div> できた。 動作も確認できたのでOK。 今後やるべきこと まだまだ最低限使えるものへの道のりは長い...。 ユーザーでのログインの仕組みづくり ユーザーの新規登録 ユーザーの編集 ユーザーの詳細表示 ユーザーの削除 見た目の改善 Bootstrapの有効化 レスポンシブデザインへの対応 データ削除時に「本当に削除しますか」の確認の実装 各種バリデーションの実装 今回はここまで。 記述ミスもまだまだあるので、見つけ次第修正したい。 あと、最低限必要なものが揃ったら、リファクタリングも時間を割いておきたい(願望)。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む