ECS(Fargate) デプロイ

ECS(Fargate) デプロイする流れ

アプリ構成としては Go, PostgreSql,Nginx

全体の流れ

 

*当初はdocker composeで本番環境を構築しようとしていたが、開発環境と本番環境は別物であると気づいた。開発と全く同じ環境を本番へと適用するわけではないので(dbはコンテナを使わずrdsを使う等)、あとdocker composeでやると勝手に作成されてしまうため、何が作られているのかよく分からないと思う。

そのため本番環境はdocker composeで作成せず、aws内で一つずつ作成していく形にする。

 

#メモ

・ECRにimageを上げ直した場合サービスを更新してデプロイの強制することで変更を反映(サービスの更新しなくても時間差で反映されるのかは分からないけど、こっちの方が確実)

・サービス内のタスクのログが見たい場合、タスクのページに行ってコンテナのタブをクリックすると色々表示されてCloudWatch のログを表示ってのが出るのでそっちいくとcloud watchのログ見れる。タスクのページにもログはあるが、こっちの方が反映は遅いし、cloud watchのほうがいい。ちなみにこの時クラスター作成時にcloud watchをオンにはしてなかったんだけど(お金かかるため)cloud watch使えた。課金してたら何か違いがあるのかは分からない。

#

 

・ECRにimageを上げる。

まず、それぞれのコンテナで使うimageをbuildする必要がある。

このimageを開発環境とは違うので本番用のimageを作成する。

またdocker-composeで作成するのではなく、一つずつDockerfile(本番用)からimageをbuildすることにした。

!!!!!超重要。m1 mac使用している場合はECRにimageを上げる時のimageは--platform amd64を指定すること。!!!!!!

今回、docker-composeはあくまで開発用という扱い。本番用のdocker-composeは存在しない。

またdbに関してはコンテナ運用するよりdbだけはコンテナと切り離して使用した方がいいらしい。(Elastic File System(efs)でvolumeを作るのも考えたけど、efsはdbにはあまり向かない?まぁRDSを使った方がいいのは確実だと思う)dbはRDSを使用するため、コンテナ使用しないのでECRに必要ない。そのため今回この作業で必要なのはGoとNginxだけ。

 

コマンド(ECR行ってpushコマンドを確認。手順2はしなくていい。)

1 ログイン

この時ログインできないことある。

下のようなエラー出た場合(AWS ACCESS KEYとAWS ACCESS SECRET KEYが正しくない)は以下のurl参照。

 when calling the GetAuthorizationToken operation: The security token included in the request is invalid.

Error: Cannot perform an interactive login from a non TTY device

(https://qiita.com/kaito_program/items/7b9ba489e44d2295cf6f)

*ターミナルを閉じると効果が切れるらしいのでその時はまた同じ設定する感じ。効果永続できるようにしたいが自分はわかってない。

*ECR以外でもこのエラーが出る可能性は高いと思うので、他のとこでも参考にしてほしい。

2 imageをbuild(最初に上でやった工程なので既にやっていればしなくていい)

3タグ付け

(例)docker tag *go-nginx-production*:latest XXXYYYZZZ.dkr.ecr.ap-northeast-1.amazonaws.com/go-nginx-ecr:latest

**で囲んだ場所に関してはbuildしたimageの名前に変更すること。

今回の場合はbuildしたimage(go-nginx-production)とecrのimage(go-nginx-ecr)が異なるので、commandを修正している。同じなら修正しなくていい。

4 push これはcommandそのまま打つだけ

 

以上でECRにimageを上げるのは終了

 

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

以降使うセキュリティグループ

go ...カスタムTCP port3000

nginx ...HTTP, HTTPS (ALBで使用)

postgresql(RDS) ... PostgreSql

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

 

* 以降使うvpcは全て一緒にすること。ない場合は作成しとく。

 

クラスター作成 

 

・タスク定義作成(goとnginx用それぞれ作成)

それぞれにコンテナを記述。コンテナimageはECRのurlをクリックして貼り付ける。

 

・サービス作成(goとnginx用それぞれ作成)

・それぞれ先ほど作ったタスク定義を割り当てる。

・サービス間の連携を行う場合、サービスの検出をオンにする。(サービスディスカバリ)。名前空間を設定してサービスを共通グループで囲み。サービス名をそれぞれ設定。これでそれぞれのサービスが繋がることできた(nginxのnginx.confにてproxy_pathにサービス名(go).名前空間とすることでgoのurlにいってる)

nginx.confにまだサービス名.名前空間を反映してない場合は変更してimage(本番用)をbuildし直し、再度ECRへupする動作を行う。

・この段階では、ALB(Nginxだけ)とAuto Scalingの設定はしない。ALBの設定に関してはサービス作成時でないといけないので、nginxサービスに関しては後で作り直す。

 

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

RDS(PostgreSql)

dbはコンテナじゃなくてRDS使う。値段高いのが痛い。

作成し終えたらrdsのurl生まれるのでgoのpostgres接続のhostにこのurlを当てる。

imageに反映されてなかったら再度build ecrにup

 

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

ALB(Application Load Balancer)

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

*設定前の準備

独自ドメイン取得。

ACM(Aws Certificate Manager)で証明書発行

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

・まずはターゲットグループ作成(EC2に行ったらある)。

Ip address選択

HTTP port 80

protocol version Http1

*healthy checkのpathはアクセスできるpath(postやログイン必要なパスはunhealthy返ってくるのでNG)自分の場合はそういうpathなかったので、goでjsonでmessageを返すだけのpathを作ることにしてそれをhealthy checkのpathとした。

IPv4 addressはいじらなくておk(最後の1桁を勝手に決めてくれる設定)

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

ロードバランサー作成

Application Load Balancer選択

vpc選択してmappingしとく。

セキュリティーグループは上のサービスのnginxと同じに(HTTP,HTTPS)

Listner and RoutingにはHTTPとHTTPSを設定する(それぞれ先ほど作ったターゲットグループ付与)

Default SSLCertificateはACMで発行した証明書設定

 

以上でALB作成完了。

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

 

・サービスとALB紐付ける

ALB設定はサービス開始時じゃないと行えないため先ほど作成したnginxサービスを削除、再度作成することにする。

ロードバランサーの設定に行ったら、Application Load Balancer選択、先ほど作成したALB名選択。

ロードバランス用のコンテナでロードバランサーに追加ボタンを押す。

プロダクションリスナーポートはHTTPS

ターゲットグループに先ほどロードバランサー作成時に作ったターゲットグループを設定(この時プロダクションリスナーポートが自動でHTTPになるけど、先ほどHTTPSとした意味はあったのか不明、元からHTTPでもいいと思うが・・・動きはする)

 

・Auto Scaling

Auto scalingの設定は簡単。サービスの更新で最後にauto scaling設定してあげればいい。

 

独自ドメインとALBを紐づける

Route53で独自ドメインのホストゾーンに。レコードの作成クリック。

レコード名はからで。

エイリアスをオンにしてそこでロードバランサーを選択してレコード作成すればおk

 

これにて終了

 

 

 

 

 

 

 

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