20210731のRubyに関する記事は15件です。

railsでGCP Natural Language APIを使ってクソアプリを作ってみた。

アプリの見た目 試しに「上京しました。」と入力して送信すると。 ↓ こんな感じのviewが帰ってきます。パーソナリティはポンコツbabyだそうです。  APIの利用手順 下記ページへ移動 「コンソールへ移動」をクリック *GCPへの登録をしていない方は、「無料で開始」というボタンが表示されるので、 手順に従って利用を開始してください。確かクレジットカードの登録が必要だったと思いますが、 有料サービスを使用しなければ基本無料で使用できます。 「APIとサービス」にカーソルを合わせて、ライブラリをクリックします。 検索フォームが出てくるので「Natural Language API」等で検索して、 「Cloud Natural Language API」をクリック 「有効にする」をクリックしAPIの使用を有効にします。 画面遷移ができれば有効化は完了です。 *左側の「割り当て」からAPIの発行回数を制限できます。心配な人は制限を付けておきましょう。 続いてAPIを発行します。 先ほどの手順でクリックしたライブラリの下に「認証情報」というリンクがあるので、クリックします。 「認証情報を作成」をクリックするとタブが出てくるので、「APIキー」をクリック。 APIキーが作成されます。作成されたキーを使用します。 APIキーは大事に扱ってください。github等のパブリックスペースに公開すると悪用される可能性があります。 (今回の画像で使用した僕のAPIキーは念のため削除済みです。) 「キーの制限」から使用するAPIを指定できます。 実装コード APIを使用している部分 ↓ post.rb def get_sentiment require 'net/http' require 'uri' require 'json' # textに入れた文章が評価されます。 text = self.post # ENV["GOOGlE_API_KYE"]に取得したAPIキーを入れます。 uri = URI.parse("https://language.googleapis.com/v1beta1/documents:analyzeSentiment?key=#{ENV["GOOGlE_API_KYE"]}") request = Net::HTTP::Post.new(uri) request.content_type = "application/json" request.body = "" request.body = { document:{ type:'PLAIN_TEXT', content: text } }.to_json req_options = { use_ssl: uri.scheme == "https", } response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http| http.request(request) end json = JSON.parse(response.body) # scoreに文章のポジティブ度が入ります。 score = json['documentSentiment']['score']*100 # magnitudeに文章の感情の強さみたいなものが入ります。 magnitude = json['documentSentiment']['magnitude']*100 sentiment = {score: score.to_i, magnitude: magnitude.to_i} end dotenv-rails等を使用して、ENV["GOOGlE_API_KYE"]に値を設定して使用。 最終的なコード  ↓ post.rb class Post < ApplicationRecord validates :post, presence: true, length: { minimum: 3, maximum: 20 } def set_sentiment_with_title sentiment = self.get_sentiment self.magnitude = sentiment[:magnitude] if sentiment[:score] > 50 title = ["ポンコツbaby", "ハッピー能天気バカ","so funy", "おしゃべりクソ野郎", "無駄話うそ太郎", "元気の押し売り", "言い訳クソメガネ", "ラッキー幸せ人間", "好感度おばけ"] self.sentiment = "ポジティブ" self.score = sentiment[:score] elsif sentiment[:score] > 0 title = ["おたんこなすび", "おまぬけ勘違いさん","おたんこなすび", "おしゃべりクソ野郎", "無駄話うそ太郎", "あほ", "言い訳クソメガネ", "クレイジー日本代表", "優しき偽善者"] self.sentiment = "ポジティブ" self.score = sentiment[:score] elsif sentiment[:score] >= -50 title = ["ポンコツbaby", "インテリくそ陰キャ","人間性皆無自己中人間", "おたんこなすび", "詐欺師", "ぴえん越えてぱおん", "だめだこりゃ", "元気出せよ?", "あほ"] self.sentiment = "ネガティブ" self.score = sentiment[:score]*-1 else title = ["ポンコツbaby", "インテリくそ陰キャ","人間性皆無自己中人間", "後ろ向きネガティブごみ人間", "ダイアモンド不愉快", "ぴえん越えてぱおん", "人間失格", "元気出せよ?", "堕天使"] self.sentiment = "ネガティブ" self.score = sentiment[:score]*-1 end self.personality_title = title[rand(10)-1] end def set_bournus return if self.random_int2 < 100 if self.personality2 == "香ばしさ" self.personality_title = "香ばしき漢" elsif self.personality2 == "神に愛され度" self.personality_title = "神に愛されし者" elsif self.personality2 == "尊さ" self.personality_title = "愛され地下アイドル" elsif self.personality2 == "足の臭さ" self.personality_title = "足クサおじさん" elsif self.personality2 == "強者の風格" self.personality_title = "範馬勇次郎" else self.personality_title = "パーリィーぴーぽー" end end def set_int_with_personality index1 = ["センス", "知性", "努力", "主人公度", "地頭力", "おもしろさ"] index2 = ["香ばしさ", "神に愛され度", "尊さ", "足の臭さ", "強者の風格", "人望"] self.random_int1 = rand(rand(200)) self.personality1 = index1[rand(7)-1] self.random_int2 = rand(rand(200)) self.personality2 = index2[rand(7)-1] end def get_sentiment require 'net/http' require 'uri' require 'json' text = self.post uri = URI.parse("https://language.googleapis.com/v1beta1/documents:analyzeSentiment?key=#{ENV["GOOGlE_API_KYE"]}") request = Net::HTTP::Post.new(uri) request.content_type = "application/json" request.body = "" request.body = { document:{ type:'PLAIN_TEXT', content: text } }.to_json req_options = { use_ssl: uri.scheme == "https", } response = Net::HTTP.start(uri.hostname, uri.port, req_options) do |http| http.request(request) end json = JSON.parse(response.body) score = json['documentSentiment']['score']*100 magnitude = json['documentSentiment']['magnitude']*100 sentiment = {score: score.to_i, magnitude: magnitude.to_i} end end hitokoto_controller.rb class HitokotoController < ApplicationController def new @post = Post.new end def create @post = Post.new(post_params) @post.set_sentiment_with_title @post.set_int_with_personality @post.set_bournus if @post.save redirect_to hitokoto_path(@post) else flash[:notice] = "ばぶばぶ(3文字以上20文字以下でちゅよ)" render "hitokoto/new" end end def index @posts = Post.all.limit(7).order(id: "DESC") end def show @post = Post.find(params[:id]) end def post_params params.require(:post).permit(:post) end end 設定した値を使って条件分岐によって、色々な値が出るように記述。 製作期間2〜3日の文字通りクソアプリです。 コントローラーの名前はちょっと失敗しています。可読性悪くて申し訳ないです。 公式の使用方法で実装した方が、APIからより詳細な値が帰ってきます。 https://cloud.google.com/natural-language/docs/quickstart-client-libraries?hl=ja 今回はgithubに公開しつつ、デプロイするという手順を時間をかけずに実装したため、簡易的な実装になっております。 アプリの詳細 viewを含めたコードはgithubに公開してます。 https://github.com/kosimaru1997/hitokoto 今回作成したクソアプリ https://ohitokoto.herokuapp.com/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ruby goldにも無事合格したので、体験記をまとめてみる(2021/06)

2月に合格したruby silverに続いて 無事ruby goldにも合格したので せっかくなのでgoldの体験記も残しておきます。 細かい部分の解説や、頻出の問題については 多くの方が記事に残されているので、そちらは他記事にお任せして この記事では、試験への向き合い方だったり、心構えみたいなものを残せればと思います。 これから同試験を受験する人少しでもヒントになれば幸いです。 silverの合格体験記はこちら 2021年6月の最終週に初受験。88点で合格しました。 75点以上で合格なので、まずまずの結果だったかなと思います。 上を目指せば、もっと勉強しないといけないことはたくさんありますが 合格点に達するだけなら、着実な勉強をして臨めば難しくない試験だと感じました。 勉強時間 3週間くらい前から準備し始めました。 勉強は平日の夜と休日にしてました。が、本腰入れて勉強し始めたのは直前になってからだと思います… 合計学習時間は25時間くらい。 勉強に使用したもの ① ruby技術者認定試験合格教本 ② CTC教育サービスの模擬問題 ③ google先生 ハッキリ言って①の巻末の問題集と、②の問題を完答できる様にしておけば よっぽど出題運が悪くない限り受かると思います。 (もちろん、答えを暗記するのではなく、なぜその答えになるのかを理解するのは前提です!) ①②の問題をとにかく解いて、間違えたり理解できなかった部分を google先生に聞いたり、①の解説を読んだりして 知識を蓄えていくのが一番効率がいいと思います。他の試験と一緒ですね。 silverの時にお世話になったRExは goldだと本番の出題傾向と違うという話を聞いたので、一度も解きませんでした。 Rexは、余裕があれば…くらいで考えて良いのではないでしょうか。 ①ruby技術者認定試験合格教本 silverの時もお世話になりました。 公式が出しているだけあって、本番の出題とほぼ同じ問題が多数収録されています。 問題はいいのですが、解説が不親切、というか最低限なので 解説読んでもよくわからなかった時は、大人しくgoogle先生に頼った方が吉です。 ②CTC教育サービスの模擬問題 60問の模擬問題がまとめられています。 こちらも合格教本に負けず劣らず、本番で実際に出題された問題に近い問題がたくさんありました。 解説も分かりやすいので、goldを受けるのであれば絶対に解いておきたいです。 ③Google先生 いつもお世話になっております。 聞けばなんでも答えてくれる万能先生。数多の合格体験記にまとめられている 参考になった記事のリンク集も大変便利に使わせていただきました。 受験当日の流れ 試験開始は16時半。秋葉原が会場でした。お昼過ぎに現着し、会場の場所を確認した後 駅近くのバーガーキングで、見直しをしてました。 ①②の問題を最後に全て解き直して、迷いなく答えられる事を確認して あとの時間はコマンドラインオプションや特殊変数など、単純な暗記が必要な項目を繰り返し頭に入れてました。 また、勉強している時に不安だった問題や、複数回間違えた問題は slackのメモにまとめておいてあったので、無駄な時間の浪費がなかったです。 自分のやりやすい方法で、不安が残る問題をまとめておくと 直前に焦らずに済むと思います。 テストセンターには20分前くらいについて、受付後、席が空いているとのことで少し早めに始めさせてもらいました。 案内された一番左奥の席が、時計のカチカチ音をモロに喰らう席で、めちゃくちゃ気になりました… どうしても音が気になる人は、会場の人に相談するといいかもです。 90分の試験ですが、アンケート含めて30〜40分位で終了。 答えに100%の自信がない問題全てに、フラッグを立てて回答を進めました。 完答後、フラッグの立った問題が9問しかなかったので、安心して終える事ができました。 12問まで間違える事ができるので、少しくらいわからない問題が出てきても大丈夫です! 焦らずにわかる問題を確実に正解していきましょう。 振り返って 終わってみればあっさりと合格できたruby goldですが 社内のエンジニアでも複数名が落ちていたので 自分も一発では受からないのでは…と、受けるまでは不安でいっぱいでした。 その不安をなくすためには、やっぱりもうとにかく問題を解くしかないと思います。 いい意味で視野を狭める必要があります。 試験範囲が広いので、教本や、CTCの模擬問題に載っていない範囲のことまで考え始めると どれだけ勉強しても勉強不足を感じてしまい、終わりがないと感じました。 確かに、教本や、CTCの模擬問題に無い問題も、本番で出るには出るのですが 本番で完全に初見だった問題は、多く見積もってもせいぜい6,7問くらいで 他の合格者に聞いても大体そんなもんです。 その6,7問を取りにいくために、終わりなき旅に出るくらいなら これは出る!という問題群を何周もして、確実に正答できる様にした方がいいと、個人的には思います。 その方が自信にもつながるし、結果前向きに試験に臨めるのではないでしょうか。 (高ければ高い壁の方が登った時気持ちいいもんですが、グッと堪えましょう) また、これは切実に感じたことですが silverを取った勢いで、goldの取得まで頑張った方が良いと思います。 silverを受験したのが2月で、そこからまたgoldの勉強を始めるまで 一度止まったやる気を再開させるため、ものすごいエンジンを吹かす必要がありました。 silverの範囲も完全に記憶の彼方だったので まずは思い出すところから始める必要があり、非常に効率が悪かったなと… 理想はsilverに受かったその日に、goldの試験を予約することだと思います。 人間、そう理想通り動けないもんだと思いますが、私と同じ轍を踏まないためにも、即日予約してしまうことをお勧めします。 受験前に読んでおいてよかった記事 Ruby Gold試験前に読むとほんの少しだけ点数が上がる記事(2020/12〜2021/03) 同じ会社のエンジニアの方が書かれた記事です。分かりやすく要点をまとめてくださっていたので助かりました。 1ヶ月後「Ruby Association Certified Ruby Programmer Gold version 2.1」に合格する方法 こんなにしっかりと試験範囲をまとめてくださって、本当に助かりました…。この記事に記載されている内容は全て本番で出たんじゃないかと思うくらい 脅威の精度の高さだったので、一読の価値ありです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]PrefixとURIパターンについて

ありがとうございます まずはじめにですが、 先ほど、過去に投稿したものについて、大変ありがたくご指摘いただきました。 返事の方法がイマイチわからず、改めて御礼申し上げます。 @shiozaki さんありがとうございます!勉強になりました! はじめに 本記事では、PrefixとURIパターンについて、説明いたします。 先日に、ルーティングネストでケアレスミスをし、 エラー対処に何時間と費やしてしまいました。 先日投稿したものも後ほどご覧ください。 改めて、詳しく説明したいと思い投稿します。 Prefixとは パスが入った変数のようなもので、 どのページに遷移したいかをパスとしてコードに記述する場合は、 末尾に「_path」を記載する必要があります。 URIパターン ルーティングのパスを表し、 このパスにてページに遷移した際に、指定のコントローラーとアクションで処理が実行されます。 ここにURIパターンに記載されているものは、遷移後のURLとほぼ同じです。 rails routesにて確認 例えですが、実際にrails routesしたものを添付します。 やや文字が小さいかもしれないです。すみません。 左から、 1番目:Prefix 2番目:HTTPメソッド 3番目:URIパターン 4番目:各テーブルのコントローラーとそれに対するアクションたち 使い方の例としては、 <%= link_to "by #{prototype.user.name}", user_path(prototype.user), class: "card__user" %> などと記述をします。 「user_path(prototype.user)」は、画像でいうところの一番下の行を使用しており、 一番右を見るとおり、「users#show」。 つまり、ユーザー情報ページに遷移するためのパスということになります。 ちなみに、適当なユーザー情報のページに遷移した場合、 URLは以下の通りになります。 http://localhost:3000/users/3 *「3」は3番目にユーザー登録したため、レコードにidが3となっており、「3」と表示されております。 ちなみに、users#showのURIパターンは、「/users/:id(.:format)」 URLの最後の記載とほぼ同じです。 また、 user_path(prototype.user)の(prototype.user)は、 誰のユーザー情報に遷移するのかを示すところになっています。 今回では、 「プロトタイプを投稿したユーザー(prototype.user)」の 「ユーザー情報に遷移user_path」するパス。ということです。 ルーティングネストによるケアレスミス 先日の投稿と内容はやや被ります。 resources :prototypes resources :prototypes do resources :comments, only: :create end resources :users, only: :show resources :prototypes resources :prototypes do resources :comments, only: :create resources :users, only: :show end この2つは、usersをネストしているか、していないかという違いです。 ネストしているかどうかでrails routesで記載されるものが違います。 つまり、ルーティングには気をつけろ!ということです。 終わりに ネストのミスについては、ルーティングのエラーの一つとして挙げられるのではないでしょうか。 今後も気をつけて学習を進めます。 また、はじめに書きましたが、 ご指摘をいただけるということは非常にありがたくと感じております。 右も左もわからず行動し投稿し始めたことによって学べることを今回身をもって知ることができました。 これからもやってみようと思った時から行動し、アウトプットを積極的に行うよう心がけます。 それでは、引き続きがんばります!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]フォロー、フォロワー機能

SNSの必須機能として、フォロー・フォロワー機能を実装していきます。 中間テーブルの理解が難しいとは思いますが、できるだけわかりやすく解説していきます。 開発環境 ruby 2.6.3 Rails 5.2.6 Bootstrap 4.5 前提 今回は、フォロー・フォロワー機能の実装のみの解説なので、deviseでユーザー認証が実装されている前提で話を進めていきます。 モデルコントローラーは以下 relationshipモデル relationshipsコントローラー 手順 モデルの作成 アソシエーション メソッド作成 コントローラー ビュー モデルの作成 まずは、Relationshipモデルを作成していきます。 RelationshipモデルはUserモデルの中間テーブルにあたります。 なぜなら、フォローするのもフォローされるのもユーザーなので、多 対 多の状態になるからです。 カラムは、 follower_id : フォローしたユーザー followed_id : フォローされたユーザー を準備します。 ユーザーのidで、user_idにしていないのがミソです。 Userモデルの中間テーブルになるため、どちらもユーザーのidが入るので、わかりやすいように、あえてuser_idを使っていません。 説明もこれぐらいにして、コマンド実行していきます。 $ rails g model Relationship follower_id:integer followed_id:integer $ rails db:migrate これで、Relationshipモデルができました。 follower_id : フォローしたユーザー followed_id : フォローされたユーザー カラム名がややこしくて、よくわからなくなるので、注意してください。 アソシエーション 中間テーブルは、このアソシエーションの理解が1番難しいです。 気合い入れてついてきてください! まず、関係性を整理しておきます。 1人のユーザーはたくさんのユーザーをフォローできる(1:N) 1人のユーザーはたくさんのユーザーにフォローされる(1:N) relationshipモデル 1人のユーザーはたくさんのユーザーにフォローもできるし、フォローもされるので、belongs_toでいきましょう。 models/relationship.rb class Relationship < ApplicationRecord # class_name: "User"でUserモデルを参照 belongs_to :follower, class_name: "User" belongs_to :followed, class_name: "User" end belongs_to :userちゃうの? と思いませんか? そうです。 本来、フォローしたユーザーとフォローされたユーザーは同じUserモデルから持ってきたいのですが、belongs_to :userとするとどっちがどっちのuserかわからなくなるので、followerとfollowedで分けています。 ただこのままだと、followerテーブルとfollowedテーブルを探しに行ってしまうので、class_name: "User"でuserテーブルからデータをとってきてもらうようにします。 userモデル では、続いてUser側のアソシエーションです。 models/user.rb # フォローをした、されたの関係 has_many :relationships, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy has_many :reverse_of_relationships, class_name: "Relationship", foreign_key: "followed_id", dependent: :destroy # 一覧画面で使う has_many :followings, through: :relationships, source: :followed has_many :followers, through: :reverse_of_relationships, source: :follower まずは、フォローをした、されたの関係から見ていきます。 ここでは、relationshipsとreverse_of_relationshipsがありますが、さきほどと同じ考え方で、わかりにくいため名前をつけているだけです。 class_name: "Relationship"でRelationshipテーブルを参照します。 foreign_key(外侮キー)で参照するカラムを指定しています。 次に、フォロー・フォロワーの一覧画面で、user.followersという記述でフォロワーを表示したいので、throughでスルーするテーブル、sourceで参照するカラムを指定。 上の例では、reverse_of_relationshipsテーブルからfollower_idのデータを参照します。 メソッド作成 モデルにメソッドを記述していきます。 models/user.rb # フォローしたときの処理 def follow(user_id) relationships.create(followed_id: user_id) end # フォローを外すときの処理 def unfollow(user_id) relationships.find_by(followed_id: user_id).destroy end # フォローしているか判定 def following?(user) followings.include?(user) end これで、コントローラーをすっきりできます。 コントローラー まずはrelationshipsコントローラを作成します。 $ rails g controller relationships followings followers で、relationdhipsコントローラーに、createとdestroyアクションを追加します。 追加したらさきほどのメソッドを使って、コントローラーに記述していきます。 controllers/relationships_controller.rb class RelationshipsController < ApplicationController # フォローするとき def create current_user.follow(params[:user_id]) redirect_to request.referer end # フォロー外すとき def destroy current_user.unfollow(params[:user_id]) redirect_to request.referer end # フォロー一覧 def followings user = User.find(params[:user_id]) @users = user.followings end # フォロワー一覧 def followers user = User.find(params[:user_id]) @users = user.followers end end ルーティング コントローラーを作成したので、ルーティングも設定しておきます。 config/routes.rb # ネストさせる resources :users do resource :relationships, only: [:create, :destroy] get 'followings' => 'relationships#followings', as: 'followings' get 'followers' => 'relationships#followers', as: 'followers' end ビュー ビューではフォローボタンとフォロー、フォロワー一覧画面を別々で見ていきます。 フォローボタン ビューのどこでもお好きなところに配置してください。 お好きなところ <% if current_user.following?(user) %> <%= link_to "フォロー外す", user_relationships_path(user.id), method: :delete %> <% else %> <%= link_to "フォローする", user_relationships_path(user.id), method: :post %> <% end %> フォロー、フォロワー一覧画面 フォロー一覧もフォロワー一覧も同じ内容を表示するので、テンプレートをつくります。 views/relationships/_follow_list.html.erb <% if users.exists? %> <% users.each do |user| %> <table> <thead> <tr> <th>name</th> <th></th> <th></th> </tr> </thead> <tbody> <tr> <td><%= user.name %></td> <td>フォロー数: <%= user.followings.count %></td> <td>フォロワー数: <%= user.followers.count %></td> </tr> </tbody> </table> <% end %> <% else %> <p>ユーザーはいません</p> <% end %> で、このテンプレートをrenderで呼び出してくれればOKです! 今回のビューはあくまで、一例なので適宜変更してください。 まとめ 手順は以下 モデルの作成 アソシエーション メソッド作成 コントローラー ビュー 中間テーブルの理解が初心者にはなかなかキツイものがありましたが、ぼくでもこうやって記事にできるぐらいにまとめられたので、みなさんもきっと大丈夫だと思います。 特にアソシエーションの部分が難しいので、理解できるまで見返してみてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

gem bullet導入(N+1問題を分かりやすくするため)

なんで導入しようと思ったのか アプリケーションを作成中にN+1問題を可視化できるにして分かりやすくするような便利な機能ないかな~と探してみましたところタイトルのgemを見つけてこれは便利だと思いましたので、備忘録として使い方を記述していきたいと思います。 そもそもN+1問題って? Mysqlなどのデータベースからデータを読み込む際に余計にデータを読み込んでしまうことです。 分かりやすく例えるなら、商品を買おうと思ってレジにもっていったら一つ一つ商品をスキャンしていくイメージですね。 アプリケーションの動作がもっさりするなどの弊害があるため基本的にこの問題はなくしたほうが良いです。 導入 以下のコードをGemfileに記述すれば大丈夫です。 grop :development do Gem 'bullet' end あとはbundle installして下のコマンドを記述すれば準備は完了です bundle exec rails g bullet:install 公式テキストを見てみましたらどうやらほかにもいろいろと設定ができるらしいですか今回は省略します。 デフォルトで設定されている内容 上記のコマンドで、```config/environments/↑上記のコマンドで、config/environments/development.rbにデフォルトで以下の様なコードが追加されました。 development.rb Rails.application.configure do # ここから config.after_initialize do Bullet.enable = true Bullet.alert = true Bullet.bullet_logger = true Bullet.console = true # Bullet.growl = true デフォルトでコメントアウトされてる Bullet.rails_logger = true Bullet.add_footer = true end # ここまでが追加される。以下、同ファイルにもとから入っていた内容は省略 end bulletの詳細な設定項目一覧←こちらにて各項目の設定内容についての確認ができます。 以上です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby on rails] 改行の反映 form_withで入力

やりたいこと 改行も反映させたい!!!!! 現状 form-withで改行して入力しても、 反映されない! <div class="form-group"> <%= f.label :introduction,"自己紹介" %><br /> <%= f.text_area :introduction, autofocus: true,class:"form-control" %> </div> <div class="col-md-8 ml-5" style="margin-top:1em;"> <h6>Introduction</h6> <p><%= user.introduction %></p> </div> 改善後 こう書くと、改行される!!! safe_joinの部分です。 <div class="col-md-8 ml-5" style="margin-top:1em;"> <h6>Introduction</h6> <p><%=safe_join(user.introduction.split("\n"),tag(:br)) %></p> </div>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

slackアプリ開発履歴

スクールの卒業制作にslackアプリをリリースした。 以前の仕事はマーケティングだったので、それと絡めたアプリを作った。 チャンネルに参加してから○日経った人に対してメッセージを送るアプリ。 github その開発の履歴をここに残しておく。 素人の主観で書いてますので、かなり割り引いて、お時間ある時にお読みください。 どんなアプリを作るかを決める ~ アプリ完成、まで約4ヶ月掛かっている。内訳は、 アプリのideaを考える + どのアプリにするか決定する = 1ヶ月 制作期間 = 3ヶ月 制作期間の詳細としては、最初の1ヶ月で全体の8割、次の1ヶ月で1割、最後の1ヶ月で1割進んだイメージ。最初の1ヶ月でアプリが形になって来て、そこからの微調整から本番、というような感じだった。 まず、どんなアプリを作るか考えた所から書く。 アプリidea 最初は、スクールが提供している「卒業制作の為のアプリidea」というリストがあって、それをやろうと思っていた。(まず自分で考えたが、いいのが思いつかなかったので)。だが、そのアプリを作るにあたって色々調べた結果、すでに市場にそれと似ているアプリが存在すると分かったので、中止にした。 なので、やはり自分で考える事にした。とりあえず思いついたideaはどんどんメモして、結果的に100ほど考えた。 その後自分でOK、NGを選別、結果的に3つに絞った。 その3つをメンターの方に提出、その内の1つが通り、作ることにしたのが今回リリースしたslackアプリ。 余談だが、考えたアプリideaのボツにした8割は「すでに市場にアプリが存在する」 残り2割が「(今の自分では)技術的に不可能」だった。 期間 + プラニング 事前にどれだけ時間を掛けるか決めておく。これを決めるのは開発にメリハリを持たせる為。アプリ制作というのは、どこまででも時間掛けられる(気がする)。ある程度機能を考え、期限に間に合いそうになければ機能を削り、最低条件で提出する方式でやる事にする。 すべての機能を備え、3ヶ月以内に終わる事はまず無い(という予想)。開発中、色んな欲が出てくると思う。UI上げる為にここはjsを使おう。drag and dropで画像アップしたい。AWSアカウント持ってない人の為に、指定が無い場合はactive storageが動くようにしよう。 モリモリ入れたいけど、期限は守りたい。 多分apiを使うアプリだと、新しい事実が分かって仕様が変わると思うので、それを考慮して期間をとる必要があると思う。 開発の進め方の方針 このアプリのコアは「slackのapiを使う」という点なので、slack apiのドキュメントを読めば大体の開発の全貌が見える...はずなのだが、ドキュメントの内容が膨大で、読み切れなかった。なのである程度読んで理解したら、実際開発を進めて、新しい事が分かったらまたドキュメント調べて修正して、、を繰り返した。 結果的に開発中、DB構成を20回以上変更している。最初からブレは無くしたいが、ここは慣れだと思う。以下に特に時間が掛かった点(数日費やしたもの)をピックアップ 特に詰まった箇所:slack api系 通信制限 同時に複数のapi通信をすると、その通信結果が保証されない場合がある。 例えばメッセージを送信する場合は1秒に1回だと保証される。(公式) なので実装する場合、一気に通信しないように工夫する必要がある この制限のドキュメントを後から見つけて、仕様を結構変更した。 メッセージに画像を含める apiを使ってslackで画像を含めるメッセージを送るには、様々なメソッドがあるが、、 画像は送れるがテキストは送れないメソッド 画像は送れるしテキストも送れるが、テキストは一行しか送れないメソッド(改行を認識しない) 画像URLをテキストとして記述すると展開されるが、画像に実体はなく(ダウンロードできない)引用っぽくなるメソッド webhookを使っても送れるが、それだとchannelが新規にワークスペース上にできる度に、webhook APIで専用のURLを発行し、ユーザーに承認をクリックしてもらわないといけない。 テキストと画像は別で送るという方法もあるが、あまりスマートでは無い。 画像専用で送るメソッドに、オプションでテキストも添付できるというのもある。が、それだと予約送信はできない。 なので、slack de stepでは最も基本的なメソッドchat.postMessageにオプションを付けて実装した。 具体的なコード例は以下(curlコマンド) {"type": "mrkdwn", "text": "これはテスト"}  のmrkdwnは、plain_textでもOK。ただしmrkdwnにしておけば、テキストのURLが受信したslackでURLリンクになる。もちろんmrkdwnなので記述がmarkdownに対応するという意味 $ curl -X POST 'https://slack.com/api/chat.postMessage' \ -d 'token=xoxb-xxxx-xxxx....' \ -d 'channel=U010B...' \ -d 'blocks=[{"block_id": "test_message", "type": "section","text": {"type": "mrkdwn", "text": "これはテスト"}}, {"type": "image", "title": {"type": "plain_text","text": "pitcure"}, "image_url": "https://画像URL", "block_id": "test_image","alt_text": "pitcure here"}]' ログイン機能の注意点 開発するアプリに、slackでのログイン機能を付けたくて、gemのomniauth-slackを使おうとするかも知れない。 本家のslack api公式でもこの方法を紹介しているので、大丈夫だと思って使うと危険。このomniauth-slack、開発がかなり前に終わっている。 しかし、本家slack apiの方はどんどんバージョンアップしているので、omniauth-slackでは新しいメソッドが使えないという事が起こる。 その時によく保守されている方法(本家の「sign in with slack」など)を使うといい。 権限設定の注意点 slack apiの権限設定で、登録しても 「何かがおかしい」 とだけ表示されて登録できない時がある 原因はchromeが日本語に訳しているから。英語表記にしたら登録できる場合がある。 slack app「公開」の意味 現段階まで開発したアプリが、他人のslackではどう動くのかを確認したい時がある しかし他のワークスペース(開発アカウント以外)で自分が作ったアプリを入れようとすると、「公開していないとできない」とエラーが出る この「公開」の意味をslack app directoryに公開する = productionとしてリリースする、と思っていたが違った。「公開」には「身内にだけ(リンクを知っている人だけ)公開」と、slack app directoryに公開(slack 全userの目に触れる)の2種類あると、だいぶ経ってから分かった。 userのslack情報にアクセスする slackのアプリを作る時、userのslack情報にアクセスしたい場合は「sign in with slack」を使えばできる。 自分は当初、利用者に自分でslackのtokenを作って、そのtokenを送ってもらってアプリを使えるようにする、以下のようなUIを考えていた。 だが、userにsign in with slackで発行したURLを踏んでもらうと、そのuser専用のslack tokenが自動で発行される。なので、上記のようなUIの画面は必要なかった。apiの仕様が分かってくるほど、作るアプリの仕様も変更した。 権限更新時の注意点 他のワークスペースに自分が作ったアプリがすでにinstallされている状態で、こちら側で権限スコープを変更すると =>権限を追加する分には、再度連携のアナウンスがされて、権限が更新される =>権限を削除する分には、反映されない。 参考資料 ので、注意が必要。 ワークスペースへのアプリのインストール後に開発者がアプリのスコープを変更した場合、アプリ管理者が新しいスコープを確認できるよう、メンバーはアプリを再度リクエストする必要があります。アプリ管理者がアプリの新しいスコープを制限した場合、メンバーはそのアプリの既存のバージョンを引き続き使用できますが、更新バージョンのインストールや使用はできません。 では、すでにinstallしているユーザーに対して権限を削除(制限)したい場合はどうするのか。 =>一旦アプリをuninstall後、再度installしてもらうしかなさそう。(注意:これをするとtokenが再発行される = 以前のtokenは無効化する) botを動かす時の注意点 botとuserという2つの存在をしっかり押さえておく必要がある。 userとは、普通にuserの事。slack上にいる同僚のAさんとかがそう。Aさんはslackをやってて、他人からメッセージが来たら「アラート」が鳴って、自分が所属しているチャンネルにBさんが参加してきたら、「Bさんが参加しました」とメッセージが表示される。要するに、userはslack上で発生する「イベント」を自動で検知する しかし、これがbotの場合はそうでなくなる。 例えば、ワークスペース上に新しいchannelができて、そこにuserが参加したとすると、そのchannelにどのuserが所属しているかどうかをbotが検知するには、botがそのchannelに所属してないといけない botはdefaultでチャンネルに所属していない。 もし、userが新規でチャンネルをslack上で作り、そのチャンネルで起こったイベントをbotで検知したければ、そのチャンネルにbotを仕込む必要がある もし、新しくチャンネルが作られて、同時にそのチャンネルに参加したuserをbotで把握したければ、userがチャンネルに参加するより先に、botがチャンネルに入り込まないといけないので、タイミングが重要。 不正確な説明内容 slack自身の説明が事実と違う場合がある。 以下はmacのslackアプリで、slackのチャンネルをアーカイブしようとした時に表示される説明である。 チャンネルを復元する事ができるが、メンバーは削除されたままだと書かれてある。 しかしチャンネルを復元すると、きっちり5分後にメンバーも復活する。デバグにかなりの時間が掛かった。 というように、slackのapiには事実と違う内容が書かれている場合があるので注意が必要。 多分ドキュメントの更新が追いついていないのでは 特に詰まった箇所:circleCI系 主に、localでは通るがcircleCIでは落ちる、という事がほとんどだった 今まで通っていたテストが、いきなり落ちる cacheが関係している可能性。特にchromedriver周りがあやしい。 chromedriverのcacheをコメントアウトしてテストに掛けると通る事がある。 CIのchromedriverは12時間制 input type="time"は、localでは以下のように見えているが、 CIのchromedriverではこのように見えている(AM、PMを指定する) CIのchromedriverは12時間制 そういうわけで、以下のようなエラーになり、テストが通っていなかった。CIのデバグをする時はscreenshotを見ないと分からない(エラーメッセージだけでは分からない) ちなみにこの場合、テストには以下のように最後にAMやPMを付けると通る find("#time").set("#{hour}:#{minutes}AM") 特に詰まった箇所:テスト系 factorybot 以下のようなテストを書いている場合、it の中で@appがnilになる before(:context) do @workspace = FactoryBot.create(:workspace) @app = FactoryBot.create(:app, workspace_id: @workspace.id) @channel = FactoryBot.create(:channel, workspace_id: @workspace.id) context "sessionを伴うtest" do let(:rspec_session) { { workspace_id: @workspace.id } } it "テキストメッセージを新規登録できる事" do p @app p @channel end end @channelはちゃんと値が出力される itの中で、@app = FactoryBot.create(:app, workspace_id: @workspace.id)を定義すると、その後で@appするとちゃんと値が出力される before(:context) この時、before(:context) doではなく、before doにすると、itの中で@appを出力できるようになる どうやら、before(:context) do => context do => it doの順で動く時、context doを噛ませると @appがnil になる という所までは分かった。だが、なぜそうなるのか分からない。 let(:app) { FactoryBot.create(:app, workspace_id: @workspace.id) }をcontext doに書くと、it do内でappで出力できた しかしなぜ@channelはできて、@appができないのか両者のmodelなどの違いを見て行ったが、原因分からず。 結局、原因は変数名(@app)だった。 予約されているらしい。他の名前にしたら出力された。すごい時間掛かった。 ログイン omniauthなどはテストモードというのがあって、テストでのログイン方法を提供しているが、「sign in with slack」では提供されてない rpsecでは、sessionが使えない為、ログイン後の挙動をテストできない => なのでrspecでsessionを書き込めるようにrails_helper.rbを書き換えて実現した。これもかなり時間掛かった。 特に詰まった箇所:環境系 JSON.parseエラー 767 macで開発したコードを、windowsで動かした時に起こった事象 JSON.parseエラー 767が出る jsonファイルの破損ではない。環境周りを見直すが問題はない。 結局、windowsでは、curlで文字列をシングルクォートで囲ってはいけないということが分かる。ダブルクォートにするとエラーが消えた。1日掛かった。 その他 削った機能 以下の機能は、期限の兼ね合いで一旦寝かせた。最低限の機能でリリースし、「slackで相対的に、自動的にメッセージを送る」というideaに反応があれば(userがいれば)バージョンアップする 画像の保存先を2つ用意する。defaultはAWSだが、AWSアカウントがなければactive storageに保存できるようにする メッセージにemojiを使えるようにする 総送信数 / 総既読数をリセットする アプリ上でのチャンネルの表示 / 非表示を切り替える userがアプリからログアウトせずにワークスペースを切り替えられる 事前に決めておけば良かった事 userが利用する環境を明確にしておけば良かった。 ブラウザ デバイス OS 漠然とchromeで、PCで、macで、と決めて開発していたが、最終工程で、「やっぱりwindowsでもテスト動いた方がいい」と思って(気持ちがブレた)、動かしたらエラー出て、それを「直そう」とした。(1日掛かった)。 当然なんだけど、環境は「明記」しておけば良かった。 感想 結局、色々と改善点は残ったけど、これは開発をしなければ分からなかった事なのであまり後悔してない、というのが正直な感想。 経験をたくさんすれば技術は上がるので、どんどん開発したい。 ただ...意識しながらの開発とそうでない開発は、全く経験値が違うだろうな、とは思う。 卒業に関して スクールを約1年で卒業。当初の予定とほぼ同じ。とにかく「焦らない」事を心がけた。 プログラマという職業はまさに「急がば回れ」だと思う。理解するまで食らいつけるか。「効率がいい学習方法」を模索していないか。トータルで考える事ができるかどうか。今時間を掛けて基礎を習得する事が、今後5年10年にどれほど影響があるか。 周囲にはいろんな立場の人がいると思う。特に転職エージェント系の担当者とはよく話をするだろう。 自分も結構相談した。そして毎回、希望とずれた案件を紹介された。 php、java、、、営業の仕事してみませんか? フロントされてましたよね? rubyを数ヶ月触っていた? 経験者という事にしましょう。 ハローワークでもほぼ同じ事を言われる。 歳を考えてください!30代は未経験では無理です。現実を見てください。それよりcobolはどうでしょう。平均年齢が高いのでまだ可能性があるかも知れません。 今回転職は2回目なので、大体の温度感は分かっているつもり。 彼らは就職させるのが仕事である(もちろん仕事を紹介してくれる事には感謝している) 通りやすい方法を紹介するのは当然。必然的にこちらの「希望」は二の次になる。 対してこちらは「希望」が第一になる。その第一希望でやるだけやって、ダメだったら妥協していくという方針になる。 確かに以前の仕事ではちょろっとバックエンドを触ったことがある。 だけどこれを経験者とアピールして仕事に就いても、会社と社員どちらも不幸になるだけだ。 転職業界の通説として、「1つ内定を取る為に23社受ける」というのがある(おそらく30代の話)。上記のような対応をするエージェントに任せたとして、23社受けて経験者としてどこかに就職する、というのは可能な気がする。さすが転職のプロと言った所か。 今回は結局特にエージェントは使わず、普通に未経験枠で転職活動して、今は普通に仕事している。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

1周目ーrailsチュートリアル第1章

目次 1.はじめに 2.大まかな流れ 3.用語のまとめ 4.感想 5.おわりに 1. はじめに 前回の続きです。 progateのweb開発パスをクリアしようと思ったらHTML中級編は有料であることが判明。 そのままrailsチュートリアルへ進み、このタイトルとなります。 どのように記事をまとめればいいのか、迷走中。 自分の勉強、方向性はチュートリアルを1周後、プロゲートやrailsの有料サポートを受けるか考え中。 railsチュートリアルでは、どこまで理解したのかグラフ化できるようなサポートもあるようで さらっと流れを掴んだあとに有料へするか否かを決めます。 2. 大まかな流れ hello worldをローカルサーバで表示 git/githubの操作 herokuへデプロイ これらの一連の操作、環境はcloud9(AWS)で行う。 3. 用語のまとめ 4. 感想 github,AWS,herokuはある程度触っておかないと少しむずかしい、敷居が高い印象。 cloud9設定時にrootユーザーでログインすると、 IAMユーザーへ変更するようにと警告が出てしまうが、 チュートリアルをよく見るとrootで問題ないとのことでそのまま進める。 自分のローカルPCで環境変数などの書き換えをせず、 Ruby、rails、herokuのインストールなどを 全てcloud9(ブラウザ)上で完結できてしまい、感動を覚える。 無事にアプリケーションまでインストール設定とローカルサーバの立ち上げまで完了。 それらのソースコードをgit/githubにてバージョン管理。 半年間ではGitHubDesktopでしか操作をしておらず、 コマンドライン上での操作は結構、新鮮であった。 branchではなく、 checkout -b ブランチ名 とブランチが作成されるので 今までのGitHubDesktopのような流れをイメージすると少し工程が違い少々手間取った。 branchでもいけるが何が違うのだろうか? 最後の演習も ローカルサーバーのデータ変更、herokuへのデプロイもコマンドであり手間取ったが なんとか初日は終えることができたので、満足である。 復習書きなぐり ruby,railsをインストール後 アプリケーションの作成 rails 6.0.3 new hello_app ローカルサーバを立ち上げてwebブラウザへアクセスできるか試す。 rails server(rails s と省略可能) ローカルPC環境:3000port cloud9:クラウドワークスペース上のウィンドウをクリック MVCは後述。既にイメージは掴めている。 git/github 後ほど別途まとめる。 Gitでパスワードを一定時間保持するように設定する $ git config --global credential.helper "cache --timeout=86400" リポジトリの再初期化 $ cd ~/environment/hello_app # Just in case you weren't already there $ git init プロジェクトの全ファイルをリポジトリに追加 $git add -A Gitにプロジェクトのファイルを追加すると、最初はステージング(Staging)という 一種の待機用リポジトリに置かれ、コミットを待つ。 現在のステータス確認 $git status commitを使い、ステージングエリアで待機している変更を本格的にリポジトリに反映させる。 $ git commit -m "Initialize repository" web上のGitHubで作成後、pushするとweb上のgithubへディレクトリなどが反映される。 $ git remote add origin https://github.com/<あなたのGitHubアカウント名>/hello_app.git $ git push -u origin master ここからよく使う場面 ブランチの方法 $ git checkout -b 【ブランチ名】 $ git branch master * 【ブランチ名】 変更があればstatusで確認。 $ git status commit -aですべてのファイルをへの変更を一括でコミット $ git commit -a -m "変更した点を記述する" margeにてmasterブランチへ変更点をmargeする $ git checkout master $ git merge ブランチ名 merge後はブランチを削除 $ git branch -d ブランチ $git push これを実行することによりGitHubのコントリビューションを更新することができる 通称、草を生やす。 herokuも同様にpushするとデプロイ上の画面も変更される ローカルサーバのgitを更新してからではないとherokuのデータも反映することができない。 $git push heroku master 5. おわりに 次回は第2章へ続く
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

deviseによるgmail認証、本番環境への適用

概要 本記事では、Ruby on RailsのGem「devise」を用いて、ユーザー新規登録機能を実装していきます。 本番環境でメール認証が適用できない点で詰まったので、本番環境での設定方法まで解説していきます。 本記事でやること Gmail認証を使ったユーザー新規登録方法 Gmail側の設定 メール内容をカスタマイズ 本番環境でもGmail認証が使えるようにする 環境 バックエンド Ruby 2.6.5 Rails 6.0.3 Gem devise devise-i18n dotenv-rails データベース MySQL 5.7 参考にした記事 【Rails備忘録】deviseまとめ Deviseでログイン機能を追加・日本語化・Bootstrap4適用まで deviseを使ったログイン機能を実装する!メール認証機能付き 【Rails】deviseでURL認証付きのメールを送信してみる 完成した後の動作 ①ユーザー新規登録画面で、情報を入力 ②入力したメールアドレス宛にメールが送信されるフラッシュが表示 ③Gmailを確認して、メールが届いていることを確認、メール内リンクをクリック ④メール認証が完了し、ログインが可能となる 補足 メール内のリンクをクリックしないと本人確認をするフラッシュが表示され、ログインができない仕様 アカウント確認メールを再送することも可能(本記事に実装方法記載していません) 前提 RailsのMVCなど基本を理解されていること Railsアプリケーションを新規作成済みであること deviseがインストール済みであること Gmailアカウントを作成済みであること 実装(準備編) マイグレーションを以下の様な構成にして、マイグレーションを実行してください 補足 - メール認証のみであれば、Confirmableの箇所のみコメントアウトすれば良いが、パスワードリセットやログイン情報保持をする場合は、以下のようにRecoverable、Rememberableの箇所もコメントアウトしてください。 20200627035125_devise_create_users.rb # frozen_string_literal: true class DeviseCreateUsers < ActiveRecord::Migration[6.0] def change create_table :users do |t| ## Database authenticatable t.string :email, null: false, default: "" t.string :encrypted_password, null: false, default: "" ## Recoverable t.string :reset_password_token t.datetime :reset_password_sent_at ## Rememberable t.datetime :remember_created_at ## Trackable t.integer :sign_in_count, default: 0, null: false t.datetime :current_sign_in_at t.datetime :last_sign_in_at t.string :current_sign_in_ip t.string :last_sign_in_ip ## Confirmable t.string :confirmation_token t.datetime :confirmed_at t.datetime :confirmation_sent_at t.string :unconfirmed_email # Only if using reconfirmable ## Lockable t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts t.string :unlock_token # Only if unlock strategy is :email or :both t.datetime :locked_at t.timestamps null: false end add_index :users, :email, unique: true add_index :users, :reset_password_token, unique: true add_index :users, :confirmation_token, unique: true add_index :users, :unlock_token, unique: true end end deviseの機能が使えるようuser.rbに以下を記載 app/models/user.rb devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :confirmable, :lockable, :timeoutable, :trackable 新規登録時のエラーメッセージなどを日本語化させましょう 以下の様な構成になります config/locales/devise.views.ja.yml ja: activerecord: attributes: user: ~~省略~~ devise: confirmations: confirmed: メールアドレスが確認できました。 new: resend_confirmation_instructions: アカウント確認メール再送 send_instructions: アカウントの有効化について数分以内にメールでご連絡します。 send_paranoid_instructions: メールアドレスが登録済みの場合、本人確認用のメールが数分以内に送信されます。             ~~省略~~ mailer: confirmation_instructions: action: メールアドレスの確認 greeting: "%{recipient}様" instruction: 以下のリンクをクリックし、メールアドレスの確認手続を完了させてください。 subject: "アカウントの有効化のご案内"              ~~省略~~ shared: links: back: 戻る didn_t_receive_confirmation_instructions: アカウント確認のメールを受け取っていませんか? didn_t_receive_unlock_instructions: アカウントの凍結解除方法のメールを受け取っていませんか? forgot_your_password: パスワードを忘れましたか? sign_in: ログイン sign_in_with_provider: "%{provider}でログイン" sign_up: 新規登録 minimum_password_length: "(6~20桁の半角英数字)"              ~~省略~~ Gmail側の設定をします。 ①Gmailにログインし、2段階認証はオンにしてください ②アプリパスワードの生成 Gmail → 設定 → アカウント → セキュリティ → アプリパスワード → パスワードを入力 → アプリを「その他」に設定し、「現在作成しているアプリケーション名」 → 生成 → 以下の画面になると思いますので、必ずメモしてください!(アプリパスワードは再生成できません) Gmailでメールを受け取れるようにする設定にします(開発環境) config/enviroments/development.rb config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { address:"smtp.gmail.com", domain: 'gmail.com', port:587, user_name: Rails.application.credentials.gmail[:user_name], password: Rails.application.credentials.gmail[:password], authentication: :login } 重要 GitHubにuser_nameとpasswordを記載するのは危険なため、credentialで保存してください! 以下のコマンドを実行 terminal EDITOR=vim rails credentials:edit INSERTにして、以下を記入したら:wqで抜ける vim gmail: user_name: Gmailアドレス password: 先ほど生成したアプリパスワード ここまできたら一度Githubにプッシュしましょう 実装(Mailer編) デフォルトのMailer設定を変更,送信元メールアドレスを変更 以下の様な構成にします。 補足 config.confirm_withinでURLの有効期限が設定できます config/initializers/devise.rb Devise.setup do |config| config.mailer = 'Users::Mailer' config.mailer_sender = '認証メール<〇〇〇〇〇〇@gmail.com>' require 'devise/orm/active_record' config.reconfirmable = true config.expire_all_remember_me_on_sign_out = true config.password_length = 6..20 config.timeout_in = 1.day config.confirm_within = 1.days config.reset_password_within = 6.hours config.scoped_views = true config.sign_out_via = :delete end Mailer自体をオーバーライド app/mailers/users/mailer.rb class Users::Mailer < Devise::Mailer helper :application include Devise::Controllers::UrlHelpers default template_path: 'devise/mailer' def confirmation_instructions(record, token, opts={}) if record.unconfirmed_email != nil opts[:subject] = "認証を行ってメールアドレス変更手続きを完了してください" else opts[:subject] = "認証を行ってユーザ登録を完了してください" end super end end 実装(MVC作成編) Viewは以下の様な構成にします。 補足 - if文をフォームの下に設置することにより、バリデーションエラーが以下に表示される様になります。 - cssクラスはご自身で設定されているものに変更してください。 app/views/devise/registrations/new.html.erb <section class="login"> <div class="login__imgBx"> <%= image_tag 'introduction.jpg' %> </div> <div class="login__contentBx"> <div class="login__formBx"> <div class="login__title">新規登録</div> <%= form_with model: @user, url: user_registration_path, id: 'new_user', class: 'new_user', local: true do |f| %> <div class="login__inputBx"> <%= f.label :name, {class: 'login__label'} %> <%= f.text_field :name, maxlength: '50' %> <% if @user.errors.include?(:name) %> <p class="login__form-error"><%= @user.errors.full_messages_for(:name).first %> <% end %> </div> <div class="login__inputBx"> <%= f.label :email, {class: 'login__label'} %> <%= f.email_field :email, maxlength: '100 '%> <% if @user.errors.include?(:email) %> <p class="login__form-error"><%= @user.errors.full_messages_for(:email).first %> <% end %> </div> <div class="login__inputBx"> <%= f.label :password, {class: 'login__label'} %> <%= f.password_field :password, autocomplete: "off", minlength: @minimum_password_length, maxlength: '20' %> <% if @minimum_password_length %> <small class="form-text text-muted"><%= t('devise.shared.minimum_password_length', count: @minimum_password_length) %></small> <% end %> <% if @user.errors.include?(:password) %> <p class="login__form-error"><%= @user.errors.full_messages_for(:password).first %> <% end %> </div> <div class="login__inputBx"> <%= f.label :password_confirmation, {class: 'login__label'} %> <%= f.password_field :password_confirmation, autocomplete: "off", minlength: @minimum_password_length, maxlength: '20' %> <% if @user.errors.include?(:password_confirmation) %> <p class="login__form-error"><%= @user.errors.full_messages_for(:password_confirmation).first %> <% end %> </div> <div class="login__inputBx"> <%= f.submit "新規登録" %> </div> <% end %> <%= render 'devise/shared/links' %> </div> </div> </section> controllerは以下の様な構成にします。(newとcreateアクションをオーバーライドします。) app/controllers/users/registrations_controller.rb # frozen_string_literal: true class Users::RegistrationsController < Devise::RegistrationsController def new super @user = User.new end def create @user = User.new(user_params) if @user.save flash[:notice] = "ユーザー認証メールを送信いたしました。認証が完了しましたらログインをお願いいたします。" redirect_to new_user_session_path else flash[:alert] = "ユーザー登録に失敗しました。" render action: :new and return end end private def user_params params.require(:user).permit(:name, :email, :password, :password_confirmation) end メール内容のカスタマイズ app/views/devise/mailer/confirmation_instructions.html.erb を作成 作成後以下の様な構成にします。 補足 登録済みの方が、再度アカウント確認をした場合を想定して、条件分岐で内容を切り替えています。 app/views/devise/mailer/confirmation_instructions.html.erb <div><%= @user.name %>様</div> <div>〇〇〇〇〇〇〇をご利用いただきありがとうございます。</div> <% if @user.try(:unconfirmed_email?) %> <div>以下のリンクからユーザの認証を行ってメールアドレス変更手続きを完了してください。</div> <div><%= link_to 'ユーザ認証を実行する', confirmation_url(@user,confirmation_token: @token) %></div> <% else %> <% if @user.confirmed_at != nil %> <div>会員登録が完了済みです。</div> <div>下記リンクからログインをお願いいたします。</div> <p>http://localhost:3000/</p> <% else %> <div>以下のリンクからユーザの認証を行ってユーザ登録を完了してください。</div> <div><%= link_to 'ユーザ認証を実行する', confirmation_url(@user,confirmation_token: @token) %></div> <div> このURLの有効期限は24時間です。</div> <% end %> <% end %> ここで開発環境でGmailに認証メールが届くか確認しましょう。 確認できたらGithubにプッシュしましょう 実装(本番環境編) production.rbに認証メール送信で必要なことがあります。 以下の様な構成になります。 補足 ドメイン名を設定してあげないと、本番環境ではGmailに認証メールが届かないため設定しましょう。 環境変数でGmailアドレスやアプリパスワードを渡す必要があるため、dotenv-rails Gemをインストールし、環境変数を.envファイルに記載しましょう config/environments/production.rb Rails.application.configure do ~~省略~~ config.action_mailer.perform_caching = false config.action_mailer.default_url_options = { host: 'アプリケーションのドメイン名'} config.action_mailer.perform_deliveries = true config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { address:"smtp.gmail.com", domain: 'gmail.com', port:587, user_name: ENV['SEND_MAIL'], password: ENV['GMAIL_SPECIFIC_PASSWORD'], authentication: :login, openssl_verify_mode: 'none', enable_starttls_auto: true } config.action_mailer.perform_caching = false config.action_mailer.raise_delivery_errors = true ~~省略~~ end 環境変数の設定は以下の様になります。 .env SEND_MAIL="Gmailアドレス" GMAIL_SPECIFIC_PASSWORD="生成したアプリパスワード" 環境変数はGithubにあげるのは危険なため、.gitignoreファイルに.envを追加しましょう .gitignore ~~省略~~ .env Githubにプッシュしします これで、本番環境でメール認証が飛んでいるはずです。 苦労した点 Gmailアドレスとアプリパスワードの設定することに気づかず、詰まりました。 本番環境でメールが飛ばない事態が発生しており、本番環境用のMailer設定については記事が少なく大変でしたが、設定できました。 まとめ deviseはブラックボックスなところが多く、大変ですが、Qiitaを参考に実際に手を動かして、試行錯誤すると、便利な部分はデフォルトでカスタマイズしたいところはカスタマイズできるのでこちらの記事を参考に皆さんのポートフォリオ作りの糧になればと思います。 ご質問などがありましたらコメントいただけると幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

param is missing or the value is empty: の原因

結論 「無駄な記述」 初学者あるあるかと思いますが、今まで作ったものを基準に。そしてお手本にしながら作成をしていたから起きたエラーなのかなと思います。  エラーの原因について詳しく こちらが、お手本にしていたスクールで作成したものです。 <%= form_with model: @purchase_address, url: item_purchases_path, id: 'charge-form', class: 'transaction-form-wrap', local: true do |f| %> 自分が記述していたコード <%= form_with model: @sum_exp, url: skill_sum_exps_path, id:'charge-form', class:'transaction-form-wrap', local: true do |f| %> 解説 エラーの原因は id: 'charge-form' でした。  これはスクールで作っていた クレジットカード決済の処理をJavaScriptでする為に必要なidの記述であって、フォームを送る処理には不必要な記述という事です。 スクールでの学習はついつい受け身になってしまい、いざ自分で作成するとこのようなエラーが起きるんですね。 自分で作るって大事ですね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby on RailsでCRUD処理をしてみる

Railsの学習を始めるにあたって、基本的な動作である、CRUD処理を学習してみた。現段階の理解度を確認する意味を込めてCRUD処理とActive Recordについてひも解いてみる。 目次 1.守破離の「守」一番大事な基礎であるCRUD処理の勉強。 2.Active Recordとお友達になる。 3.CRUD処理を噛み砕く 4.まとめ 5.参考文献 1. 守破離の「守」一番大事な基礎であるCRUD処理の勉強。 まず、Railsガイドを読みながら理解を深めた。 Active Record の基礎 - Railsガイド 中でもmodelでテーブルが作られる仕組みは、全然頭に入ってこない。 2. Active Recordとお友達になる。 Active RecordがRailsのMVCに置けるMの役割を果たしているそうだ。 Railsガイドによると、その中でも重要な機能を抜粋すると以下の5つになる。 モデルおよびモデル内のデータを表現する モデル同士の関連付け(アソシエーション)を表現する 関連付けられているモデル間の継承階層を表現する データをデータベースで永続化する前にバリデーション(検証)を行なう データベースをオブジェクト指向スタイルで操作する 正直言ってよく分からない。今理解できていることを下記にまとめてみた。 モデル名は単数、テーブルのカラム名は複数かつキャメルケースで表示される。 モデルの中にテーブルを作成し、その中に属性を入れ込んでいく。 モデルの作成 $ bin/rails g model Genba name:string description:text g modelコマンドを実行することで生成されるクラス。 class CreateGenbas < ActiveRecord::Migration[] def change create table :genbas do |t| t.string :name t.text :description t.timestamps end end end ); これで、以下のようにテーブルに格納する項目を追加することができる p = Genba.new p.name = "Some Book" puts p.name # "Some Book" 3. CRUD処理を噛み砕く Create 登録機能 def create genba = Genba.new(genba_params)#=>privateを呼び出している genba.save! redirect_to genbas_url, notice: "タスク「#{genba.name}を登録しました。"#=>登録したらrootURLにリダイレクトされる end private def genba_params params.require(:genba).permit(:name, :description)#=>:nameと:descriptionのみ許可している end .container - if flash.notice.present? .alert.alert-success= flash.notice #=>これでcreateアクションが完了した時にflashが発行される。 Read 一覧表示機能 def index @genbas = Genba.all end この時はまだSQL文は発行されていない。viewで@genbas.each doのように呼び出された時に初めて SQL文が実行される。 index.html.slim - @genbas.each do |genba| tr td= genba.name td= genba.created_at こうすることでViewで一覧を表示することができる。 Show 検索機能 def show @genba = Genba.find(params[:id]) end findはidによってモデルオブジェクトに対応するレコードをDBから検索している。 引数としてparams[:id]を設定している。 params[:id]には、リクエストされたURL"genbas/[タスクのid]"の[タスクのid]の部分が格納されている。 Update 更新機能 model def update genba = Genba.find(params[:id]) genba.update!(genba_params) redirect_to genbas_url, notice: 'タスク「#genba.name」を更新しました。' end view Edit 編集機能 def edit @genba = Genba.find(params[:id]) end = link_to '編集する', edit_genba_path(genba), class: 'btn btn-primary mr-3' Delete 削除機能 model def destroy genba = Genba.find(params[:id]) genba.destroy redirect_to genbas_url, notice: "タスク「#{genba.name}」を削除しました" end view = link_to '削除', genba, method: :delete, date: { confirm: "タスク「#{genba.name}」を削除します。よろしいですか?} ,class: 'btn btn-danger' Partialを使用したモデルの設定方法 view = render partial: 'form', **locals: {genba: @genba} #=>「インスタンス変数@genbaをパーシャル内のローカル変数genbaをして渡す」** _form.html.slim =form_with model: genba, local: true do |f| .form-group = f.label :name = f.text_field :description, class: 'form-control', id:'genba_description' 4. まとめ 簡単な掲示板アプリの作成に必要な考えを学べた。 C 作成処理 R 一覧表示やホーム画面表示 U 更新処理 D 削除処理 次はルーティングに関して勉強したい。 5. 参考文献
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

新規登録・ログイン画面実装① devise 新規登録の項目追加 

deviseで、新規登録画面、ログイン画面を実装しました。 その際にnameカラムと、入社何年目かの等級(年齢層?社歴?)を追加するカラムも追加しましたので、備忘録として残します。 カラムの追加 class DeviseCreateUsers < ActiveRecord::Migration[5.2] def change create_table :users do |t| ## Database authenticatable # メールアドレスでユーザー登録 t.string :email, null: false, default: "" # パスワードも必須 t.string :encrypted_password, null: false, default: "" 省略〜〜〜 t.timestamps null: false # 名前を登録してもらう t.string:name,null:false # 社歴?を登録 t.string:join_year,null:false 省略〜〜〜 まずは保存する用のカラムを追加します。stringで問題ないのか若干不安です。 ApplicationControllerの記述 class ApplicationController < ActionController::Base # ユーザー認証などが行われる前に、configure~が実行される before_action :configure_permitted_parameters, if: :devise_controller? protected def configure_permitted_parameters # デフォルトにないname/join_yearを追加しているので、それらを許可するよう記載している devise_parameter_sanitizer.permit(:sign_up, keys: [:name,:join_year]) end end (:sign_up, keys: [:name,:join_year])にはemailは入りません。デフォルで入ってるようです。 新規登録画面の編集 <div class="form-group"> <%= f.label :name,"お名前" %><br /> <%= f.text_field :name, autofocus: true,class:"form-control" %> </div> デフォルトで入ってるので省略 <div class="form-group"> <%= f.label :join_year,"クラス" %><br /> <%= f.select :join_year,[["1年目", "1年目"], ["2年目", "2年目"], ["3年目", "3年目"],["4年目", "4年目"],["5年以上", "5年目"]], include_blank: "選択して下さい",class:"form-control" %> </div> 終わり 追々、グレードアップしていきます。 現在の疑問点・・string型で大丈夫なのか。 参考にした記事
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Capybara

Capybaraとは? WebブラウザとWebアプリケーションの間で交わされるHTTP通信をエミュレート(模倣)するためのライブラリです。 mkdir spec/features/ _spec.rb spec/featuresに配置する。 使用方法 require 'rails_helper' RSpec.feature 'ログインとログアウト' do background do # ユーザを作成する User.create!(email: 'foo@example.com', password: '123456') end scenario 'ログインする' do # トップページを開く visit root_path # ログインフォームにEmailとパスワードを入力する fill_in 'メールアドレス', with: 'foo@example.com' fill_in 'パスワード', with: '123456' # ログインボタンをクリックする click_on 'ログイン' # ログインに成功したことを検証する expect(page).to have_content 'ログインしました' end end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】enumを使ってステータス管理をしよう!

対象者 商品等ののステータス管理を実装予定の方 セレクトタグを実装予定の方 目的 ステータスで進捗や状況を管理できるような機能を実装する 実際の手順と実例 1.前提 ケーキ屋さんのECサイトを作成しています。 ここでは注文ステータスを取り扱うので、モデル名「order」、カラム名「order_status」とします。 管理者が注文ステータスを操作できるようにしたいので、 2.カラム追加 まずは対象のモデル(order)にカラムを追加していきます $ rails g migration AddOrderStatusToOrder order_status:integer enum(イーナム)で数字管理していくのでデータ型はintegerで与えます。 3.モデルにenumを追記 以下の通り書いていきます。 order.rb enum order_status: { "入金待ち":0, "入金確認":1, "製作中":2, "発送準備中":3, "発送済":4 } 番号が0から始まっている点に注意です。 表示したい内容とそれに合わせて数字を順に並べていきます。 4.Viewで表示 今回は、管理者が操作して保存できるようにしたいので、 form_withを使って内容を保存できるように更新ボタンも付けます orders/show.html.erb <%= form_with model: @order, url: admin_order_path, method: :patch, local: true do |f| %> <%= f.select :order_status, Order.order_statuses.keys, class: "order_status" %> <%= f.submit "更新" , class: "btn btn-outline-success" %> <% end %> @order = Order.find(params[:id])とControllerに定義しています。また、ストロングぱらめーたーにも、permitでorder_statusを入力しています。 これで実装完了です。 参照 【Rails】enumを使ってステータス管理を行う 【Rails】enumを使用したセレクトボックスの実装とDBへの保存 投稿者コメント プログラミングスクールで勉強学んだことをそのままアウトプットしてみました。今後も7月に開発したものの中からどんどん発信していきます! My Profile プログラミング学習歴②ヶ月目のアカウントです! プログラミングスクールで学んだ内容や自分が躓いた箇所等のアウトプットの為に発信しています。 また、プログラミング初学者の方にわかりやすく、簡潔にまとめて情報共有できればと考えています。 もし、投稿した記事の中に誤り等ございましたら、コメント欄でご教授いただけると幸いです。 
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

// でコメントアウトできる事知りませんでした...

▪️ コメントアウトとは プログラミングはチームで各々担当分けして開発することが多いので、意思を伝えるためにコードの近くにコメントを残します。ですが、コメントをただ書いただけだと読み込まれエラーの原因になってしまいます。そこでコメントは残しつつ、エラーの原因にならないようにするためにコメントアウトを用います。 ▪️ コメントアウトの書き方 普通のコメントアウトの書き方は、< !-- > という記号で囲むとコメントアウトされると思います。 e) <! -- コメントアウトです > ですが、他にもコメントアウトのやり方があり綺麗なコードを書くプログラマーのコードを見ているとそれをよく多用しています。<下記に記載> ▪️ 他のコメントアウトの書き方  他のコメントアウトの書き方としてコードの前に//を書くとコメントアウトすることができます。 e) // コメントアウトです 今日の投稿は以上です。 他にもこんなコメントアウトの書き方があれば、是非教えてください。 wagaでした!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む