20201120のRailsに関する記事は16件です。

AWS Railsの再起動手順

この度Railsで開発中のアプリをAWSへ開発中アプリをデプロイしましたが、
今後本番環境のサーバー再起動を行う頻度は高いだろうと思い
備忘録としてUnicornの再起動手順をまとめていきたいと思います。

EC2インスタンスにログインしている前提で進めていきます。

まずは ps aux | grep unicorn コマンドでプロセスIDを確認します。

ターミナル
[ec2-user@ip-172-31-33-238 <対象リポジトリ>]$ ps aux | grep unicorn
ec2-user 26995 17.4 10.7 475732 108272 ?       Sl   13:55   0:01 unicorn_rails master -c config/unicorn.rb -E production -D
ec2-user 26999  0.0 10.0 476748 101508 ?       Sl   13:55   0:00 unicorn_rails worker[0] -c config/unicorn.rb -E production -D
ec2-user 27002  0.0  0.0 119436   908 pts/0    S+   13:55   0:00 grep --color=auto unicorn

この時 unicorn_rails master と表示されているプロセスが Unicorn のプロセスとなっているので、今回の場合は 26995 がPIDとなります。

PIDが確認できたら killコマンドを使ってプロセスを止めます。

ターミナル
[ec2-user@ip-172-31-33-238 <対象リポジトリ>]$ kill 26995

ps aux | grep unicornコマンドで実際に止まっているのか確認します。

ターミナル
[ec2-user@ip-172-31-33-238 <対象リポジトリ>]$ ps aux | grep unicorn
ec2-user 27345  0.0  0.0 119436   964 pts/0    S+   14:27   0:00 grep --color=auto unicorn

上記のような表示になっていればプロセスは止まっています。

確認が取れたら下記コマンドを入力しUnicornを起動させます。

ターミナル
[ec2-user@ip-172-31-23-189 <対象リポジトリ>]$ RAILS_SERVE_STATIC_FILES=1 unicorn_rails -c config/unicorn.rb -E production -D

以上で再起動は完了しました。

EC2インスタンスに紐付けたElasticIPを使ってブラウザにアクセスすると、正常に表示されているか確認ができます。

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

Rails新規アプリケーションの作り方

GitHubでリポジトリつくる

Screen Shot 2020-11-20 at 21.55.27.png

localにcloneしてくる

$ git clone https://github.com/k-tetsuhiro/rails_docker_sample_3.git
& cd rails_docker_sample_3

bundleを初期化する

$ bundle init
Writing new Gemfile to /Users/kakuno/sandbox/rails_docker_sample_3/Gemfile

Gemfileができているので編集する

$ vi Gemfile
Gemfile
# frozen_string_literal: true
source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem "rails"

→ # gem "rails" のコメントアウトをはずす

bundle installして必要なものをインストールする

$ bundle install --path vendor/bundle
[DEPRECATED] The `--path` flag is deprecated because it relies on being remembered across bundler invocations, which bundler will no longer do in future versions. Instead please use `bundle config set path 'vendor/bundle'`, and stop using this flag
Fetching gem metadata from https://rubygems.org/.............
Fetching gem metadata from https://rubygems.org/.
Resolving dependencies...
--- 必要なgemがはいっていく... ---

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

$ bundle exec rails new . -B -d mysql --skip-turbolinks --skip-test
      exist
      create  README.md
      create  Rakefile
      create  .ruby-version
      create  config.ru
      create  .gitignore
    conflict  Gemfile
Overwrite /Users/kakuno/sandbox/rails_docker_sample_3/Gemfile? (enter "h" for help) [Ynaqdhm]
--- ここで上書きしていいか聞かれるのでYを押して許可する ---

→最後になにかエラーメッセージが出てたらもう一度bundle installする

$ bundle install --path vendor/bundle

webpackerをinstallする

$ rails webpacker:install
      create  config/webpacker.yml
Copying webpack core config
      create  config/webpack
      create  config/webpack/development.js
--- 必要なものが入っていく ---

Railsサーバーを起動してみる

$ bundle exec rails s

→ようやくRails起動!! http://localhost:3000/ でアクセスできる(ただしまだMySQLなどの設定をしてないのでエラー画面になるはず)

gitで管理しないものを設定する

$ vi .gitignore

→無視するファイルを設定

.gitignore
# Created by https://www.toptal.com/developers/gitignore/api/rails
# Edit at https://www.toptal.com/developers/gitignore?templates=rails

### Rails ###
*.rbc
capybara-*.html
.rspec
/db/*.sqlite3
/db/*.sqlite3-journal
/db/*.sqlite3-[0-9]*
/public/system
/coverage/
/spec/tmp
*.orig
rerun.txt
pickle-email-*.html

# Ignore all logfiles and tempfiles.
/log/*
/tmp/*
!/log/.keep
!/tmp/.keep

# TODO Comment out this rule if you are OK with secrets being uploaded to the repo
config/initializers/secret_token.rb
config/master.key

# Only include if you have production secrets in this file, which is no longer a Rails default
# config/secrets.yml

# dotenv, dotenv-rails
# TODO Comment out these rules if environment variables can be committed
.env
.env.*

## Environment normalization:
/.bundle
/vendor/bundle

# these should all be checked in to normalize the environment:
# Gemfile.lock, .ruby-version, .ruby-gemset

# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
.rvmrc

# if using bower-rails ignore default bower_components path bower.json files
/vendor/assets/bower_components
*.bowerrc
bower.json

# Ignore pow environment settings
.powenv

# Ignore Byebug command history file.
.byebug_history

# Ignore node_modules
node_modules/

# Ignore precompiled javascript packs
/public/packs
/public/packs-test
/public/assets

# Ignore yarn files
/yarn-error.log
yarn-debug.log*
.yarn-integrity

# Ignore uploaded files in development
/storage/*
!/storage/.keep

# End of https://www.toptal.com/developers/gitignore/api/rails

コミットとプッシュ

$ git add *
$ git commit -m "first commit"
$ git push

→これでGitHubに更新される

Screen Shot 2020-11-20 at 22.24.27.png

番外編

rbenvを最新にする

https://qiita.com/_kanacan_/items/c1499f6c13b1c41da982

railsを最新にする

https://techacademy.jp/magazine/22193

[参考]
https://qiita.com/yuitnnn/items/b45bba658d86eabdbb26

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

Railsを使ったToDoリストの作成(5.CRUDのCreate機能)

概要

本記事は、初学者がRailsを使ってToDoリストを作成する過程を記したものです。
私と同じく初学者の方で、Railsのアウトプット段階でつまづいている方に向けて基礎の基礎を押さえた解説をしております。
抜け漏れや説明不足など多々あるとは思いますが、読んでくださった方にとって少しでも役に立つ記事であれば幸いです。

環境

  • Homebrew: 2.5.10 -> MacOSのパッケージ管理ツール
  • ruby: 2.6.5p114 -> Ruby
  • Rails: 6.0.3.4 -> Rails
  • node: 14.3.0 -> Node.js
  • yarn: 1.22.10 -> JSのパッケージ管理ツール
  • Bundler: 2.1.4 -> gemのバージョン管理ツール
iTerm
$ brew -v => Homebrew 2.5.10
$ ruby -v => ruby 2.6.5p114
$ rails -v => Rails 6.0.3.4
$ npm version => node: '14.3.0'
$ yarn -v => 1.22.10
$ Bundler -v => Bundler version 2.1.4

第5章 CRUDのCreate(new/create)

第5章では、以下の機能を実装していきます。

  • 新規登録画面(newアクション)
  • 新規登録画面で入力された情報をデータベースに保存する機能(createアクション)

では、詳しく見ていきましょう。

1 newアクション

まずは、newアクションを使って新規登録画面を実装していきます。

1 コントローラ

まず、コントローラで新規登録された情報を保存するための空のインスタンスを作ります。ActiveRecordのnewメソッドを使っていきます。

app/controllers/boards_controller.rb
def new
    @board = Board.new
end

(name: ' ', description: ' ')のような空の箱がインスタンス変数に代入されているイメージです。

2 ビュー

次に、投稿フォームを作成していきます。

new.html.haml
.container
  %h2.form_title New Board
  = form_with(model: @board, local: 'true') do |f|
    %div
      = f.label :title, 'Name'
    %div
      = f.text_field :name, class: 'text'
    %div
      = f.label :description, 'Description'
    %div
      = f.text_area :description, class: 'text'
    %div
      = f.submit :Submit, class: 'btn-primary'

フォーム作成のポイントは2つあります。

  • form_withメソッドを使ってフォームを作成しています。form_withメソッドの引数はmodel: [モデルクラスのインスタンス], local: 'true'を指定します。local: 'true'を指定しない場合はAjaxによる送信になります。
  • フォームヘルパーを使って、ラベル・1行入力欄、複数行入力欄を作成しています。フォームヘルパーは以下のように指定します。
=f.[メソッド名] :[column], class: 'class名'

フォームヘルパーの種類について知りたい場合は以下の記事が参考になります。
* フォームヘルパーの種類と例
* Action View フォームヘルパー

3 一覧表示画面にリンクを貼る

最後に、一覧表示ページから新規登録ページに遷移できるようにリンクを貼っていきましょう。pathはrails infoの'boards#new'を参照してください。

app/views/boards/index.html.haml
= link_to new_board_path do
  .add_card
    %p + Add new board

以上で、新規登録画面の実装は完了です。

2 createアクション

では、次にcreateアクションを使って新規登録画面で入力された情報をデータベースに保存し、保存が成功したら詳細表示ページに遷移する機能を実装していきます。

1 コントローラ

コントローラで以下のように記述します。詳しい説明は後述します。

app/controllers/boards_controller.rb
def create
    @board = Board.new(board_params)
    if @board.save
        redirect_to board_path(@board), notice: 'Save successful'
    else
        flash.now[:error] = 'Could not save'
        render :new
    end
end

private
def board_params
    params.require(:board).permit(:name, :description)
end

Board.newでインスタンスを作成し、インスタンス変数に代入します。引数にはStrong Parameter(後述)を渡します。
ActiveRecordのsaveメソッドを実行し、作成したインスタンスをデータベースに保存します。

Q. newアクションで新しいインスタンスを作成していたのに、なぜcretaeアクションで再度作成しなければいけないのか?

A. Webアプリケーションでは、ブラウザからのリクエストのたびに新しくプロジェクトが実行されるため、newアクション(GETリクエスト)で作成した変数は利用することができません。そのため、今回はパラメータを渡して再度インスタンスを作成することが必要となってきます。

'render'と'redirect to'

renderはアクションに続けてビューを表示させる場合に使用します。
redirect_toはアクションを実行した直後にビューを表示せず、別のURLに遷移したい場合に使用します。
今回のケースではif文を使用して、インスタンスが保存された場合は詳細表示ページ(boards#show)に遷移するようにして、保存が失敗した場合は、現在の画面のビュー(boards#new)を表示するようにしています。
redirect_toの引数に@boardを指定しているのは、idを渡すためです。id: @board.idだと思ってください。

Strong Parameter

Strong ParameterはRailsのセキュリティに関する概念の一つです。board_paramsメソッドに記載している内容が該当します。
[更新する対象のモデル名_params]と命名する必要があります。
また、Strong ParameterはBoardsControllerクラスの外で使用することはないのでprivateメソッドを使用します。

表記 意味
params リクエストパラメータとして送られてきた情報が格納されているオブジェクト
.require(:board) オブジェクトの中にboardというキーが入っている必要があります
.permit(:name, :description) boardの中で、nameとdescriptionを保存する対象として許可します

ちなみに今回のparamsの状況は以下のようになっています。
スクリーンショット 2020-11-20 16.30.38.png
これを見ると、paramsはハッシュのようになっていてboardというキーとnameやdescriptionなどのデータが入ったバリューを持っていることがわかります。

Flashメッセージ

Flashは、リダイレクトやレンダーする際に、次のリクエストに対してちょっとしたデータを伝えるために用意された仕組みです。
Flashにはハッシュでデータを格納することができます。

redirect_toの場合は、第2引数にnotice: '[表示したいメッセージ]'を指定し、renderの場合はflash.now[:error] = '[表示したいメッセージ]'を指定することで設定することができます。
今回の場合は以下のようなハッシュが格納されています。

flash = { notice: 'Save successful', error: 'Could not save' }

ビューで以下のように設定することで画面に表示することができます。

- if flash.present?
  .flash
    - flash.each do |key, value|
      %div.key= value

これは何をやっているかというと、まず、present?メソッドを使ってflashにデータが入っているかどうかを確認します。if文を使ってtrueの場合は3行目と4行目を実行します。

2 モデル

モデルではvalidationの設定を行います。
validationとは、データの内容が正しいかどうかをチェックする仕組みのことを指します。

モデルのvalidationは、モデルに対応するオブジェクトをデータベースに登録・更新する前に検証を行い、エラーがあれば登録・更新処理をせずに差し戻しを行うという仕組みになっています。
今回のケースではsaveメソッドを実行しデータベースの登録を行う前に自動的に検証を行い、エラーがあればfalseが返ってきます。検証エラーの詳細はerrorsメソッドを使うことで確認することができます。

app/models/board.rb
class Board < ApplicationRecord
    validates :name, presence: true
    validates :description, presence: true
end

今回は、Railsがデフォルトで用意してくれている検証用のヘルパーを利用し必須のデータが入っていなければエラーが出るようにしています。
記述方法は以下の通りです。

validates :[column], presence: true

3 ビュー

ビューではvalidationの検証エラーのエラー文を取得して表示できるように設定していきたいと思います。
具体的には、まずerrorsメソッドを使って検証エラーの詳細が入ったオブジェクトを取得します。このオブジェクトにfull_messagesメソッドを使ってエラー文の全てを配列で取得します。さらにこれをeach文で表現することにより、各エラーがリストで表示されるようにします。

app/views/new.html/haml
%ul
  - @board.errors.full_messages.each do |message|
    %li= message

コンソールで確認すると以下のようになっています。
スクリーンショット 2020-11-20 19.58.44.png
errorsメソッドで検証エラーの詳細がわかります。
また、full_messagesメソッドでエラー文の全てが配列で取得できていることがわかります。
これをeach文を使ってリスト化しているため、検証に引っかかった文章を表示することができます。

以上で、新規登録機能の実装は完了です。

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

RailsのDocker化 〜その2〜

とりあえず、Railsアプリケーションを動かしてみる

まずは、どういうところに環境依存があるのか、また何がめんどくさかったのか思い出すために、
Railsアプリケーションをcloneしてlocal環境で動かすところまでやってみましょう。

■sample project
https://github.com/k-tetsuhiro/rails_docker_sample

こちらはruby 2.7.2と Rails 6.0.3.4 で作成されたsampleアプリです。
これを好きなところにgit cloneしてみてください。
これを bundle exec rails s で動かせるところまで持っていってみてください!
動かすまでに様々な問題点が出てくると思います。

出てくる問題点

まずはlocalのRubyのバージョンに2.7.2が入っているでしょうか?
rbenvを使用してる場合 rbenv versions で確認できます。

$ rbenv versions
  system
  2.3.8
  2.6.3
  2.6.5
  2.6.6
  2.7.0
  2.7.1
* 2.7.2 (set by /Users/kakuno/.rbenv/version)

どうでしょうか?
2.7.2が入ってない場合、まずlocal環境にruby2.7.2のインストールが必要になります。

さっそく環境依存が出てきてしまいました!!

この先も同じような環境依存が出てくる

起動するまでにlocalのこのような環境依存にこまるかと思います
・rubyのversion
・Railsのversion
・MySQLのversion
・yarnのversion
etc...

その環境依存 dockerで解決しよう!

というわけでここで出てくるのがdockerです!

誰かが環境を整えたRailsアプリケーション
それをimageとして保存しちゃおう!!

すべての環境(Ruby, Rails, MySQL, etc...)を込み込みでimageにしてしまえば、そもそも環境依存はおきないよね!
というイメージです!

更に利点として、local環境がMacだろうがWindowsだろうが、実際にRubyやRailsが入っているのはdockerのimageなので、どのマシンでも関係なく動きます!

dockerで動かしてみる

dockerで動かすことにすごいメリットを感じますね!
では次回はどうしたら今のRailsアプリケーションをdockerで動かすことができるのか?
ここを説明していきます。

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

form_withについての周辺知識

はじめに

脱初心者にむけてアウトプットをしていこうと思って記事を書いております。
間違ったことがありましたら、ぜひコメントいただけると幸いです。

form_withとは何か

form_withとはブラウザにデータのリソースの入力や
編集を行うフォームを生成するために使用される。
(Railsで何かしらに情報を送信したい時に使うメソッド)という理解で大丈夫だと思います。

<%= form_with(モデル or URLベース,[スコープ],[オプション]) do |f| %>
  フォームの内容
<% end %>

form_forとform_tagについて

これらのヘルパーメソッドはRails5.1で導入されたform_withが導入されたことによって非推奨となりました。
(form_withはこれらの機能をまとめたもの)という理解で大丈夫です。
form_withを理解するためにはこれらのヘルパーメソッドを理解するにはこれらを知ることでさらに理解を深めることができます。

form_for

<%= form_for(モデル,[オプション]) do |f| %>
    フォームの内容
<% end %>

form_forは入力されたデータをモデルなどに保存する時に使われていた

form_tag

<%= form_tag(URL,[オプション]) do |f| %>
  フォームの内容
<% end %>

form_tagは入力されたデータを保存する必要がない時に使用されていた

form_withはこれらの機能を併せ持ったヘルパーメソッドだと覚えておきましょう。

form_withには様々なオプションがあり、今まで、分けられていた機能がまとまったメソッドなので、少しわかりづらいこともありますが、なれるととても便利な機能(らしい)なので、どんどん使って慣れていこうと思います。

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

syntax error, unexpected end-of-input, expecting end について

はじめに

初学者のアウトプットです。
訂正、追記等あればご教示いただけると幸いです。

エラー文章

エラー文章が発生しては、長くて読む気が無くなりそうですが、
大抵の内容は、エラーが発生しているアドレスを示してくれており、
SyntaxError: /アドレス/.../: エラー内容

みたいな感じになっています。
結局、アドレスのエラー内容を修正すればエラーは解決するので、
現段階では、学習の為にもエラー内容部分を翻訳してエラー修正にするのが一番だなと感じています。

syntax error, unexpected end-of-input, expecting end

表題のようなエラーが出ました。

翻訳 → 構文が誤っています、予期していない終わり方です、endを追加すると良いと思われます。

見直してみると構文にendが足りていませんでした。
endを追加してエラー解消です。

まとめ

複雑になればなるほど発生しそうなエラーです。
こんなのは体で覚えてしまってすぐ対処出来るようになりたいです。

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

【Rails5】check_boxで任意の文字列を渡す方法(ガチ初学者向け)

初投稿です。
初学者のため、参考程度に見ていただければ幸いです。

至らない点、わかりにくい点などあればコメントで教えていただければ幸いです。

version

Ruby 2.5.7
Rails 5.2.4.4

How to

まずチェックボックスフォーム機能を実装する際に、デファルトだとか機能ようなコードになると思います。(form_withを使う場合)

<%= form_with model: 変数, url:遷移先のパス do |f| %>
   <%= f.check_box :カラム名,{オプション(クラス名など} %> ←チェックボックスを表示
   <%= f.submit %>
<% end %> 

このようにするとデフォルトで"1"か"0"が送られます。

<%= form_with model: 変数, url:遷移先のパス do |f| %>
   <%= f.check_box :カラム名, {オプション(クラス名など}, "true","fales" %> ←チェックボックスを表示
   <%= f.submit %>
<% end %> 

このように記述すればtrue,falesを渡すことができます。

<%= form_with model: 変数, url:遷移先のパス do |f| %>
   <%= f.check_box :カラム名, {オプション(クラス名など}, "attende","absence" %> ←チェックボックスを表示
   <%= f.submit %>
<% end %> 

同様にtrue,falesの部分を書き換えれば、任意の値を渡すことができます。

ポイントは、オプションの後ろに記述することです。
渡したい文字列の後ろにクラス名などのオプションを指定するとエラーが出ます。

簡単なようで意外と車学者がつまずくポイントかなと思い記事にさせていただきました。

誰かの参考になれば・・・

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

[Rails]selfについての理解を深めたい[初心者]

はじめに

プログラミングの超初心者です。
selfへの理解が全然できていないと感じたので、アウトプットも兼ねて、学んだことをまとめます!
それではいきましょう!!

selfって何のこと?

オブジェクトそのものを指す特殊な変数です。

と、説明だけでは非常に分かりにくいですよね。
一緒に、どのように使用されているのか、確認していきましょう。

使用例

book.rb
class Book < ActiveRecord::Base
  def hoge                 #インスタントメソッド
  end

  def self.hoge            #クラスメソッド
  end

  def author               #インスタントメソッド
    self.hoge              #インスタンスメソッドのhogeが呼ばれる
    hoge                   #インスタンスメソッドのhogeが呼ばれる
    self.class.hoge
  end

  def self.author          #クラスメソッド
    self.hoge              #クラスメソッドのhogeが呼ばれる
    hoge                   #クラスメソッドのhogeが呼ばれる
  end
end

クラスメソッド(特異メソッド)とは?

さて、クラスメソッドとは一体、何を指しているのでしょうか?
ざっくりの認識では、クラス全体の中から何かを取り出す際に使用するものです。
一つ一つのインスタンスには紐づいておらず、クラスに対して働きかけるメソッド、とも言い換えられるでしょう。
後述する特異クラスと分ける形で、特異メソッドと呼ばれます。

メソッドの呼び出しには、必ずレシーバが必要になります。
クラスメソッドには、あらかじめレシーバとしてself(自分自身のオブジェクト)が指定されているので、クラスから直接呼び出せる仕掛けになっています。

インスタントメソッドとは?

では、インスタントメソッドとは何ぞや、となるでしょう。
簡単に説明すると、特定のインスタンスから、何かを取り出す際に使用されるもの、という認識でOKです。
逆に言うと、メソッド名にselfを付けないと、指定したオブジェクト(レシーバ)に対してしか、そのメソッドを使用することが出来ないということです。

特異クラスとは?

クラスメソッドの中で、別名特異メソッドと紹介していたのを覚えていますか。
実際は、クラスメソッドで毎回selfと記載すれば大丈夫なのですが、クラスメソッドが複数に渡る場合は、いちいちselfと打っていくのが面倒ですよね。
そこで使えるのが、特異クラスです。
使い方は、以下の通りです。

book.rb
class Book < ActiveRecord::Base
  class << Book
    def say_thanx
      puts "#{self}, thanx"
    end
  end
end

↑に、book = 'book'という条件を与えた上で、book.say_thanxを打ち込むと、book, thanxと出力されます。
全体の流れは、クラスメソッドと似通っていますが、<< Bookにより、bookオブジェクトの特異クラスを呼び出しているため、あくまでもオブジェクトに対しての特異メソッド、という定義です。

おわりに

自分でこの記事を書いていて、なんて初心者にはとっつきにくい概念なんだろうと思っています。
記事の中では、あくまで概念にしか触れていませんので、実際にアプリ制作で使用する際は、もっと実地に即した形での運用になると思います。
「なぜこの記載をしているのか」を大切にして、ただの丸暗記に終始することの無いよう、自戒を込めて執筆しました。

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

enumを使用して投稿の公開設定をする(二択式)

はじめに

やりたいこと

  • 投稿機能に公開ステータスを持たせる
  • デフォルトは非公開にする
  • 公開設定をセレクトボタンで選択させる

 やってみて

enum自体は非常に奥が深いモジュールだと感じましたが、私がやりたいことは簡単に実装できました。
ですので、この投稿は初心者でもすぐに実践できる内容になっていると思います。

参考

Rails公式リファレンス
Pikawaka <【Rails】enumチュートリアル

手順

DB設計

db/migrate/create_item.rb
class CreateItems < ActiveRecord::Migration[6.0]
  def change
    create_table :items do |t|
      t.string :title,        null: false
      t.string :tagbody
      t.string :url
      t.text :body,           null: false
      # statusカラム、integer型、deroult: 0 を指定
      t.integer :status,      null: false, default: 0
      t.integer :category_id, null: false
      t.references :user,     foreign_key: true

      t.timestamps
    end
  end
end

boolean型で作成することもできますが、2個以上の定数と紐付けたい場合はinteger型を選択。

Model

models/item.rb
enum status: { closed: 0, open: 1 }

デフォルトを非公開にするので「closed」という定数を「0」に設定しました。

Controller

通常通りストロングパラメーターとcreateアクションを定義します。

controllers/items_controller.rb
class ItemsController < ApplicationController

  def create
    @item = Item.new(item_params)
    if @item.save
      redirect_to root_path
    else
      render :new
    end
  end

  private

  def item_params
    params.require(:item).permit(:title, :tagbody, :body, :url, :status, :category_id).merge(user_id: current_user.id)
  end

end

View

formの書き方

views/items/new.html.erb
<%= form_with model:@item, url: '/items', local: true do |f| %>

  <%= f.select :status, Item.statuses.keys.to_a, {}, { class:'item-open' } %>

<% end %>

解説

f.selectの使い方

<%= f.select 属性, 選択肢の集合, {オプション}, {HTMLオプション} %>

Item.statuses.keys.to_a について

enumで指定した定数はハッシュの形で配列になっています。
したがってstatuses(複数形).keys.to_a(配列を返すメソッド)の形で呼び出します。

実装例

投稿画面

画面収録 2020-11-20 16.42.42.mov.gif

DB

スクリーンショット 2020-11-20 16.44.41.png

数字で保存される

呼び出し方

公開記事だけをインスタンス変数に含める

controllers/item_controller.rb
class ItemsController < ApplicationController

  def index
    @items = Item.open.order('created_at DESC')
  end

end

おわりに

今回はenumを使用して二択のセレクトボックスからDBに保存する形を実践しました。
今後はより多くの選択肢を与えたり、enumを使用して検索・確認・更新などの機能を実装してみたいなと思います。

✔︎

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

【Rails】ジャンル&商品名の検索機能実装

前提

ジャンルの一覧表示の中で検索したいジャンルをクリック or 検索フォームに商品名を打ち込んで「検索」ボタンをクリックすることで、searchアクションを実行します。ジャンルor商品名の検索結果を遷移後のsearch.html.erbで表示するような機能を実装していきます。検索結果画面には、該当する商品のレコード内容(商品写真や商品名など)をeach文で表示する形になります。モデル同士のアソシエーションなどの部分は省いていきます。
通常の検索機能の実装がしたい方はこちらを参考にしてください(gemなし複数モデル対象の検索機能)

実装の条件、太文字は必須のカラムです(カラム名は違くても問題ありません、デフォルトでつくidカラムは一応記述しました)

・genresテーブル(PK:id、ジャンル名:name、ジャンル有効or無効:is_active(boolean))
・itemsテーブル(PK:id、ジャンルのid(FK):genre_id、商品名:name、その他、商品写真や商品説明など)

実装環境

ruby '2.6.3'
gem 'rails', '~> 5.2.4', '>= 5.2.4.3'
gem 'bootstrap-sass', '~> 3.3.6' (無くても可)
gem 'devise'(無くても可)
・bootstrapはtableタグや、class指定でレイアウトを整えています。
・deviseはヘルパーメソッドを時々使用していますが、記述しなくても検索機能への影響はありません。

実装後のイメージ

image.png
商品の一覧画面でジャンル検索枠内のジャンル名をクリックすると、写真の検索結果一覧画面に結果が表示されます。
商品の一覧画面で検索フォームに商品名(例:ケーキ)、検索方法を指定後「検索」を押すと写真の検索結果一覧画面に結果が表示されます。
検索結画面の写真にジャンル名を載せてないのでわかりずらいですが、「ケーキ」ジャンル(genre_id:1)で検索をした結果、itemsテーブルのgenre_idカラムが1の商品だけ表示されている状態です。
※筆者は検索結果画面(写真)にも検索フォームとジャンル検索のサイドバーを表示させています。

実装していきます!

1⃣コントローラー新規作成

$ rails g controller Search search

2⃣Viewに検索フォーム & searchアクションへのlink_toを記述

@genresはviewページのコントローラーで@genres = Genre.allで定義してください。
・検索に必要な情報であるvalue(検索ワード)とhow(検索方法)をコントローラーへ送信できるようにそれぞれ定義しています。
・form_with内では、フォームに打ち込まれた文字列をvalue、プルダウンメニューで選択された「検索方法」をhowとして送信できるように定義しています。
・ジャンル検索では、genre.nameをリンクにしてsearchアクションを実行するようにしてます。search_pathの第一、第二引数にvalueとhowを定義してコントローラーで必要な情報を受け取れるようにしています。valueにgenre.idを指定してる理由ですが、itemsテーブル内にgenre_idカラムが存在するからです。genre_idカラムが一致する商品だけ探し出す処理をコントローラーで定義します。

items/index.html.erb
<div class="name-search">
  <% if customer_signed_in? %>                         #deviseのヘルパーメソッドです、省いても問題ありません。
    <h5>商品名検索</h5>
    <%= form_with url: search_path, method: :get, local: true do |f| %>
      <%= f.text_field 'search[value]', size: "26x4" %><br>
      <span class="search-button col-xs-12 text-right">
        <%= f.select 'search[how]', options_for_select({ "部分一致" => "partical", "前方一致" => "forward", "後方一致" => "backward", "完全一致" => "match" }) %>
        <%= f.submit :"検索" %>
      </span>
    <% end %>
  <% end %>
</div>
<table class="table table-bordered genre-table">
    <thead>
      <tr>
        <th>ジャンル検索</th>
      </tr>
    </thead>
    <tbody>
    <% @genres.each do |genre| %>
      <tr>
        <td>
          <%= link_to genre.name, search_path('search[value]': genre.id, 'search[how]': "match") %>
        </td>
      </tr>
    <% end %>
    </tbody>
</table>

3⃣コントローラー作成

筆者は商品名検索機能を実装してからジャンル検索をどのように実装しようか悩み、初めはsearchアクションとは別のアクションとViewページを作成する必要があるかなぁ、、と悩みましたがitemsテーブルにgenre_idを持たせていることと、itemのnameは文字列(string)なのに対してgenre_idは整数型(integer)なので下記にある.orのwhereで取得できるのではないかと考えました。(商品名が1とかだとどうなってしまうのかは試してませんが、、)

search_controller.erb
class Customer::SearchController < ApplicationController
  def search
    @value = params["search"]["value"]         #データを代入
    @how = params["search"]["how"]             #データを代入
    @datas = search_for(@how, @value)          #def search_forを実行(引数に検索ワードと検索方法)
  end

  private

  def match(value)
    #.orを使用することで、valueに一致するカラムのデータをnameカラムとgenre_idカラムから探してきます。
    Item.where(name: value).or(Item.where(genre_id: value))
  end

  def forward(value)                              #forward以降は商品名検索の定義しかしてません。
    Item.where("name LIKE ?", "#{value}%")        
  end

  def backward(value)
    Item.where("name LIKE ?", "%#{value}")
  end

  def partical(value)
    Item.where("name LIKE ?", "%#{value}%")
  end


  def search_for(how, value)
    case how                     #引数のhowと一致する処理に進むように定義しています。
    when 'match'                 #ジャンル検索の場合はmatchで固定してるので、必ず'match'の処理に進みます。
      match(value)
    when 'forward'
      forward(value)
    when 'backward'
      backward(value)
    when 'partical'
      partical(value)
    end
  end
end

検索結果表示(search.html.erb)

後は検索結果を表示するだけです。@datasをeachで該当商品の数だけ繰り返し処理します。[ブロック変数data.カラム名]でitemsテーブルの情報を表示しています。

search.html.erb
<% if @datas.empty? %>
  <h2>該当する商品はありません</h2>
<% end %>
  <% @datas.each do |data| %>
    <div>
      <%= link_to item_path(data.id) do %>         #写真を商品詳細へのリンクにしていますが、必要なければ省略してください。
        <%= attachment_image_tag data, :image, size: "190x150", class: "img_hover" %><br>
      <% end %>
      <p><%=  data.name %></p><br>
      <p>¥<%= data.price_without_tax %></p>
    </div>
  <% end %>

以上で実装できたかと思います。ジャンル検索、商品名検索セットでの記述なので参考にならなかった方はすいません。

おまけ

筆者はwhere分の.orを思いつく前までは、def match(value)の部分でif文を使ってvalue == integerならgenre_idを探しに行かせようと定義してみたりしましたが結果的に色々失敗して(他に何種類か試しましたが)、上記の処理に行きつきました。なにかもっと効率的な処理があればコメントお願いします。

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

【Ruby】バイナリーサーチを用いた配列内の要素検索

配列内に任意の値が存在するかを確認するプログラムを作成します。
任意の値が配列内に存在しない場合は、「値は配列内に存在しません」と表示し、存在する場合は、配列の何番目にあるかを表示します。

今回のプログラムではバイナリーサーチという、配列を2分割した検索方法を用います。
例えば以下のような配列がある場合、中央値である「3」から左右と比較して検索する手法です。
これにより検索したい値が「1」であった場合、「4,5」は比較を行わないため処理速度の向上につながります。

ruby
array = [1, 2, 3, 4, 5]

回答例

ruby
def binary_search(array, number_of_elements, target)
  left = 0                       //配列の先頭の番号
  right = number_of_elements - 1 //配列の末尾の番号
  while left <= right            //配列番号が逆転するまで繰り返し
    center = (left + right) / 2  //中央値の配列番号の決定
    if array[center] == target   //配列の中央値が検索したい値であった場合
      return center
    elsif array[center] < target //配列の中央値<検索した値であった場合
      left = center + 1          //arrayの「1,3,5,6,9」を対象外とする設定
    else
      right = center - 1         //それ以外の場合、arrayの「9,10,13,20,26,31」を対象外とする設定
    end
  end
  return -1                      //何も当てはまらなかった(配列に存在しなかった場合の返り値)
end

array=[1,3,5,6,9,10,13,20,26,31]

puts "検索したい数字を入力してください"
target = gets.to_i
number_of_elements = array.length

result = binary_search(array, number_of_elements, target)

if result == -1
  puts "#{target}は配列内に存在しません"
else
  puts "#{target}は配列の#{result}番目に存在します "
end

解説

まずはメソッド外。
1. 配列「array」の準備
2. 任意の文字「target」の準備
3. 配列の要素数「number_of_elements」の準備
これら3つの変数を実引数に設定し「binary_serach」メソッドを実行しています。

続いてメソッド内。
1. 配列の先頭及び末尾の配列番号を「left」「right」に代入。
2. while文にて、この「left」「right」の値が逆転するまで繰り返しを行う設定。
3. 以下の記述で中央値の配列番号を取得しています。今回の配列の要素は偶数個であるため、「(0+9) / 2 = 4.5」となります。配列番号4.5は存在しませんが、rubyにおける計算式の小数点は切り捨てられるため、今回の配列における中央値は「array[4] = 9」となります。

ruby
center = (left + right) / 2

if文による条件分岐を行い、左右のどちらに値が存在するかを繰り返し判定します。
最終的に配列内に任意の値が存在しなかった場合は「return -1」という返り値を渡してメソッドを終了します。

最後に再びメソッド外。
1. 任意の値が存在しなかった場合は「-1」が戻ってくるので、存在しない旨のメッセージを出力。
2. 任意の値が存在した場合は正しい値が戻ってくるので、存在する箇所を出力。

バイナリーサーチ知らないと思いつかない方法だと思いますので、しっかり覚えておきたいです。
ご意見等ございましたらコメントをいただけますと幸いです。

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

自走力に必要なもの?

私の結論は楽しみながら学習することです。
なぜなら、嫌々でプログラミングをしても挫折してしまうからです。

スクール2週間目に突入して…

スクールのカリキュラムを2週間終え感じたことは、「事前に勉強していて良かった...」
スクールでの受講を悩んでいる方や、これから受講するって方は”未経験からエンジニアへ”という謳い文句を勘違いして知識ゼロで臨むとなかなか苦労すると思いました。実際に自分の所属している6人のグループではProgateで事前学習していたのは自分だけでした。

私の場合は、ブラインドタッチがあまり出来ず、苦労したのでタイピング練習をもっとすべきだったなと反省をしています。また、カリキュラムの進行はスピーディで、合間にある中間試験など理解度を高めながら、スピード感を持ってカリキュラムを進めるのに、不安と焦りを感じながら学習を続けています。

転職活動の心構え :beginner:

一度でも内容に触れたことがあれば記憶の定着もしやすいですし、理解度も増します。そして、気持ちにも余裕が生まれます。

この点は、転職活動においても言えます。内定をもらって終わり!ではなく、やっとスタート地点に立てただけなので、入社前にどれだけ内定先で必要となるプログラミング言語、SQLや基本情報試験を事前学習できるかで職場での貢献度を大きく左右すると言えます。

スクール受講前(2ヶ月間) :alarm_clock:

・エンジニア界隈のリサーチ
(親戚の自社開発企業を訪問)
・キタミ式基本情報技術者
・Progate2周(HTML,CSS,Ruby,Rails)
・Youtube(自社開発、受託開発、SESの違い)

8月からProgateで独学し、スクールの無料カウンセリングを受けて受講する覚悟が決まったのが9月上旬。スクール受講開始が10月上旬。その間に23年間お世話になったWindowsからスタバで作業しているとかっこいいと言われるMacへシフト
https://twitter.com/by_miwa30/status/1301541645859434496

エンジニア界隈のリサーチ :pencil:
私は、高校生の時に情報ビジネス科でJavaとC言語を学んでいたこともあり、進路先にエンジニアを考えていました。ただ、求人が1件しかないこと、エンジニアとして働くイメージが湧かず断念。その当時、進路相談でお世話になったのが親戚の自社開発企業です。

そして、今回2度目のエンジニア現場で1時間半、話を伺いました。「基本情報技術者資格を取得するつもりがなくても、学習しておいたほうが良い!クラウドインフラではAWSが主流だよ。」などネットだけでは知り得ない現場の声を伺えました。とても貴重なご意見ありがとうございました。

キタミ式基本情報技術者 :books:
上記にある通り、現役のエンジニアのご意見から基本情報技術者を早速購入。人から言われたことは、すぐに一度調べて必要なら実践してみる考えがモットーな私。このスピード感と素直さを強みとして有益な情報かどうかを取捨選択できる収集心と分析力を磨き上げ自走力を最大化させていければと思います。

本の内容は、試験対策のためだけの資格本ではない!と思いました。なぜなら、イラストやマンガを取り入れながら暗記ではなく、楽しく理解することを大切にしている印象を持ったからです。プログラミングも暗記するつもりで学習しては行けないと言われます。いくら知識があっても、その知識を応用できる柔軟性が必要で知識を繰り返し学習することで記憶に定着されます。その為には、学習したことをアウトプットすること、復習することが欠かせません。この考えを改めて痛感させられただけでも、購入した甲斐があって良かったです。

Progate :computer:
カリキュラムが分かりやすいこと、書いたコードの動きも確認できる。環境構築が不要で自分が書いたコードを体感でき楽しくゲーム感覚で進めることが出来ました。高校の時のような講義形式で学ぶよりも、自分のペースで進められ、わからない点すぐにカリキュラムを見返すことが出来るのが嬉しかったです。
https://twitter.com/by_miwa30/status/1291192741280399360

Youtube :film_frames:
Web系エンジニアとは何か?IT業界とは?等に特化して学びました。現役の方や元々やっていた方の話を視聴して、自分のこれから進んで行く道をじっくり考える時間となりました。自社開発、受託開発、SESそれぞれのメリットデメリットを学べたので今後の転職活動にとても参考になりました。

振り返り・感想 :triangular_flag_on_post:

今思うと、Progateのコードの中身はめちゃくちゃだと思います。DRYの原則を意識すれば、もっとスッキリさせられるはず。でも、何事も初めから完璧にできる人はいません。

冒頭で「事前に勉強していて良かった‥.」と言いましたが、1日10時間、10週間を終えた時に後悔することがないようにしたいです。その為には、愚直に毎日インプットしてはアウトプットを繰り返すこれに尽きると思います。この考えを持つきっかけをくれたプログラミング。本当に出会えて良かったなと思いました。

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

個人アプリ開発日記 #3

まずはuserリソースから

ユーザー登録機能
ログイン機能
ユーザー詳細表示機能
ユーザー編集機能
ユーザー削除機能

を実装していきます!

users_controller の実装

class UsersController < ApplicationController
  include SessionsHelper
  before_action :set_user, only: [:show,:edit,:update,:correct_user]
  before_action :logged_in_user, only: [:index,:edit,:update]
  before_action :correct_user, only: [:edit, :update]

  def index
    @users = User.all
  end

  def new
    @user = User.new
  end

  def create
    @user = User.new(user_params)
    if @user.save
      log_in(@user)
      redirect_to @user
    else
      render 'new'
    end
  end

  def show
  end

  def edit
  end

  def update
    if @user.update(user_params)
      redirect_to @user
    else
      render
    end
  end

  def destroy
    user = User.find(params[:id])
    user.destroy
  end

  private
    def user_params
      params.require(:user).permit(:nickname,:email,:password,:password_confirmation)
    end

    def set_user
      @user = User.find(params[:id])
    end

    def logged_in_user
      unless logged_in?
        store_location
        # ユーザーがいきたがってたページを記憶
        flash[:danger] = "Please log in"
        redirect_to login_url
      end
    end

    def correct_user
      redirect_to(root_url) unless current_user?(@user)
    end
end

長いので、注目ポイントは フレンドリーフォワーディングを実装したlogged_in_userメソッドと、correct_userメソッドです
store_locationメソッドと、current_user?(@User
は sessions_helperメソッドに定義されてるので見ていきましょう

sessions_helper

module SessionsHelper

  # 渡されたユーザーでログインする
  def log_in(user)
    session[:user_id] = user.id
      # このコードを実行すると、ユーザーのブラウザ内
      # の一時cokkiesに暗号化済みのユーザーidが自動で
      # 作成されます
      # この後のページでsession[:user_id]を使って
      # 元通りにIDを取り出すことができる
  end

  def log_out
    session.delete(:user_id)
     @current_user = nil
  end

  # ユーザーのセッションを永続的にする
  def remember(user)
    user.remember
    # rememberメソッド呼び出し
    # つまりハッシュ化したトークンをDBに保存
    cookies.permanent.signed[:user_id] = user.id
    # 永続的に保存できるクッキーの保存期限示すため
    # permanentを書く(本当は20年)
    # 生のuser_idができるとだめなので
    # signedで暗号化
    cookies.permanent[:remember_token] = user.remember_token
    # 期限はOK
    # ランダムな文字列のトークンをクッキーに保存
  end

    # 永続セッションを破棄する
    def forget(user)
      user.forget
      #DBのremember_digestからデータ破棄
      cookies.delete(:user_id)
      cookies.delete(:remember_token)
      # クッキーからもユーザーの情報削除
    end

  # 現在ログインしているユーザーの情報を取得
  def current_user
    # DBの問い合わせの数を可能な限り小さくしたい
    # logged_in?メソッドでも使われてるし、、、
    if user_id = session[:user_id]
      # セッションがある場合
      # すなわちログインしてる時のみ

      # sessionにアクセスした結果を変数に
      # 入れておいてあとで使いまわした方が
      # 早くなる
      @current_user ||= User.find_by(id: user_id)
      # find_byでデータベースにクエリを投げる
      # ブラウザのセッションにあるuser_idをもとにUser定義

      # find_byの実行結果をインスタンス変数に保存する
      # ことで、1リクエスト内におけるデータベースへの
      # 問い合わせは最初の一回だけになり、 
      # 以後の呼び出しではインスタンス変数の結果を
      # 再利用する

      # すでに@current_userが存在する場合って何?
      # 一回current_userを実行したら、
      # @current_userがあるのでそれを使ってね

      # sessionのuser_idがあるということは
      # 既にログインしてるといてDBにユーザーの情報があるはず。
      # だからsessionのuser_idをDBでfind_byかければいい
    elsif  (user_id = cookies.signed[:user_id])
      # sessionが張られてなかったらcookiesにあるかも
      user = User.find_by(id: user_id)
      if user && user.authenticated?(cookies[:remember_token])
        # nilガード
        # クッキーのuser_idとremember_tokenが一致してる
        log_in user
        @current_user = user
      end
    end
  end

  def current_user?(user)
    user && user == current_user
    # nilガード
  end

  # ユーザーがログインしていればtrueを返す、
  # その他ならfalse
  def logged_in?
    !current_user.nil?
    # nilじゃなかったら
    # すなわちログインしてたらfalseが
    # 返ってくるけど、それだとif文とか
    # 使う時にややこしいので、!
    # で返り値の真偽値を逆にして
    # trueを返すようにしてる
  end

  # 現在のユーザーをログアウトする
  def log_out 
    forget(current_user)
    # カレントユーザーのremember_digestを破棄
    # トークン、user_idを破棄
    session.delete(:user_id)
    # セッションも破棄
    @current_user = nil
    # インスタンス変数の値を更新
  end

  # フレンドリーフォワーディングの処理

  # 記憶したURL(もしくはデフォルト値にリダイレクト)
  def redirect_back_or(default)
    # これはいい命名!!
    redirect_to(session[:forwarding_url] || default)
    # store_locationでsession[:forwarding_url]を定義
    session.delete(:forwarding_url)
    # redirectできたらforwarding_urlって情報はいらないので
    # 破棄しましょう
    # セッション情報を保持したままにしておくと、次のログインの
    # 時もこれを読み込んでおかしくなる
  end

  # アクセスしようとしたURLを覚えとく
  def store_location 
    session[:forwarding_url] = request.original_url if request.get?
    # request.original_url はリクエストされたurl
    # それをセッションに保存

    # if request.getでなぜGETリクエストだけ対応してるかと
    # いうと、before_actionのlogged_in_userで
    # updateに制限かかってるけど,PATCH users/:idを
    # 保存する意味はない
    # なぜならユーザーが元々いきたいのはeditとかだから
  end
end

僕の下手くそなコメントだけじゃ意味わからないと思うので、rails tutorialやってみてください、、、笑
本当に色々勉強になりました、、、

sessions_controller

class SessionsController < ApplicationController
  include SessionsHelper
  def new
  end

  def create
    user = User.find_by(email: params[:session][:email].downcase)
    # 受け取ったemailからfind_byでデータベースに
    # 問い合わせてユーザー取得
    if user && user.authenticate(params[:session][:password])
        # まずそのユーザーがいるかnilガード
        # いたらpasswordとauthenticateかける
        log_in user
        params[:session][:remember_me] == '1' ? remember(user) : forget(user)
        # check boxがonの時は1になるので1の時は
        # ? 以降がtrueの処理
        # : 以降がfalseの処理
        redirect_back_or user
        # redirect_back_or メソッド呼び出し
        # 引数にuser_url(user)を渡す
    else
      flash.now[:danger] = 'invalid email/password combination'
      render 'new'
    end
  end

  def destroy
    log_out if logged_in?
    # 二つのブラウザで同時ログアウトとかされたらバグが起こるので
    # ログイン
    redirect_to root_url
  end
end

注目ポイントはnilガードです

nilガードとは、例えば

user = User.find_by(email: params[:session][:email].downcase)

とかでユーザーを取得できない場合に

if user && user.authenticate(params[:session][:password])

まずuserで本当にuserがいたのか確かめます

コンピューターは Aor B と条件式があったら左(A)から実行するので、
その特性を生かしたプログラミングです。面白いですね。

user.rb

class User < ApplicationRecord
  attr_accessor :remember_token
  # remember_tokenというメソッドを作成
  # password,password_comfirmationみたいな
  # 変数のように扱える
  # u.remember_token的なことができる
  has_many :drinks
  before_save  { self.email = email.downcase }
  has_secure_password
  validates :nickname,  presence: true, length: { maximum: 50 }
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, presence: true, length: { maximum: 255 },
                    format: { with: VALID_EMAIL_REGEX },
                    uniqueness: { case_sensitive: false }
  validates :password, presence: true, length: { minimum: 6 },allow_nil: true
  # ユーザー更新時に空のパスワードでも大丈夫
  # has_secure_passwordの方でpasswordの存在性を検証するから大丈夫



  # 渡された文字列のハッシュ値を返す
  def User.digest(string)
    cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
                                                  BCrypt::Engine.cost
    BCrypt::Password.create(string, cost: cost)
  end

  # ランダムなトークンを返す
  def User.new_token
    SecureRandom.urlsafe_base64
  end

  # 永続的にログインするためにトークンをDBに保存
  def remember
    self.remember_token = User.new_token
    # 何のトークンか分かりやすいから
    # remember_tokenって名前を作った
    update_attribute(:remember_digest,User.digest(remember_token))
    # ハッシュ化したトークンをremember_digestに保存
  end

  # 渡されたトークンがダイジェストと一致したらtrue
  # を返す
  def authenticated?(remember_token)
    return false if remember_digest.nil?
    # 二種類のブラウザを使用してログアウトした場合
    # cookiesのremember_tokenはあるけど、
    # サーバー側でremember_digestをnilにしてるから
    # nilに対して.is_password?とかやるとfor nil classエラーが起きちゃう
    # remember_digestがnilの場合はfalseを返して、処理を止める

    # 後置if文に当てはまる条件があれば処理を止めて!
    # って場合はreturnとかで明示的に書くとif ~ else ~ end 
    # とか書かなくて済む
    BCrypt::Password.new(remember_digest).is_password?(remember_token)
  end

  # ユーザーのログイン情報を破棄する
  def forget
    update_attribute(:remember_digest,nil)
  end
end

これでユーザー機構に必要であろうメソッドが揃いました、、、!

どのメソッドをどのコントローラーや、ヘルパーに配置すべきかまだイマイチよく分かってないので、これから学んでいきたいですね

本当にこれでしっかり動くのか不安なので実際にビューを動かして次回試してみたいと思います、、、!

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

個人アプリ開発日記 #2

基本的なテーブル構造

users テーブル

Column Type Options
nickname string null: false
email string null: false, unique: true
password_digest string null: false
remember_digest string
reset_digest string
activation_digest string null: false
activated boolean default: false
activated_ad datetime null: false

Association

  • has_many :drinks
  • has_many :comments
  • has_many :trades

drinks テーブル

coffeeは不可算名刺なので初心者が扱うのが怖かった、、

Column Type Options
name string null: false
price integer null: false
explain text null: false
user_id integer null: false,foreign_key: true

Association

  • belongs_to :user
  • has_many :comments
  • has_many :tags,
  • has_many :tags,through: :coffee_tags
  • has_one :trade

tags テーブル

Column Type Options
name string null: false
  • has_many :drinks
  • has_many :tags,through: :coffee_tags

coffee_tags テーブル

Column Type Options
drink_id integer null: false,foreign_key: true
tag_id integer null: false,foreign_key: true
  • belongs_to :drink
  • belongs_to :tag

comments テーブル

Column Type Options
content text null: false
user_id integer null: false,foreign_key: true
drink_id integer null: false,foreign_key: true

Association

  • belongs_to :user
  • belongs_to :drink

addresses テーブル

Column Type Options
postal_code string null: false
prefecture_id integer null: false
city string null: false
house_num string null: false
building_name string
phone_num string null:false,unique: true
trade_id integer null:false,foreign_key: true

Association

  • belongs_to :trade

trades テーブル

Column Type Options
user_id integer null: false,foreign_key: true
drink_id integer null: false,foreign_key: true

Association

  • belongs_to :user
  • belongs_to :drink
  • has_one :address

次回は実際のコードに触れていきます

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

初心者はDeviseをカスタマイズするなという話

Deviseを実装したら迷走した

Deviseのことをよくわかっておらず、Contorollerやviewから全部作って設定しようとしてたので、戒めとして記事を書きます。

ControllerとViewは最初からある

Ruby初心者の私はこんなことも直感的に分からなかったんですね。

https://qiita.com/hakatatech/items/f991d54ff527edb844d9

この辺りの記事をみて「はえ〜コマンドでコントローラー作るんだなあ」とコピペで実装していきましたが、最初からあるのでこういうのは実装しなくてOKです。

それでも最初はコマンドを打たねばならない

まあgemファイル追加して必要なものは設定しないといけないので、こちらの記事を参考に実装してみてください。

実際に3日悩んだ結果、ここが一番簡潔で明快でした。

https://qiita.com/salvage0707/items/d3ddc889458ac186e62e

もう初心者はここの「Deviseの使い方」だけやってればいいと思いました。

email認証じゃなくしたいんですけど?

そういう方へは、なんとDeviseの公式記事がございます。

https://github.com/heartcombo/devise/wiki/How-To:-Allow-users-to-sign-in-with-something-other-than-their-email-address

英語ですが、email認証からusername認証にするための方法が全部書かれいてるのでDeepLで翻訳して読んでください。

終わりに

プログラミングの鉄則に「複数のタスクを同時に解決しようとするんじゃない、一個ずつやりなさい」というものがあるみたいですが、今回で身に沁みて分かりました。

あまりにもタスクが多いと「分からないことが分からない」みたいな状況に陥りがちなので、エラー画面から読み取って一つひとつ解決していきます。

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

個人アプリ開発日記  #1

:point_up:
初めましてHarasouです。これから個人アプリを開発するのでその進捗を更新指定いきたいと思います、、!

◎ポートフォリオ概要
■何を作るか
コーヒーの感想をみんなとシェアできて、コーヒーを購入できるアプリ

■なぜ作ろうと思ったか
コーヒーが大好きで、みんなのおすすめのコーヒーを
知りたかったけど、日本にコーヒー専用アプリみたいなのはなかった

スタバはみんな知ってるのに,スタバのサービスのコーヒーパスポートはみんな知らない

スタバには「コーヒーパスポート」と言う、コーヒーの記録を取れる手帳が実はあるんです、、、!

お洒落でございますね、、、(字が汚い,,)

こんな風に記録を取ることができます、、、!!

■必要な機能

コーヒー購入機能(提携した企業によって商品ラインナップが変わる)
投稿機能
ユーザー登録機能
ログイン機能
ユーザー詳細表示機能
ユーザー編集機能
ユーザー削除機能
投稿一覧表示機能
投稿に画像
投稿詳細表示機能
(ユーザー表示押したらそのユーザーの投稿表示)
投稿削除機能
フォロー機能
(いいね機能)
(レイティング機能)
コーヒータグ付け機能
検索機能
スタンプ機能
ページネーション
フレンドリーフォワーディング
動的なタイトル変更
メール認証
ゲストログイン
ハンバーガーメニュー
レスポンシブ化

次回細かな実装に入りたいと思います

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