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

視覚的に理解するフィボナッチ数と再帰的関数

はじめに

フィボナッチ数を求めるコードを書く問題に挑戦していたところ再帰的関数が出てきて頭がパンクしそうだったので整理するためにも記事にしてみます。
メインは再帰的関数ですがついでにフィボナッチ数とメモ化に関しても説明してあるのでよければ見ていってください。

フィボナッチ数列とは

本題ではないので詳しい説明は割愛しますが 0, 1 もしくは 1, 1 から始まり、前2つ前の数字を足し合わせると次の数になる数列のことです。
fib1.png
こんな感じ。

漸化式

「うわ何や難しい文字出てきたわ戻ろ」
と思って帰りかけた文系のあなた、もうちょっとお付き合いください。
ガチガチ文系の筆者ですがなんとか理解して噛み砕いてみたので大丈夫です。多分。

前項目の画像では21で数列が終わっていますが、その次の数字は何になるでしょうか?
まずはそれぞれの数字に番号をふってみましょう。配列番号を付けるイメージです。
忘れている方もいるかもしれませんが数学ではこの番号をと呼びます。$\tiny{筆者は忘れてました}$
fib2.png
21の次、つまり9番目の数字を求めたいわけです。
”前2つ前の数字を足し合わせると次の数になる”という性質を考えると7番目+8番目なので
 13+21=34 となります。
数列にふった”番号”に注目して式を作り替えるとどうなるでしょうか。
 7番目+8番目=9番目 ですね。
上の式をもう少しいじってみます。7番は9番の2つ前、8番は9番の1つ前なので
 (9ー2番目)+(9ー1番目)=9番目 ですよね。

ポイントは3つ

  • 0番目の数字は0
  • 1番目の数字は1
  • 2番目以降は前2つ前の数字を足し合わせると次の数になる

では、フィボナッチ数列(F)のn番目の数を求めるには?

 \large{F_{0} = 0,}\\
 \large{F_{1} = 1,}\\
 \large{F_{n} = F_{n-2} + F_{n-1}} \small{(n ≥ 0)}

このように数列の項同士の関係性を利用して作られた式を漸化式と呼ぶそうです。
この漸化式は再帰的な定義になっているので再起呼び出しを使って簡単にプログラムを書くことができます。

再帰的関数

再帰的関数とは何かを説明する前にまずは上記の漸化式を利用してどの様なプログラムがかけるのかをみていきます。

def fibonacci(n)
  return n if n == 0 || n == 1
  fibonacci(n - 2) + fibonacci(n - 1)
end

上でも書いた様に0番目の数=0、1番目の数=1なのでこの2択に当てはまる場合はreturnでnを返します。
それ以降は漸化式と同じ要領でプログラムを書いてあげればOKです。
fibonacciという関数内にまたfibonacci関数が存在しているわけですが、この様な自分自身を呼び出す関数を再帰的関数といいます。

処理の流れ

では処理の流れはどうなっているのでしょうか。
再起呼び出しは
関数内にある自分自身の中に入って処理→さらにその中の自分自身の中に入って処理…
という風にreturnに引っかかるまでループを繰り返します。
例えば n = 6 とした時、2行目は条件不一致によりスキップされ3行目へ進みます。
3行目では fibonacci(4) + fibonacci(5) となり、 fibonacci(4) の処理が始まるのです。

文章だと分かりづらいので図にしてみましょう。

fib3.png
こんな感じでreturnに行き当たるまでループします。
ループの戻り値である0か1を順番に足していくことで最終的にn番目の数を求めるようになっているわけですね。

再帰的関数の弱点

お気づきの方もいらっしゃるかと思いますが、シンプルに書ける便利な再帰呼び出しにも弱点があります。
それはreturnに行き当たるまでループし続けるということ!
上記の様な6番目程度の数を求める場合は大したループではないのですが、100番目の数を求めるとなると膨大な数のループを繰り返すためそれに比例して処理が終わるまでに時間がかかってしまうのです。
この欠点を補うのがメモ化です。

メモ化

上の図を見てもらうとA, B, Cと全く同じ処理が再び行われている部分がありますよね。
そこで文字通り一度計算した内容をメモ(キャッシュ)しておき2回目以降はメモをチラ見するだけで済むのがメモ化です。
メモ化を利用して書き直したプログラムがこちら

def fibonacci(n)
  @memo ||= []
  return n if n == 0 || n == 1
  @memo[n] ||= fibonacci(n - 2) + fibonacci(n - 1)
end

||=は「||」演算子の自己代入演算子です。
左辺が 偽 か 未定義 なら左辺に右辺を代入する、という意味になります。
2行目でmemoというメモ帳を用意してあげて、4行目で未だ計算したことのない値の場合はメモしておく流れになっています。

これで100番目の数字でも一瞬で求めることができるようになりました。お疲れ様でした!

おわりに

ここまでご覧いただいてありがとうございました。
フィボナッチ数の求め方は他にもあるようなので色々試してみたいと思います。

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

視覚的に理解するフィボナッチ数と再帰関数

はじめに

フィボナッチ数を求めるコードを書く問題に挑戦していたところ再帰関数が出てきて頭がパンクしそうだったので整理するためにも記事にしてみます。
メインは再帰関数ですがついでにフィボナッチ数とメモ化に関しても説明してあるのでよければ見ていってください。

フィボナッチ数列とは

本題ではないので詳しい説明は割愛しますが 0, 1 もしくは 1, 1 から始まり、前2つ前の数字を足し合わせると次の数になる数列のことです。
fib1.png
こんな感じ。

漸化式

「うわ何や難しい文字出てきたわ戻ろ」
と思って帰りかけた文系のあなた、もうちょっとお付き合いください。
ガチガチ文系の筆者ですがなんとか理解して噛み砕いてみたので大丈夫です。多分。

前項目の画像では21で数列が終わっていますが、その次の数字は何になるでしょうか?
数列を構成する各々の数を数列の「項」といいますが、この項に番号をふってみましょう。配列番号を付けるイメージです。
fib2.png
21の次、つまり9番目の数字を求めたいわけです。
”前2つ前の数字を足し合わせると次の数になる”という性質を考えると7番目+8番目なので
 13+21=34 となります。
数列にふった”番号”に注目して式を作り替えるとどうなるでしょうか。
 7番目+8番目=9番目 ですね。
上の式をもう少しいじってみます。7番は9番の2つ前、8番は9番の1つ前なので
 (9ー2番目)+(9ー1番目)=9番目 ですよね。

ポイントは3つ

  • 0番目の数字は0
  • 1番目の数字は1
  • 2番目以降は前2つ前の数字を足し合わせると次の数になる

では、フィボナッチ数列(F)のn番目の数を求めるには?

 \large{F_{0} = 0,}\\
 \large{F_{1} = 1,}\\
 \large{F_{n} = F_{n-2} + F_{n-1}} \small{(n ≥ 2)}

このように数列の項同士の関係性を利用して作られた式を漸化式と呼ぶそうです。
この漸化式は再帰的な定義になっているので再帰呼び出しを使って簡単にプログラムを書くことができます。

再帰関数

再帰関数とは何かを説明する前にまずは上記の漸化式を利用してどの様なプログラムがかけるのかをみていきます。

def fibonacci(n)
  return n if n == 0 || n == 1
  fibonacci(n - 2) + fibonacci(n - 1)
end

上でも書いた様に0番目の数=0、1番目の数=1なのでこの2択に当てはまる場合はreturnでnを返します。
それ以降は漸化式と同じ要領でプログラムを書いてあげればOKです。
fibonacciという関数内にまたfibonacci関数が存在しているわけですが、この様な自分自身を呼び出す関数を再帰関数といいます。

処理の流れ

では処理の流れはどうなっているのでしょうか。
再帰呼び出しは
関数内にある自分自身の中に入って処理→さらにその中の自分自身の中に入って処理…
という風にreturnに引っかかるまでループを繰り返します。
例えば n = 6 とした時、2行目は条件不一致によりスキップされ3行目へ進みます。
3行目では fibonacci(4) + fibonacci(5) となり、 fibonacci(4) の処理が始まるのです。

文章だと分かりづらいので図にしてみましょう。

fib3.png
こんな感じでreturnに行き当たるまでループします。
ループの戻り値である0か1を順番に足していくことで最終的にn番目の数を求めるようになっているわけですね。

再帰関数の弱点

お気づきの方もいらっしゃるかと思いますが、シンプルに書ける便利な再帰呼び出しにも弱点があります。
それは同じ項を何度も重複して計算してしまうことです!
上記の様な6番目程度の数を求める場合は大したループではないのですが、100番目の数を求めるとなると、同じ計算を何度も繰り返してしまうために処理が終わるまでに長い時間がかかってしまうのです。
この欠点を補うのがメモ化です。

メモ化

上の図を見てもらうとA, B, Cと全く同じ処理が再び行われている部分がありますよね。
そこで文字通り一度計算した内容をメモ(キャッシュ)しておき2回目以降はメモをチラ見するだけで済むのがメモ化です。
メモ化を利用して書き直したプログラムがこちら

def fibonacci(n)
  @memo ||= []
  return n if n == 0 || n == 1
  @memo[n] ||= fibonacci(n - 2) + fibonacci(n - 1)
end

||=は「||」演算子の自己代入演算子です。
左辺が 偽 か 未定義 なら左辺に右辺を代入する、という意味になります。
2行目でmemoというメモ帳を用意してあげて、4行目で未だ計算したことのない値の場合はメモしておく流れになっています。

これで100番目の数字でも一瞬で求めることができるようになりました。お疲れ様でした!

おわりに

ここまでご覧いただいてありがとうございました。
フィボナッチ数の求め方は他にもあるようなので色々試してみたいと思います。

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

【環境構築】Ruby on Rails 5.2系の開発環境を構築【1時間以内】

内容

「プログラミングをはじめたい!」「Webエンジニアを目指したい!」という思いを持ち始めたプログラミング初学者の方が、Ruby及びそれを使ったフレームワークであるRuby on Railsに挑戦するというのは王道の学習パターンなのではないかと思います。

Ruby on Railsをはじめるにあたって最初に大きな壁として立ちはだかるのが環境構築です。私自身も約3ヶ月前に異業種からの転職でRubyエンジニアとして働きはじめたばかりなので、環境構築などの「プログラミング学習を始める以前の準備」に時間を取られた時のことは記憶に新しいです。

本記事では、一人でも多くのプログラミング初学者の方にRailsの魅力を感じて欲しいという思いから「Ruby on Rails 5.2系の 開発環境を1時間以内に手に入れる」ことを目標に、そのための手順を紹介します。

※ 本記事の姉妹記事はこちら
【環境構築】Ruby on Rails 6 開発環境を1時間以内に手に入れる
【初心者向け】Ruby on Rails チュートリアル第4版(Rails5.1)の環境構築を1時間以内で!

ゴール

Ruby on Rails5.2系の開発環境を備えたUbuntuの仮想環境をmacOS上に構築する(目標1時間以内)。

前提環境

macOSCatalina バージョン 10.15.7

※ Windowsの方はゴメンナサイ。また、macOSの方でもバージョンの差異で多少の違いが発生する可能性はあります。

Virtual Box 6.1.16

※ インストール手順は後述

Vagrant 2.2.14

※ インストール手順は後述

想定する読者

・macOSユーザーの方
・macOSのターミナルを使った経験があり、基本的なLinuxコマンド(cd, mkdir, lsなど)の意味を知っている方
・ProgateなどでRuby on Railsの概要を学んだことがある方
・AWS Could9などのクラウドベースの統合開発環境に限界を感じている方
・過去にRuby on Railsの環境構築に挫折した経験のある方

仮想環境とは?

使っているOS(ホストOS:本記事ではmacOSを想定)の中に、あたかも別のOS(ゲストOS:本記事ではUbuntuを使用)が入っているような環境のことを言います。

ホストOS上に開発環境を直接構築する場合、誤った設定や削除を行ってしまったことによりホストOSに悪影響を与えてしまう可能性はありますが、仮想環境上で環境構築をする場合であれば、何かミスをしてしまった時はその仮想環境ごと削除してやり直せば良く、ホストOSに悪影響を与えることはありません。

また、AWS Could9などのクラウドベースの統合開発環境よりもリソース拡張の自由度が高く、CPUの性能限界やメモリ不足に悩まされることは(少なくともRailsでWebアプリケーションを開発するだけであれば)ほとんどないと言っていいと思います。

Virtual Box

仮想環境を構築するための「仮想化ソフト」として、まずはVirtualBoxをインストールします。

下記のダウンロードページから、(本記事ではmacOSを想定しているので)「OS X hosts」のリンクをクリックしてインストーラをダウンロードしてください。

ダウンロードしたインストーラを起動すれば、インストール手順がわかりやすく書いてあるので、それに従えばVirtual Boxのインストールは終了となります。

(※ 2021年1月11日現在の最新版は6.1.16なので、ここからはそのバージョンでの動作を前提としております)

Virtual Box ダウンロードページ

Vagrant

次に、仮想化ソフトを管理するツールであるVagrantをインストールします。
下記ダウンロードページにアクセスし、上記のVirtualBoxと同様に、インストーラのダウンロード、インストーラ起動、インストールという手順を踏めばほとんど迷うことなく完了すると思います。

仮想化ソフトである「VirtualBox」を操作するためのツールが「Vagrant」である、という認識を持っていただければとりあえず最低限の知識としてはOKです。

(※ 2021年1月11日現在の最新版は2.2.14なので、ここからはそのバージョンでの動作を前提としております)

Vagrant ダウンロードページ

環境構築手順

Virtual BoxとVagrantを問題なくインストールしたら、ここからは実際に仮想環境を構築して行きたいと思います。今回は、Ubuntu(18.04)というゲストOSが入っている仮想環境を構築します。

ここからの操作はmacOSのターミナルで行います。作業に入る前にvagrantが正しくインストールされていることを確認しましょう。ターミナルを開いてvagrant -vと入力してみてください。Vagrant 2.2.14という出力が返ってくれば準備はOKです、早速はじめていきましょう!

1. vagrant-vbguestのインストール

$ vagrant plugin install vagrant-vbguest

仮想マシンでの操作を簡単にしてくれる役割がある、という理解でとりあえずはOKです。

2. 任意の場所にディレクトリを作成

$ mkdir rails5_dev
$ cd rails5_dev

3. Vagrantfile作成

$ vagrant init

上記コマンドでデフォルトのVagrantfileが作成されます。

Vagrantfile(デフォルト|一部抜粋)
# -*- mode: ruby -*-
# vi: set ft=ruby :

# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|
  ~
  ~
  config.vm.box = "base"
  ~
  ~
  # config.vm.network "forwarded_port", guest: 80, host: 8080
  ~
  ~
  # config.vm.network "private_network", ip: "192.168.33.10"
  ~
  ~
end

上記のデフォルト状態のVagrantfileの中身を、下記のコードに書き換えてください。

Vagrantfile(書き換え後)
# -*- mode: ruby -*-
# vi: set ft=ruby :
# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|
  GUEST_RUBY_VERSION = '2.6.6'
  GUEST_RAILS_VERSION = '5.2.4.4'
  config.vm.box = "bento/ubuntu-18.04"
  config.vm.box_check_update = false
  config.vm.network "forwarded_port", guest: 3000, host: 3000
  config.vm.network "private_network", ip: "192.168.33.10"
  config.vm.synced_folder "./", "/home/vagrant/work"
  config.ssh.forward_agent = true
  config.vm.provider "virtualbox" do |vb|
      vb.gui = false
  end
  config.vm.provision "shell", inline: <<-SHELL
      echo '### installing tools ###'
      sudo timedatectl set-timezone Asia/Tokyo
      sudo apt update -y
      sudo apt upgrade -y
      sudo apt install build-essential -y
      sudo apt install -y libssl-dev libreadline-dev zlib1g-dev
      sudo apt install -y imagemagick
  SHELL
  config.vm.provision "shell", privileged: false, inline: <<-SHELL
      echo '### installing Ruby ###'
      git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
      echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
      echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
      source ~/.bash_profile
      git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
      rbenv install #{GUEST_RUBY_VERSION}
      rbenv global #{GUEST_RUBY_VERSION}
      echo '### installing Rails ###'
      gem install rails -v #{GUEST_RAILS_VERSION}
      echo '### installing SQLITE3 ###'
      sudo apt install libsqlite3-dev
      sudo apt install sqlite3
      echo '### installing NodeJS ###'
      sudo apt install -y nodejs npm
      sudo npm install n -g
      sudo n lts
      sudo apt purge -y nodejs npm
      sudo apt -y autoremove
      echo '### increasing inotify ###'
      sudo sh -c "echo fs.inotify.max_user_watches=524288 >> /etc/sysctl.conf"
      sudo sysctl -p
      echo ' -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-'
      echo 'You are now on Rails!'
      echo ' -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-'
  SHELL
end

本記事での解説は避けますが、このVagrantfileにはRailsを使えるようにするための設定が書いてあります。最初は難解に感じるかもしれませんが、ぜひ一度、読み解くことをオススメします。

もちろん私が書いたこのVagrantfileが唯一の正解では決してないので、「もっといい書き方があるよ」「こっちの方が使いやすいと思う」っていうご意見も大歓迎です!

4. 仮想環境構築

$ vagrant up

上記コマンドで仮想環境を構築します(PCのスペックにもよりますが、15〜30分ほどかかります)。

先ほどのVagrantfileに書かれた設定を1行ずつ実行していくことでRailsの開発環境が作られていっているということだけは抑えて頂けるといいかなと思います。

5. 仮想環境へSSH接続

$ vagrant ssh

上記コマンドで出来上がった仮想環境にSSH接続します。

SSH接続とは、ネットワークを経由して自身のPCから他のPC(今回は仮想環境)を安全に遠隔操作するための仕組みである、ということだけは抑えておいてください。

ここまでがmacOSのターミナルでの操作です。次の「6. Railsアプリケーション作成」と「7. ssh接続の終了」ではmacOSからssh接続をして、ゲストOSのUbuntuを操作しているということをご認識ください。

6. Railsアプリケーション作成

vagrant@vagrant:~$ cd work
vagrant@vagrant:~/work$ rails new sample-app
vagrant@vagrant:~/work$ cd sample-app
vagrant@vagrant:~/work/sample-app$ rails s -b 0.0.0.0

この状態でhttp://localhost:3000/にアクセスすると、Yay! You’re on Rails!のデフォルト画面が表示されます。

rails

立ち上げたRailsサーバーはcommand + Cで停止することができます。

7. ssh接続の終了

$ exit

上記コマンドでSSH接続を終了することができます。

8. 仮想環境のマシンをシャットダウンする

vagrant halt

上記コマンドで仮想環境上のゲストOSをシャットダウンすることができます。

再度立ち上げたい時は、vagrant upで立ち上げvagrant sshで接続します(2回目以降は数分で完了します)。

9. Railsアプリケーションの開発を進める方法

「2. 任意の場所にフォルダを作成」で作成したディレクトリ内に「6. Railsアプリケーション作成」で作成したRailsアプリと同名のファイルが作成されているはずなので、そこのコードを書き換えることで開発を進めます。

rails g controller Usersbundle install 等のコマンドはSSH接続をした状態で、作成したRailsアプリケーションのディレクトリ上で叩くことになります。

終わりに

いかがでしたでしょうか?スムーズに行けば、Yay! You’re on Rails!の表示まで1時間以内にいけるではないかなと思っております。

Rubyという言語、Railsというフレームワークは触っていると本当に面白くて飽きないものなので、この記事で環境構築をしたことをきっかけに少しでもその魅力にハマっていただければ嬉しい限りです。

本記事についての質問や改善点のご指摘等がございましたら、コメントやTwitterでのDMなどをいただければ幸いです!

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

Ruby on Rails 初心者向け!!投稿一覧・詳細表示機能まとめ

はじめに

 今は業務で主にphpを使っているのですが、今回自主学習でRailsを使用して何か成果物を作成したいと思い学習をはじめました。何かアドバイスや修正箇所があればコメントよろしくお願いします。

開発環境

  • Docker
  • ruby 2.3.7
  • Rails 5.2.4.4
  • MySQL 5.7.32

前提

 Railsの開発環境の構築を行い、新規投稿機能の実装まで完了が終わっていることが前提です。
新規投稿機能は前回の記事を参考に実装してみてください。
Ruby on Rails 初心者向け!!新規投稿機能まとめ

投稿一覧表示機能実装

コントローラーにアクションを追加

まずは投稿一覧機能を実装していきます。一覧表示機能は基本的にはindexアクションを使います。
前回作成した、postコントローラーにindexアクションを追加していきます。

post.controller.rb
 def index

 end

ルーティング設定

次にルーティング設定です。

config/routes.rbに下記の記述を追加します。

routes.rb
Rails.application.routes.draw do
  get 'posts/new' => 'posts#new'
  # ここから
  get 'posts'     => 'posts#index'
  # ここまで追加
  post 'posts'    => 'posts#create'
end

これで「GETメソッドでpostsパスにアクセスするとpostsコントローラーのindexアクションにアクセスする」という設定ができます。

ターミナルからも設定を確かめてみましょう。
以下のコマンドでルーティングの設定を確認できます。

# rails routes
posts GET  /posts(.:format)  posts#index                                                               

これでルーティング設定は完了です。

コントローラー設定

次はindexアクションの中身は書いていきます。

posts_controller.rb
def index
    @posts = Post.all
end

投稿一覧情報を取得するには、postモデルに対してallメソッドを使います。これでインスタンス変数@postsの中には、投稿されたデータ一覧が配列で格納されます。代入する変数名は複数形にしておきます。

ビュー設定

次はビューで先ほど取得した投稿データの表示を行います。
まずは、index.html.erbファイルを新規作成し、中身は書いていきます。

index.html.erb
<h1>投稿一覧</h1>
<% @posts.each do |post| %>
  <p>タイトル</p>
  <span><%= post.title %></span>
<% end %>

<% @posts.each do |post| %>は、繰り返し処理で、先ほどコントローラーで定義した投稿一覧が格納されたインスタンス変数@postsを一つずつ取り出して、ブロック変数のpostに格納しているという意味です。
これにより、each文の中で投稿の一つ一つが格納されたブロック変数postが使用できるようになります。

<%= post.title %>の記述は、ブロック変数postのタイトルカラムを一つずつ表示しています。「変数名.カラム名」で表示するカラムが指定できます。

これで「/posts」のURLにアクセスすると投稿のタイトル一覧が表示されていると思います。
スクリーンショット 2021-01-11 14.57.33.png

リンクの作成

次に新規投稿画面から、投稿一覧画面に遷移するリンクを設定していきます。
リンクの実装には、link_toメソッドを使用します。
新規投稿画面のビューに以下を記述していきます。

# ここから
<span>
    <%= link_to "投稿一覧", "/posts" %>
</span>
# ここまで追加
<h1>投稿フォーム</h1>
<%= form_for(@post, url: '/posts') do |f| %>
  <h3>タイトル</h3>
  <%= f.text_field :title %>
  <h3>本文</h3>
  <%= f.text_area :body %>
  <%= f.submit '投稿' %>
<% end %>

linl_toメソッドは、

<%= link_to 表示させるテキスト , "リンク先URL" [,オプション] %>

のように使う記述します。

これで投稿一覧ページへの遷移するテキストが投稿フォームの上に表示されていると思います。

スクリーンショット 2021-01-11 15.27.40.png

これで投稿一覧表示機能の実装ができました。

投稿詳細表示機能実装

コントローラーにアクションを追加

次に投稿詳細機能の実装をしていきます。詳細表示機能にはshowアクションを使用します。
実装の流れは一覧表示機能と同じです。
ではpostsコントローラーにshowアクションを定義していきます。

posts_controller.rb
def show

end

ルーティング設定

次にルーティング設定です。

routes.rb
Rails.application.routes.draw do
  get 'posts/new' => 'posts#new'
  get 'posts'     => 'posts#index'
  # ここから
  get 'posts/:id' => 'postss#show'
 # ここまで追加
  post 'posts'    => 'posts#create'
end

詳細表示のURLはどの詳細データか識別するために末尾にidがつきます。
IDが1の投稿の場合は、「posts/1」のようなURLになります。
コロン(:)idで設定することができます。

コントローラー設定

showアクションの中身を記述していきます。

posts_controller.rb
def show
    @post = Post.find(params[:id])
end

アクション内にparams[:id]と記述することで、先ほどルーティングで設定したURLの末尾のIDを取得することができます。findメソッドをしようすることで、idが1の場合、「IDが1のレコードを一件取得する」という意味になります。

ビュー設定

次に詳細表示画面のビューを作成します。
show.html.erbを作成し、中身を編集します。

show.html.erb
<h2>タイトル</h2>
<p><%= @post.title %></p>
<h2>本文</h2>
<p><%= @post.body %></p>

showアクションの中に定義したインスタンス変数.カラム名でそのカラムのデータを表示することができます。今回はtitleとbodyを表示します。

これでURLが「posts/1」にアクセスすると以下のようにid=1のデータが表示されます。
スクリーンショット 2021-01-11 21.46.23.png

リンクの作成

最後に投稿一覧画面から、詳細画面へのリンクを設定していきます。
投稿一覧画面のviewを以下のうように書き換えます。

index.html.erb
<h1>投稿一覧</h1>
<% @posts.each do |post| %>
  <p>タイトル</p>
  <span><%= link_to post.title, "/post/#{post.id}" %></span>
<% end %>

これで一つ一つの投稿のタイトルをクリックするとその投稿の詳細ページへ遷移することができるようになりました。

スクリーンショット 2021-01-11 21.54.03.png

まとめ

  • 投稿一覧表示機能はindexアクション、詳細表示機能はshowアクションを使用する。
  • DBからデータ一覧を取得するにはモデルに対してallメソッドを使用する。特定のデータを取得するにはfindメソッドでidを指定して取得する。
  • 画面遷移のリンクを作成するには、link_toメソッドを使用する。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Ruby] メソッド備忘録

はじめに

備忘録としてメソッドを書き留める

getsメソッド

ユーザーがキーボードで入力した値を文字列として取得する
末尾に必ず改行が入る

標準入力
abcd

input = gets
p input

出力結果
"abcd\n"  

chompメソッド

末尾の改行文字を取り除き、新しい文字列を返す

標準入力
abcd

input = gets.chomp
p input

出力結果
"abcd" 

splitメソッド

文字列を分割して文字列の配列にする

標準入力
hello new world

array = gets.split(" ")
p array

出力結果
["hello", "new", "world"]

readlinesメソッド

ユーザーがキーボードで入力した複数行の値を文字列の配列として取得する

標準入力
hello
new
world

array = readlines.chomp
p array

出力結果
["hello", "new", "world"]

mapメソッド

配列の要素の数だけ繰り返し処理を行う

array = ["1", "2", "3"]
new_array = array.map{ |v| v.to_i }
p new_array

出力結果
[1, 2, 3]

joinメソッド

配列の各要素を文字列に変換し、引数を区切り文字として結合する

array = ["hello", "new", "world"]
p array.join(" ")

出力結果
"hello new world"

pushメソッド

配列の末尾に引数を要素として追加する

array = ["a", "b", "c"]
p array.push("d")

出力結果
["a", "b", "c", "d"]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Ruby】end_with?メソッド

論理的思考強化の為、ドリルの復習をしています。
初学者のため、何かお気づきの点がありましたらご教示いただけますと幸いです。

問題

任意の2つの文字列があります。
どちらかの文字列がもう一方の文字列の最後にある場合はTrueを、ない場合はFalseを出力するプログラムを作りましょう。大文字と小文字は区別されません。

'world'と'helloworld'を渡した場合はTrueとなりますが、'world'と'worldhello'とした場合はFalseとなります。後者は二つ目の文字列には'world'が含まれているものの、文字列の最後に無いためです。

出力例:
end_other('Kilimanjaro', 'jAro') → True
end_other('Everest', 'REST') → True
end_other('Chomolungma', 'NgMA') → True

模範解答

def end_other(a, b)
  a_down = a.downcase
  b_down = b.downcase    #①

  if a_down.end_with?(b_down) || b_down.end_with?(a_down)  #②
    puts "True"
  else
    puts "False"
  end
end

解説

①「大文字と小文字を区別しない」という条件のため、downcaseメソッドで引数で渡された文字列を小文字に変換します。

end_with?メソッドで文字列の後ろにもう片方の文字列があるか確認します。





ドリルでは以下のような解答になっていましたが、面倒なのでend_with?を使用しました。
以下のメソッドの方が良い場合や理由などがありましたら、教えていただけますと幸いです。

def end_other(a, b)
  a_down = a.downcase
  b_down = b.downcase
  a_len = a_down.length
  b_len = b_down.length
  if b_down.slice(-(a_len)..- 1) == a_down || a_down.slice(-(b_len)..- 1) == b_down
    puts "True"
  else
    puts "False"
  end
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ActionView を単体で使用する(6.0以降対応)

ActionView を単体で利用する場合は、ActionView 5.2 までは次のようなコードを書いていました。

def html
  view_context.assign(staffs: [{ name: 'Alice', position: 'supervisor' }])
  view_context.render(template: 'index',
                      prefixes: 'staffs')
end

def lookup_context
  return @lookup_context if @lookup_context

  @lookup_context = ActionView::LookupContext.new('./views')
  @lookup_context.cache = false
  @lookup_context
end

def view_context
  @view_context ||= ActionView::Base.new(lookup_context)
end

p html

ActionView 6.0 からは同じコードを利用していると、次のような WARNING が発生していました。

DEPRECATION WARNING: ActionView::Base instances must implement `compiled_method_container` or use the class method `with_empty_template_cache` for constructing an ActionView::Base instance that has an empty cache. (called from html at app.rb:13)

また、2020/12にリリースされた ActionView 6.1 からは ActionView::Base.new でシンタックスエラーが発生するようになりました。

Traceback (most recent call last):
    7: from app.rb:41:in `<main>'
    6: from app.rb:6:in `execute'
    5: from app.rb:6:in `open'
    4: from app.rb:7:in `block in execute'
    3: from app.rb:12:in `html'
    2: from app.rb:27:in `view_context'
    1: from app.rb:27:in `new'
/Users/soruma/program/only_action_view_6_1_example/vendor/bundle/ruby/2.7.0/gems/actionview-6.1.1/lib/action_view/base.rb:230:in `initialize': wrong number of arguments (given 1, expected 3) (ArgumentError)

WARNING に書かれている通り、

ActionView :: Baseインスタンスは、 compiled_method_containerを実装するか、空のキャッシュを持つActionView::Baseインスタンスを構築するためにクラスメソッドwith_empty_template_cacheを使用する必要があります。

に変更しなければなりません。

Google先生に聞いても答えが出てきませんでいたが、 ActionView のテストにヒントが書かれていました。

@view = Class.new(ActionView::Base.with_empty_template_cache) do
  def view_cache_dependencies; []; end

  def combined_fragment_cache_key(key)
    [ :views, key ]
  end
end.with_view_paths(paths, @assigns)

今回、私はRails 6.1対応するにあたって次のように実装しました。

def html
  view(staffs: Staff.new.execute).render(template: 'index',
                                         prefixes: 'staffs')
end

def view(assigns)
  Class.new(
    ActionView::Base.with_empty_template_cache
  ).with_view_paths('./views', assigns)
end

今回のソースはGithubに保存しています。

参考

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

Ruby 基礎文法の復習

Rubyの基礎学習が一通り終わり、違う教材で復習をしていた時に重要だと思った点や知らなかったテクニックを備忘録的にまとめたものです。

.classと.methods(オブジェクトに対して使用できる)

  • .class
    クラスが分かる
  • .methods
    そのオブジェクトの持っているメソッドが分かる

破壊的メソッド(!がつく)

元の値を書き換えてしまうメソッドのこと
(例).upcaseメソッド(値を大文字に変えて出力)
name = "tarou"

puts name.upcase #"TAROU"
puts name #=>"tarou"

puts name.upcase! #"TAROU"
puts name #=>"TAROU"

真偽値を確認(?がつく)

name = "tarou"
p name.empty? #false
p name.include?("r") #true

配列の添字

  • マイナス数字
    [-1]とすると、末尾から1つ目
    [-2]とすると、末尾から2つ目

  • 範囲指定
    [0..2]と指定すると、0〜2の値を取得
    [0...2]で、0〜2の直前

ハッシュでよく使うメソッド

  • size ・・・ 要素の数を取得(配列でも使える)
  • keys ・・・ キーの一覧を取得
  • values ・・・ 値の一覧を取得
  • has_key?("name") ・・・ キーがあるかどうか

変換

  • to_i ・・・ 整数(integer)
  • to_f ・・・ 小数点あり(float)
  • to_s ・・・ 文字列(string)
  • to_a ・・・ 配列(array)
  • to_h ・・・ ハッシュ(hash)  

(例)配列とハッシュの変換
scores = {taguchi: 200, fkoji: 400}
scores.to_a => [[:taguchi, 200], [:fkoji, 400]]
scores.to_a.to_h => {:taguchi=>200, :fkoji=>400}

%記法

  • ダブルクォーテーション
    "文字列" ・・・ %Q(文字列) もしくは %(文字列)で書ける※Qは省略可能

  • シングルクォーテーション
    '文字列' ・・・ %q(文字列)で書ける

何が便利か?

文字列の中にダブルクォーテーションやシングルクォーテーションを記述したい時に、表記が分かりやすい

【he\"llo】と記載したい場合
=> puts "he\"llo"
=> puts %Q(he"llo)
=> puts %(he"llo)

【'he\'llo'】と記載したい場合
=> puts 'he\'llo'
=> puts %q(he'llo)

【配列の場合の%記法】%W もしくは %w

【p ["red", "blue"]】
=> p %W(red blue)

【p ['red', 'blue']】
=> p %w(red blue)

forを使った繰り返し処理

forは何らかの集合的なオブジェクト(配列やハッシュ)や、範囲を表すオブジェクトの要素数分だけ何らかの処理を繰り返すことができる命令である

for i in 10..20 do
  p i
end

#eachへの書換えも可能
(10..20).each do |i|
  p i
end
for color in ["red", "green"] do
  p color
end

#eachへの書換えも可能
["red", "green"].each do |color|
  p color
end
for name, score in {taro:100, jiro:200} do
  puts "#{name}: #{score}"
end

#eachへの書換えも可能
{taro:100, jiro:200}.each do |name, score|
  puts "#{name}: #{score}"
end

クラス(クラスメソッド、クラス変数、定数)

  • クラスメソッド
    def self.メソッド名
    end

  • クラス変数
    @@変数名

  • 定数
    大文字から始まる(慣習的に全て大文字で記述)

(例)
class User

  @@count = 0 #クラス変数

  VERSION = 1.3 #定数

  def initialize(name) 
    @name = name
    @@count += 1
  end

  def sayHi #インスタンスメソッド
    puts "hi! i am #{@name}"
  end

  def self.info #クラスメソッドを定義
    puts "#{VERSION}: User Class, #{@@count} instances."
  end

end

ichiro = User.new("ichiro")
jiro = User.new("jiro")
saburo = User.new("saburo")
User.info #クラスメソッドの実行(クラスから直接呼び出せる)
p User::VERSION #定数をクラスの外から呼び出す場合はコロン2つ記述

 クラスの継承

class Miniuser < User (Userクラスの情報が継承される)
User: 親クラス、もしくはSuper Class と呼ぶ
MinUser: 子クラス、もしくはSub Class と呼ぶ

子クラスで親クラスで定義したメソッドを上書きすることも可能(オーバーライドと呼ぶ)

privateのポイント(メソッドのアクセス権)

  • レシーバーを指定できない
  • Sub Class(子クラス)から呼び出せる(Rubyの特徴)
  • オーバーライドできる(Rubyの特徴)

※Rubyのprivate指定は他の言語のオブジェクト指向プログラミングと動作が異なるようである

モジュールについて

開発規模が大きくなると、メソッド名などが他の人が作ったものと被ってしまい、それが原因でエラーになってしまうことがある。そのような場合に、モジュールを使うと命名した名前が衝突せずに便利である。
モジュールの特徴としてはメソッドや定数をまとめることができるが、インスタンを生成したり継承したりはできない。

#モジュールの作り方
module Car #大文字から始める

  VERSION = 1.3 #定数を作れる

  def self.go
    puts "going..."
  end

  def self.stop
    puts "stopping..."
  end

end

#モジュールの実行
Car.go
Car.stop

モジュールについて その2

モジュールの用途にミックスインというものがある。
単に機能としてクラスに提供する場合に使用。
メソッドを生成する際に、selfを付けずにインスタンスメソッドとすることにより、他のクラスのインスタンスメソッドとして使用でき、これをミックスインという。
その際、クラス側では「include モジュール名」を記述する。

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

Ruby技術者認定試験 Silver合格に必要なことを振り返る

こんにちは。ハチマキと申します。

はじめに

去年目標にしていた「Ruby技術者認定試験 Silver」に無事合格しました。(2020年6月時点)

心配性な私は試験対策に5つの学習法を試しました。
そこで感じた「合格するために必要なこと」「試験で重要なこと」の2点に絞り、書いていこうと思います。

ちなみに私の点数は82点(100点満中 75点が合格)でした。点数はボチボチですね・・
20200630155443.png

では、早速行きましょう。

概要 : Ruby技術者認定試験 Silver合格に必要なこと

  • 合格するために必要なことは?
    • 教材選び
    • 出題パターンに慣れる
    • 締め切り効果を使う
  • 試験で重要なことは?
    • 問題の見直しを最低3回は行うべし
  • 下記ご参考までに
    • Ruby技術者認定試験とは
    • 自身のスペック
    • 準備期間
    • 学習教材
  • まとめ
  • 参考資料

合格するために必要なことは?

1つ目:教材選び
様々な教材がありますが、結局合格するためにはどの教材をやるべきなのかについてです。
5つ試した結論は、[改訂2版]Ruby技術者認定試験合格教本(Silver・Gold対応)です。

この書籍の「模擬試験」と「※1 出題されやすい領域やよく出るタイプの問題」を十分に理解していれば、合格できると思いました。

理由は、大多数の問題(60%程度)はこちらの「模擬試験」がそのまま出題されるか、似たような問題が出題されました。
いわゆるこの「模擬試験」を理解(なぜが説明できる)できれば、6割程度は点数が取れるということです。

合格へのポイントは、残りの15%以上(合格ライン75%)の点数を取るために、何を行うかです。
他の記事でもよく出ている「※1 出題されやすい領域やよく出るタイプの問題」をこの書籍で念入りに学習、理解を深められれば合格にグンと近ずくように感じました。

※1

  • 文字列、配列やハッシュの操作
  • 2進数、8進数、10進数、16進数
  • 配列やハッシュのエイリアスメソッド
  • 破壊的・非破壊的メソッド →試験で3問程度出題されました
  • Fileのopenメソッドモード(r, r+, w, w+, a, a+)→試験で2,3問程度出題されました

※下記私が行なった学習を、合格を目的に独断と偏見で勝手に総評しました(ご参考程度に)

学習教材 総評 模擬試験実施回数
[改訂2版]Ruby技術者認定試験合格教本(Silver・Gold対応) 3回
RUBY技術者認定試験 公式ガイド × 3回
公式模擬問題集PDF 4回
RubyExamination(REx) 30回
ミニツク 3回
  • 記号説明
    • ◎ 必ずやるべきです
    • ◯ やったほうが合格ラインに近づく
    • △ 時間があればやっても良いのでは
    • ×  やる必要はない

2つ目:出題パターンに慣れる
若干矛盾しますが、臨機応変に問題を解けるように設問パターンに慣れて置くことも重要に感じました。
単純な話、75%以上が合格ラインですので50問中38点とれば合格です。いわゆる最低12問は間違っても合格です。

試験では、ひねってくる問題や少々考える問題が見受けられたため、ある程度出題パターンに慣れ、回答できる状態を作って置くことが重要かと思いました。
そのためにお世話になった教材は、RubyExamination(REx)です。結果的に通勤時間などを活用し、30回くらいやってました。

この教材の良いところは、隙間時間に取り組める点と出題パターンの量をこなせる点です。また、試験でも似たような問題はあったように感じます。
無料かつWeb回答で、解説もわかりやすいため、問題をこなし理解を深めていく上では良い教材のように感じました。

3つ目:締め切り効果を使う
これは試験を受けると決めたら、試験予約をすぐにしてしまいましょうという話です。

いつ試験をやるのかを決めておくと、学習の集中力がグンと上がります。
また、試験日を決めず学習していると、結局学習をやめてしまったり半年後に試験を受けるなどといったことが起きます。それをこの締め切り効果を使うことで事前に防ぐことができます。

心配性な私は、受けると決めた2ヶ月ほど前から試験日程を予約しました。笑

試験で重要なことは?

問題の見直しを最低3回は行うべし
個人的にはこれが重要かと思いました。
理由は、試験スタートから冷静な回答ができていない可能性が高く、初歩的なミスをしていることが多々あるためです(これは僕に限った話かも知れません・・)

試験は1時間半、一通り回答し終えてもまだ1時間程度は時間がありました。ですので、時間を気にする必要はありません。

結果的に3度、いや4度ほど徐々に確認設問を減らしながら見直しをしました(破壊的メソッドやFileクラスなど10問程度は修正しました)

見直しが終わり、残り15分程度になったところで、自己採点を実施。
11問程度不安な箇所がありましたが、逆に38問は自身があったので合格ラインに到達してると思い、試験終了を迎えました。

基本的には考える問題より、暗記問題が多く出題されるため、回答自体はサクサクと進むと思います。残りの時間をうまく活用し、点数を取れるように見直したことがよかったと振り返ると思いました。

下記ご参考までに

Ruby技術者認定試験について

自身のスペック

  • 文系・未経験からエンジニアになり半年

準備期間

  • 2ヶ月弱
  • 平日:通勤時間、お昼休みをメインに一日あたり約1時間〜1時間半
  • 土日:正直あまりやってません・・(やっても30分とかでした)

基本的な学習時間は、上記の形で進めておりました。
学習合計時間は、45時間前後〜65時間前後でしょうか。

学習教材

まとめ

1番始めに取り組んだ模擬試験(RUBY技術者認定試験 公式ガイド)では100問中27点でした笑
その点数をみたときは、すでに絶望してましたが、そこから少しづつ学習を初めて、結果的に全ての教材で9割ほど点数が取れるまでになりました。
結局9割以上を取るためには、問題を解き続けて、わからないところをインプットし続ける。この繰り返しにつきました。

私は2ヶ月弱の準備期間を設けましたが、適切な学習、学習時間をしっかり確保できれば、1ヶ月前後で合格できる試験かと思います。
とか言ってますが、合格通知までヒヤヒヤで合格できて本当にホットしました・・

私自身、スキルも経験もまだまだなので、気を緩めず引き続き目標を立てて頑張りたいと思います。

参考資料

下記先人の方々が書かれた記事を学習スタート時から参考にさせて頂きました。

同じ教訓の方のソリューションになれば幸いです!
以上、ハチマキでした。

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

git push heroku master のエラーの解決法

herokuにpushしようとしたら、エラー起こる。

ターミナル実行コマンド
% git push heroku master

コマンド結果

ターミナル
 ! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'https://git.heroku.com/アプリケーション名.git'

  
なるほど、プッシュできませんでしたよ、と言うことですね。
エラー文で検索を掛け、いろいろ試してみましたが、効果はなし。。。

"failed to push some refs to"とあるので、その一部とはどこなのかを検討しました。

git push heroku masterを実行すると、しばらくはつらつらと結果が印字されます。
その中に発見した赤字の部分。

remote:  !
remote:  !     Could not detect rake tasks
remote:  !     ensure you can run `$ bundle exec rake -P` against your app
remote:  !     and using the production group of your Gemfile.
remote:  !     /tmp/build_f78ce664/config/boot.rb:4:in `require': cannot load such file -- bootsnap/setup (LoadError)
remote:  !     from /tmp/build_f78ce664/config/boot.rb:4:in `<top (required)>'
remote:  !     from /tmp/build_f78ce664/bin/rake:7:in `require_relative'
remote:  !     from /tmp/build_f78ce664/bin/rake:7:in `<main>'
remote:  !

あきらかに上手く行ってなさそうな部分を発見しました(笑)
何やら、

  • bundle exec
  • Gemfile
  • require
  • bootsnap/setup (LoadError)

などと書いてあるのが目に止まります。
一つ一つ確かめてみたものの、良い結果にはなりませんでした。
夜中までエラーと戦っていましたが、にっちもさっちも行かなくなりました。。。

最終手段、メンターに相談!!

翌朝さっそくメンターさんに相談しました。

5分で解決しました。

一生忘れなさそう。(´・ω・`)

原因は、bundlerのバージョンでした。
どうやら、使用していたbundlerのバージョンは、Herokuに対応していないそう。

Gemfile.lock
BUNDLED WITH
   バンドラーバージョン

記述は僕の場合一番下に書いてありました。

bundlerのバージョンを変更

以下の手順で行いました。

% gem uninstall bundler
% gem install bundler -v 2.1.4

今の所、-v 2.1.4が対応しているそうです。

インストールできたら、Gemfile.lockファイルを削除します。

Gemfile.lockファイルは、Gemfileの履歴を残しているファイルです。
わかりやすい記事があったので、載せておきます。
https://qiita.com/nishina555/items/1b343d368c5ecec6aecf

そして、bundleのインストール

% bundle install

Gemfile.lockが自動生成されているのを確認してください。

Gemfile.lock
BUNDLED WITH
   2.1.4

になっていたらOKです。

GitHubでのcommit,pushを忘れずに。

Gemfile.lockが変更されたので、GitHub Desktopからcommit,pushを行いました。

最後に、もう一度コマンドを実行してみましょう。

% git push heroku master

結果

To https://git.heroku.com/アプリケーション名.git
 * [new branch]      master -> master

成功です。

さいごに

22時から始めて、2時まで戦っていました。
今まで、ある程度のエラーは自己解決してきたので、なんとかなると思っていましたが、及ばず。。。

今回のエラーでは、エラー内容が直接的な表示がされていません。
SyntaxErrorとかって書かれていたら、それがどのファイルの何行目なのかって、すぐわかるじゃないですか。書いてあるので(笑)
今回はきつかったですね。

仕組みとしては、エラー内容にもあった部分、

bootsnap/setup (LoadError)

このbootsnapというのを、bundlerが動かしているそうで。
bundlerを設定し直してやることで、上手くいったということですね。

自己解決に至らず4時間戦い、メンターに相談して5分で解決したのは、衝撃的で記憶にバッチリ残りました。

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

【ポートフォリオ解説】『Share-read』 〜shareして本の知識を自分のものに〜

はじめに

プログラミングの学習のアウトプットとして、webアプリのポートフォリオを作成しました。
作成したwebアプリ名は『Share-read』です。
「shareして本の知識を自分のものにしよう」というコンセプトを掲げています。

トップ画.png

本記事では、制作背景から今後の課題まで本ポートフォリオの全てを記載します。
よかったら最後までご覧ください!

制作背景

社会人になってから、ビジネス本や自己啓発本を読むようになりました。
しかし、読んだらそれっきりになり、せっかく読んだ本の内容もあまり覚えておりませんでした。

ある時アウトプット大全という本を読み、本のアウトプットの重要性を考えさせられました。
そこで、本の内容をアウトプットするために、読書会に参加することにしました。
読んだ本をまとめて人に共有するという工程を踏むだけでも、これまでの読書と打って変わり、本の知識が頭に深く残っていました。
さらに、業務中も本の知識を活かせるようになってきました。

以前の私のように本を読んで終わってしまっている方は、少なからずいるのではないかと考えました。
読書というのは読んで終わりではなく、本の知識を自分の知識として使える状態にすることがゴールであると私は考えております。
そのような考えから本の内容をアウトプットするwebアプリ『Share-read』を開発しようと思いました。

コンセプト

『shareして本の知識を自分のものに』
本の内容をアウトプットして、知識を自分のものとして使える状態にすることを目的としています。

ターゲット

『20代〜30代前半の若手ビジネスマン』
ビジネス書を読もうとするビジネスマン、特に経験が浅い若手ビジネスマンをターゲットに設定しました。

ペルソナ

実際の開発では、ターゲット層からさらにペルソナを設定して担当者間の認識を合わせると思ったため、今回のアプリ開発でもペルソナを設定しました(ほぼ僕のプロフィールですが笑)。

年齢:26歳
職業:メーカーの研究開発部に所属
住居:都内
家族:独身
趣味:筋トレ、散歩、カラオケ

休日は筋トレや散歩をしつつ、空いた時間に読書をする。
読書に関しては、本を読むだけで、特にアウトプットもしていない。
SNSはTwitterとFacebookを使っている。
Twitterはほぼ毎日利用するが、Facebookの利用頻度は低い。
YouTubeを見ることが好きで、最近はSASUKEに出演されている方が取り組まれているチャンネルを見ている。

ポジショニング

本に関するwebアプリはいくつかありますが、マップ化することで、違いを明確化しました。
ビジネス本に特化したアウトプットの領域は既存サービスにはなく、しっかりと差別化できていることがわかりました。
image.png

webアプリの使用イメージ

  • 本の検索
    本の検索.gif

  • 本のレビュー
    本のレビュー.gif

機能

  • ユーザー関連

    • 新規登録機能 / 登録情報編集機能(画像登録可能)
    • ログイン機能 / ゲストログイン機能(登録なしでログイン可能)
    • フォロー機能(ajaxの利用)
    • 通知機能
  • 本関連

    • 本の検索機能(楽天APIの利用)
    • 本棚登録機能(ajaxの利用)
    • 楽天の購入ページへのリンク追加
  • レビュー関連

    • レビュー投稿機能/レビュー編集機能
    • レビューの下書き機能
    • お気に入り機能(ajaxの利用)
    • コメント投稿・削除機能(ajaxの利用)
    • コメント編集機能

技術・環境

フロントエンド

  • Bootstarp 4.50
  • HTML/CSS
  • JavaScript、jQuery

バックエンド

  • Ruby 2.7.2
  • Rails 6.0.3.4

開発環境

  • Docker/Docker-compose
  • MySQL 5.7

本番環境

  • AWS(VPC、EC2、RDS for MySQL、S3、ALB、 IAM、Route53)
  • MySQL 5.7
  • Nginx、puma
  • Capistrano

ER図

image.png

インフラ構成図

インフラ構成図.png

工夫した点

UI/UX

  • サイトのデザイン(配色や配置)は同じようなターゲット層と想定されるサイトを参考にしました。
  • 投稿のしやすさを意識し、レビューの投稿画面はmodalで表示しました。
  • 内容の吟味をしながらレビューできるように下書き機能をつけました。

環境・技術

  • チーム開発を意識し、Gitにpushする際は必ずプルリクを出し、Github上でマージするよう心掛けました。
  • 実際の開発を想定し、Rspecでテストコードを記載いたしました。

苦労した点

特に苦労した以下の2点を記載いたします。

楽天APIの利用

外部APIとして楽天APIを利用したのですが、テーブルへの格納等の部分で非常に苦労しました。
楽天APIのサイトやweb上の記事を参考にしながら、実装することができました。
アウトプットとしてQiitaにも記事にしました。
https://qiita.com/Hiroaki_jr/items/983b11a45e2b42c8f3dc

Dockerの使用

今回開発環境にDockerを用いましたが、必要なツールのインストールができていなかったり、データベースの不具合が起きたりと様々なエラーに苦しめられました。
エラー文の読み込んだり、調べたりしながらなぜエラーが起きてしまったのか考えながら進めました。

現アプリの課題

一通り使える状態であるため、アプリをデプロイしておりますが、以下のような点は課題と考えており、現在解決中です。

機能面

  • レスポンシブの完全対応
    レスポンシブ対応が不十分なところがあるため、実装した方が良いと考えています。

  • Twitterログイン
    ターゲットが若手のビジネスパーソンであり、Twitterの利用をしている方は非常に多いです。
    そのため、実際の利用を考えるとTwitterのログインはあるとより良いと考えています。

技術・環境面

  • circle CIによる自動デプロイの実装
    現在はCapistranoを用いたデプロイを実装しています。
    実際の開発現場においては、CircleCIが主流かと思いますので、こちらも課題であると考えています。

  • 本番環境のブラッシュアップ
    今後はACS等を用いて本番環境でもコンテナ技術を用いていく必要があると考えています。

  • リファクタリング
    現在、rubocop等のコード解析ツールを用いてリファクタリングを実施中です。

  • 結合テスト、統合テストコードの記載
    今回のポートフォリオでは、モデルの単体テストのみとなっているため、今後はRspecの学習に継続して取り組み、結合テストや統合テストも記載していく予定です。

感想

ここまで読んでいただきありがとうございました!
アプリを開発してみて思ったのは「やっぱり新しい価値を考えて、具現化していくのは楽しい!」ということでした。
実際アプリをデプロイした時は、ガッツポーズしながら、ニヤニヤしていました(at ガスト)。
とはいえ、現在のwebアプリにもまだ課題はあります。
実際のサービスにおいても常にブラッシュアップをしていくと思います。
そのため、本アプリでも現在の課題を改善し、webアプリのブラッシュアップをしていきます!

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

【ruby on rails + Mysql】herokuからAWSに切り替える際のデータ移行手順メモ

内容

ruby on railsで作成したアプリケーションの本番環境をherokuからAWSに移行する際のデータ移行について備忘録として書き残す。

実際には本番稼働後にサーバー環境を移すというのはリスクと手間ばかりかかって悪手だとは思うのですが、個人開発なのでお許しを・・。

環境

・ruby 2.7.1
・Rails 6.0.3.2
・mysql 5.7

以下のような形でAWSインフラを構築していることを想定

スクリーンショット 2021-01-11 10.27.31.png

heroku側:clearDB(Mysql)からデータのエクスポート

※本番環境の場合は、タイムラグによるデータ欠落等を防ぐため、一時的にサーバーを停止する等の措置が必要かと。詳しくはチームの方針に従ってください。ここではタイトル通りデータ移行の手順についてのみ記載します。

データベースURL取得

$heroku config --app アプリ名 | grep DATABASE_URL
CLEARDB_DATABASE_URL:     mysql://<USER_NAME>:<PASSWORD>@< HOST >/<DB_NAME>?reconnect=true

データのエクスポート

データのエクスポートをします。おそらくしばらく時間がかかるため気長に待ちましょう。

カレントディレクトリにdump.sqlが出力されていれば成功です。

mysqldump -uUSER_NAME -pPASSWORD -h HOST -r dump.sql --single-transaction DB_NAME --skip-column-statistics

オプションの内容を軽く説明しておくと、

--single-transaction・・・ダンプ処理をトランザクションで囲む。データの整合性を保つのに有効。MyISAMテーブルが含まれるDBでは別オプションが必要らしい。

--skip-column-statistics・・・mysqldump 8以降でそれ以前 (5.7とか) のMySQLサーバに対してダンプをする際に起こるエラーを回避。詳しくは筆者もよくわからない。。

以下記事参考
https://blog.pinkumohikan.com/entry/mysqldump-disable-column-statistics

mysqldumpのよく使うオプションを書いてくれている記事もあったため、リンクを貼っておきます。

よく使うmysqldumpのオプションと使用例
https://qiita.com/ryounagaoka/items/7be0479a36c97618907f

AWS側:EC2へsqlを転送

前提
・ssh接続ができている
・クライアントのssh/configは以下の場合

Host myapp
        HostName        IPAddress
        Port            22
        IdentityFile    ~/.ssh/myapp_rsa
        User            hoge

以下コマンドを叩く

scp 転送元ファイル myapp:/転送先ディレクトリ

転送できない場合以下サイト等を参照
http://gallardolp570.hatenablog.com/entry/2014/11/17/205325

AWS側:RDSにsqlをインポート

事前に接続先のエンドポイント、ユーザー名、パスワードを確認しておく。

mysql -h エンドポイント -u ユーザー名 -p DB名> dump.sql

進捗状況を確認したい場合はパイプで繋ぎpvコマンドをつけると良き
https://qiita.com/hiroq/items/efd8c3580c9c9457c869

反映されていればインポート成功です。お疲れ様でした。
見よう見まねの部分も多いため、何か誤り等あればご指摘いただけますと幸いです。

参考サイト

http://kayakuguri.github.io/blog/2015/09/10/mysql-postgres-import-export/

https://photo-tea.com/p/aws-ec2-to-rds-csv/

http://gallardolp570.hatenablog.com/entry/2014/11/17/205325

https://www.it-swarm-ja.tech/ja/mysql/mysqldump%E3%82%A8%E3%83%A9%E3%83%BC1045%E6%AD%A3%E3%81%97%E3%81%84%E3%83%91%E3%82%B9%E3%83%AF%E3%83%BC%E3%83%89%E3%81%AA%E3%81%A9%E3%81%AB%E3%82%82%E3%81%8B%E3%81%8B%E3%82%8F%E3%82%89%E3%81%9A%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%81%8C%E6%8B%92%E5%90%A6%E3%81%95%E3%82%8C%E3%81%BE%E3%81%97%E3%81%9F/1043190719/

https://qiita.com/hiroq/items/efd8c3580c9c9457c869

https://blog.pinkumohikan.com/entry/mysqldump-disable-column-statistics

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

Formオブジェクトを作成するために、ActiveModel::Modelをincludeする理由

概要

  • ActiveModel::Modelとは
  • ApplicationRecordを継承するのだとだめなのか
  • 参照

ActiveModelとは

Action PackやAction Viewと連携する機能を使えるようになる。

Railsガイドによると、モデル名の調査、変換、翻訳、バリデーションといった機能が使えるようになるみたいです。
またActive Recordの場合と同じような方法で、オブジェクトを属性のハッシュで初期化することができるようになります。

コードで記述すると

model
class NameList
  include ActiveModel::Model

  attr_accessor :name, :email, :prefecture

  validates :name, :email, :prefecture
end
 name_list = NameList.new(name: "Tarou", email: "tarou@example.com", prefecture: "青森")

  name_list.name # => "Tarou"
  name_list.email # => "tarou@example.com"
  name_list.prefecture # => "青森"

ざっくりな解釈ですが
定義した属性値にバリデーションをかけられて、また属性値をハッシュのキーに持つインスタンスを生成することができるみたい。

ここで一つの疑問が生まれました。
同じような機能をすでに持っているApplicationRecordクラスを継承すればいいのでは?と

ApplicationRecordを継承するのだとだめなのか

結論、フォームを用意しているViewに遷移すると、エラーが起きます。

ActiveRecord::StatementInvalid
Mysql2::Error: Table 'sample_app_development.name_lists' doesn't exis

エラー原因は、モデルに紐づくテーブルが存在しないことによるものだと考えられます。

ApplicationRecordの親である、ActiveRecord::Baseにモデルとテーブルを1対1で紐付ける機能がありそうです。

ActiveModel::Modelをincludeする理由は、機能実装に必要なメソッドの取得を最小限に留めるためだと考えます。

Formオブジェクトを作成するときはデザインパターン通りにコードを記述するのが良さそう。
疑問が浮かべばあえてエラーを起こして原因を探るのも面白そうです。
何か誤りがありましたらご教示いただけると幸いです。

参照

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

google スプレッドシートをAPIで操作する手順(ruby)

これは何?

rubyを使ってgoogleスプレッドシートをAPIで操作するまでの手順です。

基本的に https://developers.google.com/sheets/api/quickstart/ruby をやっているだけなので英語の説明でも問題ない方は公式の方を見てください。

対象者

rubyの実行環境が自分で作れる人、または既に存在する人が対象です。

手順

https://developers.google.com/sheets/api/quickstart/ruby
の[Enable the Google Sheets API]ボタンをクリックします。

01.jpg

次の画面でProject名を入力します。
[Quickstart]のままでも問題ないので入力したら[NEXT]をクリックします。
02.jpg

次の画面では作成するOAuth clientを選びます。
[Desktop app]を選択して[CREATE]ボタンをクリックします。
03.jpg

次の画面では[DOWNLOAD CLIENT CONFIGURATION]ボタンが表示されるのでクリックして credentials.jsonファイルをダウンロードします。
04.jpg

続いてrubyのプログラムを作成していきます。
まずプログラムを格納するためのディレクトリを作成していきます。
今回は「sample_google_spreadsheet_api」ディレクトリを作成しました。(ディレクトリ名はなんでも大丈夫です)

cd ~
mkdir sample_google_spreadsheet_api

続いてgemのinstallを行います。
05.jpg

GoogleのQuickstartページでは上記のように記載していますが、今回はGemfileを作成していきます。

cd sample_google_spreadsheet_api
bundle init

Gemfileが作成されるので以下のように編集します。

Gemfile
# frozen_string_literal: true

source "https://rubygems.org"

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

--- # gem "rails"
+++ gem 'google-api-client'

bundle installします。

bundle config set --local path 'vendor/bundle'
bundle install

ダウンロードしたcredentials.json を以下へ配置します。

# fileディレクトリを作成してcredentials.jsonを格納
mkdir file

sample_google_spreadsheet_api/file/credentials.json

続いてsheets/quickstart/quickstart.rb を作成します。

mkdir sheets
cd sheets
mkdir quickstart
cd quickstart
touch quickstart.rb 

quickstart.rb へ以下のプログラムを記載します。

これはGoogleのQuickstartへ記載されているものです。

require "bundler/setup"
require "google/apis/sheets_v4"
require "googleauth"
require "googleauth/stores/file_token_store"
require "fileutils"
OOB_URI = "urn:ietf:wg:oauth:2.0:oob".freeze
APPLICATION_NAME = "Google Sheets API Ruby Quickstart".freeze
CREDENTIALS_PATH = "./file/credentials.json".freeze
# The file token.yaml stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
TOKEN_PATH = "token.yaml".freeze
SCOPE = Google::Apis::SheetsV4::AUTH_SPREADSHEETS_READONLY
##
# Ensure valid credentials, either by restoring from the saved credentials
# files or intitiating an OAuth2 authorization. If authorization is required,
# the user's default browser will be launched to approve the request.
#
# @return [Google::Auth::UserRefreshCredentials] OAuth2 credentials
def authorize
  client_id = Google::Auth::ClientId.from_file CREDENTIALS_PATH
  token_store = Google::Auth::Stores::FileTokenStore.new file: TOKEN_PATH
  authorizer = Google::Auth::UserAuthorizer.new client_id, SCOPE, token_store
  user_id = "default"
  credentials = authorizer.get_credentials user_id
  if credentials.nil?
    url = authorizer.get_authorization_url base_url: OOB_URI
    puts "Open the following URL in the browser and enter the " \
             "resulting code after authorization:\n" + url
    code = gets
        credentials = authorizer.get_and_store_credentials_from_code(
            user_id: user_id, code: code, base_url: OOB_URI
        )
    end
    credentials
end

# Initialize the API
service = Google::Apis::SheetsV4::SheetsService.new
service.client_options.application_name = APPLICATION_NAME
service.authorization = authorize

# Prints the names and majors of students in a sample spreadsheet:
# https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit
spreadsheet_id = "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms"
range = "Class Data!A2:E"
response = service.get_spreadsheet_values spreadsheet_id, range
puts "Name, Major:"
puts "No data found." if response.values.empty?
response.values.each do |row|
    # Print columns A and E, which correspond to indices 0 and 4.
    puts "#{row[0]}, #{row[4]}"
end

内容としては
credentials.jsonをもとに認可コードを取得して認可コードからアクセストークンを取得し
https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit
の内容をコマンドラインへ出力するというものです。
以下が実行コマンドです。

ruby sheets/quickstart/quickstart.rb

実行結果

> ruby sheets/quickstart/quickstart.rb

Open the following URL in the browser and enter the resulting code after authorization:
https://accounts.google.com/o/oauth2/auth?access_type=offline&approval_prompt=force&client_id=735700920198-s8dgmjklquub72gljfo3vq6ca31310un.apps.googleusercontent.com&include_granted_scopes=true&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code&scope=https://www.googleapis.com/auth/spreadsheets.readonly

初回実行時はURLが表示されるのでそれにアクセスします。

1: google アカウントを選択します。
06.jpg

2:認可画面が表示されるので「許可」クリックします。
07.jpg

3: 認可コードが表示されるので実行画面へ貼り付けReturn
08.jpg

実行結果へ貼り付けてReturn

> ruby sheets/quickstart/quickstart.rb 

Open the following URL in the browser and enter the resulting code after authorization:
https://accounts.google.com/o/oauth2/auth?access_type=offline&approval_prompt=force&client_id=735700920198-s8dgmjklquub72gljfo3vq6ca31310un.apps.googleusercontent.com&include_granted_scopes=true&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code&scope=https://www.googleapis.com/auth/spreadsheets.readonly
# ↓へ上記で取得した認可コードを貼り付けReturn
[認可コード]

# GoogleSpreadSheetから取得した結果が表示されます。
Name, Major:
Alexandra, English
Andrew, Math
Anna, English
Becky, Art
Benjamin, English
Carl, Art
Carrie, English
Dorothy, Math
Dylan, Math
Edward, English
Ellen, Physics
Fiona, Art
John, Physics
Jonathan, Math
Joseph, English
Josephine, Math
Karen, English
Kevin, Physics
Lisa, Art
Mary, Physics
Maureen, Physics
Nick, Art
Olivia, Physics
Pamela, Math
Patrick, Art
Robert, English
Sean, Physics
Stacy, Math
Thomas, Art
Will, Math

サンプルのGoogleスプレッドシートの内容が表示されれば成功です。

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

【Ruby on Rails】RailsでのBootstrapの導入方法

Bootstrapとは

・Teitter社が開発したCSSのフレームワークの事です。
・サイトのデザインを簡単に作成することができます。
・よく使われるスタイルがあらかじめ定義されているので、定義されたルールに従って利用すれば整ったデザインのページを作成できます。

Bootstrapの導入方法

1.gemを追加する

Bootstrapを使用する為に、以下gemを追加します。
・bootstrap-sass

Gemfile
gem 'bootstrap-sass', '3.4.1' 

Gemfileに書き込み、bundle installを実行します。
コマンド実行後、最後に「Bundle complete!」という文字列を含む節が表示されればインストール完了です。

2.ファイルにbootstrapを読み込ませる

Bootstrapを適用させたいcssファイルを選択します。
その後、以下のようにコードを入力します。

※cssファイルでは、「//」でコメントアウトすることができます。

example.css
//bootstrap-import
@import "bootstrap-sprockets";
@import "bootstrap";

コードを入力し終えたら、拡張子を「css」から「scss」に変更します。

example.scss
//bootstrap-import
@import "bootstrap-sprockets";
@import "bootstrap";

以上で、bootstrapの導入は終わりです。

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