PHP 関数

関数について〜

関数とは処理のこと Rubyでいうメソッドです。

難しくないよ。大変便利!!

 

関数に関して

関数は前に持ってくる。

$length = strlen($str);

     前   後

RUbyのメソッドは後ろだったのでそこが違う!

 

例)

文字列に対する関数

strlen — ⽂字列の⻑さを得る

strtolower — ⽂字列を⼩⽂字にする strtoupperで大文字に

substr — ⽂字列の⼀部分を返す

explode — ⽂字列を⽂字列により分割する

implode — 配列要素を⽂字列により連結する

 

配列に関する関数

count — 変数に含まれるすべての要素を数える

array_reverse — 要素を逆順にした配列を返す

array_sum — 配列の中の値の合計を計算する

array_slice — 配列の⼀部を表示。配列自体はそのまま。要素を消したりせず一部表示

sort — 配列を並び替える。辞書順。アルファベット順である。

PHP学習2日目 反復 条件分岐 連想配列 等

PHP学習2日目になります!

学んだことブログにメモっていきま〜す

 

反復 while, for

ネストがある場合

 

外側の処理1回目

内側の処理が終わるまで繰り返す

外側の処理2回目

内側の処理が終わるまで繰り返す

・・・・

外側の処理最後

内側の処理が終わるまで

<?php
for ($i = 1; $i <= 9; $i++) {
for ($j = 1; $j <= 9; $j++) { #内側のループが終わるまで($j <= 9)続ける. 終わったのち外側に戻る
echo $i * $j;
echo " ";
}
echo PHP_EOL;
}

 

breakでforの処理を抜ける

<?php
for ($i = 0; $i < 10; $i++) {
if ($i == 5) {
break;
}
echo $i . PHP_EOL;
}
echo "end" . PHP_EOL;

この場合$i=5は行われない。

 

continueはその場合の処理を飛ばして続ける

<?php
for ($i = 0; $i < 10; $i++) {
$x = $i % 2;
if ($x == 1) {
continue;
}
echo $i . PHP_EOL;
}
echo "end" . PHP_EOL;

$x == 1だった場合その処理をしない

 

$argv[1]

コマンドラインで入力した情報を返す

<?php
$id = $argv[1];
$password = $argv[2];
if ($id == "Andy" && $password == "secret"){
echo "OK" . PHP_EOL;
}
else{
echo "NG" . PHP_EOL;
}

ターミナル

 % php choice_tr3.php Andy secret 横に書く

    ファイル名 $argv[1] $argv[2]];]

OK (出力結果)

$argv[0]はファイル名

 

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

論理演算子

==と===の違い

==は自動でデータ型を揃えてくれる。

(文字列と数値でも両方同じ型として扱う。)

===はデータ型を揃えない。

その為違う型同士であれば、falseになる。

また(int)このように書くことで整数型へと変換する

 

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

連想配列

これはrubyのハッシュだよ。

連想配列の要素を削除するのはunset

配列ではarray_spliceを使っていたので注意。

 

for each 連想配列に入っているやつそれぞれ取り出す

キーとバリューを置いてあげて使う

<?php
$score = ["english" => 90, "math" => 88, "science" => 80];

// $total = $score["english"] + $score["math"] + $score["science"];
// echo $total . PHP_EOL;

$total = 0;
foreach ($score as $key => $value) { #scoreのところには連想配列。 その後ろにas。 
keyとvalueに関しては他の名前で置いてもいい。
$total += $value;
}
echo $total . PHP_EOL;

おまけ

例えばenglishの要素を取り出し時は$score["english"]とする

これは[0]として取り出せない。配列のfor文は連想配列には使えない。添字ではないので。

=>あったら気をつけて

 

PHPマニュアル

説明のところ

()に引数 :の後ろに戻り値の型

phpマニュアルでは引数のことをパラメーターとして説明してる

戻り値のことは返り値としている。

 

 

 

 

PHP 変数 配列 他

PHP学習1日目今日学んだことメモ

 

変数には$ がつく

$color1 = "Red";

;忘れないでね

 

.は連結 文字列と数値も繋ぐことができる

<?php
$label = "AGE:";
$age = 20;
echo $label . $age; # => AGE:20
       ここね

 

論理値の場合

var_dump関数を使う

<?php
$name = "Andy";
$student = true;
echo $name; # => Andy
echo ",";
var_dump($student); # => bool(true)
// echo $student; # trueは1、falseは""を返す

 

var_dumpは配列の詳細を見るにも使うよ

<?php
$names = ["Andy", "Betty", "Carol"];
var_dump($names);

ターミナル

array(3) {

  [0]=>

  string(4) "Andy"

  [1]=>

  string(5) "Betty"

  [2]=>

  string(5) "Carol"

}

こう表示される。

 

var_dumpはこれからも色々出てきそう〜〜〜

 

改行 PHP_EOL;

<?php
$name = "Andy";
$name2 = "Betty";
echo $name . PHP_EOL; # PHP_EOLは改行を行う
echo $name2 . PHP_EOL;

ターミナル

Andy

Betty

 

おまけ コメントアウト

# を使えば改行しなくても横にコメントアウトを書くことができた。

丁度上のを見てもらえればわかる。

 

PHPの関数というのはRubyのメソッドみたいなものだと感じた!

 

 

PHP学習1日目

PHP

PHP学習1日目です。 メモがてらブログ書きます。

Udemyの基礎だけ学ぶPHPプログラミング講座を受講しています。

 

ターミナルコマンド

mkdir   フォルダを作成する

 mkdir helloword でhellowordというフォルダを作成できる。

historyコマンド

ターミナル内で入力したコマンドの履歴を見れる

 

cd .. これは使えると思う!

半角スペースいるので注意

子フォルダから親フォルダに移動できる(いつものcdと逆)

projects ←アプリフォルダ

例)

 tutorial-app % cd .. 

 projects % 

 

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

PHPプログラム

PHPプログラムは(<?php と?>)の中に記述する

 ?>は省略しても構わない。省略することが多いみたい。

・echo命令を使うとターミナルにデータを出力できる。

PHPプログラムはターミナル内でphpコマンドを使って実行できる

 

php.iniファイル

php.iniファイルはphpの設定を管理する。

Macの場合、デフォルトでは作成されていないので、

以下のコマンドで作る

 

sudo cp /etc/php.ini.default /etc/php.ini

解説)cpはコピー /etc/php.ini.defaultを/etc/php.iniにコピーしている。

vsコード 右上の「開く」

コマンド+shift+gで

/etc/php.iniを探して開ける

Railsチュートリアル14章

Railsチュートリアル14章で大事なとこメモっときます

テストコードはRspecで書いています。

 

フォローとフォロワーの関係ややこしいのでまとめる。

userモデル

has_many :active_relationships, class_name: "Relationship",
foreign_key: "follower_id",
dependent: :destroy
has_many :following, through: :active_relationships, source: :followed

has_many :passive_relationships, class_name: "Relationship",
foreign_key: "followed_id",
dependent: :destroy
has_many :followers, through: :passive_relationships, source: :follower

  has_many :following, through: :active_relationships,  source: :followed

 active_relationshipsのforeignキーはfollower_idで逆じゃないのか?ってなったがこれで正しい。

これは片方(逆の関係であるもの)を固定することによって片方の情報を持ってきている。

図を見た方がわかりやすい(14.7)

active

followed     | folower

1                       2

2                       1

3                       1

4                       1

foreignキーでfollewer_idを指定する current_user(今回であれば1)によってフォローしているfollowedの情報を取ってくる。

  has_many :followers, through: :passive_relationships, source: :follower

上と考え方一緒。逆の関係

(14.9)

 

has_many active_relationshipsとhas_many following両方いる理由

 

has_many active_relationshipsではfollower_idとfollowed_idの2つの関係を取ってきている。

has_many followingでfollowed_idの情報だけ取ってきている。

そこからnameやemailなどuserの情報を持ってこれる。

 

これは多対多 中間テーブルの考え方 と一緒

 

---------------------------------------------------------------------------------------

 

Ajax 非同期通信

 

form_withをlocal trueからremote trueに変更する。

_follow.html.erb

<%= form_with(model: current_user.active_relationships.build, remote: true) do |f| %>
                                        ここ!!
<div><%= hidden_field_tag :followed_id, @user.id %></div>
<%= f.submit "Follow", class: "btn btn-primary" %>
<% end %>

_unfollow.html.erb

<%= form_with(model: current_user.active_relationships.find_by(followed_id: @user.id),
html: { method: :delete }, remote: true) do |f| %>
ここ!!
<%= f.submit "Unfollow", class: "btn" %>
<% end %>

 

relationshipsコントローラー

class RelationshipsController < ApplicationController
before_action :logged_in_user

def create
@user = User.find(params[:followed_id])
# ここ@userに変わっているので注意! ここ注意!!
current_user.follow(@user)
respond_toメソッドを使ってajaxに対応させる
respond_to do |format|
下の文はif文のような作り。どちらか1つ
format.html { redirect_to @user }
format.js
end
end

def destroy
@user = Relationship.find(params[:id]).followed
current_user.unfollow(@user)
respond_to do |format|
format.html { redirect_to @user }
format.js
end
end
end

 

javascriptが無効になっていた時の設定

config/application.rb

require_relative 'boot'

require 'rails/all'

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module TutorialApp
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 6.0
 
# Settings in config/environments/* take precedence over those specified here.
# Application configuration can go into files in config/initializers
# -- all .rb files in that directory are automatically loaded after loading
# the framework and any gems in your application.
config.action_view.embed_authenticity_token_in_remote_forms = true ここ追加
end
end

 

app/views/relationships/create.js.erb

$("#follow_form").html("<%= escape_javascript(render('users/unfollow')) %>");
$("#followers").html('<%= @user.followers.count %>');

            escape_javascriptメソッド

JavaScriptファイル内にHTMLを挿入するときに実行結果をエスケープするために必要

app/views/relationships/destroy.js.erb

$("#follow_form").html("<%= escape_javascript(render('users/follow')) %>");
$("#followers").html('<%= @user.followers.count %>');

 

---------------------------------------------------------------------------------------

テストコード(Rspec)

relationshipモデル

require 'rails_helper'

RSpec.describe Relationship, type: :model do
before do
@user = FactoryBot.create(:user)
@other_user= FactoryBot.create(:user)
@relationship = Relationship.new(follower_id:@user.id,
followed_id:@other_user.id)
relationshipはfactory_bot内に記述書いてないです。

end


it "should be valid" do
expect(@relationship).to be_valid
end

it "should require a follower_id" do
@relationship.follower_id = nil
expect(@relationship).not_to be_valid
end

it "should require a followed_id" do
@relationship.followed_id = nil
expect(@relationship).not_to be_valid
end




end

 

system

follows_spec.rb

require 'rails_helper'

RSpec.describe "Relationships", type: :system do
before do
@user = FactoryBot.create(:user)
@other_user = FactoryBot.create(:user)
@other_users=FactoryBot.create_list(:user, 10)
# 下でフォロー、フォロワーの関係を作っている
# 一番でいうとfollowing_idは@user これはactive_relationship時に
# foreignキーとして設定してる(モデル見ればわかる)
ここでフォロー・フォロワーの関係を作る
@other_users[0..9].each do |other_user|
@user.active_relationships.create!(followed_id: other_user.id) userがother_userをフォロー
ここ大事 @user.とすることで follower_idは@userになる
@user.passive_relationships.create!(follower_id: other_user.id)other_userがuserをフォロー
end
 
end

it 'following page' do
log_in(@user)
visit following_user_path(@user)
expect(@user.following).not_to be_empty
expect(page).to have_content(@user.following.count)
@user.following.each do |user|
expect(page).to have_content(user.name)
expect(page).to have_link href: user_path(user)
end
 
end

it 'follower page' do
log_in(@user)
visit followers_user_path(@user)
expect(@user.following).not_to be_empty
expect(page).to have_content(@user.followers.count)
@user.followers.each do |user|
expect(page).to have_content(user.name)
expect(page).to have_link href: user_path(user)
end
end

it 'follow unfollow' do 下にrequestで4つ書いてるけどsystemでも書いてみた
log_in(@user)
visit user_path(@other_user)
# expect(page).to have_content("Follow")
expect do
click_on 'Follow'
sleep 1 ajaxなのでsleep使って少し待ってあげる
end.to change{Relationship.count}.by(1)
# expect(page).not_to have_content("Follow")
# expect(page).to have_content("Unfollow")
 
expect do
click_on 'Unfollow'
sleep 1
end.to change{Relationship.count}.by(-1)
 

end


end


RSpec.describe "Microposts", type: :request do
before do
@user = FactoryBot.create(:user)
@other_user = FactoryBot.create(:user)

end

it "create should require logged-in user" do
expect do
post relationships_path
end.to change{Relationship.count}.by(0)
expect(response).to redirect_to login_path
end

it "destroy should require logged-in user" do
@follow=@user.active_relationships.create!(followed_id: @other_user.id)
expect do
delete relationship_path(@follow)
end.to change{Relationship.count}.by(0)
expect(response).to redirect_to login_path
 
end

it "should follow a user the standard way" do ここ以降4つは上のsystemで書いた
                               follow unfollowと内容一緒
log_in_as(@user)
expect do
post relationships_path, params: { followed_id: @other_user.id }
end.to change{Relationship.count}.by(1)
end

it "should follow a user with Ajax" do
log_in_as(@user)
expect do
post relationships_path, xhr: true, params: { followed_id: @other_user.id }
                ajaxで書いてる
end.to change{Relationship.count}.by(1)
end

it "should unfollow a user the standard way" do
log_in_as(@user)
# @user.follow(@other_user)
@follow=@user.active_relationships.create!(followed_id: @other_user.id)
expect do
delete relationship_path(@follow)
end.to change{Relationship.count}.by(-1)
end

it "should unfollow a user with Ajax" do
log_in_as(@user)
# @user.follow(@other_user)
@follow=@user.active_relationships.create!(followed_id: @other_user.id)
expect do
delete relationship_path(@follow), xhr: true
end.to change{Relationship.count}.by(-1)
end

 

 
end

RSpec.describe "Microposts", type: :system do
before do
@user = FactoryBot.create(:user)
@other_user = FactoryBot.create(:user)
@other_user2 = FactoryBot.create(:user)
 
それぞれmicropostを10こ生成する
10.times do
content = Faker::Lorem.sentence(word_count: 5)
@user.microposts.create!(content: content)
end
 
10.times do
content = Faker::Lorem.sentence(word_count: 5)
@other_user.microposts.create!(content: content)
end
10.times do
content = Faker::Lorem.sentence(word_count: 5)
@other_user2.microposts.create!(content: content)
end
end
it "feed should have the right posts" do
log_in(@user)
visit user_path(@other_user)
# expect(page).to have_content("Follow")
expect do
click_on 'Follow'
sleep 1
end.to change{Relationship.count}.by(1)
visit root_path

@user.microposts.each do |micropost|   eachなしで@user.microposts.contentとしてたけどダメ
                         それだとどれを指しているのかわからないからeach使って
                         1つ1つcontentを表示する感じで
expect(page).to have_content(micropost.content)
end

@other_user.microposts.each do |micropost|
expect(page).to have_content(micropost.content)
end

@other_user2.microposts.each do |micropost|
expect(page).not_to have_content(micropost.content)
end
 



end
end


 

Railsチュートリアル13章 

Railsチュートリアル13章で大事なとこメモっておく

テストコードはRspecで書いています。

 

・複合キーインデックス

micropostのマイグレーションファイル

class CreateMicroposts < ActiveRecord::Migration[6.1]
def change
create_table :microposts do |t|
t.text :content
t.references :user, null: false, foreign_key: true

t.timestamps
end

add_index :microposts, [:user_id, :created_at] ここ!!
end
end

 

複数のキーをセットで扱うことができる。
こうすることでuser_idに紐付けられたmicropostsを作成順(今回は作成の逆順にする)で取り出す
 
・default scope
 
 Micropostモデル
class Micropost < ApplicationRecord
belongs_to :user
has_one_attached :image
default_scope -> { order(created_at: :desc) } ここ!!

このようにして作成の逆順で取り出すよう設定している。

 

time_ago_in_wordsメソッド

<li id="micropost-<%= micropost.id %>">
<%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
<span class="user"><%= link_to micropost.user.name, micropost.user %></span>
<span class="content"><%= micropost.content %>
<%= image_tag micropost.display_image if micropost.image.attached? %>
</span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
           ここ!!
<% if current_user?(micropost.user) %>
<%= link_to "delete", micropost, method: :delete,
data: { confirm: "You sure?" } %>
<% end %>
</span>
</li>

何分前とかって出るようにする。例えば3分前、3時間前、3日前。

 

マクロポストの作成(build)
 
root_pathのhomeでuserに紐ずくmicropostを作成する。
何かに紐付いたものを生成する場合、newではなくbuildになる。
作成はhomeで保存はmicropostのcreate
class StaticPagesController < ApplicationController
def home
if logged_in?
@micropost = current_user.microposts.build
                  ここ!!
@feed_items = current_user.feed.paginate(page: params[:page])
end
end
micropostのcreateアクションがこれでmergeでuser_id:current_user.idってしてないんだよね。だからcurrent_user.buildってしてるんだ
def create
@micropost = current_user.microposts.build(micropost_params)
@micropost.image.attach(params[:micropost][:image])
if @micropost.save
flash[:success] = "Micropost created!"
redirect_to root_url
else
@feed_items = current_user.feed.paginate(page: params[:page])
render 'static_pages/home'
end
end
def micropost_params
params.require(:micropost).permit(:content, :image) ここねmergeしてない
end
 
referrer
def destroy
@micropost.destroy
flash[:success] = "Micropost deleted"
redirect_to request.referrer || root_url
            ここ!!# これで前のページに戻している
homeでdeleteしたらhome, user/showでdeleteしたらuser/showに戻る root_urlはnilだった場合の対処
end
feed
userモデル
def feed
Micropost.where("user_id = ?", id)
end
 ?を使うことでidをエスケープしている
ここのidとは右側のこと。self.idのことつまりはUser.id
SQL文に変数を代入する場合は常にエスケープする必要がある
 
 
 
app/controllers/static_pages_controller.rb
 
class StaticPagesController < ApplicationController
def home
if logged_in?
@micropost = current_user.microposts.build
@feed_items = current_user.feed.paginate(page: params[:page]) ここ!!
end
end
 
app/views/static_pages/home.html.erb
<div class="col-md-8">
<h3>Micropost Feed</h3>
<%= render 'shared/feed' %>
</div>
 
app/views/shared/_feed.html.erb
<% if @feed_items.any? %>
<ol class="microposts">
<%= render @feed_items %> ここ!!
</ol>
<%= will_paginate @feed_items,
params: { controller: :static_pages, action: :home } %>
<% end %>

<%= render @feed_items %>

@feed_itemsはMicropostsのクラスを持っているため、micropostsのパーシャルを呼び出すことができた。_micropost.html.erbを呼び出してる

 

microposts作成失敗の場合に備えて

 app/controllers/microposts_controller.rb

def create
@micropost = current_user.microposts.build(micropost_params)
@micropost.image.attach(params[:micropost][:image])
if @micropost.save
flash[:success] = "Micropost created!"
redirect_to root_url
else
@feed_items = current_user.feed.paginate(page: params[:page]) ここ!!
renderする際に@feed_itemsを渡してる
render 'static_pages/home'
end
end

 

ページネートでエラーが出るので解決。

micropostsをcreateするcreateアクションがMicropostsコントローラーにあるので、Micropostsコントローラーでページネートしようとしている。

そこで、コントローラーとアクションを指定してあげることで解決する。

app/views/shared/_feed.html.erb

<% if @feed_items.any? %>
<ol class="microposts">
<%= render @feed_items %>
</ol>
<%= will_paginate @feed_items, ここ!!
params: { controller: :static_pages, action: :home } %>
 コントローラーとアクションを指定
<% end %>

 

 

テストコード(Rspec)

factorybot

FactoryBot.define do
 
factory :micropost do
content { Faker::Lorem.sentence }
# created_at { 10.minutes.ago }
association :user
 
end

trait :yesterday do
content { "昨日" }
created_at { 1.day.ago }
end

trait :now do
content { "今" }
created_at { Time.zone.now }
end

trait :one_week_ago do
content { "1週間前" }
created_at { 1.week.ago }
end

 
end

モデルテスト micropost

require 'rails_helper'

RSpec.describe Micropost, type: :model do
before do
@micropost = FactoryBot.create(:micropost)
ここはbuildじゃダメだった。
end

it "should be valid" do
expect(@micropost).to be_valid
end

it "user id should be present" do
@micropost.user = nil
expect(@micropost).not_to be_valid
end

it "content should be present" do
@micropost.content = " "
expect(@micropost).not_to be_valid
end

it "content should be at most 140 characters" do
@micropost.content = "a" * 141
expect(@micropost).not_to be_valid
end


end

RSpec.describe Micropost, type: :model do
before do
factroybot見て
@one_week_ago = FactoryBot.create(:micropost,:one_week_ago)
@now = FactoryBot.create(:micropost,:now)
@yesterday = FactoryBot.create(:micropost,:yesterday)
 
 
end

it 'order should be most recent first' do
expect(Micropost.first).to eq @now
end

end

RSpec.describe Micropost, type: :model do
before do
@micropost = FactoryBot.create(:micropost)
end

it 'associated microposts should be destroyed' do
expect do
@micropost.user.destroy
end.to change(Micropost, :count).by(-1)

end


end

 system spec micropost

equire 'rails_helper'

RSpec.describe "Microposts", type: :system do
before do
@user = FactoryBot.create(:user)
@micropost = FactoryBot.create(:micropost)
# @microposts = FactoryBot.create_list(:micropost, 32)
ここ!! micropost34個分作ってる
34.times do
content = Faker::Lorem.sentence(word_count: 5)
@user.microposts.create!(content: content)
end
 
end

it 'user/showのmicroposts' do
log_in(@user)
visit user_path(@user)
Micropost.paginate(page: 1).each do |micropost|
# expect(page).to have_link href: user_path(micropost.user)
expect(page).to have_content(micropost.user.name)
expect(page).to have_content(micropost.content)
end
end

it 'micropost sidebar count' do
log_in(@user)
visit root_path
expect(page).to have_content("34 microposts") s
log_in(@micropost.user)
visit root_path
expect(page).to have_content("1 micropost") sなし

end

it 'micropost interface 全体の流れ(micropost)' do
log_in(@user)
visit root_path
expect(page).to have_link"Next"
# これでページネーションあること(30個以上micropostあること)示す
fill_in 'micropost_content', with: "How are you?"
# labelがない場合idでもいい micropost_contentはid
画像のテスト!
image_path = Rails.root.join('spec/fixtures/kitten.jpg')
attach_file('micropost[image]', image_path, make_visible: true)
           これはname
# attach_file 'micropost_image', with: "#{Rails.root}/spec/fixtures/kitten.jpg"
expect do
find('input[name="commit"]').click
end.to change { Micropost.count }.by(1)
expect do
all('ol li')[0].click_link 'delete'      34個あるのmicropostの1番目 olとliはcss,
ul, li と一緒
ここ大事!!
# deleteを押すとYou sureというconfirmが出るのでそれの対処
# expectのブロック内にひとつ以上のexpectもしくはfindを入れないと、
# ダイアログが表示されてacceptされる前に次へ進んでしまうので注意が必要らしい
page.accept_confirm "You sure?"
# ここ!!!大事 これでOKの役割
expect(page).to have_content("Micropost deleted")
# 上の理由によりこれもいる
end.to change { Micropost.count }.by(-1)
# +@
# pageインスタンスにaccept_confirmとdismiss_confirmという
# ブラウザのダイアログ操作用メソッドが用意されていてる
# dismissに変えたところmicropostは減らなかった
visit user_path(@micropost.user)
expect(page).not_to have_link 'delete'
 
 
end
 

end

RSpec.describe "Microposts", type: :request do
before do
@micropost = FactoryBot.create(:micropost)
@other_micropost = FactoryBot.create(:micropost)
# @user = FactoryBot.create(:user)
end
# ****今気づいたけどuserとmicropostはネストの関係じゃないんだ。だからuserのidとかいらない
# ここではそういう風にしてない ネストにしない理由はcurrent_userでuserの情報を持ってこれるから
it 'should redirect create when not logged in' do
expect do
post microposts_path, params: { micropost: { content: "Lorem ipsum" } }
# こことかuserのidいらない
end.to change { Micropost.count }.by(0)
expect(response).to redirect_to login_url

end

it 'should redirect destroy when not logged in' do
expect do
delete micropost_path(@micropost)
# こことか
end.to change { Micropost.count }.by(0)
expect(response).to redirect_to login_url

end

it "should redirect destroy for wrong micropost" do
log_in_as(@micropost.user)
expect do
delete micropost_path(@other_micropost)
# こことか
end.to change { Micropost.count }.by(0)
expect(response).to redirect_to root_path
end

end

 

 

Railsチュートリアル12章 パスワード再設定

Railsチュートリアル12章で大事だと思ったことをメモします。

テストコードはRspecで書いています。

 

パスワード再設定

 

ログインする際、パスワードを忘れてしまった場合にパスワード再設定をする。

userのedit,updateでパスワードを変えるのは違う。それはパスワードが分かっていててログインできてる状態。

今回はパスワードが分からなくて、ログインできない状態

 

全体像

ログインページからpassword_fogetをクリックしてパスワード再設定ページへ。

emailを入力すれば(new),createアクションでreset_token,reset_digestを生成。

reset_digestをカラムに入れる。またこのcreate アクションでメールを送る。

メール内に書かれたedit_pathをクリック。edit_pathに含まれるreset_tokenとuserカラムにあるreset_digestがauthenticated?で合致すればeditにいける。

edit_pathでpassword とpassword_confirmationを入力(edit)するとupdateアクションへ。passwordとpassword_confirmationをupdateしてあげる。

変更されたpasswordでログインできるようになる。

---------------------------------------------------------------------------------------------------------------

 

テストコード(Rspec)

systemとrequest specで書きました。 ほとんどrequest になっちゃった。

spec/system/users_spec.rb

system

RSpec.describe "パスワードreset", type: :system do

before do
@user = FactoryBot.create(:user)
 
end

it 'password/new createでemailが正しくない時' do
visit password_resets_new_path
fill_in 'Email', with:"abc@111.com"
find('input[name="commit"]').click
expect(page).to have_content("Email address not found")
end

it 'password/new createでemailが正しい時' do
visit password_resets_new_path
fill_in 'Email', with: @user.email
find('input[name="commit"]').click
expect(current_path).to eq(root_path)
expect(page).to have_content("Email sent with password reset instructions")

end
end

同じくspec/system/users_spec.rb

request

RSpec.describe "パスワードreset", type: :request do

before do
@user = FactoryBot.create(:user)
@user.create_reset_digest
これによりreset_token生成、reset_digestとreset_sent_atをカラムに追加
system specではreset_tokenなどを生成できなかったため、request specにした
end

it 'password/edit ユーザーが無効の場合(not_activate)' do
@user.toggle!(:activated) ここ
# toggle!は真偽を反対にする この場合falseにしている
get edit_password_reset_path(@user.reset_token,email:@user.email)

expect(response.body).to redirect_to root_path
 
end

it 'password/editでemailが正しくない時' do
get edit_password_reset_path(@user.reset_token,email:"111@111.com")
expect(response).to redirect_to root_path
 
end

it 'password/editでreset_tokenが正しくない時' do
get edit_password_reset_path("invalid",email:@user.email)
expect(response).to redirect_to root_path
 
end

it 'password/editでreset_tokenが正しい時' do
get edit_password_reset_path(@user.reset_token,email:@user.email)

expect(response.body).to include "Reset password"

end

it 'password/updateでpasswordが空の時' do
patch password_reset_path(@user.reset_token),
params:{ email:@user.email,
user:{
password: "",
},
}
expect(response.body).to include "Reset password"

end

it 'password/updateでpasswordとpassword_confirmationが無効な時' do
patch password_reset_path(@user.reset_token),
params:{ email:@user.email,
user:{
password: "abcdef",
password_confirmation: "123456",
},
}
expect(response.body).to include "Reset password"
 

end

it 'password/updateで正しくpasswordとpassword_confirmationが入力されている場合' do
patch password_reset_path(@user.reset_token),
params:{ email:@user.email,
user:{
password: "abcdef",
password_confirmation: "abcdef",
},
}
expect(response).to redirect_to user_path(@user)
expect(is_login?).to be_truthy
expect(@user.reload.reset_digest).to eq nil
 

end

it 'Password resetが時間切れの場合' do
@user.update_attribute(:reset_sent_at, 5.hours.ago) ここで時間切れの要素を追加
patch password_reset_path(@user.reset_token),
params:{ email:@user.email,
user:{
password: "abcdef",
password_confirmation: "abcdef",
},
}
expect(response).to redirect_to new_password_reset_url
end


end