Rails sorcery

やりたいこと

sorceryについて理解を深めたかったので、GitHubを参照し、Wiki等に記載されている内容をまとめます。 実装方法については深く解説していないで、詳しく知りたい方はsorceryのGitHubを参照してください


Userモデルを作成する

bundle exec rails g sorcery:install
class SorceryCore < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
      t.string :email,            null: false
      t.string :crypted_password
      t.string :salt

      t.timestamps                null: false
    end

    add_index :users, :email, unique: true
  end
end
crypted_password

暗号化されたパスワード(crypted_password)。試しにrails consoleでユーザーを作成して、Userテーブルのcrypted_passwordを見てみるとめちゃ長い文字列になっている。

一方,users テーブルには password や password_confirmation というカラムは無い,ということも分かります。

password を暗号化した文字列を作成して crypted_passoword カラムに収めるのは sorcery が自動的にやってくれます。

パスワードが保存される過程は、 以下のように、「仮想的な」passwordフィールドを使用し、データベースに暗号化される前のパスワードをビューで扱う。 →このpassword属性はカラムに対応していないため、平文のパスワード情報がDBに保存されることは無い。 →パスワードは暗号化され、DBに保存される。


modelに記載されている内容

  authenticates_with_sorcery!

  validates :password, length: { minimum: 8 }, if: -> { new_record? || changes[:crypted_password] }
  validates :password, confirmation: true, if: -> { new_record? || changes[:crypted_password] }
  validates :password_confirmation, presence: true, if: -> { new_record? || changes[:crypted_password] }

  validates :email, uniqueness: true
  • validates :password, confirmation: trueはpasswordというDBに存在しない仮想的な属性(virtual attributes)が追加される。

  • if: -> { new_record? || changes[:crypted_password] }はユーザーがパスワード以外のプロフィール項目を更新したい場合に、パスワードの入力を省略できるようになる。


Controllerの内容

ログインページのcontorollerを参照しています。

user_sessions_controller.rb

class UserSessionsController < ApplicationController
  skip_before_action :require_login, only: %i[new create]
  def new; end

  def create
    @user = login(params[:email], params[:password])
    if @user
      redirect_back_or_to(root_path, notice: 'Login success')
    else
      render :new
    end
  end

  def destroy
    logout
    redirect_to(login_path, notice: 'ログアウトしました')
  end
end
redirect_back_or_to

githubで詳細を見てみると

      def redirect_back_or_to(url, flash_hash = {})
        redirect_to(session[:return_to_url] || url, flash: flash_hash)
        session[:return_to_url] = nil
      end

sessionのreturn to urlがなければこのurlになるみたいなメソッドが書かれている。

例えば、掲示板ページにアクセスしようとしたユーザにログインを要求する場合、require_loginメソッドでユーザをログインページに誘導し、ログインが成功したら、最初に訪れようとしていた掲示板ページにリダイレクトさせるということが可能になる。

参考

github.com

Ruby クラスを使う場合と使わない場合の比較

やりたいこと

Rubyでプログラムを作成していた時、クラスを使うメリットを発見したので記載します。


クラスを使わないで実装

users = []
users << { first_name: 'コナン', last_name: '江戸川' }
users << { first_name: '', last_name: '灰原' }

def full_name(user)
  "#{user[:first_name]} #{user[:last_name]}"
end

users.each do |user|
  puts '氏名': "#{full_name(user)}"
end

特に問題なく処理は実行されるのですが、ハッシュは新しくキーを追加したり、内容を変更したりできるので「脆くて壊れやすいプログラム」になりがち。大きなプログラムならなおさらですね。そこでクラスが使われます。


クラスを使って実装

class User
  attr_reader :first_name, :last_name

  def initialize(first_name, last_name)
     @first_name = first_name
     @last_name = last_name
  end

  def full_name
     "#{first_name} #{last_name}"
  end
end

users = []
users << { first_name: 'コナン', last_name: '江戸川' }
users << { first_name: '', last_name: '灰原' }

users.each do |user|
  puts '氏名': "#{full_name(user)}"
end

クラスにすることによっていいメリット

# 勝手に属性を追加できない
users[0].adult = '工藤新一'

# 勝手にfirst_nameを変更できない
users[0].first_name = '小五郎'

クラスはこのように、内部にデータを保持し、自分が保持しているデータを利用する独自のメソッドを持つことができます。データとそのデータに関するメソッドが常にセットになる。プログラムが大規模になる程、データとメソッドを一緒に持ち運べるクラスのメリットが大きくなります。

URL.createObjectURL() オブジェクトURLを用いた画像アップロードのプレビュー画面

やりたいこと

RailsのActiveStorageを使って画像をアップロードしているが、画像を添付した時に添付した画像をプレビュー表示したかったのでJavascriptを用いて実装した。この時使用したURL.createObjectURL()について理解できていなかったので勉強のためアウトプット。



URL.createObjectURL()の説明

今回、プレビュー機能を実装するためURL.createObjectURL()を使用した。この機能について最初説明いたします。

URL.createObjectURL()の引数に渡すと、オブジェクトURLを取得できます。 形式はblob: /

以下が例になります。

blob:http://localhost:3000/84cced13-9e4c-4486-8049-9b9058b74099

この文字列(オブジェクトURL)はブラウザがURLとして解釈でき、ブラウザがオブジェクトURLを管理する仕組みに渡すことで、対応するデータを取得します。

   const $ImgFile = e.target.files[0];
    const data = window.URL.createObjectURL($ImgFile);

Image from Gyazo


URL.createObjectURLを使ってブラウザのメモリに保存されたblobにアクセス可能な一意のURLを生成可能にする。 e.target.files[0]で取得したファイルの情報を定数fileに格納し、URL.createObjectURL(file)で取得した情報をオブジェクトURLに変換し、定数dataに格納します。

URLの参照行為、例えばimgタグのsrcにオブジェクトURLがセットされると、Blob URLストアから一致するものを検索してBlobオブジェクトから、データを取得します。



実装内容

  previewImg(e) {
    // // no-imgをdisplay:noneにする
    this.$NoImage.style.display = 'none';

    // 添付した画像にクラスやデータを付与
    const createImage = (data) => {
      const newImage = document.createElement('img'); 
      newImage.setAttribute('class', 'food_create_preview_img');
      // scrのデータをオブジェクトURLに
      newImage.setAttribute('src', data);
      // no-img画像に添付した画像を挿入する
      this.$image.appendChild(newImage);
    };

    // 続けて画像を添付する時、前回の画像を削除する
    const imageItem = this.$image.querySelector('img')
    if (imageItem){
      imageItem.remove();
    }

    // 取得した画像データを$ImgFileに代入
    const $ImgFile = e.target.files[0];
    const data = window.URL.createObjectURL($ImgFile);
    createImage(data);
    this.$image.onload = () => {
      URL.revokeObjectURL(data);
    };
  }

ajax通信

やりたいこと

ajaxの理解



ajaxとは

javascriptでサーバー側と非同期通信を行う手法のこと


同期通信と非同期通信について

同期通信

Image from Gyazo

webブラウザのあるページのリンクをクリックして、別のページに遷移する。その時にWebサーバーにリクエストを送る。

② HTMLファイル、CSSファイル、Javascriptファイルなどが応答として帰ってくる。

③ HTMLファイルやCSSファイルを解読して遷移先のページを表示する。


非同期通信 ajaxを用いた通信

Image from Gyazo

ajaxの場合は、javascriptでWebサーバーに対してリクエストを投げる

② Webサーバーは受け取ったデータをjavascriptに返す。

③ データの処理結果を表示。

Ajaxは、ページ全体を書き換えなくても、ページの特定の部分のみ表示を書き換えたりすることができる仕組み。

rails newでアプリを立ち上げるまで postgresql

やりたいこと

rails newしてアプリを作成していきたいが、様々なエラーに出くわしたので改善していきます。DBはpostgresqlを使用します。



エラー内容

エラー1

% rails _6.0.4_ new calender --database=postgresql --skip-test
    2: from /Users/urakamitakuya/.rbenv/versions/2.7.2/bin/rails:23:in `<main>'
  1: from /Users/urakamitakuya/.rbenv/versions/2.7.2/lib/ruby/2.7.0/rubygems.rb:296:in `activate_bin_path'
/Users/.rbenv/versions/2.7.2/lib/ruby/2.7.0/rubygems.rb:277:in `find_spec_for_exe': can't find gem railties (= 6.0.4) with executable rails (Gem::GemNotFoundException)
 % rails -v
Rails 6.0.4

これはエラー文の通り。can't find gem railties (= 6.0.4) gem install railsで解決できました。

gem install rails -v 6.0.4



エラー2

rails _6.0.4_ new . --database=postgresql --skip-test

無事rails newできたのですが、bin/rails db:createを実行するとエラーが発生してしまいます。

% bin/rails db:create
connection to server on socket "/tmp/.s.PGSQL.5432" failed: No such file or directory
    Is the server running locally and accepting connections on that socket?
Couldn't create 'calender_development' database. Please check your configuration.
rails aborted!
PG::ConnectionBad: connection to server on socket "/tmp/.s.PGSQL.5432" failed: No such file or directory

以下の記事を参考。 mysql使いがpostgresqlを入れてみた - Qiita

 % postgres -D /usr/local/var/postgres
2021-12-10 11:20:09.396 JST [43403] FATAL:  database files are incompatible with server
2021-12-10 11:20:09.396 JST [43403] DETAIL:  The data directory was initialized by PostgreSQL version 13, which is not compatible with this version 14.0.
% psql -V
psql (PostgreSQL) 14.0

どうやらデータベース本体のバージョンと今のpostgresqlのバージョンが合っていなかったようなので、

urakamitakuya@urakamiuyanoMBP calender % brew services list 
512
Name          Status  User          File
dbus          stopped               
mysql@5.7     started urakamitakuya ~/Library/LaunchAgents/homebrew.mxcl.mysql@5.7.plist
postgresql    error   urakamitakuya 
postgresql@13 stopped 

データベース本体のバージョン13を起動。するとbin/rails db:createが起動し、アプリが立ち上がりました。

% brew services start postgresql@13

linux 標準入出力 リダイレクト

標準入出力

標準入出力は以下の3つに分けられる。

  • 入力:コマンドを打つ際、キーボードを使って入力する。キーボードを使ってコンピュータにデータを渡すこと。

  • 出力:catなどのコマンド入力すると結果が画面に出てくる。このようにコマンドの結果やプログラムを外に出すこと。

  • 標準linuxコマンドをキーボードで打った時、結果はディスプレイに出力されます。実は入力元と出力先は指定すると変更することができる。指定しない場合、デフォルトで決まっているところに入出力される。このことを標準という。標準では入力はキーボード、出力はディスプレイ

linuxを使えば標準入出力先をファイルに変更することができる。linuxではコマンドの入出力先を抽象化することで、入出力先を柔軟に変更できる。

  • 標準エラー出力: プログラムのエラーメッセージを出力する。ディスプレイが通常は使われる。



> リダイレクト 入出力先を変更する

入力のリダイレクト

キーボードの代わりにファイルから入力する機能

$ cat < /etc/hosts


出力のリダイレクト

コマンドの実行結果を画面に表示するのではなくファイルに保存する機能。

ex) lsで長い結果が出力される場合、その結果をファイルにまとめる

$ ls > output.text
$ cat > output.text


出力とエラー出力をまとめる 出力をリダイレクトした後に2>&1と書く

2>&1: 標準エラー出力を標準出力と一緒にする。なのでoutput.textに標準エラー出力と標準出力の両方を出すことができる。

% ls / /hoge > output.text 2>&1
% cat output.text
ls: /hogeにアクセスできません
bin boot dev etc home lib


実際に入出力をしてみよう

% ls
home.html   index.html
// catの入力先をhome.htmlに変更
% cat < home.html
<p>こちら葛飾区亀有公園前派出所</p>%
// その出力先をfile.htmlというファイルにする
% cat < home.html > file.html
% ls
file.html   home.html   index.html
% cat file.html
<p>こちら葛飾区亀有公園前派出所</p>%                  


 % ls /hoge > file.html
ls: /hoge: No such file or directory

ls /hogeの結果、通常の出力は空のメッセージだったのでfile.htmlの内容を上書きしてしまう。エラー出力は画面に表示されていることから、通常の出力とエラー出力は別物。

// ファイルの中身は空
 % cat file.html
% ls /hoge 2> file.html
// エラー内容がファイルに保存される
 % cat file.html
ls: /hoge: No such file or directory

Linuxコマンド rm mv cp

rmコマンド ファイルやディレクトリを削除

ファイルの確認。dirディレクトリの中にfileというファイルがあります。

[work] $ ls
dir 
[work] $ ls dir
file

dirをrmコマンドで削除しようとするとディレクトリなので削除できないと表示されます。rmはこのようにファイルを削除するコマンドです。

[work] $ rm dir
rm: 'dir' を削除できません:ディレクトリです


ディレクトリを削除するには?

-rコマンドをつける。これでディレクトリを削除することができます。

[work] $ rm -r dir

他にも

-fコマンドをつけることで、ファイルを削除する際に警告文を表示しないようにできたり

[work] $ rm -f file

-iコマンドをつけることで、ファイルの削除前に確認することができます。

[work] $ rm -f file



mvコマンドファイルの移動、ファイル名の変更

mvコマンドは移動先がファイルかディレクトリかで意味が変わってくる。

mv [オプション] <移動元>... <移動先>


ファイル名を変更

[work] $ touch new_file
[work] $ ls
new_file
[work] $ mv new_file new_file1
new_fileからnew_file1へとファイル名変更


ファイルを移動

[work] $ mkdir dir
[work] $ mv new_file1 dir/
dirディレクトリ配下にnew_file1が入る



cpコマンド ファイルやディレクトリをコピー

cp [オプション] <コピー元>... <コピー先>


オプション

[work] $ cp -i file new_file
上書きする前に確認する
[work] $ cp -r dir new_dir
ディレクトリをコピーする


ファイルをコピー

[work] $ touch file
[work] $ ls
file
[work] $ cp file new_file
[work] $ ls
file new_file
[work] $ cp new_file dir
[work] $ ls dir
new_file
dirディレクトリ内にnew_fileがコピーされる

注意が一点! コピー先のファイルがすでにあると上書きするので注意