20200224のRailsに関する記事は23件です。

check_boxとradio_button

チェックボックスとラジオボタンを作ろう

今回はラジオボタンはスムーズに出来たのですが、チェックボックスで少し苦戦したのでメモをしたいと思います。

説明

小説サイトを練習として真似して作成中です。

まず最初に、タイトルやキーワード、小説のジャンルなどを纏めるテーブル
novel_listsテーブルを作りました。

続いて、今回のチェックボックスとラジオボタン部分です。
・ novel_keyword キーワード
・ genre ジャンル

二つのカラムを作りました。

novel_list.rb

こちらにenumを使って配列を作りたいと思います。

ジャンルの配列
enum genre:{
    different_world: 1, #異世界
    real_world: 2, #現実世界
    high_fantasy: 3, #ハイファンタジー
    low_fantasy: 4, #ローファンタジー
}
キーワードの配列
enum novel_keyword:{
    status_difference: 1, #身分差
    year_difference: 2, #年の差
    non_love: 3, #非恋
    villain_daughter: 4, #悪役令嬢

配列が出来た所で、フォームです。novel_listsのnew.html.erbに今回は作成します。

<%= form_with(model: @novel_list, local: true) do |f| %>
form_withの中に今回は作っていきたいと思います。

        <%= f.radio_button :genre, :different_world, checked: "checked" %>
        <%= f.label :genre, "異世界", class: "different_world" %>
        <%= f.radio_button :genre, :real_world %>
        <%= f.label :genre, "現実世界", class: "real_world" %>
        <%= f.radio_button :genre, :high_fantasy %>
        <%= f.label :genre, "ハイファンタジー", class: "high_fantasy" %>
        <%= f.radio_button :genre, :low_fantasy %>
        <%= f.label :genre, "ローファンタジー", class: "low_fantasy" %>

まずはラジオボタンですね。
第一引数部分にカラム名としてgenre  第2引数部分にプロパティ名 
最後にオプションとして、checked:"checked" これを入れる事で最初に異世界のラジオボタンにチェックが入った状態となります。
ちなみにradio_buttonは一つしか選択が出来ません。


続いて・・・チェックボックスです。こちら苦戦して、teratailで質問しました。後半URL置いておきます。

<%= f.check_box :novel_keyword, {}, :status_difference, nil %>
        <%= f.label :novel_keyword, "身分差", class: "status_difference" %>
        <%= f.check_box :novel_keyword, {}, :year_difference, nil %>
        <%= f.label :novel_keyword, "年の差", class: "year_difference" %>
        <%= f.check_box :novel_keyword, {}, :non_love, nil %>
        <%= f.label :novel_keyword, "非恋", class: "non_love" %>
        <%= f.check_box :novel_keyword, {}, :villain_daughter, nil %>
        <%= f.label :novel_keyword, "悪役令嬢", class: "villain_daughter" %>

<%= f.check_box :novel_keyword, {}, :status_difference, nil %>
# 第1引数:カラム名
# 第2引数:オプション
# 第3引数:チェックされたときのvalue
# 第4引数:チェックされてないときのvalue
とわかりやすく教えて頂きました。後は、radio_buttonと同じくlabelを付けて出来上がりです。

今後の試行錯誤・・・

今回このようにチェックボックスや、ラジオボタンを作りましたが。
collectionなどを使って、モデルデータから.allで作成などしてる記事があったので
カラムのデータだけをチェックボックスとして取得。などそういった事が出来るのではないかと思われます。
検索しつつ、自分でも試行錯誤してみようと思います。

現在の状態だと、多くなるほどコードが見にくいです。これが2行で収まればとても良いと思います。
またわかり次第載せたいと思います。

https://teratail.com/questions/243125

今回質問した際のURLです。

参考にした記事も載せて頂きました。大変わかりやすかったです。
さらに、
システムのDBとしてpostgresqlを使っているのであれば、1カラムに対して配列をinsertすることができたと思います。
との情報も頂いたので検索して見たいと思っています。

以上今回のメモでした。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】掲示板アプリでTopicとPostを紐づける方法(アソシエーション)

はじめに

現在、ポートフォリオとして掲示板アプリを作成しており、題目にある「TopicとPostを紐づける」部分でハマった(時間かかった)ので備忘録として。
ちなみに、初投稿なので暖かい目で見守ってやってください。笑

早速ですが、紐づけるために必要な要素として以下の2点を中心に記載します。

  1. model側の修正
  2. controller側の修正

やりたいこと

各modelを関連付けて、「@post.topic.title」のような形で値を取得したい

環境

macOS:10.14.6
Ruby:2.5.7
Rails:5.2.4.1

model側の修正(アソシエーション)

今回登場するmodelは、以下の3つ。
・Topic
・User(deviseを使ってます)
・Post

Topicがスレッド、Postがスレッドに対するレスで、
Topic(1)---(多)Post
User(1)---(多)Post/Topic
というような位置付けになります。

今回の場合は既に各modelを作成(generate)済みだったので、アソシエーション用のマイグレーションファイルを作って、マイグレートしていく形で進めます。

マイグレーションファイルを作成する

$ rails g migration AddUserToTopic
$ rails g migration AddTopicToPost

マイグレーションファイルを編集する

文法は、「add_reference table名、reference名」が基本形になります。
indexはオプションなのでお好みで。
ちなみに超ざっくりですが、indexは読み込み/取得速度を上げてくれるものです
(但し、書き込み速度は遅くなるので注意が必要)

20200222110925_add_user_to_topic.rb
class AddUserToTopic < ActiveRecord::Migration[5.2]
  def change
    add_reference :topics, :user, index: true #追記箇所
  end
end
20200223032448_add_topic_to_post.rb
class AddTopicToPost < ActiveRecord::Migration[5.2]
  def change
    add_reference :posts,:topic #追記箇所
  end
end

マイグレートする

$ rails db:migrate

上記コマンド実行後、以下のようなmigratedが出力されればOK

== 20200223032448 AddTopicToPost: migrating ===================================
-- add_reference(:posts, :topic)
   -> 0.0796s
== 20200223032448 AddTopicToPost: migrated (0.0797s) ==========================

モデルを編集する

topic.rb
class Topic < ApplicationRecord
  validates :title, presence: true
  has_many :posts #追記
  belongs_to :user #追記
end
post.rb
class Post < ApplicationRecord
  belongs_to :topic #追記
  belongs_to :user #追記
end
user.rb
class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
  validates :name,:email,:encrypted_password, presence: true
  has_many :topics, through: :posts #追記
end

これでmodel側はOKです!

controller側の修正

各modelの関連付けできたので以下のような形で、関連先の値を拾ってこれるようになりました。細かく言うとパラメーターの中から明示的に値を取得するイメージです。
params[:topic_id]という風な感じで。

topics_controller.rb
class PostsController < ApplicationController

  def new
    @topic_id = params[:topic_id]
    @post = Post.new
  end

  def create
    @post = Post.new
    @topic_id = params[:topic_id] #paramsからtopic_idを取得し、インスタンス変数@topic_idに代入
    @post.topic_id = @topic_id #上記を@post.topic_idに代入
    @post.body = params[:post][:body]
    @topic = Topic.find(@topic_id)
    @post.user_id = current_user.id
    if @post.save
      redirect_to topic_path(@topic_id), notice: '投稿しました'
    else
      render 'posts/new', alert: '投稿できませんでした'
    end
  end
end

ちょっと汚いですね、、、汗
取得したい値は「paramsから取得してインスタンスに代入」と書く形です。
なので、取得したい値に応じて編集してください。上記はあくまで参考として。

viewの修正(おまけ)

以下はトピック一覧画面(topic#index)ですが、postの数やpostの投稿時間を取得して表示することができます。

index.html.erb
<div class="container mt-5 ml-5">
  <div class="row">
      <% @topics.each do |topic| %>
        <div class="table tabel-hover">
          <%= link_to topic do %>
            <div class="list-group" style="max-width: 500px;">
              <div class="list-group-item list-group-item-action">
                <%= topic.title %> (<%= topic.posts.count %>)  #post数を計算
                <br>
                <small class="text-muted text-right">
                最終投稿日時:<%= topic.posts.last.created_at %> #最新のpost投稿時間を取得する
                </small>
              </div>
          <% end %>
          </div>
      <% end %>
      </div>
  </div>
</div>

こんな表示になります↓

スクリーンショット 2020-02-25 1.42.56.png

非常に便利ですね。

参考文献

Active Record の関連付け

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsアプリケーションの環境構築周りのまとめ

Railsの案件に携わって最初めちゃ苦しんだ。
まとめておいて後から振り替えれるようにしておく。

1. 各パッケージ管理ソフトの概要

パッケージ管理ソフト 概要
rbenv 複数のRubyバージョンを管理するツール。バージョンを指定してrubyをインストールできる。インストール後は各バージョンごとに利用するgem置き場が提供される。Railsアプリケーションごとに使用するRubyバージョンを指定できるようになる。
RubyGems Rubyライブラリのgemのパッケージ管理ツール。現在利用しているバージョンのrubyに対してgemをインストールできる。rubyをインストールすると付属してくるツール。目的のgemをインストールする時に別のgemが必要になる(=依存関係がある)場合、それもインストールして目的のGemを使える状態にすることができる。
Bundler RubyGemsをより高機能に使うgem。RailsプロジェクトごとにGemfileというファイルで使用するgemを明示的に管理できる(PC全体を汚さず、Railsプロジェクトで完結する)。こいつ自体がRubyGemsによってインストールされるgemであるが、RubyGemsを操って各Railsアプリケーションに必要なgemをGemfile, Gemfile.lockというgem一覧ファイルに基づいてインストールできる。
RubyGemsの説明を見てるとそれで十分じゃね?と思うかもしれないが、Gemfile.lockが一番重要な機能。例えばgem「A」をインストールする場合にgem「B」が必要だったとする。そんなgem「A」をRubyGemsでインストールしたとする。このときgem「B」もインストールされる。ここまではよい。しかし時が流れてなにかの拍子にgem「B」のバージョンを上げたとする。このとき、gem「A」を利用するには実はgem「B」の特定のバージョンが必要であり、互換性がなくなってしまった場合、gem「A」は使えなくなってしまう。gem「B」のどのバージョンが必要なのかももうわからなくなってしまう。Bundlerを使ってgemをインストールして使用する場合は、Gemfile.lockに具体的にどんなgemをどんなバージョンでインストールしたのかが細かく記録されるため、この問題へ対処できるのだ。また、複数人で開発するときに対象のRailsアプリケーションが使用するgemの一覧をバージョン含めて共有する時にも役に立つ。

補足

rubyenv

  • 関係するディレクトリ
# rbenv周り
~/.rbenv/
  versions/
    X.X.X/      # Ruby(Ver X.X.X)の各種ファイルを管理するディレクトリ
      bin/      # 当該Rubyバージョンの標準ライブラリ、RubyGems経由でインストールしたコマンドの実行ファイル
      include/        
      lib/      # 標準ライブラリ、RubyGems経由でインストールしたライブラリ     
      openssl/
      share/    # マニュアル類
  shims/
    <各コマンドの実行ファイル>
  version       # PC全体で使うruby環境のバージョン番号が書かれている


# Railsプロジェクト周り
<Railsプロジェクト用ディレクトリ>/
  .ruby-version # このRailsプロジェクトで使うRuby環境のバージョン番号が書かれている(優先順位は version < .ruby-version)

2. 関係図

言葉だけだと見返した時に意味わかんなくなりそうなので、イメージ図を貼っておく。

30672.jpg

3. よく使うであろうコマンド

3.1. rbenv

コマンド 説明
rbenv install --list インストール可能なRubyのバージョンを表示。無印のバージョンが最も高くてdevとかついてないやつが最新の安定版みたい。
rbenv install <バージョン> バージョン指定してRubyをインストール
rbenv rehash インストールしたRubyへパスを通す(新しくバージョン指定してインストールした場合に必須で必要になる。rbenv install とセット)
rbenv global <バージョン> 特段の指定がない場合に本マシンで使用するRubyのバージョンを設定
rbenv local <バージョン> カレントディレクトリ配下で使用するRubyのバージョンを指定。このコマンドを実行するとカレントディレクトリに.ruby-versionというファイルができ、開くと<バージョン>が記載されている。後述のGemfileではRailsアプリケーションが使うRubyバージョンは指定できない(Bundler自体が現在使っているRubyを親とするため)ため、なかなか重要なファイルである
ruby -v rbenvのコマンドではないが。カレントディレクトリにおいて使用することになるRubyのバージョンを確認する

3.2. RubyGems

基本的にgemはBundlerを使ってアプリケーションごとにインストール、管理するため、RubyGemsのコマンドを使用することは少ない。

コマンド 説明
gem install bundler 現在使用しているバージョンのRubyに足しいてBundlerをインストールする。以降、るため、基本的にこのコマンドだけで良さげ。
gem list -ra <gem名> イントール可能なgemのバージョンを確認。には正規表現も使えるため、例えばrailsならば^rails$とすると表示がすっきりする
gem update --system RubyGems自体のバージョンをあげる。

3.3. Bundler

コマンド 説明
bundle install 1. Gemfile.lockを確認してgemをインストール。
2. Gemfileを確認してGemfile.lockに存在しないgemをインストールし、Gemfile.lockに追記。
bundle update <特定のgem名> Gemfile.lockを無視してGemfileに書いてある条件に基づいて特定のgemをインストール。一部のgemをアップデートしたいときに影響範囲を小さく抑えるために使用する。<特定のgem名>を省略してしまうと、Gemfile.lockの内容をすべて無視して、イチからGemfileに基づいてgemをインストールし直してしまうため、これまで動いていたRailsアプリケーションが動かなくなった、ということが起きうる。基本的に使っちゃダメなコマンド。
bundle exec <各種コマンド> Gemfile.lockに書いてあるバージョンのgemを使用して<各種コマンド>を実行。bundle execがないと意図せぬバージョンのgemが使われたりして大変なことになる。railsアプリケーションを操作するときには絶対にbundle execをつけるべき。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Active Storageを導入する前に知っておきたかったこと(ACLディレクトリ設定)

概要

ファイルアップロード機能を実装する際に、Rails5.2で導入されたActive Storageを使ってみました。
実際に使ってみて困ったことがいくつかあったので、誰かのために役立てばと思いまとめておきます。

環境

  • Rails 6.0.2.1
  • Ruby 2.6.5

結論: 困った点と解決方法

困った点

Active Storage のサービスとして、Amazon S3を利用して、以下の点で困りました。

  1. アップロード時にアクセス権限(S3のACL)をpublic-readに設定できない
  2. ファイルを保存するディレクトリが設定できない
    (正確には、S3にはフォルダという概念は存在しないので、ファイルのキー名につけるprefixがつけられない)

解決方法

以下のパッチで対応しました。
この書き方だと、ファイルごとにアクセス権限を変更したり、ディレクトリを変更したりといった細かい設定はできません。

# lib/active_storage/service/better_s3_service.rb

require 'aws-sdk-s3'
require 'active_storage/service/s3_service'
require 'active_support/core_ext/numeric/bytes'

module ActiveStorage
  class Service::BetterS3Service < Service::S3Service
    attr_reader :root

    def initialize(bucket:, upload: {}, root:, **options)
      @root = root
      super(bucket: bucket, upload: upload, **options)
    end

    def upload(key, io, checksum: nil, content_type: nil, **)
      instrument :upload, key: key, checksum: checksum do
        object_for(key).put(upload_options.merge(body: io, content_md5: checksum, content_type: content_type, acl: 'public-read'))
      rescue Aws::S3::Errors::BadDigest
        raise ActiveStorage::IntegrityError
      end
    end

    private
      def object_for(key)
        path = root.present? ? File.join(root, key) : key
        bucket.object(path)
      end
  end
end

以下のようにrootを設定する。

# config/storage.yml
amazon:
  service: BetterS3
  root: root-directory

1. アップロード時にアクセス権限(S3のACL)

アップロードしたファイルにパブリック読み取りアクセス許可をつけようとしましたが、v6.0.2.1ではアップロード時に権限を設定することはできません。

ただ2019年7月のPR1で、publicというオプションをstorage.ymlで設定できる変更がマージされたので、将来的にはサポートされそうです。
いくつかの記事2やissueへのコメント3を参考にしてパッチをあてる形で対応しました。

2. ファイルを保存するディレクトリが設定できない

S3にアップロードするファイルにprefixをつけて、フォルダを作成したいことは多々あると思いますが、現時点(v6.0.2.1)のActive Storageではサポートされていません。
「Active Storageでフォルダーを使う」といった旨のissue4 で議論されていますが、あまり議論が進んでいないので、まだまだサポートされなそうですね…。

こちらも先述のissue4にあったパッチ5を利用して、config/storage.ymlから特定のディレクトリ名を設定できるようにしました。

まとめ

Active Storageを使ってみましたが、意外とサポートされていない機能が多く、少し驚きました。
Delyさんの記事にもありましたが、Rails本体に入っていても過信せず、使う前にユースケースを満たしているか利用すべきだと勉強になりました。

読んでいただきありがとうございました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ERROR: While executing gem ... (Errno::EACCES)が出た時の解決法

実現したいこと

railsでWEBアプリケーションを作るための環境構築。

エラー内容

Rubyの拡張機能(gem)を管理するためのbundlerをインストールした時にでたエラー
ERROR: While executing gem ... (Errno::EACCES)

bundlerとは

gem一種
railsでアプリケーションを開発する際には複数のgemを使用します。gem同士の互換性を保ちながらバージョン等を管理してくれる仕組みのこと。複数人、複数環境で開発を行う際に各環境で扱うパッケージの種類やバージョンを合わせてくれて非常に便利である。

解決方法

参考記事

今回このエラーの解決までに参考にした記事です。
https://qiita.com/nachiguro1003/items/4b564b92eca3ba35744a

修正内容

上記参考記事にも記載されていますが、今回のエラー内容は、gemをインストールする時に不必要sudoをつけて実行してしまったため、ファイルへ書き込みする権限を失ってしまったためです。
ですので解決策としてはこの権限を修正します。
ターミナルで以下の通り実行します。
sudo chown -R (ユーザ名):staff /Users/(ユーザ名)/.rbenv
これで権限が戻りますので、再度'gem install bundler'することで無事解決できました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby on Rails 学習メモ.1

Ruby on Rails学習メモ.1

以下、初学者の独学によるメモ(チラシの裏)。
Progateでの学習もある程度進んできたため、アウトプットをすることにした。
学習中のため、誤った解釈もあるかもしれない。

Railsでのサイト作成は、
view(ブラウザ)→
ルーティング(アクションを呼び出す)→
コントローラー(URLに対応したHTMLを送信)
(→view…)
の編集の連続である。

これらの基本をよく理解すること。
また、何をやろうとしているのか(目的を)理解しないまま、コードを覚える事を深追いしながら無理矢理学習を進めようとすると単なる写経になってしまうので、まず目的を理解する事に努めること(理解できない場合は何度も読む)。(先の学習内容まで進めた後に復習すると理解できる場合もあるので、まず進める方が良い場合もある。)

1.新規投稿

HTML(view)の設定

<div class="form-body">
  <%= form_tag("/posts/create") do %>
  <textarea name="content"></textarea>
  <input type="submit" value="投稿">
  <% end %>
</div>

<%= form_tag("/posts/create") do %>のform_tagが設定されていることにより、
<input type="submit" value="投稿">、つまり投稿ボタンを押すと、submitされたデータを「/posts/create」のURLに送信するようになっている。
<textarea name="content"></textarea>
入力したデータにname属性を指定している。
これにより、コントローラーのアクション内(変数:params)で入力したデータを受け取れるようになる。

ルーティングの設定

get "posts/create" => "posts#create"

URL「/posts/create」から情報を受け取り、
「posts」コントローラーの「create」アクションを呼び出す。

コントローラーの設定

def create
    @post = Post.new(content: params[:content])
    @post.save
    redirect_to("/posts/index")
end

ルーティングからcreateアクションが呼び出され、以下の処理が実行される。
@post = Post.new(content: params[:content])
@post.save

変数@postに投稿された内容を代入(受け取るために変数paramsを使用している)し、データベースに保存する。
redirect_to("/posts/index")
URL:/posts/indexに転送(ページ移行)する。

@user = User.new(name: params[:name], email: params[:email])のように、配列として複数のデータを受け取ることもできる。

2.投稿詳細画面の表示

HTML

<% @posts.each do |post| %>
      <div class="posts-index-item">
        <div class="post-right">
          <%= link_to(post.name, "/posts/#{post.id}") %>
        </div>
      </div>
<% end %>

idを取得・送信するように設定。

ルーティング

get "posts/index" => "posts#index"
get "posts/:id" => "posts#show"

showアクションのルーティングはindexアクションより下に書かないといけないことに注意する。

コントローラー

def show
    @posts = Post.find_by(id: params[:id])
end

3.投稿成功・失敗の表示

クラス定義:投稿の制限(Validate:検証の意)

class Post < ApplicationRecord
  validates :content, {presence: true}
  validates :content, {length: {maximum: 140}}
end

Validateはクラス内(models/post/rb)で定義するので、postクラス配下すべてで適用される。
上記は空の投稿と140字以上の投稿にfalseを返すようになっている。

コントローラー(アクション)

def create
    @post = Post.new(content: params[:content])
    if @post.save
    redirect_to("/posts/index")
    flash[:notice] = "投稿を作成しました"
    else
    render("posts/new")
end

if @post.saveがtrueの場合、「/posts/index」に飛び、
「flash[:notice] = "投稿を作成しました"」を実行する。

falseの場合、「posts/new」に飛ぶ

HTML
flashはいろいろな箇所で共通で使っていくのでapplication.html.erbに設定する。
投稿成功の場合:

 <% if flash[:notice] %>
  <div class="flash">
    <%= flash[:notice] %>
  </div>
 <% end %>

投稿失敗の場合
投稿失敗の場合posts/newに飛ぶので、application.htmlではなくnew.htmlを編集する。(2行目から6行目までが該当。)

<%= form_tag("/posts/create") do %>
  <% @post.errors.full_messages.each do |message| %>
       <div class="form-error">
       <%= message %>
       </div>
       <% end %>
       <textarea name="content"><%= @post.content %></textarea>
       <input type="submit" value="投稿">
<% end %>

saveメソッドを呼び出した際にバリデーションに失敗すると、Railsでは自動的に@post.errors.full_messagesの中にエラーメッセージが配列で入るようになっている。
each文を用いることで、配列の中のメッセージ全てを表示させる。
また、<textarea>内に<%= @post.content %>を設定しているため、入力した(投稿に失敗した)文章が出力される。
(newアクションではこの時点では変数postが定義されていないため、@post = Post.newを定義する必要があることに注意。←newアクション作成時に作っておく?)

4.投稿一覧ページの作成

先にターミナルで「rails g model Post content:text」(Post:モデル名、content:カラム名、text:データ型)を実行し、モデルとマイグレーションファイル(データベース)を作成しておく。

コントローラー(アクション)

def index
  @posts = Post.all
end

HTML

<% @posts.each do |post| %>
      <div class="posts-index-item">
          <%= post.content %>
      </div>
<% end %>

each文で変数@posts内の内容を繰り返し(全て)表示させる。

5.投稿の編集

HTML(edit.htmlを作成する必要あり)

<div class="main posts-new">
  <div class="container">
    <h1 class="form-heading">編集する</h1>
    <%= form_tag("/posts/#{@post.id}/update") do %>
    <div class="form">
      <div class="form-body">
        <% @post.errors.full_messages.each do |message| %>
            <div class="form-error">
              <%= message %>
            </div>
          <% end %>
        <textarea name="content"><%= @post.content %></textarea>
        <input type="submit" value="保存">
      </div>
    <% end %>
  </div>
</div>

4行目:idを取得し「/posts/id/update」のURLを送信する。
7行目:アクション(コントローラー)内のifでエラーが出てrenderメソッドを介してページに戻ってきた場合、エラーメッセージを表示させるようになっている。

ルーティング

post "posts/:id/update" => "posts#update"

コントローラー(アクション)

def update
    @post = Post.find_by(id: params[:id])
    @post.content = params[:content]
    if @post.save
      flash[:notice] = "投稿を編集しました"
      redirect_to("/posts/index")
    else
      render("posts/edit")
    end
end

@post = Post.find_by(id: params[:id]):送信されてきたURLとidを参照し、変数@postに代入する。
@post.content = params[:content]:送信されてきた内容をparamsで受け取り、変数@postのcontentに代入する。

if @post.saveが成立したら「投稿を編集しました」のメッセージとともに、「/posts/index"」に戻る。
成立しない(エラーの)場合、renderで「posts/edit」に戻る。
renderで戻らないと「/posts/index」を介し投稿内容の変数が更新されてしまう(編集内容が最初の投稿に戻り、編集したかった投稿がフォームに維持されない。)

6.投稿の削除

HTML(show.html)

 <div class="post-menus">
        <%= link_to("編集", "/posts/#{@post.id}/edit") %>
        <%= link_to("削除", "/posts/#{@post.id}/destroy", {method: "post"}) %>
      </div>

(投稿詳細画面のshow.htmlで完結するので、新しくhtml(view)を追加する必要はない。)
link_toメソッドの第三引数に{method:"post"}を設定しないと、次のルーティングでpostではなくgetを探してしまい、エラーとなってしまう。

ルーティング

post "posts/:id/destroy" => "posts#destroy"

コントローラー(アクション)

def destroy
    @post = Post.find_by(id: params[:id])
    @post.destroy
    flash[:notice] = "投稿を削除しました"
    redirect_to("/posts/index")
end

編集の際と同様、HTMLより送信されたidを変数に代入し、destroyアクションで削除する。
(実際のポートフォリオでは「削除しますか?」の質問を出せるようにする?)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails+AxiosでCSRF対策用のトークンを使う設定

Rails+WebpackerのJavaScriptでAxiosを使うときは、POSTやPATCHで送信する際にCSRF対策用のトークンを指定する必要があります。送信のたびに指定するのは面倒なので、あらかじめ設定しておきましょう。

AxiosのInterceptorsの機能を使います。送信前にHTTPメソッドがPOST, PUT, PATCH, DELETEだったら、meta要素からトークンを取り出してHTTPヘッダにセットします。ここではjQueryを使っています。

サンプル: https://github.com/kazubon/blog-rails6-vuejs

app/javascript/axios_config.js
import Axios from 'axios';

Axios.interceptors.request.use((config) => {
  if(['post', 'put', 'patch', 'delete'].includes(config.method)) {
    config.headers['X-CSRF-Token'] = $('meta[name="csrf-token"]').attr('content');
  }
  return config;
}, (error) => {
  return Promise.reject(error);
});

このJavaScritptをどこかで動かします。とりあえずapplication.jsに入れとけばいいでしょう。

app/javascript/packs/application.js
import '../axios_config';

あとは、ふつうにAxiosが使えます。

import Axios from 'axios';

Axios.post('/entries.json', { entry: this.entry });
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Windows10にRuby on Rails環境をつくってみたらコケた

Rials環境をWindows10でも作ろう

新しくWindows10を手に入れたので、こっちでも環境を作ろうと動き出しましたが、コケまくりました。
コケまくったので、解決法を記録として残そうと思います。

Rubyのインストール

公式サイトで以下をダウンロードします。
https://rubyinstaller.org/downloads/
コメント 2020-02-24 193536.png
私の環境では「rubyinstaller-devkit-2.6.5-1-x64」をダウンロードしました。

ダウンロード後、exeファイルを実行し、各内容はデフォルトで行いました。
参考サイト: Windows10環境にRubyとRailsをインストールしてみた
途中Toolkitのインストールがあったので、チェックを付けてインストール。

コマンドプロンプトかPower Shellを起動して、[ruby -v]でバージョンを確認します。

>ruby -v
ruby 2.6.5p114 (2019-10-01 revision 67812) [x64-mingw32]

Railsをインストール

先程立ち上げたコマンドプロンプトかPower Shwlleに[gem install rails]を入力してEnter

>gem install rails

そのままインストールが始まって勝手に終わります。
時間がかかるのでちょっと放置。。。

Railsをインストールしたら、バージョンを確認します。

>rails -v
Rails 6.0.2.1

Bundlerのインストール

[gem install bundler]を入力してインストールします。

>gem install bundler

インストールが終わったらお決まりのバージョンの確認

>bundler -v
Bundler version 2.1.4

Sqlite3もインストール

だいたいの説明がここが抜けてたりして、[Rails new]してSqlite3でコケる。。。
ので、これも忘れず行います。

>gem install sqlite3 --platform ruby

大丈夫だとは思うのですが、Path通してやるのが良いかもです。
色々としすぎて途中でpathを追加したので、そのままインストールして出来るのかわかりません。

pathの通し方

↓サイトがわかりやすいです。
参考サイト:Windows10で実行ファイルへのパスを通す手順

>sqlite3 --version
3.31.1 2020-01-27 19:55:54 3bfa9cc97da10598521b342961df8f5f68c7388fa117345eeb516eaa837bb4d6

で、終わりかと思ったら。。。
これでrails newしたらnode.jsでコケたりします・・・・

なので、node.js入れます。

Node.jsをインストールする

Node.jsインストールページ
コメント 2020-02-24 1935.png
私は『推奨版』のインストーラーでインストールしました。
node.jsのバージョンと同梱のnpmのバージョンを確認します。

>node -v
v12.16.1
>npm -v
6.13.4

もしバージョン確認ができなかった場合は、上で書いたpathを追加します。
自動で出来たような気もしますが、色々とやりすぎて記憶が曖昧なので一応追記しておきます。

追加するpathは

C:\Program Files\nodejs\
バックスラッシュになっていますが、Windowsなので「¥」です。
後ろの¥を忘れずに。。。

で、Yarnのインストール
先ほど入れた同梱のnpmでインストールします。

>npm install -g yarn

そして確認のための

>yarn -v
1.22.0

これで大丈夫!!!

Railsアプリケーションを作る

アプリケーションの作り方は Railsチュートリアル を参考にします。

チュートリアルの最初のアプリケーション通りにしてみます。

rails newはバージョンをしていせずにしました。

>rails new hello_app

めっちゃ時間かかります。。。。

>cd hello_app

でディレクトリを移動してから

>bundle install

したら

>rails server

http://localhost:3000/


riding_rails_4th_edition_aws.png
が表示されてば完了です!!!

お疲れさまでした!!!

昨日(2020/02/23)にやっと解決したのですが、それまで色々やりすぎて 何回も書きすぎ うろ覚えもあります。
やらなくて良いこともあると良いのですが、エラー出まくったのを一つ一つ解決していったところ、この様になった次第です。
参考になると嬉しいです♪

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

deviseのユーザー登録失敗後にビューがずれる。

登録画面ビュー

通常の登録画面でapp/view/devise/registrations/new.html.hamlにCSSを当てたもの
スクリーンショット 2020-02-24 19.37.41.png

これにフォームに空などの無効な値を入れると以下のようになる。

登録失敗後のビュー

スクリーンショット 2020-02-24 19.38.43.png

//登録失敗時のnickname部分のHTML
<span class="nickname">
<div class="field_with_errors"><label for="user_nickname">ニックネーム</label></div>
<div class="field_with_errors"><input type="text" value="" name="user[nickname]" id="user_nickname"></div>
</span>

"field_with_errors"クラスが自動生成されてしまっていることが原因

registrations.scss
.field_with_errors {
  display: contents;
}

上記のCSSを追記して解決

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby on rails でTodoアプリを作る

今回はRuby on railsを使ってTodoアプリを作っていきます!!!

image.png

完成品が↑

railsの学習のために作ります!

1.プロジェクトを作ろう!

コンソールを起動して、

$ rails new #app名

これでプロジェクトを作ります!
次にcdでプロジェクトに移動しましょう

一旦作成したファイルの方にいってgemfileというものがあると思うのでそれを編集します!
ここをいじってBootstrap等を導入するワケですね!

# gem 'bcrypt', '~> 3.1.7'
gem 'bootstrap', '~> 4.1.1' #この1行だけ追加
# Use ActiveStorage variant

このように記載すればOKです!

ではコンソールに戻りましょう
※ここまででsqliteについてエラーが発生した人がいる場合
ridk exec pacman -S mingw-w64-x86_64-sqlite3
こちらのコマンドを一度うってください

そして全て完了したらコンソールにて

bundle install

こちらのコマンドをうちます
( bundlerというgemを使って、Gemfileに従ってgemをインストールするためのコマンドです!)

ここまで完了したら

rails s

でサーバーを起動してみましょう!
image.png
こういうのが出たら成功です!
成功を確認できたら、ブラウザを開いて
localhost:3000を入力してください!
image.png
このような画面が出てきたら無事、railsアプリの作成準備が整いました!!
(サーバーを終了させたい場合はCtrl+cでyを入力して終了できます)

2.データベースを構築しよう!

RailsはModelを使ってデータベースを操作します
id、created_at、updated_atの3つのカラムがデフォルトで作成されます。
今回はstring型のcontentというカラムを追加したいと思います。
Model名は頭文字が大文字で、単数形でなくてはなりません。
ではコンソールに戻って

$ rails g model Task content:string
$ rails db:migrate

を実行してください!
これで(migurate)データベースにテーブルが作成されます。
このようにTaskというテーブルを作成すると
tasksというように複数形のテーブルができます

以上でデータベースの構築は終わりました!

3.Controllerを使ってみよう!

controllerはMVCを構成するコンポーネントの1つです。
MVCとは
Model:データベースを取り扱う
View:画面表示を取り扱う
Controller:ModelとViewと連携する
といったようなものです。

では今回はタスクの一覧を表示するアクションcontrollerを作っていきましょう!

$ rails generate controller Tasks index

これでタスク一覧を表示するトップページのtasks_controllerを作ります!
作成されたControllerに、登録されている全てのタスクを取得する処理を記述します。

/app/controllers/tasks_controller.rb
 def index
    @tasks = Task.all
  end

Task.allでtasksテーブルの全てのレコードを取得します!

4.ルーティングを設定しよう!

URLに対するリクエストを受けた時にcontrollerのどのアクションを実行するか、を設定します。
resources :{Controller名}で複数のルーティングが一気に設定できるようになります!
これでlocalhostに入った際にindexアクションが動くようになっています

/config/routes.rb
Rails.application.routes.draw do
  root 'tasks#index'
  resources :tasks
end

設定されているルーティングはrails routesで確認しよう!

5.Viewを作ろう!

Controller内のアクション(タスク一覧表示する)が実行されると、対応するViewファイルが呼び出されます!
今回の場合は対応するViewファイルはindex.html.erbです!

/app/views/tasks/index.html.erb
<div class="mx-auto" style="width: 200px;">
<h1>Task list</h1>
</div>
<h2><%= link_to 'Creat new Todo', new_task_path ,class: 'btn btn-info'%></h2>
<table class="table">
  <thead>
    <tr>
    <th>タスク</th>
    <th>編集</th>
    <th>削除</th>
    </tr>
  </thead>
  <tbody>

  <% @tasks.each do |task| %>
    <tr>
      <td><%= task.title %></td>
    </tr>
  <% end %>
  </tbody>
</table>

Bootstrapを使ってテーブルとかを綺麗に表示するようにしてるよ
<% @tasks.each do |task| %>
にて格納されているタスクを表示するようにしています!

https://hackerthemes.com/bootstrap-cheatsheet/#input-group-append
↑もっとBootstrap使いたい方向けにチートシート紹介しときます↑

6.タスクを追加するための準備!

タスク追加画面を表示するためのnewアクションを作ります!

/app/controllers/tasks_controller.rb
  def new
    @task = Task.new
  end

これを追記します!
リクエストがあった時にフォームを表示するために@task = Task.newでTaskのオブジェクトを作成しています!

次に、タスク追加画面のviewファイルを作成します。

/app/views/tasks/new.html.erb

<%= form_for(@task) do |f| %>
  <div><%= f.text_field :title %></div>
  <div><input type="submit" value="追加" class="btn btn-success"></div>
<% end %>
<%= link_to '戻る', tasks_path %

今回はform_forを使っていますがこの説明は長くなるので下記のリンクを参考にしてください!
form_forを使って「フォーム作成→データをテーブルに保存」を実装しています。
https://qiita.com/jumpyoshim/items/ee5af466ef7959567174

7.タスク追加処理を作っていこう!

/tasksにPOSTリクエストがあった時、タスクの追加処理を行うaddアクションを作成します。

/app/controllers/tasks_controller.rb

  def add
    @task = Task.add(task_params)
    redirect_to tasks_path
  end

  private
    def params
      params.require(:task).permit(:title)
    end

POSTで送信された物をデータベースに登録し、その後redirect_to tasks_pathを使ってタスク一覧画面に遷移するようにしています!

またpraivateの部分ですが、Taskモデルのtitleを渡された場合のみ追加処理が実行されるようになっています。

以上で追加処理の部分は完成です!

8.タスクを編集する機能を作ろう!

/tasks/{id}/editにGETリクエストがあった時、タスク編集を表示するeditアクションと
編集した後のデータベースを更新するアクションを一緒に作っときましょう!

/app/controllers/tasks_controllers.rb
  def edit
    @task = Task.find(params[:id])
  end

def update
    @task = Task.find(params[:id])
    @task.update(task_params)
    redirect_to tasks_url
  end

-edit-
リクエスト部分の数値をparams[:id]で取り出しています。

-update-
タスク編集画面から送信された内容を受け取り、データベースを更新します!
findで主キーに対応するレコードを取り出します。

続いて、タスク編集画面のviewファイルを作成します。

/app/views/tasks/edit.html.erb
<h1>タスク編集</h1>
<%= form_for(@task) do |f| %>
  <div><%= f.label :title %></div>
  <div><%= f.text_field :title %></div>
  <div><input type="submit" value="編集" class="btn btn-success"></div>
<% end %>
<%= link_to '戻る', tasks_path %>

これで編集機能は終了です!

9.タスクの削除機能を作ろう!

/tasks/{id}にDELETEリクエストがあった時
idに対応するレコードをデータベースから削除するdestroyアクションを追加します。

/app/controllers/tasks_controller.rb
  def destroy
    @task = Task.find(params[:id])
    @task.destroy
    redirect_to tasks_url
  end

ではこれまで編集機能、削除機能を作ってきたのでそれをタスク一覧画面に追加しよう!

/app/views/tasks/index.html.erb
<tr>
      <td><%= task.title %></td>
      <td><%= link_to '編集', edit_task_path(task) ,class: 'btn btn-success' %></td>
      <td><%= link_to '削除', task_path(task), method: :delete, class: 'btn btn-danger' %></td>
    </tr>

classはBootstrapを使っていい感じのボタンにするため設置

destroyアクションを実行するには、DELETEメソッドのリクエストを送らないとダメなので引数にmethod: deleteを指定しています!

以上で投稿、編集、削除の機能は完成です!!!

10.空の投稿や文字数制限の機能をつけよう!

空の投稿やあまりに多い文字数は色々邪魔だと思うのでこちらを規制しましょう!!

/app/models/task.rb
 validates :title, presence: true, length: { maximum: 50 }

上記のように何か入力内容に規制をしたいとなった場合
validatesを使って titleに入力される内容に規制をかけます
presenceで空白禁止にして、length:{maximum:30}で30文字までしか入力できないようにしました!

/app/views/tasks/new.html.erb
<h1>Task add</h1>
<% if @task.errors.any? %>
  <div class="alert alert-warning">
    <ul>
      <% @task.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
    </ul>
  </div>
<% end %>

を追加して、空白や文字数超えた時にはエラーを表示するように追記しましょう!
これの詳しい説明は
https://qiita.com/ryuuuuuuuuuu/items/1a1e53d062bff774d88a
を参考に!

11.最後に

今回todoアプリを作って、railsでアプリを作る流れは掴めたかと思います。
けど実際にもっと複雑なWebアプリを作るってなるとまだまだ途方にくれそうなのでまずはこういう物を作ったりしてみて
もっと基礎等が掴めてきたら応用に活かせたらいいなと思いました!
https://github.com/t4k3p0n/todo-rails
↑githubです

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【rails】deviseとdate_select使って歳の数だけウ●コ表示させる

「歳の数だけホニャララ」というのは何か理由をつけたい時によく使われる手法かと思います。
誕生日に歳の数だけ薔薇をプレゼントなんてのは、100本の薔薇だと予算オーバーとなるのでちょうどいい理由となりますよね。

さて、今回は年齢の数だけウ●コ表示させてみようかなと思い、実装できたので紹介します。

まずは完成した状態を見てみましょう。

2a805aafa95a7d5480a38bff5ebbc13a (2).gif

では、どのように作ったのか書いていきます。

[1]deviseを導入してdate_selectで誕生日を入力できるようにする

まずはdeviseをインストールし、誕生日をdata形式で入力できるよう実装します。

gem 'devise'をgemファイルに記載し、$ bundle installします。
 ↓
$ rails g devise:installでdeviseをアプリにインストールします。
 ↓
$ rails g devise Userでmodelを作成しましょう。この際に誕生日を入力するためのカラムを作成します。

# 2020XXXXXXXXXXX_devise_create_users.rb
# frozen_string_literal: true

class DeviseCreateUsers < ActiveRecord::Migration[5.2]
  def change
    create_table :users do |t|
      ## Database authenticatable
      t.string :nickname, null: false, default: ""
      t.date :birth_date,          null: false # ←こんな感じで追加します。
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""

      ~~ 省略 ~~
end

上記のようにマイグレーションファイルにdate型のカラムを追加して、$ rake db:migrateします。
これで、値を入れるカラムができました。

次に、入力エリアを増やしてnicknameとbirth_dateを入力できるようにします。

$ rails g devise:viewsのコマンドでviewファイルを生成します。

今回はhamlで書いていくので、gemfileにgem 'haml-rails'を記載し、$ bundle installします。
そのあと、$ rails haml:erb2hamlでerb→hamlに変換します。

hamlに変換した/app/views/devise/registrations/new.html.hamlを下記のように編集します。

%h2 Sign up
= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f|
  = render "devise/shared/error_messages", resource: resource

  -# ~~~~ここから追加~~~~
  .field
    = f.label :nickname
    %br/
    = f.text_field :nickname, autofocus: true, autocomplete: "nickname"
  .field
    = f.label :birth_date
    %br/
    = f.date_select :birth_date, use_month_numbers: true,start_year: 1930, end_year: (Time.now.year - 10), default: Date.new(1989, 1, 1)
  -# ~~~~ここまで追加~~~~

  .field
    = f.label :email
    %br/
    = f.email_field :email, autofocus: true, autocomplete: "email"

  -# ~~~~ここから下は省略~~~~

上記のように追記することで、入力フォームは作成できました。
次に、カラムに保存できるように許可する記述を/app/controllers/application_controller.rbに記載します。

application_controller.rb
  before_action :configure_permitted_parameters, if: :devise_controller?

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname, :birth_date])
  end

上記のように記載すると、nicknamebirth_dateをカラムに入れることを許可することができました。
これで、まずは誕生日を入力して保存できるところまでできました。

[2]誕生日から年齢が表示できるようにする

まずは$ rails g controller unkos indexunkos_controller.rb/views/unkos/index.html.hamlを生成します。

date形式で保存された年月日から年齢を算出するための便利なgemを入れます。
gemfileにgem 'happybirthday'を記載して$ bundle installします。

それでは/app/controllers/unkos_controller.rbを下記のように追記します。

unkos_controller.rb
class UnkosController < ApplicationController
  before_action :authenticate_user!

  require 'happybirthday'

  def index
    birthday = Happybirthday.born_on(current_user.birth_date)
    @birthday = birthday.age.years_old
  end
end

before_action :authenticate_user!は、ログインしてなければ、ログイン画面へリダイレクトさせるメソッドです。deviseを入れると使用できるメソッドです。

require 'happybirthday'は先ほど導入したgem 'happybirthday'unkos_controller.rbで使用するための記述です。

Happybirthday.born_on(current_user.birth_date)では、現在ログイン中のユーザの誕生日を取得します。
@birthday = birthday.age.years_oldにて、現在の年齢に置き換え@birthdayに代入しています。

では、/app/views/unkos/index.html.hamlを下記のようなコードを追記してください。

index.html.haml
%h2
  = current_user.nickname + "さん"
%string
  = "現在#{@birthday}歳です。"

どうですか、これで年齢が表示できたでしょうか。

[3]年齢の数だけウ●コを表示させる

では、画竜点睛になりますね。ウ●コを表示させるくだりです。
/app/views/unkos/index.html.hamlに下記のような記述を追記してください。

%h2
  = current_user.nickname + "さん"
%string
  = "現在#{@birthday}歳です。"
%p それでは、歳の数だけウンコを表示します。
.unko_box{style: 'width: 550px; margin:0 auto'}
  - count = 0
  - loop do
    - count += 1
    = image_tag 'unchi.png', alt: 'unko',  width: '50px', class: ''
    - if count == @birthday
      - break

loop doで繰り返しウ●コの画像を表示させるようにしまして、
if count == @birthdayの箇所で、年齢の数でループ処理を止めています。
これで、ウ●コが年齢分繰り返しされるわけですね。

応用編

ウ●コを年齢分表示させるって、一体どんなことに使うんだ?
そう思われる方もいらっしゃるかと思われますが、例えばこんな時に応用して使用できるかと思います。

[応用1]歳の数だけ豆を表示する

0f8aed49605f5a842815bacbc0a012a2 (2).png

節分の時期にどうでしょうか?

[応用2]バースデーケーキーに歳の数だけロウソクを立てる

f8ec92e8591aa1b61c8b1738181891a5 (2).png

一番実用的じゃないでしょうか?

おわりに

いかがでしたでしょうか。
年齢の数だけ●●というのは、こじづけしがちな理由なので、
ぜひプログラミングでも実装してみてくださいね!

参考記事

【rails】deviseで生年月日を登録する時のコツ

誕生日から年齢を計算するhappybirthday gemをリリースしました

Deviseの設定手順をまとめてみた。 その1 導入編

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Tips】Rails の Minitest 実行時に想定外のデータベース名になって ConnectionError となる原因と対処法

問題

rails test実行時にデータベース名末尾に-0 -1などが付きアクセスが拒否されてしまう。

入力

rails test

出力

ERROR["test_【テストの名前】", #<Minitest::Reporters::Suite:0x******** @name="【テストクラスの名前】">, 0.143750600022031]
 test_n【テストの名前】#【テストクラスの名前】 (0.14s)
Minitest::UnexpectedError:         Mysql2::Error::ConnectionError: Access denied for user '********'@'%' to database '【テスト用データベースに設定した名前】-0'

ERROR["test_【テストの名前】", #<Minitest::Reporters::Suite:0x******** @name="【テストクラスの名前】">, 0.143750600022031]
 test_n【テストの名前】#【テストクラスの名前】 (0.21s)
Minitest::UnexpectedError:         Mysql2::Error::ConnectionError: Access denied for user '********'@'%' to database '【テスト用データベースに設定した名前】-1'

ERROR["test_【テストの名前】", #<Minitest::Reporters::Suite:0x******** @name="【テストクラスの名前】">, 0.143750600022031]
 test_n【テストの名前】#【テストクラスの名前】 (0.31s)
Minitest::UnexpectedError:         Mysql2::Error::ConnectionError: Access denied for user '********'@'%' to database '【テスト用データベースに設定した名前】-2'

...

環境

  • Windows 10
    • Ubuntu 18.04 LTS
  • anyenv 1.1.1
    • rbenv 1.1.2-20-g143b2c9
      • ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-linux]
        • Rails 6.0.2.1
  • MySQL

原因

test/test_helper.rbでマルチスレッド実行設定が行われているため複数のデータベースにアクセスされてしまう。

解決方法

マルチスレッド設定をコメントアウトする。

test/test_helper.rb
class ActiveSupport::TestCase
  # Run tests in parallel with specified workers
  # parallelize(workers: :number_of_processors)
  # ↑をコメントアウト

  # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
  fixtures :all

  # Add more helper methods to be used by all tests here...
end

補足

おそらく-0 -1などとついたデータベースを作成することでも解決可能。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

bootstrap_form を使って devise の form_for を form_with に置き換える

Rails中級チュートリアル」に取り組んでいて実践したことのメモです?

本文中では、form_for が使われていますが、Rails5.1 からform_with の使用が推奨されているので、変更に挑戦!

参考 : Railsガイド「form_forとform_tagのform_withへの統合

bootstrap_form gem をインストール

Gemfile
gem 'bootstrap_form'

bootstrap_form の GitHub の README を見ると form_withでの使用方法 が書かれているので、それを参考にします。(コピペすると全角記号で死ぬので注意です)
スクリーンショット 2020-02-24 18.03.33.png

devise のフォームを bootstrap_form & form_with バージョンに変更

参考 : 【Rails】deviseのビューのform_forをform_withに置き換える

app/views/devise/sessions/new.html.erb
<%= bootstrap_form_with(model: @user,
                                url: session_path(resource_name),
                              local: true) do |f| %>
        <%= f.email_field :email,
                          autofocus: true,
                        placeholder: 'メールアドレスを入力してください' %>
        <%= f.password_field :password,
                            autocomplete: "off",
                             placeholder: 'パスワードを入力してください' %>

        <% if devise_mapping.rememberable? %>
          <%= f.check_box :remember_me %>
        <% end %>

        <%= f.submit "ログイン" %>

      <% end %>

スクリーンショット 2020-02-24 18.20.03.png

?こんな感じのフォームが簡単に作成できます ✅

生成される HTML の Eメール入力欄をみてみると、

<input autofocus="autofocus" placeholder="メールアドレスを入力してください" class="form-control" type="email" value="" name="user[email]" id="user_email">

となっており、クラスで form-controlを追加しなくても勝手に付いてくれるようですね!

ログインボタンにはこのようなクラスが付与されています。

<input type="submit" name="commit" value="ログイン" class="btn btn-secondary" data-disable-with="ログイン">

ログインボタンのクラスを変更してみました。

<%= f.submit "ログイン", class: 'form-control btn btn-info' %>

スクリーンショット 2020-02-24 18.20.03.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

devise token authの出力をserializerで整形する

はじめに

この記事ではdevise token authでのjsonの出力をactive_model_serializer gemを使って整形する手順を解説するわ。実際の開発中に少し詰まったことを元にしているからゴールとして"ユーザーのログイン成功時に返されるjsonをserializerで整形する"を設定するわ。他のアクション後に返されるjsonにも応用できるように心掛けるから安心しなさい。
devise関連の導入まで含めちゃうと長くなるからそこは割愛するわ、ごめんなさいね。

準備するわよの巻

まずはgemのインストールから始めるわ。

Gemfile.
gem 'active_model_serializers'
bundle install

これでactive_model_serializersのインストールが完了したわ。
このgem自体の説明は他の記事に譲るとしてまずは設定をするわ。
設定用のconfig/initializers/ams.rb ファイルを手動で追加しなさい。

config/initializers/ams.rb
ActiveModel::Serializer.config.adapter = :json_api

この部分では出力するjsonのフォーマットを指定しているわ。オプションの詳しい説明についてはこちらの記事に詳しいわ。

Railsのactive_model_serializerについて学ぶ100DaysOfCodeチャレンジ10日目(Day10:#100DaysOfCode)

ここではjson_apiを指定したけど、あなたの好きなように設定しなさい。

さて、次はいよいよserializerのファイルを作っていくわよ。

rails generate serializer User

最後のUserの部分は整形したいモデル名を指定することね。このコマンドでapp/serializers/user_serializer.rbファイルが作成されたわ、早速いじいじしちゃいましょう。

app/serializers/user_serializer.rb
class UserSerializer < ActiveModel::Serializer
  attributes :id, :name, :nickname, :image, :confirmed_at, :updated_at
end

ここではjsonで返すモデルのカラムを指定しているわ。他にも色々設定できるけど適宜自分で調べなさい、頑張ることね。

本題に入るわよの巻

ここまでで準備は完了よ、configに追加した設定を読み込ませるためにこのあたりで一旦サーバを再起動しておくことね、ん?もうやった? 良い子ね、クマちゃんのビスケットをあげるわ。

さて、まず見て欲しいものがあるの、serializerで整形をしない場合のログイン成功時に返されるjson(ログインに成功したユーザーの情報)よ。

スクリーンショット 2020-02-24 16.16.37.png

続いて同じjsonをserializerで整形した場合よ。

スクリーンショット 2020-02-24 16.22.57.png

データが構造化されているのと設定したカラムのみが出力されていることが分かるわね。
ちなみに下は

app/controllers/api/v1/user_controller.rb
class Api::V1::UsersController < ApplicationController
  def show
    @user = User.find(params[:id])
    render json: @user, serializer: UserSerializer
  end
end

こんな感じのコードよ。
見ての通り、deviseの方のjsonも整形しないとアプリケーション全体として返すjsonのフォーマットに統一性が取れないことが分かるわ。これはフロント側としても混乱するしメンテナンス性も大きく損なうわ。
でも、自分で一から書くアクションなら上のコードみたいに簡単に実装できるんだけどdeviseは内部で勝手にやってくれるから分かりづらいわよね。
そこで今回は
1. コントローラを継承する
2. そこで各アクションごとのレンダリングメソッドをオーバーライドしてjsonをserializerで整形する

という手順でdeviseで返されるjsonの整形をするわ。

コントローラの継承をするわよ編

今回はログイン時のjsonを整形したいということで、ログイン時に呼ばれるアクションは
devise_token_auth/sessions#createよ。ということで
devise_token_auth/sessionsコントローラを継承するわ。

app/controllers/api/v1/auth/sessions_controller.rb
class Api::V1::Auth::SessionsController < DeviseTokenAuth::SessionsController 

end 

新しく作ったコントローラが参照されるようにルーティングも変更よ。
私の場合は

config/routes.rb
Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      resources :users, only: [:show]
      mount_devise_token_auth_for 'User', at: 'auth', controllers: {
        registrations: 'api/v1/auth/registrations',
        # ここの部分ね
        sessions: 'api/v1/auth/sessions'
      }
    end
  end
end

さて、準備が整ったところでさっきの手順の2の太字になっている各アクションごとのレンダリングメソッドの説明をするわ。出力を制御する、と聞いて最初に対象のアクションをそのままオーバーライドすることを考えた子も多いと思うの。でも、出力だけを変えたいのにいちいち大元のアクションメソッドをオーバーライドするのは面倒じゃない?deviseちゃんでは特定のアクションで成功、失敗した時にjsonをレンダリングするメソッドが個別で用意されてるの。要は必要に応じてそこをいじればいいってわけね。

このメソッドちゃんたちの存在は公式ドキュメントに載ってるの。この記事で日本語訳してくださってる方がいるので貼っておくわ。
【翻訳】devise-auth-token公式ドキュメント

表を見れば分かる通り、今回私が整形したいのはログインの成功時のjsonよ。つまりレンダリングメソッドで言うとrender_create_successね。早速オーバーライドしちゃいましょう。

app/api/v1/auth/session_controller.rb
class Api::V1::Auth::SessionsController < DeviseTokenAuth::SessionsController 

  def render_create_success 
   render json: @resource, serializer: UserSerializer
  end 

end 

ログインが成功した時のリソース、つまりuserの情報は@resourceで取得できるわ。これをserializerで整形すると...

スクリーンショット 2020-02-24 16.48.03.png

この通り!jsonが構造化されてカラムも設定したものしか返されていないわ!

参考文献

情報が比較的少ない中で有益な記事の存在に助けられました。心よりの感謝を申し上げます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

devise token authのjsonをserializerで整形するわよの巻

はじめに

この記事ではdevise token authでのjsonの出力をactive_model_serializer gemを使って整形する手順を解説するわ。実際の開発中に少し詰まったことを元にしているからゴールとして"ユーザーのログイン成功時に返されるjsonをserializerで整形する"を設定するわ。他のアクション後に返されるjsonにも応用できるように心掛けるから安心しなさい。
devise関連の導入まで含めちゃうと長くなるからそこは割愛するわ、ごめんなさいね。

準備するわよの巻

まずはgemのインストールから始めるわ。

Gemfile.
gem 'active_model_serializers'
bundle install

これでactive_model_serializersのインストールが完了したわ。
このgem自体の説明は他の記事に譲るとしてまずは設定をするわ。
設定用のconfig/initializers/ams.rb ファイルを手動で追加しなさい。

config/initializers/ams.rb
ActiveModel::Serializer.config.adapter = :json_api

この部分では出力するjsonのフォーマットを指定しているわ。オプションの詳しい説明についてはこちらの記事に詳しいわ。

Railsのactive_model_serializerについて学ぶ100DaysOfCodeチャレンジ10日目(Day10:#100DaysOfCode)

ここではjson_apiを指定したけど、あなたの好きなように設定しなさい。

さて、次はいよいよserializerのファイルを作っていくわよ。

rails generate serializer User

最後のUserの部分は整形したいモデル名を指定することね。このコマンドでapp/serializers/user_serializer.rbファイルが作成されたわ、早速いじいじしちゃいましょう。

app/serializers/user_serializer.rb
class UserSerializer < ActiveModel::Serializer
  attributes :id, :name, :nickname, :image, :confirmed_at, :updated_at
end

ここではjsonで返すモデルのカラムを指定しているわ。他にも色々設定できるけど適宜自分で調べなさい、頑張ることね。

本題に入るわよの巻

ここまでで準備は完了よ、configに追加した設定を読み込ませるためにこのあたりで一旦サーバを再起動しておくことね、ん?もうやった? 良い子ね、クマちゃんのビスケットをあげるわ。

さて、まず見て欲しいものがあるの、serializerで整形をしない場合のログイン成功時に返されるjson(ログインに成功したユーザーの情報)よ。

スクリーンショット 2020-02-24 16.16.37.png

続いて同じjsonをserializerで整形した場合よ。

スクリーンショット 2020-02-24 16.22.57.png

データが構造化されているのと設定したカラムのみが出力されていることが分かるわね。
ちなみに下は

app/controllers/api/v1/user_controller.rb
class Api::V1::UsersController < ApplicationController
  def show
    @user = User.find(params[:id])
    render json: @user, serializer: UserSerializer
  end
end

こんな感じのコードよ。
見ての通り、deviseの方のjsonも整形しないとアプリケーション全体として返すjsonのフォーマットに統一性が取れないことが分かるわ。これはフロント側としても混乱するしメンテナンス性も大きく損なうわ。
でも、自分で一から書くアクションなら上のコードみたいに簡単に実装できるんだけどdeviseは内部で勝手にやってくれるから分かりづらいわよね。
そこで今回は
1. コントローラを継承する
2. そこで各アクションごとのレンダリングメソッドをオーバーライドしてjsonをserializerで整形する

という手順でdeviseで返されるjsonの整形をするわ。

コントローラの継承をするわよ編

今回はログイン時のjsonを整形したいということで、ログイン時に呼ばれるアクションは
devise_token_auth/sessions#createよ。ということで
devise_token_auth/sessionsコントローラを継承するわ。

app/controllers/api/v1/auth/sessions_controller.rb
class Api::V1::Auth::SessionsController < DeviseTokenAuth::SessionsController 

end 

新しく作ったコントローラが参照されるようにルーティングも変更よ。
私の場合は

config/routes.rb
Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      resources :users, only: [:show]
      mount_devise_token_auth_for 'User', at: 'auth', controllers: {
        registrations: 'api/v1/auth/registrations',
        # ここの部分ね
        sessions: 'api/v1/auth/sessions'
      }
    end
  end
end

さて、準備が整ったところでさっきの手順の2の太字になっている各アクションごとのレンダリングメソッドの説明をするわ。出力を制御する、と聞いて最初に対象のアクションをそのままオーバーライドすることを考えた子も多いと思うの。でも、出力だけを変えたいのにいちいち大元のアクションメソッドをオーバーライドするのは面倒じゃない?deviseちゃんでは特定のアクションで成功、失敗した時にjsonをレンダリングするメソッドが個別で用意されてるの。要は必要に応じてそこをいじればいいってわけね。

このメソッドちゃんたちの存在は公式ドキュメントに載ってるの。この記事で日本語訳してくださってる方がいるので貼っておくわ。
【翻訳】devise-auth-token公式ドキュメント

表を見れば分かる通り、今回私が整形したいのはログインの成功時のjsonよ。つまりレンダリングメソッドで言うとrender_create_successね。早速オーバーライドしちゃいましょう。

app/api/v1/auth/session_controller.rb
class Api::V1::Auth::SessionsController < DeviseTokenAuth::SessionsController 

  def render_create_success 
   render json: @resource, serializer: UserSerializer
  end 

end 

ログインが成功した時のリソース、つまりuserの情報は@resourceで取得できるわ。これをserializerで整形すると...

スクリーンショット 2020-02-24 16.48.03.png

この通り!jsonが構造化されてカラムも設定したものしか返されていないわ!

参考文献

情報が比較的少ない中で有益な記事の存在に助けられました。心よりの感謝を申し上げます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[font-awsomeのエラー]File to import not found or unreadable: font-awesome-sprockets.のエラーについて

1.エラーの様子

スクリーンショット 2020-02-24 15.34.42.png

デスクトップ(裏側で製作していたファイル)では動いていたデータをホームディレクトリ(本番のファイル)に移した時上記のようなエラーが出ました。

2.エラーの原因

1.一般的な原因

・Gemfileのなかにgem 'font-awesome-sass'が抜けている

・app/stylesheets/application.scssの中に@import "font-awesome-sprockets"@import "font-awesome";が抜けている

2.見落としがち(特殊)な原因

@import "font-awesome-sprockets"とするところを@import "font-awesome-compass";としている。またはその逆。

・別フォルダで作成したファイルを移すときに、rails sをしたままgemを追記し、bundle installしている

3.解決方法

1.Gemfileのなかにgem 'font-awesome-sass'が抜けている

Gemfileにgem 'font-awesome-sass'を追記して下記コマンドを打ちましょう

$ bundle install

2.app/stylesheets/application.scssの中に@import "font-awesome-sprockets"@import "font-awesome";が抜けている

app/stylesheets/application.scssの中に下記2行を追記しましょう

@import "font-awesome-sprockets"
@import "font-awesome";

3.@import "font-awesome-sprockets"とするところを@import "font-awesome-compass";としている。

githubに記載されている公式の使用方法(readmeの部分)を読んで、どちらが自分の使用すべきコマンドか確認してみましょう。公式は下記urlです。

https://github.com/FortAwesome/font-awesome-sass

4.別フォルダで作成したファイルを移すときに、rails sをしたままgemを追記し、bundle installしている

・Gemfilはサーバーを起動したままだと反映されないため、railsを再起動しましょう。コマンドはcontroll+cでrailsサーバーを落とし、再度rails sをしましょう

・上記でうまくいかない場合、他のもともとあったgemと干渉しあっている可能性があるためGemfile.lock(gemの実行コードが自動生成されたファイル)を全て削除しbundle installをしましょう。その際もサーバーを起動中の方はcontroll+cでrailsサーバーを落とし、再度rails sをしましょう

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Bootstrap]ドロップダウンボタンのアイコンを変更&「ドロップダウンがボタンに対して右下に出ちゃう!」と「選択肢の下に変な余白が!?」を解消

概要

表題の通りですが、長いので下記にまとめます。

1.ドロップダウンボタンのアイコンを変更する。
2.ドロップダウンがボタンに対して右下に出ちゃう!」を解消。
3.「選択肢の下に変な余白が!?」を解消。

すべて簡単ですが、2.3に少しハマったのでメモを兼ねて共有させていただきます。

環境

Ruby:2.6.3
Rails:5.1.6
bootstrap:4.4.1
FontAwesome

方法

1.ドロップダウンボタンのアイコンを変更する。
-button.btn.dropdown-toggleの中にFontawesome(iタグ)を通常通り配置。
-デフォルトのボタンを消す(.dropdown-toggle::after { display: none; })。

2.ドロップダウンがボタンに対して右下に出ちゃう!」を解消。
div.dropdown-menuにdropdown-menu-rightクラスを追加。
→position:absolute関連のleftプロパティの値をいじっていたが、これだけでよかった。
要は、ドロップダウンメニューの起点がボタンに対して左上(デフォルト)になっているのを右上に変更する。

3.「選択肢の下に変な余白が!?」を解消。
div.dropdown-item(下記link_toメソッド(HTMLではaタグ))の後にいくつかの半角スペース
→これが選択肢の内容として認識されていた為、選択肢の下の「変な余白」として反映されていた。

home.html.erb
<div class="dropdown">
  <button class="btn dropdown-toggle" type="button" id="dropdownMenuButton", data-toggle="dropdown">        
    <i class="fas fa-chevron-down"></i>
  </button>
  <div class="dropdown-menu dropdown-menu-right">
  <%= link_to "削除する", dreampost, class: 'dropdown-item', method: :delete,
                                        data: { confirm: "削除してよろしいですか?" } %>
  </div>
</div>
custom.scss
.dropdown-toggle::after { display: none; }

スクリーンショット 2020-02-24 16.11.03.png

スクリーンショット 2020-02-24 15.15.06.png

スクリーンショット 2020-02-24 15.16.38.png

スクリーンショット 2020-02-24 15.19.27.png

スクリーンショット 2020-02-24 16.09.01.png

補足

ドロップダウンボタンのアイコンは、親要素にdisplay:flexとjustify-content:space-betweenで右端寄せしています。

ご指摘などございましたら、ぜひよろしくお願いいたします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ActiveRecordでバリデーションを無視してデータを保存する方法

テストデータがほしいときに rails c してActiveRecordを使ってデータを挿入したいことがよくあるのですが、バリデーションが多く保存できるデータがかなり制限されているようなモデルの場合では、とりあえずテストデータ突っ込みたいときに結構な面倒になってしまいます。

そこで、validationを通さずにsaveする方法がいくつかありそうなので試してみました。

save(validate: false)

 Model.new(name: 'hoge', fuga_column: 'piyo', ... ).save(validate: false)

そんな引数渡せたのか

update_attribute

Model.new.update_attribute(:name, 'test')

カラムが一度に一つしか渡せないのつらい

感想

save(validate: false)がカラムを一度設定できて楽かなあという印象でした。

update_allやupdate_column(s)などはUPDATEクエリを発行するので新規作成できないという点でここでは除外しました。

参考サイト

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

どなたか Rails の System Test が得意な方、これをどうやったら解決できるかおしえてください

なにこれ

Docker Compose で Rails 6.0 を動かそうと思って、コードを書こうとしているのだけど、System テストがどうにもこうにも適切にうごかすことができません。おそらく System Test は Fail がきているだけなので Rails 側は適切に処理はしているのだと思いますが、Selenium 側が Rails(Puma) のサーバにアクセスできていないのではないかと思われます。もうかれこれ1日以上、この問題でスタックしております。どなたか知識をご教示ください。
Screenshot from 2020-02-24 15-13-20.png

コード

Dockerfile

FROM ruby:2.6-alpine

WORKDIR /usr/src/app

COPY Gemfile Gemfile.lock yarn.lock package.json ./

RUN apk update && \
    apk upgrade && \
    apk add --no-cache yarn tzdata libxml2-dev curl-dev make gcc libc-dev g++ imagemagick6-dev postgresql-client postgresql-dev chromium chromium-chromedriver udev && \
    cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
    gem update --system && \
    bundle install && \
    yarn install  && \
    rm -rf /usr/local/bundle/cache/* /usr/local/share/.cache/* /var/cache/* /tmp/* && \
    apk del libxml2-dev curl-dev make gcc libc-dev g++ tzdata

Docker compose File

version: '3'

volumes:
  pg_data:
  redis_data:

networks:
  main:

services:
  pg:
    image: postgres:11-alpine
    restart: always
    networks:
      main:
    volumes:
      - pg_data:/var/lib/postgresql/data/
    environment:
      TZ: Asia/Tokyo
      DATABASE_HOST: db
      POSTGRES_USER: dev
      POSTGRES_PASSWORD: dev
      POSTGRES_DB: dev

  redis:
    container_name: myapp-redis
    networks:
      main:
    image: redis:4.0
    environment:
      TZ: Asia/Tokyo
    volumes:
      - redis_data:/var/lib/redis

  hub:
    image: selenium/hub:3.141.59-iron
    ports:
      - 4444:4444
    environment:
      TZ: Asia/Tokyo
    networks:
      - main
  chrome:
    image: selenium/node-chrome:3.141.59-iron
    depends_on:
      - hub
    environment:
      TZ: Asia/Tokyo
      HUB_HOST: hub
      HUB_PORT: 4444
    networks:
      main:

  web:
    build:
      context: ./
    ports:
      - 3000:3000
    volumes:
      - ./:/usr/src/app/     
    stdin_open: true
    tty: true
    environment:
      - TZ=Asia/Tokyo
    command: /bin/sh -c "rm -f /var/app/tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0' --early-hints"   
    depends_on:
      - pg
      - redis
    networks:
      main:
        aliases:
          - web

application_system_test_case.rb

require "test_helper"
require 'selenium-webdriver'
require 'capybara/rails'

class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
  driven_by :selenium, using: :headless_chrome, screen_size: [1400, 1400], options: { url: "http://hub:4444/wd/hub" }
end

users_test.rb

require "application_system_test_case"

class UsersTest < ApplicationSystemTestCase
  setup do
    @user = users(:one)
  end

  test "visiting the index" do
    visit users_url
    assert_selector "h1", text: "Users"
  end  
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

paramsとwrap_parameters

JSONでデータを送るとき、コントローラ名と送信するパラメータのキー名(name属性のプレフィクス)の関係についての話です。

UsersControllerというコントローラがあるとします。このコントローラに対して、Ajaxを使いJSON形式かつPOSTメソッド(またはPUT、PATCH)でデータを送ります。パラメータには"user"のようなキーを付けません。

Axios.post('/users.json', { name: 'Taro', email: 'taro@example.com' });

すると、コントローラのparamsには、"user"というキーが自動的に付きます(値がラップされます)。これが、ActionController::ParamsWrapperの機能です。

{"name"=>"Taro", "email"=>"taro@example.com", "user"=>{"name"=>"Taro", "email"=>"taro@example.com"}}

次のようにストロングパラメータを使っていても大丈夫、というわけです。

def user_params
  params.require(:user).permit(:name, :email)
end

しかし、コントローラ名とは違うキー名を使いたい場合があります。次の例では"member"です。

Axios.post('/users.json', { member: { name: 'Taro', email: 'taro@example.com' }});

すると、コントローラのparamsには、"user"というキーが付き、中は空となります。

{"member"=>{"name"=>"Taro", "email"=>"taro@example.com"}, "user"=>{}}

これが気になるときは、wrap_parametersメソッドでキー名を指定します。このコントローラでラッピングに使われるキー名が"member"になります。

class UsersController < ApplicationController
  wrap_parameters :member

あるいは、次のようにすると、JSON形式ではラップしなくなります。

class UsersController < ApplicationController
  wrap_parameters format: []

コントローラすべてに適用するには、initializers下の設定ファイルで指定します。配列[:json]を空にします。

config/initializers/wrap_parameters.rb
ActiveSupport.on_load(:action_controller) do
  wrap_parameters format: []
end

実際のプロジェクトでは

一番上のサンプル(キーを付けない場合)では、値が配列やハッシュの場合はラップしてくれません。ActionController::ParamsWrapperの設定をデフォルトにしたままだと混乱を招きそうです。

新規プロジェクトではinitializers下の設定でJSONを外しておくのがいいと思います。既存のプロジェクトでは、デフォルトでオフにするのはちょっとこわいので、コントローラごとに指定しておくことにします。

そのうえで、「AjaxでPOST、PATCHするときは、パラメータにキーを付けて値をラッピングして、ストロングパラメータを使うこと」というガイドラインを作っておくとよいかと。

補足

この機能いつからあったっけと調べたら、だいぶ昔で、2011年のRails 3.1からでした

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails男子必見】エラー系女子との会話が弾む3つのテクニック【モテたい】

はじめに

添野です。TECH::EXPERTというプログラミングスクールに通っています。
7日目で基礎本試験を突破しました。

今日はエラー系女子にモテたい男子必見
Railsのエラー問題で苦戦中の方向けの記事です。

モテる男子の会話テクニック

モテる男子がどんな会話をしているのか、渋谷のエラー系女子にインタビューしてみました。

✅ちゃんと話(エラー文)を聞いてくれて、私のことをしっかり考えてくれる(20歳 OL)
✅私が怒って(エラーを出して)も何の話をしてるのか考えてくれて、先回りで行動してくれる(17 歳JK)
✅前に話したこと(エラー)を覚えていて、何度も同じことを言わなくていいと気が楽ですね(26歳 看護師)

話を聞いてくれる男子と一緒にいたいと感じるようですね。
それでは、インタビューで出た女子の意見について詳しく見ていきましょう。

1. ちゃんと話を聞く「エラー文を見る」

皆さんは彼女とケンカになって、一方的に自分の意見を言ってしまった経験はありませんか?

モテる男子になるにはまず、エラー系女子の話をちゃんと聞いてあげましょう
例えば、次のエラー文を見てください。
スクリーンショット 2020-02-24 10.19.17.png

実はエラー文には「直すべきファイルが何か」「どう直せばいいか」が書かれているときがあります。
彼女は「あんたのここがダメだって言ってんでしょ!こうしてよ!」ってわざわざ教えてくれてます
「showビューの6行目"user_signed_in"を"user_signed_in ? "に直せ」と書いてあるので、
これを直すだけで彼女の怒りは収まり、あなたはモテる男に生まれ変わります。

とにかく、最初は彼女の言っていることをしっかり聞きましょう。

2. 何の話か考える「対象のファイルを特定する」

上記のように直すべき点を指摘してくれる場合は楽ですが、世の中親切な女の子ばかりではありません。
それでも、好きな子には振り向いて欲しいですよね。そんなアナタにとっておきの方法です。

次のエラー文を見てください。
これは、pictweetアプリでログアウトボタンをクリックしたら発生しました。
スクリーンショット 2020-02-24 10.36.37.png

これだけでは何のエラーかさっぱりですが、モテる男子はここで「あの話かな?」と先読みをします
そして、先読みをするには「直前にあなたがとった行動」を思い出してください。
例えば、上記のエラーでは直前に「ログアウトボタン」をクリックしました。
ということは「ログアウトボタンに関係する何か」で彼女が怒っているのだと先読みできます。

ログアウトボタンはapplicationビューに書かれているので、ファイルの中を見てみると、
「, method: :delete」という記述が抜けていることに気付きます。これが彼女の怒りの原因です。

before
<%= link_to "ログアウト", destroy_user_session_path>

after
<%= link_to "ログアウト", destroy_user_session_path, method: :delete %>



ここで、「記述抜けなんて、そんなの気づけなくね?」と思うかもです。
そんな時は作りたいアプリの完成形を横に並べておきましょう。補助輪を付けるんです。

「そんなのダサいし勉強にならない」と思うかもしれません。ですが、プライドを捨ててください
この方が早く成長できます。モテる男は、無駄なプライドは捨てています。
モノマネ・パクりから始めましょう。自分磨きは、その後です。

こうして、モテる男は常に女の子の不満を先読みし、自分の行動を変えていきます

3. 前に話したことを覚えている「エラーリストを作る」

これも特に多かった意見です。何度も同じことを言わせる男子はモテません
一度指摘されたり、先読みで特定した彼女の不満をリスト化して、常にエラーに備えましょう。

インタビューでは、「エラー系女子を怒らせたRails男子の行動Top14」も調査してきました。
ランキング形式で発表していきます。モテたい男子の皆さん、要チェックですよ!!

第14位「検索すると全ての投稿が表示される」

検索用パラメータ受け渡し時のキー名の記載ミス
対象ファイル:indexビュー

before
<%= form.text_field :keywords, placeholder: "投稿
検索", class: "search-input" %>

after
<%= form.text_field :keyword, placeholder: "投稿
検索", class: "search-input" %>

第13位「ArgumentError」

引数指定の記載漏れ
対象ファイル:tweetモデル

before
def self.search

after
def self.search(search)

第12位「ActionControl ler::ParameterMissing」

form_withを使用しているのにformの記述が抜けている
対象ファイル:showビュー

before
<%= form_with(model: [@tweet,@comment], local: true) do |form| %>
  <textarea cols="30" name="text" placeholder="" rows="2"></textarea>
  <input type="submit" value="SEND">
<% end %>

after
<%= form_with(model: [@tweet,@comment], local: true) do |form| %>
  <%= form.text_area :text, placeholder: "text" , rows: "2" %>
  <%= form.submit "SEND" %>
<% end %>

第11位「リファクタリング問題」

省略できる記述が省略せずに書かれている
対象ファイル:indexビュー

before
<% @tweets.each do |tweet| %>
  <%= render partial: "tweet", locals: { tweet: tweet } %>
<% end %>

after
<%= render @tweets %>

彼氏のメールが長すぎて何を言いたいか分からない

第10位「Undefined method」

パターン1「ログイン判定記述ミス
対象ファイル:applicationビュー

before
<% if true %>

after
<% if user_signed_in? %>

パターン2「deviseメソッドの記述位置ミス」
対象ファイル:ルーティングファイル

before
resources :tweets
devise_for :users

after
devise_for :users
resources :tweets

第9位「AcctiveRecord::RecordNotFound」

パターン1「ログアウトボタン記述ミス
対象ファイル:applicationビュー

before
<%= link_to "ログアウト", destroy_user_session_path %>

after
<%= link_to "ログアウト", destroy_user_session_path, method: :delete
%>

パターン2「namespaceメソッドとresourcesメソッドの記述順ミス
対象ファイル:ルーティングファイル

before
resources :tweets do
  resources :comments, only: :create
end
namespace :tweets do
  resources :searches, only: :index
end

after
namespace :tweets do
  resources :searches, only: :index
end
resources :tweets do
  resources :comments, only: :create
end

第8位「NotNullViolation」

「deviseパラメータに指定しているキー名の記述ミス」
対象ファイル:applicationコントローラー

before
devise_parameter_sanitizer.permit(:sign_up, keys: [:name])

after
devise_parameter_sanitizer.permit(:sign_up, keys: [:nickname]

イタリアンを食べに行こうって言ってデートにきたら寿司屋に連れてかれた

第7位「空のツイートを保存できてしまう」

バリデーションの記述がない
対象ファイル:tweetモデル

before

after
validates :text, :image, presence: true

第6位「ターミナルでNo template found」

ページ切替のリダイレクトアクション記載漏れ
対象ファイル:commentsコントローラー

before

after
redirect_to tweet_path(@comment.tweet)

第5位「テキストや画像、コメントが表示されない」

ストロングパラメーターの記述ミス
対象ファイル:tweetsコントローラー

before
{ user_id: current_user.id }

after
params.permit(:image, :text).merge(user_id: current_user.id)

LINEで素直な感謝の気持ちを送ったのに、未読スルー

第4位「NoMethodError」

パターン1「renderメソッドで誤字
対象ファイル:newビュー

before
<%= render partial: "form", locals: { form: @form } %>

after
<%= render partial: "form", locals: { form: form } %>

デートの集合場所の住所をLINEで送ってきたのに、間違ってる

パターン2「commentのルーティングがネストされてない
対象ファイル:ルーティングファイル

before
resources :tweets
resources :comments, only: :create


after
resources :tweets do
  resources :comments, only: :create
end

デートの集合場所を、私が入ってないLINEグループにLINEしたことに気づいてない

パターン3「コントローラーの誤字
対象ファイル:tweetsコントローラー

before
def index
  @post = Post.includes(:user)
end


after
def index
  @posts = Post.includes(:user)
end

パターン4「パス指定順のミス
対象ファイル:showビュー

before
<%= form_with(model: [@comment, @tweet], local: true) do |form| %>

after
<%= form_with(model: [@tweet, @comment], local: true) do |form| %>

第3位「ツイート保存不可(表示されない)」

ストロングパラメーターのrequireメソッド記述抜け
対象ファイル:tweetsコントローラー

before
params.permit(:image, :text).merge(user_id: current_user.id)

after
params.require(:tweet).permit(:image, :text).merge(user_id: current_user.id)

第2位「ActionView::MissingTemplate」

パターン1「ビューファイルがない/配置が間違ってる
対象ファイル:_tweetビュー(部分テンプレート)

before
部分テンプレート名が"tweet.html.erb"

after
部分テンプレート名を"_tweet.html.erb"に修正

東京タワーを展望できるホテルというタレコミでデートにきたのに、東京タワーが見えない部屋を予約していた

パターン2「renderメソッドの記述ミス
対象ファイル:showビュー

before
<%= render partial: "/post", locals: { post: post } %>

after
<%= render partial: "posts/post", locals: { post: post } %>

第1位「Rounting Error」

対象ファイル:ルーティングファイル

パターン1 resourcesメソッドの誤字または抜け
indx→indexに修正、:createが抜けているので追加

before
resources :tweets, only: [:indx, :show, :new, :destroy, :edit, :update] do
after
resources :tweets, only: [:index, :show, :new, :create, :destroy, :edit, :update] do

パターン2 rootメソッドの誤字

before
root to: 'post#index'

after
root to: 'posts#index'

デート当日になって、デート先を決めてない/デート先を忘れた/道を間違えた

まとめ

いかがだったでしょうか。
最後にもう一度、モテる男子の特徴をおさらいしましょう。

✅ちゃんと話を聞く「エラー文を見る」
✅何の話か考える「対象のファイルを特定する」
✅前に話したことを覚えている「エラーリストを作る」

大まかに理解できたら、上記でなぜモテるのかを考えて、自分を磨いていきましょう。
以上、Rails男子がエラー系女子にモテる方法でした。

おすすめ記事
Rails消化のコツ
Rails用語集 基礎
Railsは"5つの属性"を意識しろ


添野又吉(@enjoy_omame)
https://twitter.com/enjoy_omame

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails6 Access denied for user 'root'@'localhost' (using password: NO)のエラー

目的

  • 記事名のエラーが出た時の筆者の解決方法をまとめる
  • とりあえずエラーを回避する方法をまとめる、MySQLのrootパスワードをRailsの設定ファイルに直接記載するので個人プロダクト以上のアプリでは本方法はオススメしない。

筆者の環境

  • Rails version
    • 6.0.2.1
  • MySQL vsersion
    • Ver 8.0.18 for osx10.13 on x86_64 (Homebrew)
  • OS
    • macOS 10.13.6

手順概要

  1. MySQLのrootパスワードを確認
  2. RailsのDB設定ファイルにMySQLのrootアカウントのパスワードを入力
  3. 接続確認

手順詳細

  1. MySQLのrootパスワードを確認
    1. すでに知っている方はこの手順は飛ばす。
    2. パスワードを失念した場合は下記の方法に従いパスワードを再設定する。
  2. RailsのDB設定ファイルにMySQLのrootアカウントのパスワードを入力

    1. 下記のファイルを開く
      • アプリ名ディレクトリ/config
        • database.yml
    2. パスワードを追加する。

      • password:の後ろにMySQLのrootアカウントのパスワードを記入する。

        # MySQL. Versions 5.5.8 and up are supported.
        #
        # Install the MySQL driver
        #   gem install mysql2
        #
        # Ensure the MySQL gem is defined in your Gemfile
        #   gem 'mysql2'
        #
        # And be sure to use new-style password hashing:
        #   https://dev.mysql.com/doc/refman/5.7/en/password-hashing.html
        #
        default: &default
          adapter: mysql2
          encoding: utf8mb4
          pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
          username: root
          password: 
          host: localhost
        
  3. 接続確認

    1. $ rails sなどを実行してローカル環境でエラーが出ないか確認する。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails/Heroku/Cloud9:本番pgでマイグレーションができない!

デプロイは問題なかったけど、マイグレーションで問題に直面した人向け。

- 結論

herokuにPostgreSQLのアドオンがなかった!!
なので、解決策は、
heroku addons:create heroku-postgresql
ターミナルでこれだけでした。

- 経緯

rails db:migrate
は問題なかったし、
git push heroku
も問題なかったのに、
heroku run rails
で、問題発生。
PG::ConnectionBad: could not connect to server: No such file or directory
Is the server running locally and accepting
connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?

と表示され、
「サーバーに接続できない。ファイルもしくはディレクトリがないよー。ローカルでサーバー動いてて、UNIXドメインソケットの○○の接続を許諾してる?」とかなんとか。

Heroku上で、gemで指定されているDBであるpgを使おうとしてんのに、heroku上では、pgなんてないっすよ。という状態だった様子。

- 参照元

macOS上で、同じ問題に遭っている人が多くいたのですが、

Cloud9上だとソースがなかなか無く、探すのに苦労しましたため、記念すべき初投稿にしました。。。

こちらの記事が参考になりました!知らない方の発信に感謝感謝。。。
[https://qiita.com/suzuki-x/items/b878723080aea1a673ed]

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む