Ransackを使って検索機能を実装

69日目です。
個人アプリにRansackを使って検索機能を実装したのですが、思いの外大変だったので残します。

Ransackとは

検索機能を実装してくれるgem。ユーザーの名前だけなどでなく、様々な検索方法が可能になるとのこと。

実装したアプリ

Image from Gyazo

実装の流れ

Gemfileにファイル名を記述

gem 'ransack'

ターミナルでbundle install
ローカルサーバー再起動

posts/index.html.erbにusers/index.html.erb(ユーザー検索ページ)へのリンクを追記

<div class="contents row back">
  <% @posts.each do |post| %>
    <%= render partial: "post", locals: { post: post } %>
  <% end %>
  <%= paginate(@posts) %>
  <%= link_to "ユーザー検索", "/posts/:post_id/users" %> # この行を追記
</div>

users_controller.rbにindexアクションを追記

def index
    @search = User.ransack(params[:q])
    @users = @search.result
end

users/index.html.erb(ユーザー検索ページ)を作成

<%= search_form_for @search, enforce_utf8: false do |f| %>
  <%= f.label :name_cont, "ユーザー名"%>
  <%= f.search_field :name_cont %>
  <div class="actions"><%= f.submit "検索" %></div>
<% end %>

<% if request.url.match(/commit/)%>
  <% @users.each do |user| %>
    <p><%= user.id %></p>
    <p><%= link_to "#{ user.name}", "users/#{user.id}" %></p>
  <% end %>
<% end %>

ransackを使うことで「search_form_for」というメソッドが使えるようになります。

検索結果のところで条件分岐を書かないと、検索する前から候補一覧が表示されてしまいます。
そのため、もしURLに「commit」が含まれていれば検索結果を表示するという条件分岐にしています。
また検索結果の名前を押したら、そのユーザーのマイページに飛べるようリンクにしています。

躓いたところ

「No Ransack::Search object was provided to search_form_for!」(Ransack :: Searchオブジェクトがsearch_form_forに提供されていません) というエラーが発生しました。
調べても検討がつかなかったため、初めてteratailで質問させて頂きました。

Ruby - 「No Ransack::Search object was provided to search_form_for!」エラーを解決したい|teratail

「ArgumentError in Posts#index」というエラー文があったのですが、回答者様はここを見て「application.html.erbはどの画面でも共通で呼ばれるため search_form_for が実行されますが、@searchは posts#indexで定義されていないので掲題のエラーにつながっている。」ということを見抜かれました。

詳細はリンクをご覧頂ければと思いますが、回答いただいた「ApplicationControllerとapplication.html.erbではなく、UsersControllerとusers/index.html.erbなど別のコントローラーとビューに処理を持っていく」方法で解決致しました。

今後の予定

今のままだと少しでも名前が分かっていなければ検索が出来ないので、学習時間で検索できるように改良予定です。

参考にしたサイト: Railsでransackを使って検索画面を作る【初心者】 - 平成生まれの資産運用記

Ruby on Railsでransackを用いた検索機能の実装 - Qiita


明日から一番大切なチーム開発2週目が始まります。ここの出来によってほぼ3週目前半でメルカリ開発が終わるチームもあれば、雲行きが怪しく分裂するチームも出てくるとのことなので、コミットできるよう頑張ります。

1週間の個人アプリ開発で得たこと

61日目です。
1週間(8/16~8/23午前中)で「プログラミング学習記録アプリ」を作りました。
メンターのサポートは一切なし、カリキュラム以外の技術も使って好きなものを作る課題です。

まずは発表スライドと成果物をご覧下さい。

発表スライド

スクリーンショットで失礼します。

f:id:tattaka_s:20190823174856p:plain

f:id:tattaka_s:20190823195331p:plain

f:id:tattaka_s:20190823195419p:plain

f:id:tattaka_s:20190823195436p:plain

f:id:tattaka_s:20190823195457p:plain

f:id:tattaka_s:20190823195520p:plain

f:id:tattaka_s:20190823195541p:plain

f:id:tattaka_s:20190823195617p:plain

成果物

当初つけたいと思っていた機能の60~70%ほどが実装できました。
※デプロイできていないので、GIFで失礼します。
未ログイン

Image from Gyazo

Image from Gyazo

Image from Gyazo

Image from Gyazo

ログイン後

Image from Gyazo

Image from Gyazo

Image from Gyazo

Image from Gyazo

Image from Gyazo

Image from Gyazo

得たこと

  1. 一日かかっても解決しなかったことでも、日が経つと不思議と実装方法が見えてきて解決できることがあると知った。
  2. やはり1つの言語を早い段階で満足に使えるようになるべきだと実感した。
  3. 音楽を聞いていると、解決困難な壁に当たった時に途端に集中力が落ちてくるので、ノイズキャンセル的なイヤホンで無音状態を保つと集中力が落ちない
  4. RailsはちょっとしたコマンドでSQLに変えてくれて便利。DBを用いて何か機能を実装したい時に真価を発揮するように思える。(逆にDB関係ない時はHTML&CSSJavaScriptで作った方が軽量で良いと思った。)
  5. 最初は全然ロジックが思いつかなかったことがだんだんと実装できたので、課題が解決する毎に「快感」を味わえた!!!

特に2の「1つの言語を早い段階で満足に使えるようになるべき」ということを強く実感したことがありました。
データベースからの情報の取得、マイページのuser情報との紐付けで行き詰まり、データ解析の「SAS」を専門とする弟を頼った時のことです。弟はRubyなど触ったことすらありませんが、ささっと携帯で調べてRubyの公式ページから「これ使えるんじゃない?」とアドバイスをくれました。このように、違う言語でも自身の経験に照らし合わせて解決策を導き出せるというのは本当に大事なことだと思います。
その後2人で試行錯誤しながら解決の糸口を見つけましたが、SASSQLだったら10分で解決できると言ってました笑
また、個人アプリ開発を通して「カリキュラムで理解できていたと思っていただけだった」というのがありました。違う言語などで個人アプリを作ってたら理解できずに進んでいたかと思うとゾッとします。
これらのことから「1つの言語を早い段階で満足に使えるようになり、他への応用を効かせられるようになる」ことが大切だと思いました。

56期(一個上の先輩)の発表会で感じたことを活かせたか(8/3の投稿)

80点くらいです。

・自分自身が使いたいと思えるものを開発したいです。
・何故そのアプリは必要だったのかをしっかり答えられるように、「何故」を深掘りしてから取り掛かります。
・自分の技術力不足で、開発途中で方針転換を余儀無くされることもあるかもしれないが、 最後は「どういうところを見て欲しいか」 「誰のために(何を解決するために)作ったのか」は しっかりアピールしようと考えています。

自分が使いたいと思えたか
→使いたいと思える。まだ機能が不十分なところもあるが、学習記録アプリの機能は最低限実装できた。

何故アプリが必要だったか
→プログラミング専用の学習記録アプリは見受けられず、初学者はツイッターやブログなどバラバラなところにアウトプットすることが多い
→初学者がモチベーションを保ちつつアウトプットができ、学習時間を記録できるアプリが欲しい
→マイページを作り、学習時間で検索できる機能をつければ、未経験採用を進める企業の方からもコンタクトできるのでは?

しっかりアピールできたか
→できた。JavaScriptでもっと動的なサイトにしたり、検索機能やランキング機能をつけたかったが、それよりも4分という短い時間で魅力を簡潔に伝えるよう意識して発表した。

他の人の発表を見て

時間が1週間以上取れていた人はRails+α(JavaScriptjQueryCSSフレームワーク)で作っている人が多く見受けられ、あまり時間が取れなかった人はLINE botを作っているという印象でしたが、どれも「こんなことができるんだ」とワクワクしました。自分が使ってない技術で実装しているのを見ると、「この実装は簡単でした」と言われてもとても難しそうに見えるので、どんどんチャレンジして「経験していないものに対する恐怖」を無くして行くようにします!


昨日から、朝4時まで実装や発表資料の作成、7時から起きてバグの解決に当たっていました。
実装が思うように行き出すと本当に楽しくて、体が元気ならずっとやっていたいという気分になってきます。
なんとか発表会30分前にずっと直せていなかったバグを直し、気持ちよく発表に望むことができました。
この感覚、病みつきになりそうですね笑
明日からチーム開発に入るので、気持ちを切り替えて取り組みます。

モーダル3分クッキング(jQuery)

60日目です。
JavaScriptでモーダル作ろうとしたら、わけわからずに苦しんだのですが、jQueryを使ったら(本当に)3分で終わったので、紹介します。

作り方

①HTML(自分は「index.html.erb」に書きました。)

<a class="modal-btn">モーダル</a>

<div class="modal-screen">
    <a class="close-btn">閉じる</a>
</div>

CSS(自分は「style.scss」に書きました。)

.modal-btn {
    display: block;
    width: 150px;
    height: 50px;
    text-align: center;
    line-height: 50px;
    cursor: pointer;
    background-color: skyblue;
    border-radius: 5px;
}

.modal-screen {
    display: none;
    height: 400px;/* 画面中央に配置↓ */
    width: 500px;
    position: absolute;
    top: 50%;
    left: 50%;
    margin-top: -200px;
    margin-left: -250px;/* 画面中央に配置↑ */
    background-color: #fff;
    z-index: 2;
    border: 1px solid black;
}

.close-btn {
    display: block;
    width: 80px;
    height: 40px;
    text-align: center;
    line-height: 40px;
    cursor: pointer;
    border: 1px solid;
    float: right;
}

jQuery(自分は「index.html.erb」内の、scriptタグの中に記述しました)

$(".modal-btn").click(function() {
    $(".modal-screen").fadeIn(200);
});

$(".close-btn").click(function() {
    $(".modal-screen").fadeOut(200);
});

さて、jQueryを読み込むためには、あと一つやることがあります。
application.html.erbの中の「」の中に、下記を記述します。
この時注意しなければならないのは、全てのJavaScriptの記述より上に書くことです。

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>

※バージョンが変わるかもしれないので、こちらのサイトの「3.x.snippet」の記述をコピーして下さい

https://developers.google.com/speed/libraries/#jquery

これで完成です。

実物

下記のようなモーダルが浮かび上がります。

f:id:tattaka_s:20190823000318p:plain

ソースコード(HTML)

<a class="modal-btn">アプリの説明</a>
<div class="modal-screen">
  <p>[ 基本機能 ]</p>
    <li>投稿一覧を見る</li>
    <li>投稿の詳細(コメント)を見る</li>
    <li>投稿者のマイページを見る</li>
    <li>便利ツール・学習サイトへのアクセス</li>
  <p>[ ログインして使える機能 ]</p>
    <li>文章の投稿(学んだことをアウトプットしましょう!)</li>
    <li>投稿に対してのコメント(感想やアドバイスをお願いします!)</li>
    <li>マイページの編集</li>
    <li>時間の記録(本日の学習時間・目標学習時間)</li>
    <a class="close-btn">閉じる</a>
</div>

<script>
  $(".modal-btn").click(function() {
    $(".modal-screen").fadeIn(200);
  });
  $(".close-btn").click(function() {
    $(".modal-screen").fadeOut(200);
  });
</script>

大変お世話になったサイト:

【jQuery】モーダルウィンドウの簡単な作り方 | knowts


ついに明日個人アプリの発表会!!まだまだつけたい機能はたくさんありますが、ひとまず形になったので良しとします!
そのうち個人アプリの内容も載っけます!!

URLからある部分だけを取得する方法(Rails)

59日目です。
取ってきたい「user_idとの紐付け」ができずに、とても苦しんだのですが、URLからIDを取得することでひとまず解決したので紹介します。
(もっと良い方法がある気がしますが、、笑 ちなみに他の言語でもできるかはわかりません。)

URL取得する方法

「request.url」で取ってこれます。
例えば、下記のような正規表現と条件分岐を使って、「もしこのURLに当てはまったら、下記を実行する」なんてことができます。
(実際にアプリでも取り入れました。)

if request.url.match(/\/posts/)

URLからある部分だけを取得する方法

「request.url[X, Y]」で取得できます。
「X」は URLの一番左側を0とし、そこから何番目を取得したいか。
「Y」はXから右に何個取得したいかを指定できます。

使い方例

私が作成中のアプリで、学習時間を記録するページがあります。
そのページのURLが下記のようになっています。

http://localhost:3000/users/5/records/

そして、学習時間記録ページからマイページに戻るリンクを作りたいとします。
マイページに戻るURLは、Rails routesで確認すると下記のようになっています。

 GET    /users/:id(.:format)              users#show

そこで「request.url[X, Y]」を使い、下記のようにしてマイページに戻ることができるようになりました。

<a href="/users/<%= request.url[28, 1].to_i %>">マイページに戻る</a>

「「request.url[28, 1]」で左から28番目の文字を1文字だけ取得し、「to_i」で取得した文字(string)をintegerに変える」という処理を行なっています。

この記事が参考になりました。

Rails4・Rails5 で現在のURLを取得する。 - ECのウェブ担当者のメモ


発表会まで使える時間は後1日!少しづつ形にはなってきたけど、まだ問題があるので最後まで頑張ります。

カリキュラム以外のことをやろうとすると凡ミスが多発する

57日目です。
本日は初歩的なミスに何時間も苦しみました。

1. binding.pryができない

  1. Gemfileに「gem 'pry-rails'」を追加し「bundle install」
  2. 「さあ、binding.pry使ってデバッグすっぞ!」
  3. エラーメッセージ発生「pryって何?tryの間違いか?」
  4. バグを解決したいのにデバッグのエラーのために悶え苦しむ

、、、bundle installのあとはサーバー再起動を絶対に忘れないようにしましょう😭

2. 「uninitialized constant」に苦しむ

これはコントローラーだけ作成してモデルの作成(rails g model モデル名)をし忘れた結果出たエラーです。
最初に必要だと思ったモデルは全て作成していたのですが、途中で新しいコントローラーの作成の必要が生じてコントローラーだけしか作るのを忘れていました。

3. データベースに登録はできるが、情報を持ってこれない

これは配列で取ってきたものを配列のまま使っていることが原因でした。

def index
    @records = Record.where(user_id: 5)
end

上記のようにコントローラーにて、DBから配列で取得してきているのにも関わらず、

<%= @records.day_hour %>

ビューファイルにて配列のままrecordsテーブルのday_hourカラムの情報を取ってこようとしてました。
もし上記のコントローラのように定義したのであれば、ビューファイルは下記のようにすべきでした。

<%  @records.each do |record|  %>
  <%=  record.day_hour  %>
<%  end  %>

こんな初歩的なミスで恐ろしいほど時間を無駄にして苦しみました。
(binding.pryは自分で気づきましたが、他の二つは優秀な同期にヒントをもらって解決しました。)
振り返れば「なんでこんなことで躓いたのだろう」という気持ちですが、自分で気づかない辺り、基礎がまだまだだなと痛感しました。


個人アプリ製作4日目でしたが、投稿通り進まず、、、
本当に完成すんのか?と思いつつ、オリジナルの機能の実装は諦めたくないので後3日頑張ります。

deviseでユーザー編集機能を作るときはストロングパラメーターの設定をお忘れなく

56日目です。

本日犯したミス

  1. ユーザー(プロフィール)編集機能を作成
  2. 編集して更新ボタンを押したにも関わらず更新されず(サインアップ時の情報と変わらず)

これは、deviseで「メールアドレスとパスワード」だけはデフォルトでストロングパラメーターが設定されているのですが、
それ以外の情報(例えばnameなど)を登録する際は、自分でストロングパラメーターを設定する必要があります。

今回、新規ユーザー情報作成(サインアップ)機能をつけた際には、自分でストロングパラメーターを設定していたのですが、ユーザー編集機能をつけた際にも設定しなければいけないことを知らず、「更新ボタンを押しても更新されない」という事態に陥ってしまいました。

※deviseの導入や新規ユーザー情報作成(サインアップ)のところは下記の記事をご参考ください。

deviseを使う際の初期設定 - tattaka_s’s blog

ストロングパラメーターの設定方法

application_controller.rbに「account_update」の行の記述を追記
(今回は、メール、パスワードに加えて、「name」「avatar」「 place」を設定していました)

class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:name, :avatar, :place])
    devise_parameter_sanitizer.permit(:account_update, keys: [:name, :avatar, :place])
  end
end

下記の記事がとても参考になりました。

devise導入からユーザーのプロフィール画面を作成するまで - Qiita


本日個人アプリ制作3日目でした。
ユーザー編集機能の実装後、ユーザー検索機能を付けようとするも、とりあえず見辛い検索フォームができただけで検索は機能せず、、
そんなこんなしているうちに「あれ?アプリとして必要な機能実装できてなくね?」と気づき焦るもタイムオーバー、、

下記の工程が大切だと気づきました。

  1. 最低限つけなくてはならない機能の洗い出し
  2. どの機能にどれだけ時間がかかるか予測を立てる
  3. 優先順位をつける
  4. 実装
  5. 必要ではないが、実装したらユーザビリティがよくなるであろう機能を洗い出す
  6. どの機能にどれだけ時間がかかるか予測を立てる
  7. 優先順位をつける
  8. 実装

残り4日ですが楽しむことを一番に作っていきます!!

シーザー暗号とは

55日目です。 スクールのミニ問題で、全く聞いたことがなかった「シーザー暗号を復号せよ」という問題が出てきて衝撃を受けたので、調べました。

シーザー暗号とは

古代ローマの政治家、軍人だった「ジュリアス・シーザーが」使っていたとされる暗号。 平文で使われているアルファベットを辞書順に3文字分シフトした(ずらした)もの(文字のシフト数は固定であるが、3に限る必要はない)。 例えば左に3文字分シフトさせる場合、「D」は「A」に置き換わり、同様に「E」は「B」に置換される。 そのため、暗号文のアルファベットをそれぞれ逆方向に同じ文字数だけずらせば復号できる。

シーザー暗号 - Wikipedia

シーザー暗号を Ruby で解読する - ✲゚。.ヾ(⌒(ノ'ω')ノ☆.。

実際の例文

下記を復号するコードをかけ。
char = "frqjudwxodwlrq"

答え

char = "frqjudwxodwlrq"
char_ary = char.split("")

changed_char_ary = []

char_ary.each do |char|
  changed_char_ary << (char.ord - 3).chr
end

p changed_char_ary.join

最初は全く意味がわからなかったので、メソッドの意味を調べていきました。

ord
stringをintegerに変換してくれるメソッド。 例えば、irb上で

"a".ord

と打ち込めば、「97」という数字を返してくれます。

chr
integerをstringに変換してくれるメソッド。 こちらもirb上で

97.chr

と打ち込めば、「"a"」という数字を返してくれます。

Class: String (Ruby 2.2.0)

Class: Integer (Ruby 2.2.0)

【Ruby】ordメソッドとchrメソッドで、1文字とコードポイントの変換 - コンパイラかく語りき

ざっくりと解説してみます

  1. 暗号文「frqjudwxodwlrq」を変数charに代入
  2. split("")で「 ["f", "r", "q", "j", "u", "d", "w", "x", "o", "d", "w", "l", "r", "q"]」に分けてchar_aryに代入
  3. 空の配列のかっこ[ ]を用意して変数changed_char_aryに代入
  4. each文でchar_aryの配列に入った文字を一つずつ取り出す
  5. 取り出した文字を「ord」で数字に変換し3を引いた後に「chr」で文字に戻し、順番にchanged_char_aryに入れていく
  6. joinメソッドを使ってchanged_char_aryに入れられた文字を合体する
  7. pで出力する
  8. "congratulation"という文字列が返ってくる

本日は個人アプリ制作2日目でした。テーブルを作り終わり、バックエンドとフロントを徐々に弄って形になってきました。
ただ、カッコイイアプリにするためにはJavaScriptの力が必要だと思うので、残り5日間でなんとか学習&実装していきます!!