20200811のRailsに関する記事は30件です。

before_aciton と redirection_to の無限ループ

【結論】:無限ループしないように:exceptを使おう!

❶なぜ無限ループになるのか
1:def indexに行く前にbefore_aciton :goindexが行われます。
2:そうすると定義先に設定されている goindexにあるindexが行われます。
3:そうなるとredirection_toというのはroutes(ルーティング)に戻り、
ルーティングのアクションはindexになります。
4:そうなると、また1:が繰り返され無限ループとなります!

スクリーンショット 2020-08-11 22.25.44.png
スクリーンショット 2020-08-11 22.30.43.png

無限ループすると、更新ボタンを押した際に
”リダイレクトが繰り返し行われました”とメッセージがでます。

本来無限ループはプログラミング用語なので、まさにこの状況のことですね

❷いつ使うのか
パスワード等をもたないユーザーが
ログイン無しに勝手に投稿ができないよう際に必要です!

❸どのようにつかうのか
before_aciton :goindexのあとにexcept:[:index]とすると
[:index]は省きますよ!ということなのでdef index しか行われません!

❹ここから学んだこと
1:”リダイレクトが繰り返し行われました”とメッセージがでた際は
まっさきに無限ループを疑う癖がつきました!

2:そもそもルーティングからコントローラーに行く際に
おかしいことがわかるのでMVCモデルがきちんとわかっていたことも
エラー解決の時間短縮につながりました!

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

before_aciton と redirection_to  〜リダイレクトが繰り返し行われることについて〜

【この記事の概要】

『結論:無限ループしないように:exceptを使おう』
❶なぜループになるのか
❷いつ使うのか
❸どのようにつかうのか
❹ここから学んだこと


❶なぜ無限ループになるのか
1:def indexに行く前にbefore_aciton :goindexが行われます。
2:そうすると定義先に設定されている goindexにあるindexが行われます。
3:そうなるとredirection_toというのはroutes(ルーティング)に戻り、
ルーティングのアクションはindexになります。
4:そうなると、また1:が繰り返され無限ループとなります!

スクリーンショット 2020-08-11 22.25.44.png
スクリーンショット 2020-08-11 22.30.43.png

無限ループすると、更新ボタンを押した際に
”リダイレクトが繰り返し行われました”とメッセージがでます。

本来無限ループはプログラミング用語なので、まさにこの状況のことですね

❷いつ使うのか
パスワード等をもたないユーザーが
ログイン無しに勝手に投稿ができないようにする際に必要です!

❸どのようにつかうのか
before_aciton :goindexのあとにexcept:[:index]とすると
[:index]は省きますよ!ということなのでdef index しか行われません!

❹ここから学んだこと
1:”リダイレクトが繰り返し行われました”とメッセージがでた際は
まっさきに無限ループを疑う癖がつきました!

2:そもそもルーティングからコントローラーに行く際に
おかしいことがわかるのでMVCモデルがきちんとわかっていたことも
エラー解決の時間短縮につながりました!

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

before_aciton と redirection_to  〜リダイレクトが繰り返し行われないようにするには〜

【この記事の概要】

『結論:無限ループしないように:exceptを使おう』
❶なぜループになるのか
❷いつ使うのか
❸どのようにつかうのか
❹ここから学んだこと


❶なぜ無限ループになるのか
1:def indexに行く前にbefore_aciton :goindexが行われます。
2:そうすると定義先に設定されている goindexにあるindexが行われます。
3:そうなるとredirection_toというのはroutes(ルーティング)に戻り、
ルーティングのアクションはindexになります。
4:そうなると、また1:が繰り返され無限ループとなります!

スクリーンショット 2020-08-11 22.25.44.png
スクリーンショット 2020-08-11 22.30.43.png

無限ループすると、更新ボタンを押した際に
”リダイレクトが繰り返し行われました”とメッセージがでます。

本来無限ループはプログラミング用語なので、まさにこの状況のことですね

❷いつ使うのか
パスワード等をもたないユーザーが
ログイン無しに勝手に投稿ができないようにする際に必要です!

❸どのようにつかうのか
before_aciton :goindexのあとにexcept:[:index]とすると
[:index]は省きますよ!ということなのでdef index しか行われません!

❹ここから学んだこと
1:”リダイレクトが繰り返し行われました”とメッセージがでた際は
まっさきに無限ループを疑う癖がつきました!

2:そもそもルーティングからコントローラーに行く際に
おかしいことがわかるのでMVCモデルがきちんとわかっていたことも
エラー解決の時間短縮につながりました!

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

before_aciton と redirection_to   〜リダイレクトが繰り返し行われないようにするには〜

【この記事の概要】

『結論:無限ループしないように:exceptを使おう』
❶なぜループになるのか
❷いつ使うのか
❸どのようにつかうのか
❹ここから学んだこと


❶なぜ無限ループになるのか
1:def indexに行く前にbefore_aciton :goindexが行われます。
2:そうすると定義先に設定されている goindexにあるindexが行われます。
3:そうなるとredirection_toというのはroutes(ルーティング)に戻り、
ルーティングのアクションはindexになります。
4:そうなると、また1:が繰り返され無限ループとなります!

スクリーンショット 2020-08-11 22.25.44.png
スクリーンショット 2020-08-11 22.30.43.png

無限ループすると、更新ボタンを押した際に
”リダイレクトが繰り返し行われました”とメッセージがでます。

本来無限ループはプログラミング用語なので、まさにこの状況のことですね

❷いつ使うのか
パスワード等をもたないユーザーが
ログイン無しに勝手に投稿ができないようにする際に必要です!

❸どのようにつかうのか
before_aciton :goindexのあとにexcept:[:index]とすると
[:index]は省きますよ!ということなのでdef index しか行われません!

❹ここから学んだこと
1:”リダイレクトが繰り返し行われました”とメッセージがでた際は
まっさきに無限ループを疑う癖がつきました!

2:そもそもルーティングからコントローラーに行く際に
おかしいことがわかるのでMVCモデルがきちんとわかっていたことも
エラー解決の時間短縮につながりました!

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

before_aciton と redirect_to   〜リダイレクトが繰り返し行われないようにするには〜

【概要】

1.結論

2.ループになるのはどういう時か

3.なぜループになるのか

4.どのように解決し、使うのか

5.ここから学んだこと

1.結論

[:except]を使う。

2.ループになるのはどういう時か

I)def indexに行く前にbefore_aciton :goindexが行われます。
II)そうすると定義先に設定されている goindexにあるindexが行われます。
III)そうなるとredirection_toというのはroutes(ルーティング)に戻り、
ルーティングのアクションはindexになります。
IV)そうなると、また1:が繰り返され無限ループとなります!

スクリーンショット 2020-08-11 22.25.44.png
スクリーンショット 2020-08-11 22.30.43.png

無限ループすると、更新ボタンを押した際に
”リダイレクトが繰り返し行われました”とメッセージがでます。

本来無限ループはプログラミング用語なので、まさにこの状況のことですね

3.なぜループになるのか

ルーティングに設定されているアクション と   
redirect_toのアクションが永遠と
繰り返されるために起こります!

4.どのように解決し、使うのか

before_aciton :goindexの
あとにexcept:[:index]とすれば解決できます!
[:index]は”省きますよ!”という意味なので、
”def index”しか行われません!

5.ここから学んだこと

I)”リダイレクトが繰り返し行われました”とメッセージがでた際は
まっさきに無限ループを疑う癖がつきました!

II)そもそもルーティングからコントローラーに行く際に
おかしいことがわかるのでMVCモデルがきちんとわかっていたことも
エラー解決の時間短縮につながりました!

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

【Rails】いいね機能を非同期実装

1. はじめに

以下のデモ動画の様に、ユーザーが投稿した内容に対して"いいね"が出来る機能を実装していきます。
Image from Gyazo

2. 前提条件

既にユーザー登録機能と投稿機能は実装されている前提で、そこに"いいね機能"を追加実装する。という流れで進めていきます。

下記の様なデータベース構造をイメージしてもらえたら分かりやすいと思います。
ER 図(Qiita).png

3. いいね機能の実装

■実装するまでの流れ

ざっくり説明すると、以下の流れで実装していきます。
・モデルの作成
   ↓
・ルーティングの追加
   ↓
・コントローラーの作成
   ↓
・ビューの作成

それでは、早速いってみましょー。

3-1. Likeモデルの作成

まずはLikeモデルを作成します。
ターミナルで以下のコマンドを実行してください。

ターミナル
$ rails g model Like

新しくマイグレーションファイルが作成されるので、以下の通りに編集してください。

db>migrate>xxxxxx_create_likes.rb
class CreateLikes < ActiveRecord::Migration[6.0]
  def change
    create_table :likes do |t|
      # ===追記部分===
      t.references :tweet, foreign_key: true, null: false
      t.references :user, foreign_key: true, null: false
      # ===追記部分===
      t.timestamps
    end
  end
end

上記の様にreferences型で保存すると、tweet_iduser_idを外部キーとして指定することが出来ます。
それではマイグレーションファイルを実行して、likesテーブルを作成しましょう。

ターミナル
$ rails db:migrate

上記のコマンドを実行した後、likesテーブルが作成されたかどうか確認して下さい。
無事作成されている事が確認出来たら、次はアソシエーションの設定です。

3-2. アソシエーションの設定

アソシエーションとは、2つのモデル同士の関連付けのことを指します。
UserモデルとLikeモデル、TweetモデルとLikeモデル、それぞれのアソシエーションを設定していきます。

UserモデルとLikeモデルのアソシエーションの設定

まずはUserモデルとLikeモデルのアソシエーションを設定していきます。

2つのモデルの関係性は以下の通りです。
・ ユーザーは複数のいいねが可能
・ いいねAをしたユーザーは1人しかいない

つまり、UserモデルとLikeモデルは「1対多」の関係になります。
それでは、実際にコードを書いていきましょう。

Userモデルに以下の通りコードを追記して下さい。

app>models>user.rb
class User < ApplicationRecord

  has_many :tweets, dependent: :destroy

  # この行を追加
  has_many :likes

end

has_many は、他のモデルとの間に「1対多」の関係があることを示します。

次はLikeモデルに以下の通りコードを追記して下さい。

app>models>like.rb
class Like < ApplicationRecord

  # この行を追加
  belongs_to :user

end

belongs_tohas_many の逆で、他のモデルとの間に「多対1」の関係があることを示しています。

これで、UserモデルとLikeモデルのアソシエーション設定が出来ました。

TweetモデルとLikeモデルのアソシエーションの設定

同じ要領でTweetモデルとLikeモデルのアソシエーションも設定していきます。

2つのモデルの関係性は以下の通りです。
・ 1つの投稿に対して、複数のいいねがつく
・ いいねAに紐づく投稿は1つしかない

つまり、TweetモデルとLikeモデルも「1対多」の関係になります。
それでは、実際にコードを書いていきましょう。

Tweetモデルに以下の通りコードを追記して下さい。

app>models>tweet.rb
class Tweet < ApplicationRecord
  belongs_to :user

  # この行を追加
  has_many :likes, dependent: :destroy

end

dependent: :destroy をつける事で、投稿が削除された時に、その投稿に紐づくいいねも削除されます。

次はLikeモデルです。

app>models>like.rb
class Like < ApplicationRecord

  belongs_to :user

  # この行を追加
  belongs_to :tweet

end

以上で、全てのモデルのアソシエーションの設定が完了しました。

3-3. バリデーションの設定

投稿Aに対して1人のユーザーがいいねを押せる回数は1回にしたいので、1回以上は押せない様にバリデーションを設定します。

Likeモデルに以下の通り追記して下さい。

app>models>like.rb
class Like < ApplicationRecord

  belongs_to :user
  belongs_to :tweet

  # この行を追加
  validates :user_id, uniqueness: { scope: :tweet_id }

end

上記の様に書く事で、user_idtweet_id が重複しない様にする事が出来ます。
以上で、バリデーションの設定は完了です。

3-4. ルーティングの追加

いよいよ本格的にいいね機能を実装していきます。

まずは、いいね機能で使うルーティングを追加しましょう。
以下の通りコードを追記して下さい。

config>routes.rb
Rails.application.routes.draw do
  devise_for :users,
    controllers: { registrations: 'registrations' }

  resources :tweets, only: [:index, :new, :create, :show, :destroy] do

    # この行を追加
    resources :likes, only: [:create, :destroy]

  end

end

いいね情報の保存と削除のルーティングを追加する必要があるので、likesコントローラーのcreate アクションとdestroy アクションを定義しています。

ルーティングをネストにする事で、いいねがどの投稿に紐づくかを明示できます。

コードを追加したらrails routes コマンドで、ルーティングの設定が問題ないか忘れずに確認しておきましょう。

3-5. likesコントローラーの作成

次にlikesコントローラーを作成していきます。
ターミナルで以下のコマンドを実行してください。

ターミナル
$ rails g controller likes

上記のコマンドを実行すると、likesコントローラーが作成できます。

それでは作成したlikesコントローラーにcreate アクションとdestroy アクションを作成していきます。
以下の通りコードを追記して下さい。

app>controllers>likes_controller.rb
class LikesController < ApplicationController

  # ===追記部分===
  def create
    @like = current_user.likes.build(like_params)
    @tweet = @like.tweet
    if @like.save
      respond_to :js
    end
  end

  def destroy
    @like = Like.find_by(id: params[:id])
    @tweet = @like.tweet
    if @like.destroy
      respond_to :js
    end
  end

  private
    def like_params
      params.permit(:tweet_id)
    end
  # ===追記部分===

end

privateメソッドやparamsは理解できているものとして、追加したコードについて簡単に説明していきます。

createアクション

まず@like には投稿に"いいね"をしたユーザーのuser_id と、"いいね"された投稿のtweet_id の情報が入っています。
このコードはbuildメソッドを使って、インスタンスを作成しています。

次に@tweet には@like に紐づく投稿の情報、つまり"いいね"された投稿の情報が入ります。
@tweet はどの投稿に"いいね"を押したのかを判断するために、ビューを作成するところで使います。

最後のif @like.save の部分は、"いいね"が押された時に返すレスポンスのフォーマットをrespond_to メソッドで切り替えています。
"いいね"が押されたらリアルタイムでビューを反映させるために、JS形式のフォーマットでレスポンスを返すようにしています。

destroyアクション

createアクションのところで説明した内容と重複している部分が多いので簡単に説明すると、受け取ったHTTPリクエストからid を判別し、@like に指定のレコードの情報を入れています。

こちらもリアルタイムでビューを反映させるために、JS形式のフォーマットでレスポンスを返すようにしています。

3-5. ビューの作成

いよいよビューの作成です。

まずは投稿一覧のビュー画面を編集していきましょう・・・と言いたいところですが、ビューで使うためのメソッドを先に定義しておきます。

Tweetモデルに以下の通りコードを追記して下さい。

app>models>tweet.rb
class Tweet < ApplicationRecord
  belongs_to :user
  has_many :likes, dependent: :destroy

  # 追加部分(liked_byメソッド)
  def liked_by(user)
    Like.find_by(user_id: user.id, tweet_id: id)
  end
  # 追加部分

end

上記で追加したliked_by メソッドは、user_idtweet_id が一致するlikeを探して、無ければnillを返します。

それでは、app/views/tweets/index.html.erb に以下のコードを追記して下さい。

app>views>tweets>index.html.erb
<% @tweets.each do |tweet| %>

  # いいねボタンを表示したい部分に追加
  <div class="content-like">
    <ul class="content-like__icons">
      <li id="<%= tweet.id.to_s %>">
        <% if tweet.liked_by(current_user).present? %>
          <%= link_to (tweet_like_path(tweet.id, tweet.liked_by(current_user)), method: :DELETE, remote: true, class: "liked") do %>
            <i class="far fa-thumbs-up"></i>
          <% end %>
        <% else %>
          <%= link_to (tweet_likes_path(tweet), method: :POST, remote: true, class: "like") do %>
            <i class="far fa-thumbs-up"></i>
          <% end %>
        <% end %>
      </li>
    </ul>
  </div>
  # 追加部分はここまで

<% end %>

liked_by に引数としてcurrent_userを渡すことで、現在ログインしているユーザーが投稿に"いいね"をしているかどうか判断しています。

これでユーザーが"いいね"をしていない時に"いいねボタン"をクリックすると、先ほど作成したcreate アクションを実行、ユーザーが"いいね"をしている時はdestroy アクションを実行と、条件分岐させる事ができました。

リンクが押された時に.js.erb ファイルを呼び出す必要があるので、link_toremote: true オプションを追加することを忘れないでください。

なお"いいねボタン"のアイコンについては、Font Awesome を利用しています。
導入方法については、以下のqiita記事などが参考になるかと思います。
rails font-awesome-sass導入方法

次は、createアクションが実行された時に出力するファイルを作成します。
app/views フォルダ直下にlikes フォルダを作成し、その中にcreate.js.erb ファイルを作成してください。

ファイルの作成ができたら、以下の通りコードを追記してください。

app>views>likes>create.js.erb
$('#<%= @tweet.id.to_s %>').
  html('<%= j render "tweets/liked", { tweet: @tweet } %>');

上記のコードで、createアクションが実行されたらtweets フォルダ内の_liked.html.erb ファイルを呼び出しています。

tweets フォルダの中に_liked.html.erb ファイルを作成し、以下のコードを追加してください。

app>views>tweets>_liked.html.erb
<%= link_to (tweet_like_path(tweet.id, tweet.liked_by(current_user)), method: :DELETE, remote: true, class: "liked") do %>
  <i class="far fa-thumbs-up"></i>
<% end %>

上記のコードで、"いいねボタン"を押したら"いいね"を取り消すHTMLを表示するようにしています。

同じ流れで、destroyアクションが実行された時に呼び出されるファイルも作っていきましょう。
app/views>likes フォルダの中にdestroy.js.erb ファイルを作成してください。

ファイルの作成ができたら、以下の通りコードを追記してください。

app>views>likes>destroy.js.erb
$('#<%= @tweet.id.to_s %>').
  html('<%= j render "tweets/like", { tweet: @tweet } %>');

tweets フォルダの中に_like.html.erb ファイルを作成し、以下のコードを追加してください。

app>views>tweets>_like.html.erb
<%= link_to (tweet_likes_path(tweet), method: :POST, remote: true, class: "like") do %>
  <i class="far fa-thumbs-up"></i>
<% end %>

以上で非同期でいいね機能を実装する事ができました。

あとは見た目ですが、クラス名を、いいねされている時はliked されていない時はlike としていますので、CSSで自分好みにカスタマイズしてみてください。

私の場合は以下の様にして、いいねがされた時はレッド、されていない時はグレーにアイコンの色を変えています。

app>assets>stylesheets>tweets>_tweet.css
.like {
  color: gray;
}

.liked {
  color: red;
}

4. さいごに

今回がはじめての投稿になりますが、記事を書くのって想像していたよりも大変ですね。
もし分かりにくい部分や、間違っている部分がある場合はご指摘いただけると嬉しいです。

最後まで読んでいただき、ありがとうございました☺️

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

独学未経験エンジニアがweb系自社開発企業でアルバイトを2ヶ月してみて感じたこと

簡単な自己紹介

  • 大学では機械工学を専攻 
  • プログラマに魅力を感じ2019年新卒でメーカー子会社のIT会社に入社
  • 入社してから10ヶ月で会社を退職
  • そこから4ヶ月間独学で勉強し、現在web系の自社開発企業でアルバイト中

ニートになって独学していた時代

新卒で入社したときに感じたことや退職理由はまた今度別の記事で書いてみたいと思います。

勉強したこと一覧

  • progate(HTML,CSS,Javascript,Ruby,RubyonRails)

  • railsチュートリアル解説動画
    2周はした。1周目は動画を見るだけで、2周目は手を動かしながらでした。
    かなりお世話になり、railsチュートリアルとは友達になった気がする

  • Ruby on Rails5 超入門
    amazonリンク
    読んだのは3割ぐらいだけ

  • Ruby on Rails5 速習実践ガイド
    amazonリンク
    辞書的な感じで使ったりした。暇なときに読んでみてもためになることが多い。

  • Gitが、おもしろいほどわかる基本の使い方33
    amazonリンク
    gitの操作の基礎を学んだ。すぐに読み終わる内容なので、読みやすい

  • キタミ式イラストIT塾 基本情報技術者
    amazonリンク
    コンピュターの基礎を理解するのにはちょーど良かった。時間をかけてちょっとずつ読んだ

  • webを支える技術
    amazonリンク
    何を書いているのか全然分からなかった。kindle版で技術書は読みにくい。

  • 図解即戦力 AWSのしくみと技術がしっかりわかる教科書
    amazonリンク
    AWSのサービスの概要は大体理解したけど、実装まではできず

  • プログラミングスクール受講(ポテパンキャンプ)
    ポテパンのリンク
    gitの操作、N+1問題、Rspecの書き方はここで自主的に学んだ

  • Everyday Rails - RSpecによるRailsテスト入門 購入リンク
    ちょくちょく参考にできた。これも詰まったときに検索する用。

ポートフォリオ作成

当時はAWS,Docker, CI/CD, Kubernetes等についてはほぼほぼ理解できずに、断念していました。
開発環境なんかはrailsチュートリアルで使っていたcloud9を使っていました。
まずはHerokuに自分が一生懸命作成したアプリケーションをデプロイし、公開できるようにしました。
一応独自ドメイン,SSL化程度は行っておき、Herokuは有料プランを使用し、少しでも応答速度が早くなるようにしておきました。
一応当時のポートフォリオのソースコードのリンクを貼っておきます。
https://github.com/ak2-lucky/clothes-app
ファッションが好きだった僕は洋服のレビューサイトを作りました。

この記事を見ている駆け出しエンジニアの日々勉強されている方々はこう思っているかもしれません。
AWSにデプロイすらできないのに、webエンジニアになれるの?
Dockerで開発環境も構築できないの?
自動テストぐらいはやっといたら?

現役のwebエンジニアも同じことを思うでしょう。
ポートフォリオも作りきれないような奴はエンジニアになる資格なんかないよって。
なぜならポートフォリオだけは運にも左右されない自分の努力だけで100パーセント作りきれる成果物だからです。

確かにそうです。
僕の努力不足です。何の異論もありません。
認めます。

そして就職活動をはじめます。

就職活動時代

結果からゆうとほぼ全落ち。
面接までいったのは1.2回でした。
面談してくれた会社で研究開発で人材を募集しているからそこに来ないか?と言ってくれた企業もありました。(結局いかず)
コロナの影響とかいう言い訳はしません。
全て自分の実力不足。
アルバイトも採用している企業に応募し,1社だけ何とかアルバイトとして採用していただきました。

今回で自分の市場価値を知りました。
大学は何も考えずに過ごし、新卒で入社した会社を何の成果も残さず10ヶ月で退職。
世間はそんな奴のことを評価はしてくれません。
もちろん当然の評価です。

アルバイトとしてweb系自社開発企業で働く(今)

技術スタック:AWS,Docker,Laravel+vue.js

働いてみるまでLaravelはおろかPHPも触ったことありませんでした。vue.jsもですが。
詳細は省きますが、実際に働いてみて感じるのは、プログラムを書く以外のことです。
技術のキャッチアップ云々の話ではなくて、組織作り、開発の体制やフローなどの重要さについてです。
自分はプログラミングが苦ではありません。
しかし、プログラミング業務以外の部分でストップすることが多い場合があります。(例えば、なぜこの変更を加えられたのかというコメントがないandコードを見ても分からない)
連携がうまく取れなくて、個々の意思で変更が加えられたりするので、デザインがバラバラ。
issue作成者等に確認もしないで変更を進められていたり。
CI/CDもそのうちの一つですが、issueを作成するのは誰なのか、誰の判断を最終的に仰ぐのか、UIのデザインは好きに決めていいのか、プルリクではどこを見ればいいのか、など他にもいろいろありますが。。。

会社によって開発の進め方などは違うと思うので、一概にあれが悪い、これが悪いというわけではありませんが、開発体制や組織作りはサービス開発にかなり大きく影響するように感じました。

開発言語や技術以前にこういった根本的な開発の導線を確保する重要性を学びました。

最後に

ここまで読んでいただいた方には、いろいろ思うことがあると思います。
2ヶ月しか働いていない、しかもアルバイト如きが何を偉そうなこと言ってるんだ!
とか思ってる方いるかもしれません。

ですが、これは個人の一意見であります。
ただ同じように駆け出しエンジニアの方の参考になればいいなと思って書きました。

webエンジニアへの道は高く険しいように感じます。
しかし、自分にとって登りたい山がどれだけ高かろうが、険しかろうが、関係ありません。
ただ登るだけ
毎日頑張って積み上げて、疲れたら一緒にサボりましょう。

長くなりましたが、初投稿は以上になります。

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

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

RailsをAWS ECS(Fargate)でホストする環境構築のすべて【1. 前談、N/W+α】

本記事シリーズの構成(予定)

  1. 前談、N/W+α(これ)
  2. Docker定義、ECRの設定
  3. ECSの設定、デプロイ
  4. SSMの設定

構築環境全体像

こんな感じを目指します。
スクリーンショット 2020-08-11 17.39.58.png

実現したいポイント
* Docker環境で開発を行っているRailsプロジェクトを、Fargate起動のECSでホストします。
* ECSタスクは複数設け、負荷分散します。
* AWS System Manager セッションマネージャを使用してECSに「接続し、CUIで操作可能にします。

ネットワーク+ALB+RDSの環境構築

VPC

サンプルなのでシンプルに作ります。
スクリーンショット 2020-08-11 18.03.43.png

作成した後、VPC一覧右上のアクション>DNSホスト名の編集から有効化をしておきます。
スクリーンショット 2020-08-11 18.18.48.png

サブネット

下記の通り、わかりやすくサブネットを切っておきます。

subnet CIDR
public-subnet-a 10.0.10.0/24
public-subnet-c 10.0.20.0/24
public-subnet-d 10.0.30.0/24

スクリーンショット 2020-08-11 18.04.13.png

インターネットゲートウェイ

68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3433303630332f62343233356531372d633765372d353266352d616636622d3963356164396331336638622e706e67.png

作成したら、右上のアクション>VPCにアタッチから、
sample-vpcにアタッチするのを忘れずに。

スクリーンショット 2020-08-11 18.07.34.png

エンドポイント

この後設定していくサービスを利用するために、4つのエンドポイントを設けます。

  • com.amazonaws.ap-northeast-1.ecr.api
  • com.amazonaws.ap-northeast-1.ecr.dkr
  • com.amazonaws.ap-northeast-1.s3
  • com.amazonaws.ap-northeast-1.ssm

エンドポイントを選択して、
アタッチしたいサブネットはpublic-subnet-a,public-subnet-cの2つのみ指定。
スクリーンショット 2020-08-11 18.08.57.png

Interfaceタイプのエンドポイントには、セキュリティグループを適用する。
スクリーンショット 2020-08-11 18.18.06.png

エンドポイント用のセキュリティグループとして、443ポートを開けたセキュリティグループを作っておきましょう。
スクリーンショット 2020-08-11 18.17.09.png

S3はGatewayタイプなのでセキュリティグループではなく、ルートテーブルに従います。
スクリーンショット 2020-08-11 18.20.17.png

ルートテーブル

VPC作成時に自動作成されているものを編集します。

ルート一覧に、先ほど追加したエンドポイントの設定が加わっています。
ルートの編集ボタンを押下します。
スクリーンショット 2020-08-11 19.37.07.png

ここにインターネットゲートウェイへのルーティングを加えます。
スクリーンショット 2020-08-11 19.37.37.png

編集したルートテーブルにサブネットを関連づけます。
インターネットゲートウェイのルーティングを設定したルートテーブルに、サブネットを関連づけることで、
いわゆるpublic subnetになります。
スクリーンショット 2020-08-11 18.06.37.png

public-subnet-a,cはSSMを利用した接続時にパブリックでないとうまくできなかったため、セキュリティグループでIP、ポート制限をかけつつパブリック化する判断にしました。

public-subnet-dはデータベース用に取っていますが、データベースに外部からクライアント接続したいのでパブリックにしています。ここをパブリックにするかどうかは任意です。


ALB

アプリケーションロードバランサーを選択して作成していきます。
スクリーンショット 2020-08-11 18.12.59.png

実用するときには、SSL証明書を適用して443ポート開けることになると思いますが、
今回は検証なので、80ポート開けておきます。
スクリーンショット 2020-08-11 18.13.22.png

ルーティング先はECSをおくサブネットに絞って設定しています。
スクリーンショット 2020-08-11 18.13.27.png

セキュリティグループはここでついでに新規作成しています。
80ポートのインバウンドを設定しています。検証なのでマイIPのみにしたほうがよかったかも。
スクリーンショット 2020-08-11 18.14.02.png

ターゲットグループをここで一緒に新規作成します。
ターゲットの種類はIPにします。
ヘルスチェックはアプリケーションに用意したヘルスチェック用のパスを指定してください。
スクリーンショット 2020-08-11 18.14.34.png

ここはそのままでOK。
スクリーンショット 2020-08-11 18.14.54.png


RDS

作成内容は完全に任意です。
MySQL 8.0.19でインスタンスをパブリックアクセスありで作成しました。
スクリーンショット 2020-08-11 19.58.23.png

このときに適用したセキュリティグループは以下の通りです。
3306ポートをマイIPからのみアクセス可能にしています。
スクリーンショット 2020-08-11 20.01.22.png

まとめ

ここまででこんな感じの大枠まで作りました。
次の記事で、Dockerの定義と、コンテナイメージをECRにプッシュするまでを記載したいと思います。
スクリーンショット 2020-08-11 20.10.14.png

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

削除機能の追加方法

削除機能の追加の備忘録です。

設定手順

① ルーティングの設定 
・httpメソッド 「delete」

②削除ボタンの作成 →ビューファイルの変更
 ・条件の指定に注意。(例:ログインユーザかつ投稿したユーザ❓)
 

③コントローラのアクション作成
 ・「destroy」メソッドの定義。
 ・削除権限を考える。例は②と同様。

④削除後のビューファイルの作成。
 ・コントローラのアクションが「destroy」なので、ファイル名は「destroy」をつける。
 ・トップページのURL作成。

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

docker buildするとextconf failedout of memoryエラーが発生した話

使用技術

  • amazon linux2
  • Docker 19.03.6

エラーが発生した状況

イメージをbuildしようとしてdocker buildコマンドを実行すると下記エラー

docker extconf failedout of memory

その下に赤字でnokogiriがインストール出来ないと書いてあり指定されたコマンドを走らせ再度buildしてもエラーが直らず...。
(目立つ方ばかり気にしていて上記エラーに気づくのが遅れてしまった)

上記エラー文で検索するとAWSの無料枠内のEC2インスタンスタイプ、t2.microでは容量が足りないため課金しなくてはいけないとの記事を見つけ驚き。しかも月2000円ですと...。
課金しなくて済む方法が知りたい!

調べて判明した原因

dockerのvolumeが膨れ上がっていました。

以前もEC2のメモリが足りなくなり増設したのにまた!?と思いつつディスクの空き容量を調査

$ df -h
devtmpfs         474M     0  474M    0% /dev
tmpfs            492M     0  492M    0% /dev/shm
tmpfs            492M  436K  492M    1% /run
tmpfs            492M     0  492M    0% /sys/fs/cgroup
/dev/xvda1        32G   13G   20G   40% /
tmpfs             99M     0   99M    0% /run/user/1001

大丈夫そう。

お次はディスクの使用量を調査

$ du -hs /*
.
.
.
4.4G    /usr
6.9G    /var  

6.9Gも使っていました。
ここがdockerコンテナの居場所なので原因はdockerですね。
ここまで分かると使えるキーワードも増えて検索がしやすいです(嬉しい)

ついにvolumeを減量していきましょう

$ docker system prune -a --volumes

docker systemは後ろにオプションをつけて、未使用のコンテナ、イメージ、ネットワーク、ボリュームを削除するコマンドです。

--volumes オプションはdocker17.06.1から追加されたようですのでそれ以前のバージョンでは使用できません。

これで再度使用量を調べると

$ du -hs /*
.
.
.
4.4G    /usr
2.9G    /var

減りました。よし。

参考

https://qiita.com/shione/items/dfa956a47b6632d8b3b3

https://qiita.com/381704/items/5d526e34da95fd307480

https://docs.docker.com/engine/reference/commandline/system_prune/

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

Secretsantasができるまで③  post機能(CRUD)

更新予定

memo
- index
- new
- show
- destroy
- edit

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

【Ruby on Rails】アプリケーション作成〜サーバー起動まで&ポート番号って何?

Ruby on Railsの初歩中の初歩ですが、まとめです。

1.アプリケーションを作成する。

ターミナルを起動します。

$ rails new アプリケーション名

上記コマンド実行により、入力したアプリケーション名と同名のフォルダが作成され、その中に開発に必要なフォルダやファイルが用意されます。

アプリケーション名/
  ├ app/ アプリケーションのメインフォルダ
  ├ config/ 設定情報に関するフォルダ
  ├ db/ データベースに関するフォルダ
  └: その他

2.サーバーを起動する。

ターミナルに入力

$ rails server

Google ChromeやSafariなどのブラウザでlocalhostを3000番ポートで開きます。

localhost:3000

スクリーンショット 2020-08-09 9.27.41.png
無事、Ruby on Railsが起動しました。

参考: Ruby on Rails5 学習コース I | プログラミングの入門なら基礎から学べるProgate[プロゲート]

ポート番号とはなんでしょうか?

今回、3000番ポートと出てきましたが、ポート番号という言葉は、インフラ関係の知識としては必須です。

インターネット用語1分解説~ポート番号とは~ - JPNICには、以下のように説明されています。

ポート番号とは、TCP/IP通信において、 コンピュータが通信に使用するプログラムを識別するための番号です。 ポート番号は16ビットの整数であり、 0番~65535番まであります。

更に深いところで、ポート番号には大きく分けて3種類あり
* ウェルノウンポート番号(0番~1023番) - 使用目的が定められたポート番号
* 登録ポート番号(1024番~49151番) - IANAが登録を受け付け、 公開
* ダイナミック/プライベートポート番号(49152番~65535番) - 誰でも自由に使用できるポートとして開放

上記のように分類されます。

ちなみに、今回は、Ruby on Railsだと3000番ポート、Djangoだと8000番ポートがデフォルトで使われていますが、この3000番と8000番は登録ポート番号(1024番~49151番)に該当する為、IANAが登録を受け付け、 公開できるようになっています。

IANAとは?

Internet Assigned Numbers Authorityの略称。
ドメイン名、IPアドレス及びAS番号、各プロトコルで使われるプロトコル名及び番号といったインターネット資源を管理する機能です。
IANAは、南カリフォルニア大学情報科学研究所(ISI)の故ジョン・ポステル(Jonathan Bruce Postel)氏が中心となり、1988年に設立されました。IANAはインターネットに関する最も古い機関の一つであり、その活動は1970年代にまで遡ることができます。
その後、1998年10月にICANNが設立され、IANAが行っていた各種資源のグローバルな管理・調整の役割は ICANNの一部局として引き継がれました。そのような経緯から、歴史的なことも含めインターネット資源を管理する機能を表す意味で使用されています。 引用:JPRS用語辞典|IANA(アイアナ)

TCP/IP通信やHTTPなどは、更に深掘りできるので機会があったら記事にする予定です。

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

【Ruby on Railsの手順】アプリケーション作成〜サーバー起動まで&ポート番号って何?

Ruby on Railsの初歩中の初歩ですが、まとめです。

1.アプリケーションを作成する。

ターミナルを起動します。

$ rails new アプリケーション名

上記コマンド実行により、入力したアプリケーション名と同名のフォルダが作成され、その中に開発に必要なフォルダやファイルが用意されます。

アプリケーション名/
  ├ app/ アプリケーションのメインフォルダ
  ├ config/ 設定情報に関するフォルダ
  ├ db/ データベースに関するフォルダ
  └: その他

2.サーバーを起動する。

ターミナルに入力

$ rails server

Google ChromeやSafariなどのブラウザでlocalhostを3000番ポートで開きます。

localhost:3000

スクリーンショット 2020-08-09 9.27.41.png
無事、Ruby on Railsが起動しました。

参考: Ruby on Rails5 学習コース I | プログラミングの入門なら基礎から学べるProgate[プロゲート]

ポート番号とはなんでしょうか?

今回、3000番ポートと出てきましたが、ポート番号という言葉は、インフラ関係の知識としては必須です。

インターネット用語1分解説~ポート番号とは~ - JPNICには、以下のように説明されています。

ポート番号とは、TCP/IP通信において、 コンピュータが通信に使用するプログラムを識別するための番号です。 ポート番号は16ビットの整数であり、 0番~65535番まであります。

更に深いところで、ポート番号には大きく分けて3種類あり
* ウェルノウンポート番号(0番~1023番) - 使用目的が定められたポート番号
* 登録ポート番号(1024番~49151番) - IANAが登録を受け付け、 公開
* ダイナミック/プライベートポート番号(49152番~65535番) - 誰でも自由に使用できるポートとして開放

上記のように分類されます。

ちなみに、今回は、Ruby on Railsだと3000番ポート、Djangoだと8000番ポートがデフォルトで使われていますが、この3000番と8000番は登録ポート番号(1024番~49151番)に該当する為、IANAが登録を受け付け、 公開できるようになっています。

IANAとは?

Internet Assigned Numbers Authorityの略称。
ドメイン名、IPアドレス及びAS番号、各プロトコルで使われるプロトコル名及び番号といったインターネット資源を管理する機能です。
IANAは、南カリフォルニア大学情報科学研究所(ISI)の故ジョン・ポステル(Jonathan Bruce Postel)氏が中心となり、1988年に設立されました。IANAはインターネットに関する最も古い機関の一つであり、その活動は1970年代にまで遡ることができます。
その後、1998年10月にICANNが設立され、IANAが行っていた各種資源のグローバルな管理・調整の役割は ICANNの一部局として引き継がれました。そのような経緯から、歴史的なことも含めインターネット資源を管理する機能を表す意味で使用されています。 引用:JPRS用語辞典|IANA(アイアナ)

TCP/IP通信やHTTPなどは、更に深掘りできるので機会があったら記事にする予定です。

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

【Rails】RSpecでテストしたい。そんなあなたの一歩を応援します【導入手順】

参考対象者

  • Rails6.0で、RSpecでテストしたいなと考えている方

環境

$ rails -v
Rails 6.0.3.1
$ ruby -v
ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-darwin19]

RSpecを導入する

Gemfile
group :development, :test do
  gem 'rspec-rails'
end
$ bundle install

$ rails g rspec:install

gemをインストールし、設定ファイルをジェネレータで作成する。

.rspec
--require spec_helper
--format documentation

テストをドキュメント形式に設定する。

System Specを導入する ブラウザテスト

Gemfile
group :test do
  gem 'capybara', '>= 2.15'
  gem 'webdrivers'
end
$ bundle install

まずは、gemをインストールする。

spec/rails_helper.rb
RSpec.configure do |config|
# 一番下の直前に追加
  config.before(:each) do |example|
    if example.metadata[:type] == :system
      driven_by :selenium, using: :headless_chrome, screen_size: [1400, 1400]
    end
  end
end

ブラウザテストが機能するように、RSpecの設定を変更する。

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

[Rails] 画像の投稿機能を実装する

はじめに

初心者なので間違ってるところがあれば教えてください(>_<)

掲示板サイトのpostに画像投稿機能をつけました。
難しかったので再確認の為に記事を書きます。

今回はpostに対して複数の画像を投稿出来るようにしたかったのでpostに対して1対多の関係になるようにimageモデルを作りました。

1. Imageモデルの作成

Modelとマイグレーションファイルを作成します!

$ rails g model Image image_url:string post:references

作成されたマイグレーションファイル開いて確認をします!

db/migrate/年月日時_create_images.rb
class CreateImages < ActiveRecord::Migration[5.0]
  def change
    create_table :images do |t|
      t.string :image_url
      t.references :post, foreign_key: true

      t.timestamps
    end
  end

特に変更する所はありません。
カラムが合っているか確認します。

マイグレーションの実行

$ rails db:migrate

Image Modelを開きます

app/model/image.rb
class Image < ApplicationRecord
  belongs_to :post
end

既にbelongs_to :postが書かれています。

Post Modelを開きます

app/models/post.rb
class Post < ApplicationRecord
  belongs_to :user

  validates :content, presence: true, length: { maximum: 255 }

  has_many :favorites
  has_many :likeds, through: :favorites, source: :user
end

has_many :imagesを書き足します!

app/models/post.rb
class Post < ApplicationRecord
  belongs_to :user

  validates :content, presence: true, length: { maximum: 255 }

  has_many :favorites
  has_many :likeds, through: :favorites, source: :user

  has_many :images #この行を追加
end

これでとりあえず1対多の関係はできました。

2. accepts_nested_attributes_forとfields_forを使ってhas_many関連の子レコードを作成/更新するフォームを作成

今回は、postを投稿した時にimageも同時に投稿出来るように一つのフォームで複数のフィールドの登録をまとめて行うようにします。
その為に必要なのが

・accepts_nested_attributes_for
・fields_for

です!
これを使うとhas_many関連の子レコードをまとめて登録出来るようになります!

入れ子のフォームを扱う為の下準備

テーブルをまとめて登録するために入れ子のフォームを作成することになります。それを可能にするためにaccepts_nested_attributes_forというメソッドを使いましょう。

それでは、accepts_nested_attributes_forの設定を行ます。
post.rbに追記していきましょう。

app/models/post.rb
class Post < ApplicationRecord
  belongs_to :user

  validates :content, presence: true, length: { maximum: 255 }

  has_many :favorites
  has_many :likeds, through: :favorites, source: :user

  has_many :images
  accepts_nested_attributes_for :images #この行を追加
end

posts_controller.rbの追記

posts_controller.rbにも追記していきます。

app/controllers/posts_controller.rb
class PostsController < ApplicationController

  def new
    @post = Post.new
    @post.images.build #この行を追加
  end

  def create
    @post = current_user.posts.build(post_params)
    if @post.save
      flash[:success] = 'メッセージを投稿しました。'
      redirect_to root_url
    else
      @posts = current_user.feel_posts.order('created_at DESC').page(params[:page]).per(10)
      flash.now[:danger] = 'メッセージを投稿に失敗しました。'
      render 'posts/new'
    end
  end

  private

  def post_params
    params.require(:post).permit(:content, :security, images_attributes: [:image_url]) #この行を追加
  end

@post.images.build
とparams内の
images_attributes: [:image_url]
を追記しました。
nested modelの許可するパラメーターの値はparams内images_attributes:を使って記述します。

fields_forを使ってフォームの中に入れ子を作る

views/posts/new.html.erb
<%= form_for(@post) do |f| %>
  <div class="form-group">
    <%= f.label :content, 'コメント' %>
    <%= f.text_area :content, class: 'form-control', rows: 5, placeholder: 'コメントを入力してください'  %>
  </div>

  <div class="form-group">
    <%= f.label :security, 'Security_Level' %>
    <%= f.number_field :security, class: 'hoge', min: 0, max: 100 %>
  </div>

  <%= f.fields_for :images do |i| %>  #この行に追加しました。
    <%= i.file_field :image_url %>
  <% end %>

  <div class="text-right">
    <%= f.submit 'Post', class: 'btn btn-primary' %>
  </div>
<% end %>

さて、これで一つのフォームで複数のフィールドの登録をまとめて行うよにはできましたがまだ画像をアップロードできません(+_+)

次は、CarrierWaveというgemを使って画像をアップロード出来るようにしていきます!!

3. CarrierWaveをインストールして画像をアップロードできるようにする

Gemfileにgemを追加します。

Gemfile
gem 'carrierwave' #画像アップロード
$ bundle install

コマンドを実行します!

アップローダーの作成

$ rails g uploader image

imageのところは、適当な名前を記入してください。今回は、imageと名付けました。
コマンドを実行すると、

create app/uploaders/image_uploader.rb

となり、アップローダーが作成されました。

モデルの関連付け

/models/image.rbに以下を追記し、カラムの名前をmount_uploaderに指定します。

app/models/image.rb
class Image < ApplicationRecord
  belongs_to :post

  mount_uploader :image_url, ImageUploader #この行に追加
end

imageモデルにはimage_urlというカラムがあり、そこに画像のURLを格納するような仕様になっています。

Viewに表示

view/posts/_posts.html.erb
 <div>
   <%= link_to post.user.name, user_path(post.user) %><span class="text-muted">posted at<% post.created_at %></span>
 </div>
 <div>
   <p><%= post.content %></p>
 </div>
 <div>
   <% post.images.each do |image| %>  
     <%= image_tag image.image_url.url %>  #ここに表示
   <% end %>
 </div>
 <div>
   <p>Security_Level <%= post.security %></p>
 </div>

 <div class="batton">
   <%= render 'favorites/favorite_button', post: post %>
   <% if current_user == post.user %>
     <%= link_to "Delete", post, method: :delete, data: { confirm: "本当に削除しますか?" }, class: 'btn btn-danger btn-xs' %>
   <% end %>

以上で画像投稿機能終わりです!!

終わりに

今回複数の画像を投稿出来るようにしたかったのですが、今のままだと画像を一つしか投稿できませんでした。
勉強不足です。すみません。(>_<)

また複数画像を投稿出来るやり方がわかったら記事を書こうと思います!!!

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

画像の投稿機能

はじめに

初心者なので間違ってるところがあれば教えてください(>_<)

掲示板サイトのpostに画像投稿機能をつけました。
難しかったので再確認の為に記事を書きます。

今回はpostに対して複数の画像を投稿出来るようにしたかったのでpostに対して1対多の関係になるようにimageモデルを作りました。

1. Imageモデルの作成

Modelとマイグレーションファイルを作成します!

$ rails g model Image image_url:string post:references

作成されたマイグレーションファイル開いて確認をします!

db/migrate/年月日時_create_images.rb
class CreateImages < ActiveRecord::Migration[5.0]
  def change
    create_table :images do |t|
      t.string :image_url
      t.references :post, foreign_key: true

      t.timestamps
    end
  end

特に変更する所はありません。
カラムが合っているか確認します。

マイグレーションの実行

$ rails db:migrate

Image Modelを開きます

app/model/image.rb
class Image < ApplicationRecord
  belongs_to :post
end

既にbelongs_to :postが書かれています。

Post Modelを開きます

app/models/post.rb
class Post < ApplicationRecord
  belongs_to :user

  validates :content, presence: true, length: { maximum: 255 }

  has_many :favorites
  has_many :likeds, through: :favorites, source: :user
end

has_many :imagesを書き足します!

app/models/post.rb
class Post < ApplicationRecord
  belongs_to :user

  validates :content, presence: true, length: { maximum: 255 }

  has_many :favorites
  has_many :likeds, through: :favorites, source: :user

  has_many :images #この行を追加
end

これでとりあえず1対多の関係はできました。

2. accepts_nested_attributes_forとfields_forを使ってhas_many関連の子レコードを作成/更新するフォームを作成

今回は、postを投稿した時にimageも同時に投稿出来るように一つのフォームで複数のフィールドの登録をまとめて行うようにします。
その為に必要なのが

・accepts_nested_attributes_for
・fields_for

です!
これを使うとhas_many関連の子レコードをまとめて登録出来るようになります!

入れ子のフォームを扱う為の下準備

テーブルをまとめて登録するために入れ子のフォームを作成することになります。それを可能にするためにaccepts_nested_attributes_forというメソッドを使いましょう。

それでは、accepts_nested_attributes_forの設定を行ます。
post.rbに追記していきましょう。

app/models/post.rb
class Post < ApplicationRecord
  belongs_to :user

  validates :content, presence: true, length: { maximum: 255 }

  has_many :favorites
  has_many :likeds, through: :favorites, source: :user

  has_many :images
  accepts_nested_attributes_for :images #この行を追加
end

posts_controller.rbの追記

posts_controller.rbにも追記していきます。

app/controllers/posts_controller.rb
class PostsController < ApplicationController

  def new
    @post = Post.new
    @post.images.build #この行を追加
  end

  def create
    @post = current_user.posts.build(post_params)
    if @post.save
      flash[:success] = 'メッセージを投稿しました。'
      redirect_to root_url
    else
      @posts = current_user.feel_posts.order('created_at DESC').page(params[:page]).per(10)
      flash.now[:danger] = 'メッセージを投稿に失敗しました。'
      render 'posts/new'
    end
  end

  private

  def post_params
    params.require(:post).permit(:content, :security, images_attributes: [:image_url]) #この行を追加
  end

@post.images.build
とparams内の
images_attributes: [:image_url]
を追記しました。
nested modelの許可するパラメーターの値はparams内images_attributes:を使って記述します。

fields_forを使ってフォームの中に入れ子を作る

views/posts/new.html.erb
<%= form_for(@post) do |f| %>
  <div class="form-group">
    <%= f.label :content, 'コメント' %>
    <%= f.text_area :content, class: 'form-control', rows: 5, placeholder: 'コメントを入力してください'  %>
  </div>

  <div class="form-group">
    <%= f.label :security, 'Security_Level' %>
    <%= f.number_field :security, class: 'hoge', min: 0, max: 100 %>
  </div>

  <%= f.fields_for :images do |i| %>  #この行に追加しました。
    <%= i.file_field :image_url %>
  <% end %>

  <div class="text-right">
    <%= f.submit 'Post', class: 'btn btn-primary' %>
  </div>
<% end %>

さて、これで一つのフォームで複数のフィールドの登録をまとめて行うよにはできましたがまだ画像をアップロードできません(+_+)

次は、CarrierWaveというgemを使って画像をアップロード出来るようにしていきます!!

3. CarrierWaveをインストールして画像をアップロードできるようにする

Gemfileにgemを追加します。

Gemfile
gem 'carrierwave' #画像アップロード
$ bundle install

コマンドを実行します!

アップローダーの作成

$ rails g uploader image

imageのところは、適当な名前を記入してください。今回は、imageと名付けました。
コマンドを実行すると、

create app/uploaders/image_uploader.rb

となり、アップローダーが作成されました。

モデルの関連付け

/models/image.rbに以下を追記し、カラムの名前をmount_uploaderに指定します。

app/models/image.rb
class Image < ApplicationRecord
  belongs_to :post

  mount_uploader :image_url, ImageUploader #この行に追加
end

imageモデルにはimage_urlというカラムがあり、そこに画像のURLを格納するような仕様になっています。

Viewに表示

view/posts/_posts.html.erb
 <div>
   <%= link_to post.user.name, user_path(post.user) %><span class="text-muted">posted at<% post.created_at %></span>
 </div>
 <div>
   <p><%= post.content %></p>
 </div>
 <div>
   <% post.images.each do |image| %>  
     <%= image_tag image.image_url.url %>  #ここに表示
   <% end %>
 </div>
 <div>
   <p>Security_Level <%= post.security %></p>
 </div>

 <div class="batton">
   <%= render 'favorites/favorite_button', post: post %>
   <% if current_user == post.user %>
     <%= link_to "Delete", post, method: :delete, data: { confirm: "本当に削除しますか?" }, class: 'btn btn-danger btn-xs' %>
   <% end %>

以上で画像投稿機能終わりです!!

終わりに

今回複数の画像を投稿出来るようにしたかったのですが、今のままだと画像を一つしか投稿できませんでした。
勉強不足です。すみません。(>_<)

また複数画像を投稿出来るやり方がわかったら記事を書こうと思います!!!

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

RailsアプリをAWSにあげるために必要な知識

はじめに

プログラミング未経験の筆者がRailsアプリをAWSにあげるのに、必要となった最低限の知識をざっくりとまとめました。

まとめた内容は以下の通りです。
・AWS
・VPC
・EC2
・RDS

AWSのネットワークサービス

image.png
参照:アベイラビリティゾーンとは

まずはじめにAWSのネットワークについてです。
AWSを使うときにはじめに触れるのがリージョンアベイラビリティーゾーン(AZ)です。

リージョン

リージョンはAWSがサービスを提供している拠点(国と地域)のことを指しています。
遠いリージョンをを使うとネットワーク遅延が発生してしまう可能性があるので、基本的には東京リージョンを使いましょう。

アベイラビリティーゾーン(AZ)

アベイラビリティーゾーン(AZ)データセンター(サーバー機などのIT機器を設置・収容する場所を提供し、安定的に運用できるようさまざまなサービスを提供する施設)とほぼ一緒です。
AZはリージョンごとに用意されていて、東京リージョンでは4つのAZが用意されています。

AWSでは複数のAZを利用すること(マルチAZ構成)が推奨されています。
マルチAZ構成であれば、もし一つのAZに障害が起きても、別のAZを利用することでネットワーク障害を回避できるからです。

続いてAWSのサービスについて説明していきます。

VPC(Virtual Private Cloud)

image.png

参照:VPC とサブネット

VPCはネットワークを作成するサービスです。
上図の通りAWSのネットワークの中にVPCのネットワークを作成します。
そしてVPCはサブネットを作成することで分割できます。

ざっくり言うと
大きな箱(AWS)の中にそこそこ大きい箱(VPC)があって、その中に小さな箱(サブネット)があるイメージですね。

そして、この小さな箱(サブネット)の中にこれから説明するEC2やRDSが入っていきます。

EC2(Elastic Compute Cloud)

image.png

参照:インスタンスの開始方法

EC2は仮想サーバ(インスタンス)ファイアウォール(セキュリティグループ)などを利用できるサービスです。

知らない単語が出てきましたね...

ざっくり説明していきます。

インスタンス

インスタンスは従来のオンプレミス環境上のサーバに相当します。

インスタンスはOS、CPU、メモリ等の情報を持っています。

つまり「インスタンスを作成する」というのは
「OS、CPU、メモリ等の条件を選択し、自分が求めるサーバーを作成する」ことです。

セキュリティグループ

セキュリティグループはAWS標準のファイアウォール機能です。
EC2インスタンスへのアクセスを許可したり、トラフィックを制御することができます。

デフォルトでは全ての通信が遮断してあります。(重要)
そのため特定の通信を許可する必要があります。

アクセスの許可は通信の方向で分けられ、インバウンドアウトバウンドに分けられています。
インバウンド: 外部からインスタンスへの通信を許可(外側→インスタンス)
アウトバウンド: インスタンスから外部への通信許可(インスタンス→外側)

初心者がAWSを扱う上で一番エラーが発生しやすい部分がこのセキュリティグループだと思います。

複数のサービスを紐づけるためにセキュリティグループは必須なのでよく理解しておきましょう。

RDS(Relational Datebase Service)

image.png

参照:VPC の DB インスタンスにアクセスするシナリオ(公式)

RDSはクラウド上でリレーショナルデータベース(RDBMS)を利用できるサービスです。

AWS上でRDBMSを利用する方法は2種類あります。(重要

1. EC2インスタンスにRDBMSをインストールする方法
2. RDSを利用する方法

RDSではデータベース用のインスタンス(仮想サーバー)が作成され、その上にOSやデータベースエンジンが構築されます。そのため、利用者はサーバやミドルウェアのメンテナンスが不要となります。

基本的にはRDSを利用した方が構築・運用のコスト削減できるため、RDBMSを運用する場合はRDSを選択されることが多いです。

まとめ

かなりざっくりですがAWSに関する知識をまとめました。

以下の記事を参考にしながら実際にAWSを触れば、詳細についての理解も深まると思います。

参照

世界一丁寧なAWS解説。EC2を利用して、RailsアプリをAWSにあげるまで

【画像付きで丁寧に解説】AWS(EC2)にRailsアプリをイチから上げる方法【その1〜ネットワーク,RDS環境設定編〜】

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

Rails 画像投稿機能作成

概要

※自分用メモです。
carrierwave,miimagickを使って画像投稿機能を作成手順について説明します。

使用イメージ

Dockerfile
FROM ruby:2.5
RUN apt-get update -qq && apt-get install -y \
    build-essential \
    libpq-dev \
    node.js \
    postgresql-client \
    yarn \
    vim \

【参考】
以下の記事を参考にしました。

Rails 画像アップロード機能の実装方法 メモ
  https://qiita.com/STHEXA/items/05a492bd9cf4cf31ba98

【Rails】CarrierwaveとMiniMagickを使って画像を投稿する方法
  https://techtechmedia.com/carrierwave-minimagick-image/

Gemの紹介

・Carrierwave

画像をアップロードするためのgem

・Minimagick

画像を加工してくれるgem

※MiniMagickを使うにはImageMagickというものをインストールする必要があるので注意しましょう。

ImageMagickは、画像処理ライブラリです。

・画像サイズ変更
・画像反転
・画像回転
・画像フォーマット変換
・色調整
・グラデーション

といった様々な画像処理を行うことができます。

1.Dockerfile編集

参考にした記事によるとCarrierwaveの導入には「ImageMagick」がインストールされている必要があるみたいでしたので
DockerfileにImageMagickをインストールする記述を追記します。

Dockerfile
FROM ruby:2.5
RUN apt-get update -qq && apt-get install -y \
    build-essential \
    libpq-dev \
    node.js \
    postgresql-client \
    yarn \
    vim \
    imagemagick     #←追記

2.Gemfile編集

GemfileにCarrierwaveとMinimagickを追記します

gemfile
gem 'carrierwave'
gem 'mini_magick'

3.docker-compose build 及び bundle installを実行

※dockerfileにRUN bundle installを記述してあればdocker-compose buildを行えば
gemもインストールすることができます。

$ docker-compose build 

4.アップローダーの作成

carrierwaveを利用するためのアップローダーを作成します。
※carrierwaveを導入したことで以下のコマンドが使えるようになります。

$ docker-compose exec web rails g uploader image 

もしくは

$ docker-compose exec web bash
$ rails g uploader image

上記のどちらかのコマンドで作成できます。

出力結果
# rails g uploader image
      create  app/uploaders/image_uploader.rb

5.画像アップロードのフォームを作成(モデル,ビュー,コントローラー)

・モデル編集

app/models/post.rb
class Post < ApplicationRecord
    mount_uploader :image, ImageUploader
end

・コントローラー編集

app/controllers/posts_controller.rb
  def create
    @post = Post.new(post_params)
  end

  private

  def post_params
    params.require(:post).permit(:image)
  end

・ビュー編集

app/views/posts/new.html.erb
<%= form_with model: @post, local: true do |f| %>

    <div class="form-group">
        <%= f.label :image, '画像' %>
        <%= f.file_field :image, accept: 'image/jpeg,image/gif,image/png' %>
    </div>
<%end%>

スクリーンショット 2020-08-11 14.16.11.png

※ここで以下のようなエラーが出ました。
uninitialized constant Post::ImageUploader

コンテナを一旦落として再度コンテナを立ち上げ直すと解消されました。

いろいろ調べていると再起動すればうまくいく?
とのことが書かれていたので試してみたところうまくいきました。

6.アップロードされたときのサイズを設定

標準でアップロードされると大きいサイズの画像がアップロードされるのでサイズを変えるように設定します。

app/uploaders/image_uploader.rbに設定していきます。

app/uploaders/image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
  # Include RMagick or MiniMagick support:
  # include CarrierWave::RMagick
  # ↓コメントアウト外す。
  include CarrierWave::MiniMagick

  # アップロードできるファイルの種類を制限
  def extension_whitelist
    %w(jpg jpeg gif png)
  end

  # アップロードするサイズの制限
  process resize_to_fit: [700, 700]

  # サムネイルサイズの設定
  version :thumb do
    process resize_to_fill: [100, 100]
  end

補足

アップロードされた画像はどこへ保存されるの?

app/uploaders/image_uploader.rbに記述されています。

app/uploaders/image_uploader.rb
  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

これはuploadeの中に保存されるという意味です

publicの中にuploadeのフォルダが作成されて保存されます。

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

ポートフォリオ投稿型プラットフォーム Techfollioをリリースしました。

開発に至った経緯

プログラミングスクールが流行り駆け出しエンジニアは増えてきているはずなのにポートフォリオが少ないのが不思議に思いました。転職でもポートフォリオは求められることが多いですしね。それをまとめたプラットフォームを作れば見た人がモチベーションも高まるし面白そうだと思い開発に至ります。

Techfollio

PCサイズ

スクリーンショット 2020-08-09 19.25.10.png

スマートフォンサイズ

IMG_3246.jpg

開発環境

ruby 2.6.3
rails 5.0.7.2
capistrano 3.11.1
nginx 1.16.1

機能紹介

Techfollioはまだ開発最中ですが現時点での機能を紹介します。(随時更新します)

ログイン機能

ログイン説明.gif

gem deviseを使用
ログインしていなくてもみんなのポートフォリオを見ることができます。
当たり前ですが、ログインしているユーザーのみポートフォリオ作成,編集、削除可能。

メール認証にはSendgridを利用しており、登録したメールアドレスに確認メールが届きます。
スクリーンショット 2020-08-10 20.10.22.png

Twitter連携機能

Twitter連携.gif

コメント機能

ポートフォリオにコメントを書くことができます。(ログインしないとコメントできません)
スクリーンショット 2020-08-11 0.20.37.png

ポートフォリオ検索

プログラミング言語ごとポートフォリオを見やすくするために検索機能を実装しました。
スクリーンショット 2020-08-11 0.34.08.png
スクリーンショット 2020-08-11 0.35.21.png

SNSシェア

ポートフォリオをFacebook,Line,Twitterにシェアできます。
sns ss1.png
sns ss2.png
sns ss3.png

最後に

より多くの人に利用してもらうために今後も改善続けていきます。
ポートフォリオを投稿して繋がろう!
Techfollio

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

RailsのRoutesをcsvで出力

何を書いた記事か

Railsのroutes一覧をcsv形式で出力する方法

→ 新しい組織でシステムキャッチアップする際、ルーティングから辿って全体を俯瞰したい、その情報を体系的にSpreadSheetなどにまとめたい場合に利用できる

どうやるか

参考になるソース

railsのroutesの実装を参照
https://github.com/rails/rails/blob/5ccdd0bb6d1262a670645ddf3a9e334be4545dac/railties/lib/rails/tasks/routes.rake
- inspector に全てのRoute情報を詰めて、 .format() で整形すれば良さそう
- ConsoleFormatter の実装を真似て独自のFormatterを作れば良さそう

実践

適当なブランチを切る

  • 個人的な理解のために出力する情報なので、本番ソースに影響ださないよう、ローカルで適当なブランチを切る

taskを作成

$ bundle exec rails g task route_formatter csv
lib/tasks/route_formatter.rake
namespace :route_formatter do
  desc "get route as csv format"
  task csv: :environment do |t|
    class CSVFormatter
      def initialize
        @buffer= []
      end

      def result
        @buffer.join("\n")
      end

      def section_title(title)
      end

      def section(routes)
        routes.each do |r|
          @buffer << [r[:name], r[:verb], r[:path], r[:reqs]].join(",")
        end
      end

      def header(routes)
        @buffer << %w"Prefix Verb URI_Pattern Controller#Action".join(",")
      end

      def no_routes
        @buffer << ""
      end
    end
    require "action_dispatch/routing/inspector"
    all_routes = Rails.application.routes.routes
    inspector = ActionDispatch::Routing::RoutesInspector.new(all_routes)
    puts inspector.format(CSVFormatter.new, ENV['CONTROLLER'])
  end

end

実行

bin/rails route_formatter:csv
  • MacでClipboardに入れたい場合は下記
bin/rails route_formatter:csv | pbcopy
  • あとはExcelとかSpreadSheetで処理

参考リンク

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

【Rails5】gem gonの使い方 ~RailsからJSに変数を渡す方法~

実装した機能

開発環境

ruby > 2.6.5
rails > 5.2.4.2
gon > 6.3.2

今回はシンプルに、RailsのControllerで定義した変数をJSに渡してhtmlに表示します。
Railsに標準搭載されているhoge.js.erb形式やcoffeescriptで書いたほうがスムーズだったりしますが、うまく表示できないときの回避策としても使えるGemかなと思ったのでご紹介します。

※注意
Gemの仕様上、htmlソース内に変数自体が表示されるので、セキュリティ的に隠す必要があるものには使わないほうが良いです。

導入方法

まずは、Gemfileに追加

Gemfile
gem 'gon'

そして、インストール

Terminal
$ bundle install

RailsのView内にコードを記述
今回は全ページで使う想定ですが、必要なviewに記述されていればOKです。
JSを読み込むのと同じく、読み込みのタイミングによって挙動が異なるので読み込み順には注意しましょう。

application.html.erb
・・・
<%= include_gon %>
<%= javascript_include_tag "application" %>
・・・
</body>

これで準備完了です。

変数を渡す

RailsのControllerでGon用の変数を定義します。
といっても、変数の頭にgon.をつけるだけです。

Users.erb
def show
  @user = User.find(1)
  gon.username = @user.name #これをJSに渡します
end

JSに変数を渡してhtmlに表示します。

index.html.erb
・・・
<p id="name"></p>
・・・
application.js
・・・
let name = gon.username
$('#name').html(name);
・・・

このように表示されればOKです。

index.html
・・・
<p id="name">こうへい</p>
・・・

html内の表示について

冒頭で「html内に変数が見えちゃう」という話をしましたが
実際にはこのように読み込まれています。
良くも悪くもわかりやすい構造になっていますね笑

index.html
・・・
  <script>
    //<![CDATA[
      window.gon={};gon.username="こうへい";
    //]]>
  </script>
</body>
・・・

その他機能

単純に変数を渡す以外にも

  • 配列、ハッシュを渡す
  • 変数をすべて読み込む

などの使い方もできるようです。
むしろ、Gemの役割としてはこっちのほうが大きいのかもしれませんね。
詳しくは公式ページをご確認ください。

Gon公式
https://github.com/gazay/gon

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

[Rails]コントローラでUserAgentを取得する

UserAgentとは?

UserAgentとはHTTPリクエストヘッダーに含まれるWEBの使用環境に関する情報です。
ブラウザの種類、ブラウザのバージョン、端末のOSの種類などの情報が含まれています。

HTTPリクエストヘッダーとは?

HTTPリクエストヘッダーとはWEBサイトにアクセスされた時にブラウザからWEBサイト側に送られるリクエスト情報と一緒に送られる付加的な情報です。
UserAgent、Referer(リンク元URL)、Authorization(認証情報)などの情報があります。

RailsにおけるUserAgentの取得方法

Railsのコントローラにはrequestオブジェクトを指すアクセサメソッドが用意されており、
それを利用することでコントローラにおいて、以下のように簡単にUserAgentを取得することができます。

request.user_agent

UserAgent以外にも色々と取得できるrequestオブジェクト

requestオブジェクトを利用すると以下のようにリクエスト情報を簡単に取得することができます。

request.url           # リクエストで使われるURL全体
request.remote_ip     # クライアントのIPアドレス
request.query_string  # URLのクエリ文字


(参考)
Railsガイド:requestオブジェクトとresponseオブジェクト

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

【Rails6】Devise + OmniAuthでGitHub認証を実装する【初学者向けチュートリアル】

はじめに

Omniauthを勉強してRails何もわからない? 状態になりましたw Rails最初はとても分かりやすかったのですが、OmniAuthがとても難しくてかなり苦労しました。やっと理解できたので、過去の自分が見たら泣いて喜ぶくらいにまとめます!!なおこの記事ではomniauthをメインに解説するのでdeviseの説明は少なめです。

アプリを作る

0からアプリを作って説明します。

ターミナル
➜ rails new report_app
➜ rails g scaffold report title:string content:text
➜ rails db:migrate

➜ rails server
http://localhost:3000/reports にアクセスしてレポートの保存や削除をできることを確認してみましょう。

Deviseを導入する

1. Gemfileに追記してbundle install

Gemfile
gem 'devise'

2. Deviseの設定

ターミナル
➜ rails generate devise:install

するとこんなメッセージが出ます。この手順にしたがって設定していきます。このようなメッセージは超重要なので英語だからといって逃げてはダメです。

===============================================================================

Depending on your application's configuration some manual setup may be required:

  1. Ensure you have defined default url options in your environments files. Here
     is an example of default_url_options appropriate for a development environment
     in config/environments/development.rb:

       config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

     In production, :host should be set to the actual host of your application.

     * Required for all applications. *

  2. Ensure you have defined root_url to *something* in your config/routes.rb.
     For example:

       root to: "home#index"

     * Not required for API-only Applications *

  3. Ensure you have flash messages in app/views/layouts/application.html.erb.
     For example:

       <p class="notice"><%= notice %></p>
       <p class="alert"><%= alert %></p>

     * Not required for API-only Applications *

  4. You can copy Devise views (for customization) to your app by running:

       rails g devise:views

     * Not required *

===============================================================================

1. デフォルトURLの設定

どこに書いてもいいと思いますが自分は最後に書きました。(endの前です)

config/environments/development.rb
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

2. ルートパスの指定

どこでもいいですが今回は/reportsをルートにします。

config/routes.rb
Rails.application.routes.draw do
  resources :reports
  root to: "reports#index"
end

3. フラッシュメッセージの作成

Viewを編集の時にやります。

4. Viewを作成

これをしないとビューをカスタマイズできないのでこれはやります。

ターミナル
➜ rails g devise:views

Deviseの機能が使えるUserモデルを作る

Deviseの機能が使えるUserモデルを作成します。すでにUserモデルがある時はdevise導入時、既にUserモデルがある場合の対応を参考にしてみてください。

ターミナル
➜ rails g devise User
➜ rails db:migrate

ルーティングを確認する

ルーティングを確認しましょう。devise_for :usersが追加されています。

config/routes.rb
Rails.application.routes.draw do
  devise_for :users
  resources :reports
  root to: "reports#index"
end

このdevise_for :usersによって以下のルートが追加されます。これはとても大事なので自分で適当なURLを入力してRouting Errorを発生させてじっくりと見てみてください。

image.png

注目すべきはコントローラーとアクションです。(ここを理解できないとomniauthで詰みます!)

例えば一番上のパスを見てみましょう。

| GET | /users/sign_in(.:format) | devise/sessions#new とありますがこのdevise/sessionsコントローラのnewアクションはどこにあるのでしょうか??

答えはhttps://github.com/heartcombo/devise/blob/master/app/controllers/devise/sessions_controller.rb です。下の画像の一番上にある階層を確認してください。app/controllers/devise/sessions_controller.rbですね!!

image.png

omniauthを使う時はこのGemのコントローラーをカスタマイズしなければいけません。ここが超難しいのですが、とりあえず前に進みましょう。

Viewを編集

application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>ReportApp</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <!-- 追加 -->
     <nav>
          <% if user_signed_in? %>
            <%= link_to 'プロフィール変更', edit_user_registration_path %>
            <%= link_to 'ログアウト', destroy_user_session_path, method: :delete %>
          <% else %>
            <%= link_to 'サインアップ', new_user_registration_path %>
            <%= link_to 'ログイン', new_user_session_path %>
          <% end %>
      </nav>
    </header>
    <p class="notice"><%= notice %></p>
    <p class="alert"><%= alert %></p>
    <!-- 追加 -->
    <%= yield %>
  </body>
</html>

サーバーを再起動してからhttp://localhost:3000/users/sign_up にアクセスしてユーザーを登録してログインしてみましょう。このように表示されればログイン成功です。なおreports/index.html.erbにもフラッシュを表示する記述があるのでフラッシュメッセージが2つ表示されています。

image.png

お疲れ様でした!これでログイン機能は完成です!

GitHubの設定

ここからomniauthの設定をしてきます。まずGitHubにログインしてください。そして右上のアイコンをクリックしてSettings → Developer settingsと進んでください。

Your_Profile.png

そしてOAuth Apps → New OAuth Appを押してください。

Developer_applications.png

最後にアプリ名、ホームページのURL、認証コールバックURLを入力してRegister applicationを押しましょう。Client ID と Client Secretをメモしておきましょう。(無くしてもGitHubのSettings/Developer settingsで見ることができます。)
アプリ名:任意
ホームページのURL:今回はローカルなのでhttp://localhost:3000/
認証コールバックURL:http://localhost:3000/users/auth/github/callback このURLは任意ですが、このURLにGitHubからユーザー名やメアドなどの情報が送られてきます。よって極めて重要です。このURLに対応するルーティングとコントローラーを自作する必要があります。

New_OAuth_Application.png

環境変数の設定

さっきのClient ID と Client SecretはGitHubにpushするとまずい情報です。なので環境変数に保存しておいてファイルから使います。

.zshrc
export GITHUB_ID=メモしたClient ID  
export GITHUB_SECRET=メモしたClient Secret  

これでRubyの文法でENV["GITHUB_ID"]のように自分のClient IDを参照することができます。
設定を読み込むためにターミナルを再起動しておきましょう。

irbpryENV["GITHUB_ID"]の中身を確認しておきましょう。ここが原因でハマることがあるので?

Gemとカラムの追加

Gemの追加

Gemfileにgem 'omniauth-github'を追記してbundle installしてください。

カラムの追加

Userモデルにuidproviderという名前のカラムを追加します。

ターミナル
→ rails g migration AddColumnsToUsers uid:string provider:string

作成されたマイグレーションファイルを開いてnot null制約とunique indexを追加しておきます。

db/migrate/xxxxxxxxxx_add_columns_to_users.rb
class AddColumnsToUsers < ActiveRecord::Migration[6.0]
  def change
    add_column :users, :uid, :string, null: false
    add_column :users, :provider, :string, null: false, default: ""
    # uidとproviderの組み合わせを一意にする
    add_index :users, [:uid, :provider], unique: ture
  end
end

マイグレーションファイルを適応します。

ターミナル
rails db:migrate

もしusersテーブルにデータが入っているとエラーになります。

StandardError: An error has occurred, this and all later migrations canceled:

SQLite3::ConstraintException: NOT NULL constraint failed: users.uid

その時はrails db:resetで全てのテーブルを削除し、"db/schema.rb"を元にテーブルの再作成を行います。先ほどのマイグレートがエラーになっているので、そのあともう一回rails db:migrateをする必要があります。

ターミナル
→ rails db:reset
→ rails db:migrate

Deviseの設定

deviseの設定ファイルにさっき環境変数に設定したGithubの情報を記述します。

270行目くらいにOmniAuthの設定を書きましょう。

config/initializers/devise.rb
  # ==> OmniAuth
  # Add a new OmniAuth provider. Check the wiki for more information on setting
  # up on your models and hooks.
  # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo'
  config.omniauth :github, ENV["GITHUB_ID"], ENV["GITHUB_SECRET"], scope: "user:email"

scope:には自作のアプリにGitHubの情報をどこまで与えるかを記述します。今回はGitHubからメアドと名前をもらいます。(他の情報もたくさん来ます)

詳しくはAvailable scopesを見てみてください。

Userモデルを編集

Userモデルにバリデーションをつけて、Deviseのモジュールを追加します。そしてGitHubからもらったデータでユーザー登録するメソッドを作ります。最後にGitHub以外でアカウント登録した人のuidを埋めるためのメソッドを作ります。

app/models/user.rb
class User < ApplicationRecord
  validates :email, presence: true, uniqueness: true
  # uidとproviderカラムの組み合わせを一意にする
  validates :uid, presence: true, uniqueness: { scope: :provider }

  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable,
         :omniauthable, omniauth_providers: [:github]

  # authの中身はGitHubから送られてくる大きなハッシュ。この中に名前やメアドなどが入っている。
  # providerカラムとuidカラムが送られてきたデータと一致するユーザーを探す。
  # もしユーザーが見つからない場合は新規作成する。
  def self.find_for_github_oauth(auth)
    where(provider: auth.provider, uid: auth.uid).first_or_create! do |user|
      # 名前を取得するときはこのように書く(今回はUserモデルにname属性がないのでエラーなる) 
      # user.name = auth.info.name
      user.email = auth.info.email
      # 任意の20文字の文字列を作成する
      user.password = Devise.friendly_token[0, 20]
    end
  end
  # 最後に使います。
  def self.create_unique_string
    SecureRandom.uuid
  end
end

ルーティングを変更する

ここから一気に難しくなります。ルーティングを確認するの理解はバッチリですか?
devise_for :usersと書くことでgemのコントローラーとアクションが設定されるんでしたね。

ルーティングを次のように変更します。これはdeviser_for :usersで決まるコントローラーを一部変更しています。つまりdevise/omniauth_callbacksコントローラーの代わりに、自作のusers/omniauth_callbacksコントローラーを使う。devise/registrationsコントローラの代わりに、自作のusers/registrationsコントローラーを使うということです。

ruoutes.rb
Rails.application.routes.draw do
  devise_for :users, controllers: {
    omniauth_callbacks: "users/omniauth_callbacks",
  }
  resources :reports
  root to: "reports#index"
end

変更前(右のコントローラーとアクションをみてください)

image.png

変更後(右のコントローラーとアクションをみてください)

image.png

自作のコントローラーを作る

けっこう長いですね? ですがもうちょっとです!

先ほどルーティングで設定したコントローラーを作っていきます。

app/controllers/users/omniauth_callbacks_controller.rb
# Deviseのコントローラーを継承することでDeviseのコントローラーと同じメソッド(つまりアクション)が使える
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def github
    # request.env["omniauth.auth"]にGitHubから送られてきたデータが入っている
    # binding.pryで確認してみましょう
    @user = User.find_for_github_oauth(request.env["omniauth.auth"])

    if @user.persisted? # データベースに保存されていればログイン成功
      sign_in_and_redirect @user, event: :authentication
      set_flash_message(:notice, :success, kind: "Github") if is_navigational_format?
    else # ログイン失敗
      session["devise.github_data"] = request.env["omniauth.auth"]
      redirect_to new_user_registration_url
    end
  end
end

一応ログイン成功!! だが・・・

やっとできました!!
image.png

ですがまだいろいろ問題があります?

問題その1. プロフィール編集ができない

例えばプロフィール編集に現在のパスワードが必要になっています。しかし現在のパスワードはGitHubのパスワードではなくこのメソッドで作ったランダムのパスワードです。つまりGitHubでログインしたユーザーはプロフィール編集できない仕様になっています。

app/models/user.rb
  def self.find_for_github_oauth(auth)
    where(provider: auth.provider, uid: auth.uid).first_or_create! do |user|
      user.email = auth.info.email
      user.password = Devise.friendly_token[0, 20]
    end
  end

image.png

問題その2. GitHubを使わないアカウント登録ができない

db/schema.rbを見てください。t.string "uid", null: falseとあると思います。uidカラムにはGitHubからのデータが格納されるのですが、GitHubを使わない場合このuidカラムが埋まりません。ですのでここを埋める必要があります。

image.png

原因

なぜこのような問題が発生するのでしょうか? 読み進める前に自分で考えてみましょう。この記事の中(今まで読んだ部分)に答えは書いてあります。自分の頭で考えて死ぬほど苦労することが重要です!


はい。答え合わせをします。答えはGem(Devise)のコントローラーをそのまま使っているからです。コントローラーとアクションを見てみてください。
image.png

解決策

二つの問題をまとめて解決します!Gemのコントローラーのアクションを少しカスタマイズしてあげれいいです。モンキーパッチってやつですね。まず本家のユーザー登録・編集をするためのコントローラーを見てみましょう。

https://github.com/heartcombo/devise/blob/master/app/controllers/devise/registrations_controller.rb

app/controllers/devise/registrations_controller.rb
class Devise::RegistrationsController < DeviseController
  prepend_before_action :require_no_authentication, only: [:new, :create, :cancel]
  prepend_before_action :authenticate_scope!, only: [:edit, :update, :destroy]
  prepend_before_action :set_minimum_password_length, only: [:new, :edit]

  # GET /resource/sign_up
  def new
    build_resource
    yield resource if block_given?
    respond_with resource
  end

  # POST /resource
  def create
    build_resource(sign_up_params)

    resource.save
      yield resource if block_given?
      if resource.persisted?
        if resource.active_for_authentication?
          set_flash_message! :notice, :signed_up
          sign_up(resource_name, resource)
          respond_with resource, location: after_sign_up_path_for(resource)
        else
          set_flash_message! :notice, :"signed_up_but_#{resource.inactive_message}"
          expire_data_after_sign_in!
          respond_with resource, location: after_inactive_sign_up_path_for(resource)
        end
      else
        clean_up_passwords resource
        set_minimum_password_length
        respond_with resource
      end
    end

    def update
      self.resource = resource_class.to_adapter.get!(send(:"current_#{resource_name}").to_key)
      prev_unconfirmed_email = resource.unconfirmed_email if resource.respond_to?(:unconfirmed_email)

      resource_updated = update_resource(resource, account_update_params)
      yield resource if block_given?
      if resource_updated
        set_flash_message_for_update(resource, prev_unconfirmed_email)
        bypass_sign_in resource, scope: resource_name if sign_in_after_change_password?

        respond_with resource, location: after_update_path_for(resource)
     else
       clean_up_passwords resource
       set_minimum_password_length
       respond_with resource
     end
   end

   def update_resource(resource, params) 
    resource.update_with_password(params) 
   end 

   # Build a devise resource passing in the session. Useful to move 
   # temporary session data to the newly created user. 
   def build_resource(hash = {}) 
     self.resource = resource_class.new_with_session(hash, session)
   end 

このコントローラーを継承したコントローラーを自作します。そして2つのメソッドをオーバーライドします。
users/配下に新規コントローラーを作成してください。

app/controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
  def build_resource(hash = {})
    # 自作したメソッドを使いuidを必ず埋める
    hash[:uid] = User.create_unique_string
    super
  end

  protected
    def update_resource(resource, params)
      return super if params["password"]&.present?
      # 現在のパスワードなしでアカウントの更新をする
      resource.update_without_password(params.except("current_password"))
    end
end

最後に今作ったコントローラーを使えるようにルーティングを変更しましょう!

routes.rb
Rails.application.routes.draw do
  devise_for :users, controllers: {
    omniauth_callbacks: "users/omniauth_callbacks",
    registrations: "users/registrations"
  }
  resources :reports
  root to: "reports#index"
end

ようやくできた!!

メアドとパスワードでのログイン成功!

image.png

パスワードなしでアカウント編集成功!

image.png

おわりに

お疲れ様でした! 最初は一般的なログイン機能(ログインしていないと投稿できない、自分の投稿のみ削除や編集ができるなど)もやるつもりだったのですが、そこまでの時間を裂くことができませんでした? とはいえそれらの情報はたくさんあるので問題ないかと思います。

omniauthは自分にとってRails最強の敵でした。ユーザーフォロー、コメント機能など一般的な機能はググれば出てきたのですがomniauthはググってもほとんどヒットせず、かなり大変でした。正確にはヒットしてもコードが書いてあるだけで何をやっているのかサッパリ分かりませんでした。この記事でomniauthを理解できた人が一人でもいれば嬉しいです?

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

Railsアプリケーションの立ち上げ方【初学者向け】

新しいRailsアプリケーションを作成

以下のコマンドを実行して、新しいRailsアプリケーション「My App」を作成しましょう。
※もちろん名前は「My App」じゃなくてもいい。

# projectsディレクトリの作成
$ mkdir ~/projects

# projectsディレクトリに移動
$ cd ~/projects

# Railsのバージョン5.2.3を用いて、「my_app」を「-d」オプションでMySQLを指定して作成。
$ rails _5.2.3_ new my_app -d mysql

# 「my_app」ディレクトリに移動
$ cd my_app

# 現在のディレクトリのパスを表示
$ pwd

上記の操作を行って、/Users/ユーザー名/projects/my_appと表示されれば一旦は成功です。

:warning:最後にエラーが出る場合
もし、アプリケーション作成段階でエラーが生じた場合は、以下のような赤字の文章が表示されます(必ずしも同じ文面ではありません)。
【例】

Installing mysql2 0.5.3 with native extensions

Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

current directory: /Users/user_name/Programs/web/foobar-repo/vendor/bundle/ruby/2.5.1/gems/mysql2-0.5.3/ext/mysql2

/Users/user_name/.rbenv/versions/2.5.1/bin/ruby -r

(中略)

An error occurred while installing mysql2 (0.5.3), and Bundler cannot continue.
Make sure that `gem install mysql2 -v '0.5.3'` succeeds before bundling.

エラーが表示されている場合は、以下のコマンドを実行してください。

$ bundle config --delete build.mysql2
$ bundle config --global build.mysql2 --with-opt-dir="$(brew --prefix openssl)"

$ cd ~/projects/my_app
$ bundle install

正しく関連ファイルが読み込まれているか確認

以下のコマンドを実行して、関連ファイルが読み込まれているか確認しましょう。

# Users/ユーザー名/projects/my_appと表示されることを確認する
$ pwd

# 関連ファイルが読み込まれていることを確認する
$ bundle install

データベースを作成しましょう

# データベースを作成
$ rails db:create

# 以下のような表示がされれば成功
Created database 'my_app_development'
Created database 'my_app_test'

サーバーを立ち上げて確認しましょう

正しくアプリケーションが作成できたか、サーバーを立ち上げて確認しましょう。使用するコマンドは、rails sコマンドです。以下の通りに実行しましょう。

# サーバーの起動
$ rails s

http://localhost:3000 にアクセスして、Railsのデフォルト画面が表示されれば成功です。
:warning:サーバーを終了させる際はcontrol+c

テキストエディタ(VS Code)を開いて、comand+oで作成したファイルを開いてコードを書きはじめましょう!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

bundle install でmysql2がインストールされない

はじめに

僕のローカル環境で毎回エラーになるので解決策をメモします
*まだ厳密な原因はわかってませんが、とりあえずこれで通りました程度の内容です

$ bundle install --path vendor/bundle 
...
エラー文↓
An error occurred while installing mysql2 (0.5.3), and Bundler cannot continue.

解決方法

$ bundle config --local build.mysql2 "--with-ldflags=-L/usr/local/opt/openssl/lib"
$ bundle install --path vendor/bundle

上記コマンドを実行すると、.bundle/configに下記一行が追加されます

BUNDLE_BUILD__MYSQL2: "--with-ldflags=-L/usr/local/opt/openssl/lib"

とおるようになる。

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

Rails Tutorialを咀嚼する【第1章ゼロからデプロイまで】

1.1 はじめに

■REST
SQLでよく使う「Create/Read/Update/Delete」をそれぞれ「GET/POST/PUT/DELETE」に当てはめたもの。セッションなどの状態管理を行わないため、命令をいちいち書いていく必要がある。
基本的には「route.rb」に記載する。

■コマンドライン
ターミナル。クラウドIDEに標準的に備わっている。

1.2 さっそく動かす

■Cloud9
AWSが提供しているクラウド上で開発可能な統合開発環境。面倒な環境設定もいらないため超便利

■gem
パッチファイル?追加要素?みたいなもの。「Gemfile」というファイルに記載して、bundle installとターミナルに打ち込むと自動でインストールしてくれる。

1.3 最初のアプリケーション

■rails new
Railsを使ってアプリケーションを作る際に初めに書く呪文。必要なファイルを自動で生成してくれる。

■フレームワーク
Railsがフレームワーク。エディタがあれば自由にプログラミングできるが、将来的にめちゃくちゃな書き方になってしまうため、「ある程度書き方を揃えましょうね」というもの。
Rubyで書くと何十行も掛かる処理をRailsを使えば独自の短い表現で同じ処理ができる。まだ実感値はない。

■gameごとのバージョン
バージョンが違うと挙動も違う可能性があるので、バージョンを固定させて開発をした方が良い。
spring 2.0.2がインストールできないときは「bundle update spring」を打ってから再度インストール。

【演習】
railsサーバーを立ち上げれた先に書いてある。

■MVCモデル
どこにどのファイルを置くのかを定義した概念。本来どこに何を置いてもいいが、後々管理が面倒になるため、ある程度統一性をもって書こうというもの。
どこに何を書くべきかという実感値はまだない。

■renderメソッド
レスポンスの出力をするメソッド。コントローラーでもビューでも使う。

■メソッド
関数。defとendの間に挟んで使う。
呼び出すときは定義したメソッド名を書く。

■routes.rb
どのリクエストにはどのページを返すか、を記載する。
基本的にはコントローラーを記載するのかな?

■1.3.4 Hello, world!に書き換える
最初の「Yay!You're on Rails!」をHello, world!に書き換える。
今回は大した内容じゃないので恐らくビューはいじらない。
「hello, world!」というHTMLを表示する、というrenderメソッドをコンロトーラーで定義して、
rootをhelloメソッドにする。

【演習】
1.文面を変えればOK
2.Windowsだから表示の仕方が分からないため跳ばす。
3.
def goodbye
render html: "goodbye, world!"

end

Rails.application.routes.draw do
root 'application#goodbye'
end

こんな感じ。

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

Rails Tutorialを咀嚼する【第1章ゼロからデプロイまで】前半

1.1 はじめに

■REST
SQLでよく使う「Create/Read/Update/Delete」をそれぞれ「GET/POST/PUT/DELETE」に当てはめたもの。セッションなどの状態管理を行わないため、命令をいちいち書いていく必要がある。
基本的には「route.rb」に記載する。

■コマンドライン
ターミナル。クラウドIDEに標準的に備わっている。

1.2 さっそく動かす

■Cloud9
AWSが提供しているクラウド上で開発可能な統合開発環境。面倒な環境設定もいらないため超便利

■gem
パッチファイル?追加要素?みたいなもの。「Gemfile」というファイルに記載して、bundle installとターミナルに打ち込むと自動でインストールしてくれる。

1.3 最初のアプリケーション

■rails new
Railsを使ってアプリケーションを作る際に初めに書く呪文。必要なファイルを自動で生成してくれる。

■フレームワーク
Railsがフレームワーク。エディタがあれば自由にプログラミングできるが、将来的にめちゃくちゃな書き方になってしまうため、「ある程度書き方を揃えましょうね」というもの。
Rubyで書くと何十行も掛かる処理をRailsを使えば独自の短い表現で同じ処理ができる。まだ実感値はない。

■gameごとのバージョン
バージョンが違うと挙動も違う可能性があるので、バージョンを固定させて開発をした方が良い。
spring 2.0.2がインストールできないときは「bundle update spring」を打ってから再度インストール。

【演習】
railsサーバーを立ち上げれた先に書いてある。

■MVCモデル
どこにどのファイルを置くのかを定義した概念。本来どこに何を置いてもいいが、後々管理が面倒になるため、ある程度統一性をもって書こうというもの。
どこに何を書くべきかという実感値はまだない。

■renderメソッド
レスポンスの出力をするメソッド。コントローラーでもビューでも使う。

■メソッド
関数。defとendの間に挟んで使う。
呼び出すときは定義したメソッド名を書く。

■routes.rb
どのリクエストにはどのページを返すか、を記載する。
基本的にはコントローラーを記載するのかな?

■1.3.4 Hello, world!に書き換える
最初の「Yay!You're on Rails!」をHello, world!に書き換える。
今回は大した内容じゃないので恐らくビューはいじらない。
「hello, world!」というHTMLを表示する、というrenderメソッドをコンロトーラーで定義して、
rootをhelloメソッドにする。

【演習】
1.文面を変えればOK
2.Windowsだから表示の仕方が分からないため跳ばす。
3.

app/controllers/application_controller.rb
def goodbye
    render html: "goodbye, world!"
end
config/routes.rb
Rails.application.routes.draw do
  root 'application#goodbye'
end

こんな感じ。

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

[devise カスタマイズ]ユーザー登録完了前に(仮登録の状態で)他のカラムを追加させる

[devise]ユーザー登録完了前に(仮登録の状態で)他のカラムを追加させる

はじめに

ruby on railsのユーザー周り(会員登録など)のgemといえばdeviseですかね・・?
一瞬で会員登録とかができるのは物凄い便利です。

そんな便利な反面、カスタマイズするのにはかなり時間がかかります。
(今回もかなり時間がかかりました。)

今回実装するのは、以下のような機能です。

# 実装したい内容
会員登録(emailpassword

認証メール送信

メールのリンクからプロフィール(今回はname)を追加するページへ

プロフィールを入力して確認画面へ

確認画面で送信

会員登録完了(ログイン画面へ)

イメージ
hoge.gif

今回のissueは大きく二つ

  • 会員登録を仮登録の状態で編集する
  • 変更時に編集内容を確認するページを挟む

開発環境

- rails (6.0.3.2)
- ruby 2.6.5p114
- devise (4.7.2)
- letter_opener (1.7.0)

- mac Catalina 10.15.6

セットアップ

前提として、以下の状態を想定しています

  • deviseがインストールされている
  • deviseのview,controller,modelが作成されている
  • Useモデルが存在する(カラムはデフォルトにnameを追加)
  • letter openerをgemにインストールしている(初期設定している)
  • メール認証をtrueに変更している

使い方

今回のissueは大きく二つ

  • 会員登録を仮登録の状態で編集する => (解決策) confirmation_tokenを保持して、最後にconfirmation_pathに引数として与える
  • 変更時に編集内容を確認するページを挟む => (解決策)hidden_fieldを入れることでデータを保持する

1つ目のissueの肝は、[confirmation_token]
仮登録完了時に送られるメールをみるとわかりやすい

/app/views/devise/mailer/confirmation_instructions.html.erb
<p>Welcome <%= @email %>!</p>

<p>You can confirm your account email through the link below:</p>

<p><%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %></p>

なんか、認証トークンとuserのデータを引数に持ってconfirmation_urlに入ったら仮登録→本登録完了になるらしい
つまり、
これをやらずにプロフィールを編集して、その後認証トークンを与えてconfirmation_tokenにアクセスして完了すればいい。










それでは実際にやってみる

① ルーティングを設定

まず、追加するアクションは三つ。そのルーティングを行う。

今回はこの三つ。カッコの中身がアクション名(センスなくてすみません・・・)
[入力、確認、更新]
- 入力:プロフィール入力アクション(before_create)
- 確認:入力内容確認アクション(before_confirm)
- 更新:入力内容で更新するアクション(before_update)

config/routes.rb
Rails.application.routes.draw do

  devise_for :users, :controllers => {
    # コントローラーを見に行くようになる
    # 無しだと、そもそも見に行かずにデフォルトが実行される
    # (結論:deviseのコントローラーを変更したら記述が必須)
    :registrations => 'users/registrations',
    :sessions => 'users/sessions',
    :confirmations => 'users/confirmations'
  }

  devise_scope :user do
  # ルーティングを指定
  # アクションが追加されたらそのルーティングを指定する

    get "sign_in", :to => "users/sessions#new"
    get "sign_out", :to => "users/sessions#destroy"

    #プロフィール編集画面(仮登録状態)
    get "before_sign_up", :to => "users/registrations#before_create"
    #プロフィール編集内容確認画面(仮登録状態)
    post "before_sign_up_confirm", :to => "users/registrations#before_confirm"
    #プロフィール編集内容のアップデート処理(仮登録→本登録に)
    post "before_sign_up", :to => "users/registrations#before_update"

  end

  # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
  root 'pages#index'
  get 'pages/show'
end

大きくやったことは二つ

  • device_forでdeviseのcotrollerをカスタマイズ可能に
  • devise_scoopeで新しいアクションのパスを指定

② プロフィール編集アクションを追加(入力、確認、更新の三つ)

続いて、アクションの追加

この三つ。カッコの中身がアクション名
- 入力:プロフィール入力アクション(before_create)
- 確認:入力内容確認アクション(before_confirm)
- 更新:入力内容で更新するアクション(before_update)

とりあえず、binding.pryで引数を見て回ったのと、superが表す内容をそれぞれ見て回った(こんなサイトで)



まずは

入力:プロフィール入力アクション(before_create)

registrations_controller.rb
  # 仮登録状態のプロフィール入力画面(メール認証後のアクション)
  def before_create
    # 引数のresourceを使ってユーザーを取得
    @user =  User.find(params["resource"])
    @token = params["confirmation_token"]
  end
before_create.html.erb
<h2>プロフィールを登録</h2>
<%= form_for(resource, as: resource_name, url: before_sign_up_confirm_path(resource_name, confirmation_token: @token) ,html: {method: "post"}) do |f| %>
  <%= render "devise/shared/error_messages", resource: resource %>
  <%= f.hidden_field :user_id, :value =>  resource.id %>
  <div class="field">
    <%= f.label :name %><br />
    <%= f.text_field :name, autofocus: true, autocomplete: "name" %>
  </div>
  <div class="field">
    <%= f.label :email %><br />
    <%= f.email_field :email, autofocus: true, autocomplete: "email",:readonly => true %>
  </div>
  <div class="actions">
    <%= f.submit "Sign up" %>
  </div>
<% end %>

<%= render "devise/shared/links" %>

デフォルトだったらparamsとかじゃなくてresourceで一発で呼べるんですけど、新しく追加したメソッドでresourceではとれない

余談
(厳密には、以下のような感じで取れるようにできるんですが、ログイン必須なので、仮登録では面倒そうだったので断念)
registrations_controller.rb
prepend_before_action :authenticate_scope! , only: [:before_create]を追加

(余談の出来事)ということもあり、地道に実装することに。

1- 実は仮登録で、newされてるらしいので、newせずに@userにとってくる
2- confirmation_tokenは持ち続けたいので一旦@token
3- viewのhidden_fieldでuserのidを保持(これしなくても、paramsから取れる)

確認:入力内容確認アクション(before_confirm)

registrations_controller.rb
  # 仮登録状態のプロフィール入力完了画面(プロフィール入力後のアクション)
  def before_confirm
    @user =  User.find(params["user"]["user_id"])
    @user.name = params["user"]["name"]
    @token = params["confirmation_token"]
    if @user.valid?
      render :action => 'before_confirm'
      flash.now[:success] = '確認して完了してください'
    else
     render :action => 'before_create'
     flash.now[:alert] = '失敗しました'
    end
  end
before_confirm.html.erb
<h2>確認画面</h2>
<%= form_for(@user, url: before_sign_up_path(@user, confirmation_token: @token),html: {method: "post"}) do |f| %>
  <%= render "devise/shared/error_messages", resource: @user %>
  <%= f.hidden_field :user_id, :value =>  @user.id %>
  <%= f.hidden_field :email, :value =>  @user.email %>
  <%= f.hidden_field :name, :value =>  @user.name %>

  <div class="field">
    <%= f.label :name %><br />
    <%= f.text_field :name, autofocus: true, autocomplete: "name",:readonly => true %>
  </div>

  <div class="field">
    <%= f.label :email %><br />
    <%= f.email_field :email, autofocus: true, autocomplete: "email",:readonly => true %>
  </div>
  <div class="actions">
    <%= f.submit "Sign up" %>
  </div>
<% end %>

<%= render "devise/shared/links" %>

ここでsaveしないんですね。
@userに入れてみて、validかinvalidかを調べてるだけです。次のupdateで再び呼び出してからsaveします(ここがかなり効率悪いですよね・・・)

更新:入力内容で更新するアクション(before_update)

registrations_controller.rb
  # 仮登録状態のプロフィール入力内容更新処理
  def before_update
    @user =  User.find(params["user"]["user_id"])
    @token = params["confirmation_token"]
    @user.save
    if @user.valid?
      # ここが肝!
      #  confirmation_pathにuserデータと認証トークンを付与することで本会員登録される
      redirect_to confirmation_path(@user, confirmation_token: @token)
      flash[:success] = '確認して完了してください'
    else
      render :action => 'before_create'
      flash.now[:alert] = '失敗しました'
    end
  end

③ mailのパスを変更

最後に、
送られてくるメールの遷移先をを変更します。
confirmation_url → before_sign_up_url
resourceもparamsで呼び出せるように追加 @resource → resource:@resource
(この辺深く理解してないのでミス多いかもです。すみません。)

app/views/devise/mailer/confirmation_instructions.html.erb
<p>Welcome <%= @email %>!</p>

<p>You can confirm your account email through the link below:</p>

<p><%= link_to 'Confirm my account', before_sign_up_url(resource:@resource, confirmation_token: @token) %></p>

デモ

ソースコード(github)

終わりに

gemってデフォルトで使う分にはすごい便利だけど、カスタマイズするにはgem自体をすごい理解しないといけないですよね。
特に初学者にはキツすぎる・・・

勉強不足で間違いなどあるかもしれませんが、その時は優しく指摘していただきたいです。
よろしくお願いします!

参考サイト

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

dockerの起動中に起きたエラー

railsアプリを立ち上げるためにdocker-compose buildを実行しようとすると次のエラーが出た。

$ docker-compose build
Building db
Step 1/4 : FROM mysql:8.0.17
 ---> b8fd9553f1f0
Step 2/4 : RUN apt-get update &&  apt-get install -y apt-utils
:
:
Fetching spring 2.0.2
Installing spring 2.0.2
Gem::Ext::BuildError: ERROR: Failed to build gem native extension.
  current directory: /usr/local/bundle/gems/nokogiri-1.10.9/ext/nokogiri
/usr/local/bin/ruby -r ./siteconf20200529-6-18ba5hy.rb extconf.rb
Cannot allocate memory - /usr/local/bin/ruby -r ./siteconf20200529-6-18ba5hy.rb
extconf.rb 2>&1
:
:
An error occurred while installing nokogiri (1.10.9), and Bundler cannot
continue.
Make sure that `gem install nokogiri -v '1.10.9' --source
'https://rubygems.org/'` succeeds before bundling.
:

nokogiri?
gemファイルに記述した覚えは無いが、、

エラー分にあるとおりnokogiriのインストールを試みたが、同様のエラーがでた。
$ gem install nokogiri -v '1.10.9'

調べると次の記事にあるように、容量不足によるエラーの可能性があるかもしれない。
https://qiita.com/HrsUed/items/c156ed69e927b6165717
記事通り容量を増加させたが、違うエラーが発生した。

$ docker-compose up -d
:
ERROR: Service 'web' failed to build: failed to register layer: Error processing tar file(exit status 1): write /usr/lib/x86_64-linux-gnu/libx265.so.165: no space left on device

エラーメッセージで調べると以下の記事が参考なった。
https://scrapbox.io/tsuchinaga/Docker%E3%81%A7%E5%AE%B9%E9%87%8F%E4%B8%8D%E8%B6%B3%E3%81%8C%E7%99%BA%E7%94%9F%E3%81%99%E3%82%8B
$ docker volume rm $(docker volume ls -qf dangling=true)

説明通りコマンドを実行するとbuildが成功した。

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

未経験エンジニアはこんなポートフォリオを作ってみてはどうか②〜PDF出力編〜

今回は前回同様に未経験エンジニアがポートフォリオを作るにあたってこんな機能を取り入れたらどうかなって思うものを一つ紹介したいと思います。

僕が実務を経験した中で

「あ、この技術は未経験のうちに習得できそうだな」

と思ったものを提案していくので、難易度的にもそんなに難しくはないはずです。

Youtubeに同じ内容の動画を掲載しているので興味がある人は観てください

未経験エンジニアはこんなポートフォリオを作ってみてはどうか②〜PDF出力編〜。
スクリーンショット 2020-08-11 0.59.08.png

あと、
【実体験をもとに】30歳未経験から独学4ヶ月でバックエンドエンジニアとしてWeb系自社開発企業へ転職するまでのロードマップ
という記事も書いてますので是非

では今回の内容はこんな感じ。

・機能紹介〜PDF出力〜
・意外とポートフォリオに取り入れている人は少ない
・なぜ取り入れておいた方が良いか
・導入するためには
・まとめ

◾️機能紹介〜PDF出力〜

今回はPDF出力ですね

これは何をするのかパッと聞いて分かるとは思うのですが、目的としてはシステムの持つデータをきれいにまとめてPDFファイルとして出力するという事です。

だいたい皆さん「PDFファイルで出力」って動作はしたことあると思います。
(めちゃくちゃメジャーな機能ですからね。)

◾️意外とポートフォリオに取り入れている人は少ない

僕の感覚ではあリますが、機能自体のメジャーさのわりに意外とポートフォリオに取り入れている人が少ない気がします。

何でそうなるのかっていうと、僕が見てきた限りですが多くの方が作成するポートフォリオって対個人を想定したアプリだからですね。

感覚的に7〜8割のポートフォリオが
「インスタグタムのようにユーザーが写真をつけて何かを投稿するとか個人が何かしら情報発信をする系」
って感じでした。

※投稿する何かっていうのはアプリの趣旨に依存しますが、例えば景色・動物・食べ物・趣味とかですね。

こういったアプリにおいてはあんまりPDF出力って必要にならないことが多いのでこの機能を追加していない人が多いと思います。

◾️なぜ取り入れておいた方が良いか

世の中には対企業を想定したアプリを開発する企業がたくさんあるわけです。例えば経費の申請アプリとかタスクの管理アプリもそうですね。
特にベンチャー企業とかはこういったアプリを積極的に使う傾向がありますし、僕の勤務先も使っています。

対企業を想定したアプリになってくると、こういったデータの吐き出し機能はほぼ必須級かなと個人的には思いますし絶対にあって損はない機能って感じです。

だいたいどこの会社でも「見積書」とか「報告用の資料」とかでシステム内のデータを使う事は多いですからね。

今後対企業向けアプリを開発する企業の選考を受ける可能性もあるので、そういったアプリでよく使用されるPDF出力機能はとりあえずポートフォリオに入れておこうって事です。

例えば自分が作ったポートフォリオがインスタグラムみたいなものなら、投稿された日にちとか付けられたタグとかでデータを集計してPDFで出力する機能をつけると良いのではないかな〜って思います。

「6/20(土) 投稿数100」
「未経験タグ:投稿数 20、エンジニアタグ:投稿数10」

とかですね。
先の話ですが、JsやjQueryを使ってグラフ化できれば完璧です。

いきなり企業向けを想定したポートフォリオを作るのもなかなかハードルが高いものだと思うので個人向けアプリに導入するくらいでいいと思います。

そんなポートフォリオを携えて面接の時に

「自分のポートフォリオにはあまり必要ない機能かとは思いましたが、エンジニアとして仕事をしていく上でPDF出力機能はいつか必ず必要になるかと思ったので学習目的で取り入れました」

なんて言えれば

「お、こいつはちゃんと先のことも考えてる」

って思ってもらえますし、あまりないとは思いますが仮に選考を受けた企業の開発ツールがまだブラッシュアップされていなくてPDF出力機能が必要なデータに対して完備されていなければ

「御社のツールのOOのデータを管理しやすくするため、僕が入社したらPDF機能つけます」

みたいなことを言えれば、それが必要と判断されるかどうかはともかく未経験エンジニアの口から出てくる内容として上等なんじゃないでしょうか。

◾️導入するためには

詳しい解説は行わないのですが、Ruby on Railsを使っている方であればwicked_pdfというgemを使用すればそこそこ簡単に導入できると思います。

このgemはインストール・初期設定・フォーマット用のHTMLを作成・controllerに4行くらい記述するくらいでとりあえず実装できます。
参考資料もたくさんあるので使い方はググればすぐ見つかるのも良いですね。

PDFを作成するだけではあんまり意味がないので、ダウンロードもできるよう設定するのも忘れないようにしましょう。
やり方はめちゃくちゃ簡単なので、Railsを使用されてる方なら「wicked_pdf  send_data」とかで調べればすぐ見つかると思います。

前に紹介したwheneverよりは難易度が上がりますが、未経験の方でも十分実装できると思うのでぜひ調べてみてください。

◾️まとめ

今回はポートフォリオに追加する機能としてPDF出力機能を紹介しました。
紹介したのはPDFでしたが、CSVで出力する機能を実装しても全然構わないと思います。
ただCSVの方がちょっと難易度が高いです。

未経験エンジニアのポートフォリオというのは選考で使われるものだと思いますので、追加する機能に関してはより相手にヒットしやすいものをチョイスした方がいいと思います。

いかに自分が興味を持って取り入れたものでも相手から
「へーそうなんだよくわかんないけどがんばったね乙」
と思われるのはもったいないですよね。

なので「今後自分がどんな会社の選考を受けることになってゆくのか」を考えて、その会社で使っていると思われる機能を自分のポートフォリオに盛り込むのが一番手っ取り早いんじゃないかっていうのが僕の見解です。

今後もそういった観点で僕なりにお勧めの機能を紹介していきたいと思います。

はい、ということで今回は以上です。

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