LoginSignup
1
2

More than 3 years have passed since last update.

railsで学ぶサーバーサイド

Last updated at Posted at 2020-03-17

ruby,railsの特徴的な書き方

・組み込み関数:helperのなかで開発で使用する関数を記述してやる。その関数をcontrollerやview上で使用する。元々入っているお助け関数もある。

・カッコ無しメソッド:関数を読み出すときに()を省略して良い。コードがいまいちわからないことがあると、それは省略による可能性がある。

以下のコードが同じコードとなる。

stylesheet_link_tag('application', media: 'all',
                                   'data-turbolinks-track': 'reload')
stylesheet_link_tag 'application', { media: 'all',
                                     'data-turbolinks-track': 'reload' }
stylesheet_link_tag 'application', media: 'all',
                                   'data-turbolinks-track': 'reload'

またreturnも省略できる。

def now_user
  if session[:user_id]
    User.find_by(id: session[:user_id])
  end
end

・ハッシュ:{}で構成された辞書のような変数、キーとバリューを決めて値を複数持つ変数

・シンボル:ハッシュのキーとして使われがちな文字列(頭に:をつける)?
例(以下二つは同じ意味)

user = { :name => "Michael Hartl", :email => "michael@example.com" }
user = {name: "Michael Hartl",email: "michael@example.com" }

routingとcontrollerとview

説明

rails newコマンドでアプリを作成するとroutingとcontrollerとviewが出来る。これらを使ってURLでデータを扱った処理ができる。

routingでやっている事
任意のURLに対してどのcontrollerのどの関数に飛ばすかを決める。

controllerでやっている事
controllerの中には複数の関数名があり、routingからの指示を受けてその関数名を持つHTMLファイル(view)に飛ばす。またviewの中で使う変数を定義したりする。

viewでやっている事
ここはHTMLファイルでブラウザでなにを見せるかを書いている。HTMLだけでは変数や構文を扱えないので、rubyやphpを組み込んでいる。

主なコマンド

rails new アプリケーション名:アプリ作成
rails g controller コントローラー名(複数形にするのが慣習) view名: コントローラーとビュー作成

これを取り消したければ
rails destroy controller コントローラー名 view名

rootingでの注意

・routingでresouece系を使うことでRESTFUL(get,post,patch,deleteが揃った)なURLを一括で(7種類)提供することができる。

config/routes.rb
Rails.application.routes.draw do
  root   'static_pages#home'
  .
  .
  .
  resources :users
end

これはuserに関して、viewつなげるroot情報が網羅的に入っている。参考文献はRuby on Rails チュートリアル
Railsのresourcesとresourceついて

・railsでは、ルーティングの中で決めたURLを名前付きルートとして扱う事ができる。つまりURLに依存して名前付きルートが決まる。名前付きURLの知り方はrails routes

config/routes.rb
Rails.application.routes.draw do
  get  '/help',    to: 'static_pages#help'
  #この場合はstatic_pagesコントローラーを介したhelpアクションへの繋ぎを/helpというURLにしている。
  #URLが/helpなので名前付きルートはhelp_pathやhelp_urlとなる
  get  'static_pages/help'
  #この場合はURLがstatic_pages/helpなので名前付きルートはstatic_pages_help_pathやstatic_pages_help_urlとなる。
end
  #help_pathは '/help'、help_urlは'http://www.example.com/help'となる。
  #controller内でのやり取りならpathを基本的に使い、urlはリダイレクトの時や、別の場所(メールなど)からアクセスする時に使用する

またresourcesで移行指示を一括で得た場合、そのURLの名前付きルートは特徴的な書き方をするものもある
例 @user→user_url(@user)→/user/:id

またresourcesに自分で作成したURLを入れたい時

  resources :users do #resources :usersに以下のURLを仲間に入れてやる
    member do #これで、idを組み込める
      get :following, :followers #/users/1/followingと/users/1/followersを作成できる
    end
  end

重要:名前付きURLを何故使うのか
名前付きURLにすると、名前付きURL(引数)として引数を受け取りアクセスできるから。

 delete micropost_path(micropost) #引数は基本的にモデルのインスタンスになりがち。


conntrollerではこの引数をparams[:引数名]として取り出してやれる。
渡せる引数に制限を掛けたければ、ストロングパラメーターを使用する。

その他にもたくさんありました。こちらのサイトが非常にわかりやすかったと感じました。Railsのルーティングの種類と要点まとめ

controllerでの注意

・railsではcontrollerから別の場所に飛ばせる変数は頭に@がついた変数だけ。
・またUsersコントローラーのshowアクションのrootingはusers/:idとして定義されている。そのためshowアクションでURLにあるidを取り出して処理をする。

app/controllers/users_controller.rb
class UsersController < ApplicationController

  def show
    @user = User.find(params[:id]) #rootingにあるidをprramsで取り出して使用して、userを探している。
  end

users/:idの:idのようなURLに挿入されている値を取り出す時は基本的にparams[:id]としてやると取り出せる。users/:idの他にもURLに値をいれるURLは存在するため、params[:id]が出てくれば警戒しよう

・form_tagやform_forで送られてきた値を取得するときは、params[送られてきた変数]をする。送られてきた変数の中身が空の時はparams[送られてきた変数]は空として扱う。(form_tagは値をcontrollerに送りたい時に使い、form_forは値をcontrollerに送り保存したい時に使う)。要するにモデルに対して操作を行うならば、form_forを使い、そうでないならばform_tagを使用する。参考文献は【Rails】form_for/form_tagの違い・使い分けをまとめた

app/controllers/users_controller.rb
class UsersController < ApplicationController
  .
  .
  .
  def create
    @user = User.new(params[:user]) #form入力内容を示す:userを取り出している。
    #厳密にはこれではダメ。ストロングパラメーターにしないといけない。
  end
end

またparams[:aaa][:bbb]でaaaの中のbbbを取り出すことになる

・controllerのアクション内でリダイレクトを指示することがある。(アクション名のつくHTMLファイルに飛ばずに、別のrootingに飛ばしてしまうこと)

・app/helpersとapp/controllers/application_controller.rb
app/helpersでは開発で使用する関数を定義する
app/controllers/application_controller.rbでは、全てのcontrollerの親玉になるのでどんなライブラリーを入れるかなど記述する

アイデアとして、Aのhelperで定義した関数をBのcontrollerで使用したければ全てのcontrollerの親玉である
application_controller.rbにAのhelperをincludeすれば良い

viewでの注意

・お作法としてapplication.htmlには統一レイアウトを置き
yeildという場所を書き、そこに全てのviewの内容が反映される。

app/views/layouts/application.html.erb

<html>
  <head>
    <title><%= full_title(yield(:title)) %></title>
    <%= csrf_meta_tags %>
    <%= stylesheet_link_tag    'application', media: 'all',
                               'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application',
                               'data-turbolinks-track': 'reload' %>
    <%= render 'layouts/shim' %>
  </head>
  <body>
    <%= render 'layouts/header' %>
    <div class="container">
      <%= yield %>
      <%= render 'layouts/footer' %>
    </div>
  </body>
</html>

同様に全てのviewは、別のviewファイルから読みだして自身のviewに反映させてもいい


<%= render '反映させたいviewの場所' %>
<%= render @microposts %>
#これをするとviewの中のmicroposts/_micropost.html.erbを追加することになる

これをパーシャルを追加するという。他にも

<%= render 'shared/error_messages', object: f.object %>
#これを行うとshared/error_messagesの方で、f.object(つまりf自身、入力内容自身)を変数objectと設定してパーシャリ川で使用できるという事

_error_messages.html.erb
<% if object.errors.any? %> #このようにして使用する
.
.
.

これも同じような意味

this.erb
<%= render partial: "list_footer", locals: {username: @username, listname: @listname} %>

この意味はlist_footerパーシャル(view)の中でthis.erbで定義した@usernameをusername、 @listnameをlistnameとして使用できるという事。しかしloacalsを使わずに、@usernameをそのまま使用することも可能。しかしlocalsを使った方がパーシャルを再利用しやすいため使用される。参考文献は部分テンプレートにlocals:を使って変数を渡す時は partial:もつけないとエラー

・erb(rubyの埋め込まれたHTML)
erbでは<% 中身 %>で中身の処理をして、<%= 中身 %>では中身の処理をして結果をHTMLに出す。

app/views/static_pages/home.html.erb
<% provide(:title, "Home") %> #ここで:titleにHomeを代入
<!DOCTYPE html>
<html>
  <head> #ここで読み出して、HTMLに出す
    <title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title>
  </head>

provideもyieldも関数で、変数に値を入れる、読み出すの処理をしてくれているということ。

データベースの取り扱い

手順

①まずデータベースを変更するためのマイグレーションファイルを作る。
rails g model モデル名 列名:種類

モデル名は何用のデータなのかということ、単数形で書くこと。
例 User
列(カラム)はモデルの何のデータかを示す。
例 name
種類はカラムのデータをどんな種類で保存するか
例 string つまり文字列

このコマンドで何が作成されるのか
・モデルのファイルができる。
そこでどんなデータなら保存するなどの制限を記述したりする。

・モデル名(複数形)という名前のマイグレーションファイルができる。
dbを変更する命令が書かれている。
こいつをmigrateすることでdbに行列を作れるようになる。

②rails db : migrate
マイグレーションファイルに書いてある指示を実行して行列(データ)をdbに作成できる

rails db:rollbackでdbを1つ前の状態に戻します。
rails db:migrate VERSION=0で最初の状態に戻る。

migrationファイルの記述にミスをすると、ロールバックした時にエラーが発生してしまう。→rails dbmigrate:redoで自分の書いたマイグレーションファイルがロールバックする際にトラブルをおこなさない事を確認する。

なぜマイグレーションファイルがあるか。

ファイルならgitで管理できるから!データベースは人それぞれ違うので、管理できないから。

どのマイグレーションファイルがdbに反映されているか見るにはdb/schema.rbを見る。

マイグレーションファイルの注意点

column_changeを使ってカラムの内容や条件を変更したい場合はup、downメソッドを定義してmigrateする。そうでないとrails db:rollbackができなくなる。
参考文献は【Ruby on Rails】changeとup・downの使い分けについて

migrationファイルの記述にミスをすると、ロールバックした時にエラーが発生してしまう。→rails dbmigrate:redoで自分の書いたマイグレーションファイルがロールバックする際にトラブルをおこなさない事を確認する。詳しくは現場で使える Ruby on Rails 5速習実践ガイド
migrationファイルはデータベースの歴史なので大切だが数が多くなると管理できなくなるので、Squasherというgemを使って複数のマイグレーションファイルを一つに集約させたりする

またマイグレーションファイルにモデルのメソッドを記述するのは危険。モデルのメソッドは不変ではなく、将来的に新たな制限をq加えたりする可能性があるから。詳しい対処法は現場で使える Ruby on Rails 5速習実践ガイド のP384

モデルが持っている主な関数

モデル名.new:インスタンス作成

インスタンス.valid?:インスタンスが有効か調査

インスタンス.save:インスタンスを保存(インスタンスが有効でないと保存できない)

モデル名.find_by(条件):モデルの中から条件にあったインスタンスを探す

インスタンス.authenticate():引数の文字列をハッシュ化して、dbに保存されたそのインスタンスのハッシュ化されたパスワードと同じか見る。目的のモデルファイルでhas_secure_passwordが有効になれば使えるようになる。

注意:既存のインスタンスの値を更新するとき
user.email = "新しい値"

user.saveではうまくいかない

user.update_attributes(email: "新しい値")でうまくいく

モデルファイルでは何をするのか

どんなデータなら保存するなどの制限を記述。
例 存在するか、文字数が多すぎないか、フォーマットがあっているか?
またモデルファイルの中で関数を定義して、データであるインスタンスの関数としてcontrollerで使用できる。

app/models/user.rb
class User < ApplicationRecord
  validates :name,  presence: true, length: { maximum: 50 }
  validates :email, presence: true, length: { maximum: 255 }

  def adult_check #Userモデルの関数
    if self.age >= 20  
       return "お酒が飲めます"
    end
  end 
end

emailのvalidateを詳しく知りたい場合はRuby on Rails チュートリアル。またvalidateで元々使用できる関数(presence: trueなど)は限りがあるため、モデルファイルの中で自分で定義しても良い

データ保存のイメージ

modelというクラスがありカラムという変数をたくさん持っている。

そこからインスタンスを作り、そのインスタンスにあった値を各カラムに代入して保存する。

カラムにインデックスをつけると

rails g migrationで任意のカラムにインデックスを加えるマイグレーションファイルを作る(emailカラムに使われる事が多い)

rails db migrateでデータに反映
emailカラムにインデックスが付く→これによって検索が速くなる(インデックスのおかげでアルファベット順に並べ替えたりできるから)

またマイグレーションファイルでadd_index :users, :email, unique: trueを記述すると、dbのuserテーブルのemailカラムの中身自体を一意性にできる。

つまりdatabaseのカラム自体に条件を反映させたい場合はmigrationファイルで記述する必要がある。インスタンスの変数自体に条件を反映したい場合はモデルのファイルの中に記述する。

モデルのデータから条件付きで抽出したい時

RubyでSQL言語の役割ができるActiveRecordのメソッドを使う

  def feed
    Micropost.where("user_id = ?", id) #Micropostモデルに対してwhereというActiveRecordのメソッドを行っている。
  end

ActiveRecordのメソッドを使うことでRuby言語でDBにアクセスして、必要なデータを得ることができる。
参考文献は【初心者向け】RailsのActive Recordの解説&メソッドまとめ

またモデルファイルの中で、特定の条件や並べ方を指定するメソッドをscopeを利用して定義できる。

app/models/tasks.rb
scope :recent, -> { order(created_at: :desc) }

created_atが一番新しい物が最初に来るように並び変えるメソッドをrecenとして定義

tasks_controller.rb
@tasks = Task.recent

それをtasks_controller.rbでモデルに使用する

モデルAにモデルBを従属させたい時(Userがたくさん投稿できるようにしたい)

まずモデルBがモデルAと連携しているようにしなければいけない。そのためにモデルBのカラムにモデルAから参照したidを保存する。
そのためにモデルBがモデルAを参照できるようにする。rails g model モデルBの時にその設定を行う。

モデルBのカラム(モデルAのidを保存する)をreference型にする。

例(MicropostがモデルB userがモデルA(この時は頭文字を小文字にする?))

rails generate model Micropost content:text user:references

db/migrate/[timestamp]_create_microposts.rb
class CreateMicroposts < ActiveRecord::Migration[5.0]
  def change
    create_table :microposts do |t|
      t.text :content
      t.references :user, foreign_key: true #外部キーであるuser_idを保存するカラムをuser_idという名前で作る
      #この時の注意はuser_idではなくuserとしてやらないといけないこと
      t.timestamps
    end
    add_index :microposts, [:user_id, :created_at] #インデックス追加もやっておく
  end
end

rails db:migrate


モデルAがモデルBをたくさん持てるように設定

app/models/micropost.rb
class Micropost < ApplicationRecord #モデルB
  belongs_to :user #belong_toの後ろは単数形
end
app/models/user.rb
class User < ApplicationRecord #モデルA
  has_many :microposts, dependent: :destroy #has_manyのあとは複数形→micropostsからMicropostモデルを探し出し従属させる
  #dependent: :destroyをつけると主のインスタンスが消されるとそれに従属した投稿も消されるのでuserのインスタンスが消えるとそこに従属していたmicropostも消える。
end

この後に以下のように
A.Bの複数形.Bの関数or変数を使用することができる。

def setup
    @user = users(:Tsuyoshi)
    @micropost = @user.microposts.build(content: "Hello")
  #これでTsuyoshiはHelloという投稿をする
end

参考文献は
reference型のカラムを作成、追加する時の気をつける点
外部キーをreferences型カラムで保存する

多対多はhas_many_thoroughをうまくつかう
https://qiita.com/taigamikami/items/d6a2b5e4611eb4d8e13a#has_many-through

パスワード保存、利用方法

パスワード作成

パスワードをハッシュ化(元の形に戻せないようなデータにして暗号化)して保存

ログインする時にパスワードを受け取り、それをハッシュ化して、dbとの比較をして、ログインを許可

ハッシュ化導入方法
モデルファイルの中で has_secure_passwordを記述するだけ。has_secereを利用したい場合はパスワード保存カラムはpassword_digestにしないといけない。has_secure_passwordを記述することでpasswordとpassword_confirmationという変数が作成され、それらが一致しなければ検証に失敗するようになる。

参考文献はRuby on Rails チュートリアル

ログインはどうやる?

手順

sessionの説明はこちら

ログインのためのsession controllerを作る

ログイン画面で入力されたメアドとパスワードを持つデータがあるか探す。

あればログイン、ログインでsession変数の中にuser.idの値を入れる!(sessionの導入はSessionsHelperをincludeすることで可能)
session変数の値はブラウザ内でなら不変で存在できるので、画面遷移しても存在できるため、ログインしているユーザーのためのページが閲覧できる。ログアウトする時はsession変数の値を空にするか、ブラウザを閉じる。

session[:user_id] = user.id 

session変数はハッシュ(辞書)のような形なのでキー(:user_id)と値(user.id)を指定してやる

  def now_user #session変数の有無で新たな変数の存在を定義してやる
    if session[:user_id]
      @now_user ||= User.find_by(id: session[:user_id])
    end
  end

このnow_userの存在の有無を判別することで、ログインしているかいないかを見分ける

ログアウトする時は
session変数の中身を空にし
@now_userも空にする

cookiesとsession

sessionとcookieの違いはsessionはブラウザを閉じれば値が破棄されるがcookieは破棄されないこと。

cookies保存法
記憶トークンを作る
トークンとはPCがランダムに作ったパスワードみたいなもの(普通のパスワードは人間が考えて作るけど)

記憶トークンを暗号化する(記憶トークンのハッシュ値、つまり暗号化した値)

暗号化された記憶トークンをユーザーインスタンスのデータとして保存する

ブラウザのcookies(これはテキストファイル)の中に
signedで暗号化したユーザーIDと
暗号化されていない記憶トークンを有効期限付きで保存する

再度ブラウザを開いた時に
cookiesに保存した暗号化したIDの暗号化を解く

解いたIDであるユーザーをdbから探し、そこに保存されている記憶トークンのハッシュ値を取得
記憶トークンのハッシュ値の暗号を解いた値とブラウザのcookiesに保存した暗号化されていない記憶トークン
が同じであれば

ログインを許可する
ログイン処理の中には
session(:user_id) = user_idがあるので
sessionに値が入り、画面遷移してもログインしているアカウント用のページを返すようにする

cookiesを駆動させるための準備

記憶トークンをインスタンスデータ保存
・記憶トークン作成メソッドをUserモデルファイルで書く(最終的にユーザーインスタンスに保存されるから)

・暗号化される前の記憶トークンを格納するための変数(railsチュートリアルではremember_token)をUserモデルファイルで定義
この変数はデータとして保存される必要はないため、カラムを作らず変数のみを定義する

・暗号化される前の記憶トークンが入った変数を暗号化して、更新保存する関数もUserモデルファイルで書く

参考文献はRuby on Rails チュートリアル


暗号化されたIDと暗号化していない記憶トークンをcookiesに保存
cookiesはsession同様、ハッシュ(辞書)の形をしているためキーと値が必要

cookies.signed[:user_id] = user.id 
#この意味は値であるuser.idをsignedで暗号化して、cookiesのキーである:user_idに紐付ける?
User.find_by(id: cookies.signed[:user_id]) 
#このようにcookies.signed[:user_id]と記述すると自動的に暗号が解除される


ブラウザを開いた時に以下の関数を使って確認が行われる

・Userモデルファイルで関数てきぎ(cookiesの保存されている暗号化していない記憶トークンを引数として受け取り、インスタンスのデータとして保存されている暗号化していない記憶トークンを暗号化を解除した値が同じであるか確認する関数)

自動ログインの許可を得る方法

ログイン時、今後自動ログインを許可しますかと聞くチェック欄を儲け

OKならば記憶トークン作成、変数代入→暗号化してインスタンスのデータとして更新→暗号化されたIDと暗号化していない記憶トークンをcookiesに保存

cookies導入した後にログアウトすると適切にログアウトする方法

cookiesの中身を空にする
sessionの中身を空にする
変数now_userをnilにする

cookies使用時のnow_userの定義(ログインを見分けるための変数)

cookiesでの自動ログインを行いたければ@now_userの定義をsession変数のみでのログイン処理から変更しないといけない。

session変数のみを使用する時

  def now_user #session変数の有無で@now_userの存在を定義
    if session[:user_id]
      @now_user ||= User.find_by(id: session[:user_id])
    end
  end

cookiesの自動ログインも組み込む時

def now_user #if文の意味はsession[:user_id]をuser_idに代入して、それがtrueならば
  if (user_id = session[:user_id]) #session変数があれば、その値にそったuserを@now_userに
    @now_user ||= User.find_by(id: user_id)
  elsif (user_id = cookies.signed[:user_id]) #そうではなくてもcookiesに暗号化されたidがあるならばそれに沿ったuserを@now_userに
    user = User.find_by(id: user_id)
    if user && user.authenticated?(cookies[:remember_token])
      log_in user
      @now_user = user
    end
  end
end
cookies利用の注意点

cookiesとはブラウザに存在するファイル。つまりブラウザ毎に対応したcookiesを持つ。
しかしデータベースは一つである。

同種の二つのブラウザでログインしており、片方をログアウトしてその後もう片方もログアウトしようとする

1回目のログアウト処理によってcookiesの中身を空、sessionの中身を空、now userがnilになっている

もう片方でログアウトしようとするがcookiesの中身を空、sessionの中身を空、now userがnilになっているのでできない

解決策
ログアウトをするという関数はログイン状態(cookiesの中身を空、sessionの中身を空、now userがnilでない)の時にのみ行うようにする

二種類のブラウザでログインしていて、片方をログアウト、その後もう片方をログアウトせずにブラウザ終了する

一つをログアウトしたことでデータベースに保存されていた暗号化された記憶トークが消去

もう一種のブラウザを再度立てると
cookiesの中に値はあるがデータベースには暗号化された記憶トークがない状態。
この場合、以下の内容がfalseでないと@now_userを定義できてしまう。

user.authenticated?(cookies[:remember_token])


暗号化された記憶トークがない状態でuser.authenticated?(cookies[:remember_token])を行うとfalseになるようにしないといけない

Userインスタンスの値を更新したい時

最初に入力作成した時と同じようにform_forを使って同じように記述すれば良い

その時railsで行われること
erbでform_forを書くとHTMLで読み出した時、postを使ってrootingに送信する

しかし入力作成のときもpost送信、更新もpost送信になると、rootingではpostにするかpatchにするかをどう決める?

@userが既に作成されているかを見極める

Userインスタンスの値を更新する時、気をつけたいこと
ログインしていない人や、他のユーザーが勝手に更新できないようにbefore_actionで対策する。
before_actionはコールバックの一種である。
コールバックとはモデルの検証や登録、削除、更新のイベントの前後で発生するイベントのこと。before_~やafter_~として記述する。before_~やafter_~に処理を記述することで、そのコールバックのタイミングで処理が実行される。コールバックは連なっており、一つのコールバックの処理が失敗すると、以降のコールバック処理は実行されない。

ログインに関する補足

*ログインやログアウトを命ずる関数(あるいはその中で使うcookiesをいじる関数)はappのコントローラーhelperの中で定義したりする。つまり開発で必要になる関数はhelperで書くって感じ。

*ログインしていないユーザーがいじってはいけないページを設定したければ、beforeactionで、ログインを確認して、アクションまで到達させるかを見極める。
注意: beforeactionはcontroller内でのみ発動できる。そのためcontrollerで記述することが必要?

  before_action :logged_in_user, only: [:edit, :update] #edit,updateアクション実行の前にlogged_in_userを発動。
  .
  .
  .
  private

    # ログインされているかを確認
    def logged_in_user
      unless logged_in?
        flash[:danger] = "Please log in."
        redirect_to login_url
      end
    end
end

全てのcontrollerのアクションで発動させたければ、controllerの親玉であるapplicationcontrollerでbefore_actionを発動してやる

*ログインテストなどでテスト環境のデータベースにデータを置きたい時はfixturesに記述する。
参考文献はRuby on Rails チュートリアル

*ログインする前にログインしないといじれないページにアクセスした時
ログインした後に直接ログイン前にアクセスしたページに飛ばす工夫

アクセス失敗時に変数にそのurlを代入

ログインしたらそこに飛ばせるようにする

飛べば、変数の値を空にする

*ユーザーの削除
ユーザーの削除を可能にするのは管理者ユーザーのみにする。
管理者ユーザーを見極めるために、Userモデルに管理者を見極めるカラムを作る。

アカウントの有効化(新規登録などで行う)

手順

まずUsersのtableデータに以下のカラムを追加する。

*有効化しているかいないか(初期状態は無効にしておく)
*有効化トークン暗号化版
*いつ有効化されたか


モデルファイルにbefore_createを使って、ユーザのインスタンスが作られる前に、有効化トークンをpcに作らせて暗号化し、それをインスタンスのデータとして保存する

ユーザーにメールを送信する(appの中にあるmailerとそれに対応するアクションが送信をしてくれる。
rails g mailer メイラー名 アクション名で作れる)。メールの中にはリンクがあり、そのリンクにはあらかじめ引数としてユーザーのメアド、有効化トークンが組み込まれている

りンクを押すとappにメアドと有効化トークンを送る、メアドからユーザーを探し、そのユーザーのdbに保存された有効化トークンと同じか比べる

同じならば、有効化にしてログインさせる(新規登録完了)

rails g mailer メイラー名 アクション名 で何ができるのか

・名前のついたmailer(controller見たいな感じ)
→viewと同じ名前のアクションが存在する。アクションの処理内容は対応したviewをメールとして送るというもの。
普通のアクションと異なる部分がある。それは誰にメールを送るか認識しないといけないため、引数としてUserの情報を受け取る。
そしてviewで必要な変数を設定

user_mailer.rb
  def account_activation(user)
    @user = user
    mail to: user.email, subject: "Account activation"
  end

・アクション名のhtmlviewファイルとアクション名のtextviewファイル
→メールの内容が入っている。メイラーで定義された変数を読み出す。
アカウント有効化の場合、有効化のためのリンク(メアド、有効化トークンを持った)を置いておく
何故htmlviewファイルとアクション名のtextviewファイルを作るかに関してはmailerでtextを作成する意味を知りたい

・上のアクション名のコントローラー
→メールアドレスのリンクが押された処理をするためのコントローラー

・testの中にメイラー名のpreviewファイル
→メールの内容をブラウザで確認するためにある。

具体的な準備

アカウント有効化のためのcontrollerを作成。rootingでも有効化トークンを受け取れつようなURLを作成

Userモデルに必要なカラムを追加

Userモデルファイルで
・有効化トークン作成メソッドをUserモデルファイルで書く(最終的にユーザーインスタンスに保存されるから)

・暗号化される前の有効化トークンを格納するための変数(railsチュートリアルではactivation_token)をUserモデルファイルで定義
この変数はデータとして保存される必要はないため、カラムを作らず変数のみを定義する

ここでは、記憶トーク作成時の関数を使い回す。

rails g mailerでmailerと対応するviewを作成。
mailerでは引数をとり、誰に送るかを決める。viewには有効化のためのリンク(メアド、有効化トークンを持った)を置いておく。

メールの送り方

新規作成アクション内で、インスタンスが有効ならばmailerのアクションを使用してメールを送る

users_controller.rb
  def create
    @user = User.new(user_params)
    if @user.save
      UserMailer.account_activation(@user).deliver_now
      flash[:info] = "Please check your email to activate your account."
      redirect_to root_url
    else
      render 'new'
    end
  end

deliver_nowとは即時送信という意味。その他にも非同期処理や定期送信、5分後送信などのメソッドもある。

previewの使い方

previewはメール内容を視覚で確認するためのもの

まずブラウザでメール内容を見るために
config/environments/developmentでメールを送るための設定をする

previewの中にはmailerのアクションと同名のアクションが存在する。それぞれのアクションに対応したurlも記述してある。(そこにアクセスすることでメールの内容が見れる)。url先のメールで使う変数やメール送信の命令をアクションの中で記述する

対応したurlにつなぐとpreviewのアクションで定義した変数を使ったメール内容がみれる

previewの他にも外部からmailcatcherをインストールし使用することで、視覚化できる

mailerのテスト

まずmailerのテストができるようにenvironments/test.rbでテストの環境設定をする
参考文献はRuby on Rails チュートリアル

user_mailer_test.rb
  test "account_activation" do
    user = users(:michael) #インスタンスを所得
    user.activation_token = User.new_token #インスタンスの有効化トークンに作ったトークンを代入 
    mail = UserMailer.account_activation(user) #この意味はuserのためのメールを作成している
    #この後メール内容に~はあるかなどテスト
    .
    .
  end
mailerの補足

全てのmailerの親玉はpplication_mailer.rb。ここにどこからmailを送信するか、mailerのレイアウトはどうするかを記述

パスワード再設定

手順

ユーザーがメアドを入力をしてpassword再設定をリクエスト

入力されたメールアドレスからユーザーを見つける

再設定用トークンを作り、暗号化された再設定用トークンはdbに保存する

メルアドにメルアドと暗号化されていない再設定トークンが組み込まれたURLを送信する

URLがクリックされれば、URLの中にに組み込んだメアドから対応するユーザーを探し、そのユーザーのデータの再設定トークンとURLに組み込まれた再設定トークンが同じか確認する

同じならばならばパスワード変更画面に誘導

パスワード変更が完了すればログインし、dbに保存された暗号化された再設定用トークンを空にする

パスワード再設定のための準備

パスワード再設定用のcontrollerを作成。名前付きURLを使えるようにrootingも作成

パスワード初期化トークンを保存するためのカラムをUserモデルに作る

あとは有効化と似たような感じ
参考文献はRuby on Rails チュートリアル

パスワード更新時のチェック項目

パスワード再設定の有効期限が切れていないか
無効なパスワードでないか
新しいパスワードが空ではないか

パスワード更新後、データとして保存されているパスワード初期化トークンを空にする

twitterの独特の構造

投稿の仕方

投稿のモデルを作成し、Userモデルに従属するように設定する(やり方はこの記事の「モデルAにモデルBを従属させたい時」に書いている)
投稿モデルのカラムは内容、user_idなどを持つ。

モデル名.allにした時の順番を変えたい時

モデルファイルの中で並べ方を指定してやる。

app/models/micropost.rb
class Micropost < ApplicationRecord
  default_scope -> { order(created_at: :desc) }
  #descとしてやることで、作成された時間が一番新しい投稿がMicropost.firstになる
end
フォローとフォロワーの関係構築

ユーザーAが誰をフォローしているかを知るために、Userモデルがあるテーブルhas_manyをしてやる(User_idをreferenceして、そのユーザーがフォローしているユーザーの情報を全てカラムに持つテーブル)

しかしこのやり方は、テーブルに別のユーザーの情報を全てカラムに保存する必要があるため無駄が多いので、これは行わない。

そこで誰が誰をフォローしているのかの関係性を示すテーブルを作ってやる

Userはそのテーブルを通して自分がフォローしている人のidをしり、そこからその人の情報を知れば良い
このためにhas_many throughを使用する。

上記は、ユーザーがフォローしている人をたくさん持てるようにするための手順である。そのため同様にユーザーがフォローされて
いる人をたくさん持てるようにするためにもhas_many throughを使用する。

やり方としては
関係性を示すテーブルを作るために、関係性を示すモデルを作る。このモデルはユーザーモデルに挟まれている状態。

ユーザーがフォローしている関係、ユーザーがフォローされている関係、それぞれについてメソッドを実行できるようにする

参考文献はRuby on Rails チュートリアル

ユーザーがフォローしている関係をどのように取り出すか
app/models/user.rb
  has_many :active_relationships, class_name:  "Relationship",
                                  foreign_key: "follower_id",
                                  dependent:   :destroy

Relationshipモデルを探し、そこのfollower_idカラムにuserのuser_idをいれる。そしてuserのRelationshipにアクセスしたければuser.active_relationshipと読み出す

app/models/relationship.rb
  belongs_to :follower, class_name: "User" 

この意味はrelationshipモデルはUserモデルに従属して、follower_idにuser_idを持つということ

これでuser.active_relationshipでuserのRelationshipのfollower_id=user_idのテーブルにアクセスできるようになるます。これを使ってuserがフォローしたを演出する。これと同様にやることでフォローされている関係も演出できます。
 
注意点
普通のhas_manyなどと違う点

app/models/user.rb
has_many :micrposts #この場合はmicrpostsからMicropostモデルを探し、そこのuser_idカラムにuserのuser_idをいれる。
#そしてuserのmicropostsにアクセスしたければuser.micropostと読み出す
app/models/user.rb
has_many :active_relationships, class_name:  "Relationship",
                                  foreign_key: "follower_id",
                                  dependent:   :destroy
#この場合はRelationshipモデルを探し、そこのfollower_idカラムにuserのuser_idをいれる。
#そしてuserのRelationshipにアクセスしたければuser.active_relationshipと読み出す

参考文献はRuby on Rails チュートリアルフォロー機能、完成版【Rails入門】has_many、belongs_toの使い方まとめ

ユーザーがフォローしている人を知りたい時

ユーザーモデルの中でhas_many B(自分で決めた名前) through A(モデル名) source C(カラム)

前提としてユーザーがAを従えている状態にする。
Aモデルにアクセスして、Cカラムの中身を見たい。その時はuser.BとすることでAモデルのCの集合体を得ることができる。

app/models/user.rb
has_many :following, through: :active_relationships, source: :followed
#これでuserのactive_relationshipsのfollowedの集合体を見たいならばuser.followingとしてやればよくなる

その他、補足

<a>と<%= link_to %>の違い

<a>は外部のURLにアクセスするための要素
<%= link_to %>はアプリ内部のrootingにアクセスするために作られた関数

ストロングパラメーター

セキュリティーのためにUserのインスタンスに発生させることが多い。インスタンス作成時にどんな引数も受けとってしまうことを防ぐ。これを行わないと、管理者権限があるかないかを示す変数の値を引数として渡し変更されてしまうので、どんなインスタンスでも管理者権限を持てるようになってしまう。

app/controllers/users_controller.rb
@user = User.new(params[:user]) #form入力内容を示す:userを取り出している。

app/controllers/users_controller.rb
class UsersController < ApplicationController
  .
  .
  .
  def create
    @user = User.new(user_params)
  end

  private #外部アクセスを制限

  #user_paramsとは:userの中身をpramsで紐解いているが、入力が許されているのはname,email,password,password_confirmationだけという意味
    def user_params
      params.require(:user).permit(:name, :email, :password,
                                   :password_confirmation)
    end 
end

ストロングパラメーターを適用しないとエラーが発生することもある。
【Rails】パラメータからcreateするときにはまったところメモ

エラーメッセージの表示の仕方

app/views/sharedのviewに@のついたインスタンスのエラー内容を表示するようにする。
そのviewをパーシャルとして、必要なviewで読み出す。
参考文献はRuby on Rails チュートリアル

form_for(@インスタンス名)で気をつけること

controllerで定義する変数を@変数名にすることで、他のcontrollerやviewで使えるようになる。

しかしform_for(@インスタンス名)によってcontrollerに送られてきたインスタンスの情報は

app/controllers/users_controller.rb
@user = User.new(params[@user]) 

ではなく

app/controllers/users_controller.rb
@user = User.new(params[:user]) 

で読み出す

またform_for()の中身ではインスタンスと送信メソッドを指定します。

引数に必ずしも、インスタンスの名前で入力いけないわけではない。インスタンスが入れば良いので以下のようにしても良い

<%= form_for(current_user.active_relationships.build) do |f| %>
#これはcontrollerでインスタンス変数を作成していない場合。そのためviewの中でインスタンスを作成している
module

これは抽象クラスのようなもので、そのクラスから直接的にインスタンスを作成することはできない。
includeを使用することでファイルに指定したmoduleを取り込むことができる。moduleの例としてはお助け関数を多くもつhelper.erbなどが挙げられる。

flashの使い方

Controller内でflash[:キー] = 値をしてやる。
viewでflashを読み出すと、一度だけ表示されるようになる。
flashはハッシュ(辞書)なのでキーと値入力が必要。そのため読み出す時もeach doで行う。

renderとredirect

controllerで使用されるrenderとredirectの違いは

renderは今のcontrollerからそのまま指定したviewに行く
redirect今のcontrollerから指定したURLに繋がる(つまりrootingを通してviewに向かう)

クエリとエスケープ

どちらもurlに関する事

クエリとは
urlの最後に?条件と書く事で
?以下の条件に正しく対応していればurlに繋ぐ

エスケープとは
クエリの条件で使われることが多く
urlには書き込めない文字を別の文字列で表現すること。主にセキュリティーの面で使用される。入力された文字をそのまま読み出すと悪意のあるアクセスを許してしまうのでエスケープを利用する。

例 @ → %40

これらはmailerでの有効化リンクで記述される。

以下のURLは

edit_account_activation_url(@user.activation_token, email: @user.email)

次のURLと同じになる

http://www.example.com/account_activations/q5lt38hQDc_959PVoo6b7A/edit?email=foo%40example.com

このように名前付きURLで引数を受け取れるように設定することで、URLに変数を入力することができる

POSTでの送信とは異なり、?(クエリ)を使用する時はgetメソッドと引数を使用している。

このように入力内容に送り方は複数あるが、railsの場合は受け取る時に、どちらもparams[:中身]をしてやれば読み出せる。

before_saveとbefore_create

before_actionとは別にbefore_saveとbefore_createが存在する。これらはモデルファイルで書かれる。

before_save: 関数→インスタンスのデータが保存される前に実行する関数を指定(例 保存前にメアドを全て小文字化したい)
before_create: 関数→インスタンスのデータが作成される前に実行する関数を指定(例 インスタンスができる前に有効化トークンを作りたい)

sendメソッド

send("引数")→渡された引数名の命令を実行する。

しかしsend(params[:変数])ではセキュリティー的に問題があるため、sendに渡す引数はホワイトリスト方式などで精査する。

写真のアップロードの仕方

uploaderのgem をダウンロードする

rails g uploader 〜でuploaderを作る
rails g migration ~でtableにに写真を保存する場所を示す
例 rails generate migration add_picture_to_microposts picture:string

rails db:migrateで写真を保存したいモデルのなかで写真を保存するためのカラムを作る

写真を保存したいモデルファイルで、保存したいカラムに対してuploaderが働きかけるようにする
参考文献はRuby on Rails チュートリアル

uploaderファイルの中には
アップする写真がどんな種類か見分ける、などの指示が書かれている

それを使ってviewのなかでoo種ならば写真を載せるとかにする

その他にもactive storageをインストールして使用することで、クラウド
ストレージサービスにファイルをアップロードすることができ、データベース上でモデルに紐付けを行ってくれる。
参考文献は現場で使える Ruby on Rails 5速習実践ガイド

Ajaxとは

サーバーとの通信でデータを受け取り、ページを書き換える時に、
処理をするために別のcontrollerに飛んで、処理後に再度今のページの中身を読み込んで戻ってくるのは処理が多くて遅くなる

Ajaxを使えば今のページのままで処理が行える(今のページの再読み込みをせずに処理できる)
Ajaxの処理は全てJavaScriptが行わせている

Ajaxを発動させるためにやること
form_tagに対してAjaxを行うよう設定

_follow.html.erb
<%= form_for(current_user.active_relationships.build, remote: true) do |f| %>

#remote: trueをつけることで,Ajaxを命令


コントローラー内でAjax命令を受け取れるようにする

controller.rb
  def create
    @user = User.find(params[:followed_id])
    current_user.follow(@user)
    #ここからしたが受け取れるようにする設定
    respond_to do |format|
      format.html { redirect_to @user }
      format.js
    end
  end

Ajaxの例(Jqueryで書かれている)
.load→サーバーのHTMLを読み出す
.getや.post→送ったパラメータを使ってサーバーで処理をしてその結果を返してもらう。どちらも処理結果を得られるが、getの場合は処理結果を得るだけ、postは結果も得るがサーバー側のデータも書き換えも行う?

.getや.postの関数の中では処理してもらうサーバーファイルと送るパラメータと結果取得後の何をするかを書く

Ajaxの注意
Ajaxは非同期である。つまりその処理が完了する前に次の処理移る。

注意例
①Ajaxでサーバー側のAファイルを読み込む
②Aファイルの文字色を変える
単純にこの順番に命令を書くとファイルは読み込まれるが色は変わらない。

Ajax命令の後に、その中身に対する命令をしたいならばコールバック関数を使ってやる。

railsでは予めセキュリティトークンを作成し、JavaScriptのhttpに埋め込む。そうすることで、javascriptのサーバーへのアクセスがアプリ内からの物なのかを判断できる。

参考文献はRuby on Rails チュートリアル【JavaScript入門】Ajaxの使い方とGET・POST通信まとめ!初心者目線でAjaxの説明はじめてのAjax(jQuery) 2018年版

CSS、SCSSについて

SCSSは最終的にプロセッサーによってCSSに変換されるので、railsでSCSSを使用しても大丈夫。
Application.htmlは大枠の共通レイアウトとして作成される。そしてこのファイルは、Application.cssを読み出している。またapp/assets/stylesheets/に置かれたCSSやSCSSは最終的にApplication.cssの一部として読み出される。

そのため各viewファイルに基づいたCSSを書かなくてもに、app/assets/stylesheetsの中でSCSSを書けば勝手にApplication.cssに反映され、それが共通レイアウトであるApplication.htmlに反映される。

またBootstrapを使用するときはSCSSファイルにimportして使用しないといけない?

Admin::Userscontroller

app/controller内にadmin/Userscontrollerを配置する。このようにcontroller内でadminディレクトリを作成することで、ユーザーを持っている人用とそうでない人用のcontrollerを見やすくできる。

モデル名.human_attribute_name(定義されているシンボル)

これはconfig/locales/ja.ymlに定義されている内容を読み出すためのActiveRecord::Baseのメソッド。
Railsのモデル名.human_attribute_name(:カラム名)って何だっけ?

入力内容確認画面

newアクションの後にcreateにいかず、newアクションのインスタンスの内容を全て表示させるようなアクションを作り、そこにnewに戻るボタンとcreateに進むボタンを作成する

検索機能の追加

ransackをインストールすることで そのメソッドが使えるようになる。このメソッドを検索させたいviewのアクション内で使用することで検索を可能にする。
しかしデフォルトだと検索内容に何を入れても良くなるため、制限を加える。検索したいモデルのファイルに制限を記述するが、ストロングお粗メーターとは異なり、self.ransackabeの関数に関してoverrideする形をとる
参考文献は現場で使える Ruby on Rails 5速習実践ガイドのP292

モデル名.allを上手く並ぶ変えるためのリンク(index_viewで使用)

そのリンクが押された時、渡されてきたインスタンスを並び変える。そんなリンクを作るためにはsort_linkを使用する

th = sort_link(@q, '名前')

これで@qをascで並び変えたり、descで並び替えることができる。

assigns

assignsメソッドはインスタンス変数の中身(プロパティー)を知るために使用する。

assign(:user) #@userと定義されたインスタンス変数の中身を知るにはこのように書く

参考文献

Rails チュートリアル
Railsガイド
Ruby on Rails5 アプリケーションプログラミング

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2