zipメソッドで配列を転置する

配列を転置するためには、一度対象を行列にした上でtransposeなどをしてあげれば転置できる。 ただ、配列は行列の要素を満たしていないこともあるので、足りない部分にnilを入れる必要がある。これが地味にめんどくさい。

そこで登場するのが、zipメソッド。この子は、配列で行列として足りない要素にnilを入れて、行列を転置したような配列を返してくれる。

[1,2,3].zip([4,5], [7])
# => [[1, 4, 7], [2, 5, nil], [3, nil, nil]]

ファイルタイプ

ls -lで表示されるタイムスタンプについて、『具体的に何の時間?』という部分が曖昧だったのでまとめておく。 man lsを叩くと下記の説明を確認できた。タイムスタンプは『modified(変更された)』時間とのことだった。

The Long Format If the -l option is given, the following information is displayed for each file: file mode, number of links, owner name, group name, number of bytes in the file, abbreviated month, day-of-month file was last modified, hour file last modified, minute file last modified, and the pathname.

ここで、class File::Statをみてみると、取得できる時間は、atime,ctime,mtimeの3種類がある。

もう少し調べてみると、下記の違いがあることを理解した。多分atimeaaccessで、ctimeccreatemtimemmodifiedだと思われる。

タイムスタンプ名 概要 説明
atime アクセス時間 指定日数内にアクセスされたファイル
ctime 作成時間 指定日数内に属性変更されたファイル
mtime 修正時間(iノード管理) 指定日数内に修正、更新されたファイル

参考:ファイルのatime/ctime/mtimeってなに? - ITmedia エンタープライズ

ハッシュと配列でcase-whenっぽく書く

ファイルタイプの分類をとりあえず3パターン以上ならcase文しか思い浮かばず、下記のように記述していた。ただ、見た目はわかりやすいけど、冗長でなんだんかなーと思っていた。

# case-when
def replace_file_type(file_name)
  p File.ftype(file_name)
  case File.ftype(file_name)
  when 'file'
    '-'
  when 'directory'
    'd'
  when 'link'
    'l'
  end
end

rubocopを実施するとハッシュで書けと指摘があった。下のように書き直すことができた。めちゃスッキリした😄

# ハッシュで返す
def replace_file_type(file_name)
  { file: '-', directory: 'd', link: 'l' }[File.ftype(file_name).interm]
end

注意としては、上記はハッシュのシンボルになっているので、ftypeメソッドで取得したタイプをシンボル変換するために、intermメソッドにかける必要がある。

もう一個caseで冗長に書いていたところがあった。

# case-when
def replace_permission(permission)
  case permission
  when 0
    '---'
  when 1
    '--x'
  when 2
    '-w-'
  when 3
    '-wx'
  when 4
    'r--'
  when 5
    'r-x'
  when 6
    'rw-'
  when 7
    'rwx'
  end
end

ハッシュのヒントから、『0~7に意味がある(権限を示す)から、配列の要素番号と紐付けて表現しちゃえば良くね?』と考えて、下記のように修正してみた。個人的にとてもスッキリかけたと思った😄

# 配列でやってみた
def replace_permission(permission)
permission_array = ['---', '--x', '-w-', '-wx', 'r--', 'r-x', 'rw-', 'rwx']
end

ただ、上のコードには改善の余地があって、メンターさんに『変わらない値なら定数化してしまったほうがいいよ』確かにその通りで、定数化すれば変更されることないし、メソッドを呼び出すこともないしでいいことばかりだと思いました🧐というわけで、下記のようにしました。

# 配列でやってみた(定数化)
PERMISSION_ARRAY = ['---', '--x', '-w-', '-wx', 'r--', 'r-x', 'rw-', 'rwx'].freeze

nginxのインストールと実行

nginxの特徴

  • 静的なコンテンツを提供するWebサーバ
    動的なコンテンツはnginx単体では提供せずに。Webアプリケーションサーバと連携することで動的コンテンツを提供する。Webアプリケーションサーバから取得したコンテンツをキャッシュする機能を持つ。

    動的なコンテンツを提供するサーバ
    apacheは、Webサーバ内にWebアプリケーションを組み込んだWebサーバらしい。

nginxのインストール

Debian版のインストール方法を参考に、サクラVPSdebianに入れてみた。

sudo apt update
sudo apt install curl gnupg2 ca-certificates lsb-release debian-archive-keyring
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
  | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
gpg --dry-run --quiet --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg
pub   rsa2048 2011-08-19 [SC] [expires: 2024-06-14]
      573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62
uid                      nginx signing key <signing-key@nginx.com>
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/debian `lsb_release -cs` nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list
echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" \
| sudo tee /etc/apt/preferences.d/99nginx
sudo apt update
sudo apt install nginx

nginxの実行

ここも公式のBeginner’s Guideを参考に作業をすすめた。

nginxをスタートして、ブラウザでアクセスしてみた。めちゃくちゃ簡単にブラウザ表示できて感動。

sudo nginx

nginx.png

停止させるためには nginx -s <sinal>というフォーマットで停止させる。<signal>の部分は下記の要素がある。

要素 意味
stop fast shutdown
quit graceful shutdown
reload reloading the configuration file
reopen reopening the log files
sudo nginx -s stop

とりあえず今日はここまで。

Webアプリケーションの構造

イラスト図解式 この一冊で全部わかるWeb技術の基本を読んだので、大事だなと思ったことをまとめる。

大雑把には、『Webアプリケーションの構造』を学べる。

Webアプリケーションの3層構造

ネットワークを介してWebブラウザ上で動作するアプリケーションをWebアプリケーションという。Webアプリケーションは下記の3層構造になっている。

階層名 説明 担当
プレゼンテーション層 ユーザへの表示、
ユーザへの操作受け付け
Webブラウザ
Webサーバ
アプリケーション層 ユーザからの命令実行、
表示画面の作成
アプリケーションサーバ
データ層 データ抽出、データ更新、
データ保管
データベースサーバ

また、アプリケーションについては更にMVCモデルという考え方がある。

要素名 説明
Model アプリケーションの扱うデータと業務処理
View ユーザへの画面表示
Controller ユーザの命令を受けて、ModelとViewに指示を出す

上記の内容をまとめると、下記のような感じになる。

web_application.png

次に、Webクライアント(Webブラウザ)、Webサーバ、アプリケーションサーバ、データベースサーバの役割についてみていく。

Webクライアント(Webブラウザ

Webクライアントは、Webサーバから送られてきたHTTPレスポンスを解釈して、ユーザが理解できるように表示するソフトウェア。代表的なものがWebブラウザ

web_client.png

Webサーバ

WebサーバはWebアプリケーションにおいて、Webクライアントに対する窓口の役割を果たすプログラム。nginxがここに該当する。

web_server.png

アプリケーションサーバ

アプリケーションサーバは、Webアプリケーションの中核で業務処理を行うプログラム。Railsがここに該当する。

web_appserver.png

データベースサーバ

Webアプリケーションのデータベースを管理する役割を持つのがデータベース管理システム(DBMS:Database Management System)で、DBMSを搭載したサーバ機器をデータベースサーバと呼ぶ。アプリケーションサーバからデータ検索やデータ更新命令を受けて、データ管理を行う。

web_dataserver.png

キャッシュサーバ

キャッシュサーバは、WebサーバやDBMSの負荷を減らすために、『リクエストに対するレスポンスを覚えておく』という役割を担う。更新頻度の少ないページやデータベースに対する同じ検索文などを覚えておくサーバがいれば、負荷減らせるよねって話。

この時の『リクエストに対するレスポンスの記憶』をキャッシュというらしい。キャッシュは2種類ある。

  • コンテンツキャッシュ:文書・画像・動画のようなコンテンツのキャッシュ
  • クエリキャッシュ:DBMSのデータ検索要求(クエリ)のキャッシュ

web_cashe.png

また、更新頻度が少なくても、更新が全くないことはないため、キャッシュサーバは定期的にキャッシュを更新する必要がある。でないと新しい内容を反映できないから。

参考

Cookieとセッション

ステートフルとステートレス

Cookieとセッションを理解するためには、ステートフルとステートレスを知っておくわかりやすい。 HTTPはシンプルなプロトコルで、ステートレスという特徴がある。

HTTPのステートレスというのは、状態を保持せず、リクエスト/レスポンスの1往復でやりとりが完結する処理とみなされ、複数の処理を関連づける仕組みを持たないことを言う。

一方、ステートフルは次の処理内容に関連づける方式のこと。相手の状態を覚えることになるので、ステートレスに比べてサーバの負担は大きくなる。

概念的なイメージを載せておく。

http_statefull_stateless.png

HTTPはステートレスであるが、ECサイトなどの一連の動作をして購入するようなものがWebに増えてきたため、どうにかしてステートフルにしたいという状況になった。

そこで、HTTPを補完する(状態を保持し管理する)別の仕組みが導入されている。そこに利用されているのがCookie

Cookieとは

上に書いた通り、HTTPはステートレスなプロトコルであるため、何かしら状態を補完する仕組みが必要になる。その仕組みとしてCookieが利用されている。

Cookieとは、Webサイトの提供者が、Webブラウザを通じて訪問者のコンピュータに一時的にデータを書き込んで保存させる仕組み Cookie 【HTTP Cookie】

Cookieのやりとりは、Webサーバに接続してきたWebブラウザに対して、レスポンスと一緒に保持してほしい情報をCookieとして送信する。Webブラウザでは、そのCookieを保持しておき、次にサーバにリクエストするときには、Cookieも併せて送信する。そうすることで、サーバはCookieを参照することでWebブラウザを識別できるようになる。概念的なイメージは下記のような感じ。 Cookie.png

Cookieはメッセージヘッダーに入っている。サーバがCookieを送るときはSet-Cookieヘッダーを、クライアントがCookieを送るときはCookieヘッダーを含めることで、Cookieを送信できる。

ヘッダー名 内容 ヘッダー種別
Set-Cookie 状態を保持・管理するための情報(Cookie レスポンスヘッダーフィールド
Cookie サーバから受け取ったCookieの値 リクエストヘッダーフィールド

Set-Cookieヘッダーに属性を付与して、Cookieの有効期限というようなオプションを設定できる。 ここでの注意点は、name属性とそこに付与する値は必須という点。下の表の通り、Cookieの名前と値になるので、これが空だとSet-Cookieなのに設定されていない状態になってしまうため必須になっていると思われる。

属性 内容
name=value Cookieにつける名前とその値
expaires=date Cookieの有効期限。この値がない場合、ブラウザを閉じるとCookieは削除される
max-age=seconds Cookieの残存時間を秒数で指定
secure HTTPS通信している場合のみCookieを送信
httponly JavaScriptからのCookieへの参照制限
domain=DOMAIN_NAME Cookieが利用されるドメイン
path=PATH Cookieが利用されるサーバ上のパス

上の表の通り、有効期限が指定されていないCookieはブラウザを閉じると削除される。こうしたCookieセッションCookieと呼ぶ。

一方、有効期限が設定されたCookieはブラウザを閉じても削除されず、有効期限が来るまでブラウザ上に残り続ける。セキュリティの観点から、ECサイトではセッションCookieがよく利用される。

ここまでは、ステートレスなHTTPプロトコルをどうやって、ステートフルにするかの話。次は、ステートフルな通信をどう活かしているってお話。

セッションとは

Cookieを使うことで、HTTPプロトコルでもステートフルな通信ができるようになった。ステートフルになることの旨味は『一連の処理を保持しながら処理できる』という点。この一連の処理のことをセッションというらしい。

セッションの具体的な例をショッピングサイトでの購入の流れで示す。大雑把な流れは下記のように分類できる。

  1. ログインする
  2. 商品を選ぶ
  3. カートに入れる
  4. カートを確認する
  5. 購入する
  6. ログアウトする

この一連の処理をセッションという。Webサーバには複数のWebブラウザからリクエストが来るため、Webブラウザとセッションを関連づけてセッションを管理したい。そんなときに使えるのが、上で学んだCookie

セッション管理では、Webブラウザを識別するための情報を『セッションID』と呼び、セッションIDはサーバで生成されて、Cookieに含めてブラウザに送信される。Webブラウザでは次回以降のリクエスト時に、受け取ったセッションIDを含めた処理を行うことで、サーバとのセッションを維持したまま通信できる。

概念的には下記のような感じ。下の例だと、セッションIDは簡単なものをつけているけど、Cookieを悪用すればなりすましができてしまうので、推測しにくい値である必要がある。 settion.png

ちなみに、セッション中に行われる一連の作業の最小単位をトランザクションというらしい。イメージ的には下記。 transaction.png

参考

telnetでHTTP通信(GET,POST)をやってみる

フィヨルドブートキャンプのプラクティスで、telnetでHTTP通信してみたよーって話。

ホスト名はどこまで?

telnetを使ってHTTPメッセージを出すのは、ブラウザでポチポチして情報を取得していたのを、telnetのコマンドとして要求するようにするみたいなイメージでいた。

で、とりあえず試してみたのが下記コマンド。(リクエストメッセージのフォーマットに沿って、改行もしっかり入れた。)

% telnet dummy-bootcamp-fjord-jp.herokuapp.com 80
Trying 174.129.128.48...
Connected to dummy-bootcamp-fjord-jp.herokuapp.com.
Escape character is '^]'.
GET /articles HTTP/1.1

GET /articles HTTP/1.1を叩いて、しばらく待っていたけど何も変化しなくて、コネクションがクローズされてしまった😅何で??ってなったので、プラクティスの質問やネットを漁ってみると、リクエストのヘッダーフィールドを入力しないといけなかった(HTTP入門にも書いてあった😅)。必須だったのはHost:だったので入力して再度実行。

GET /articles HTTP/1.1
Host: http://dummy-bootcamp-fjord-jp.herokuapp.com/articles

そしたら、エラーが出てコンテンツがないという意味の404が出た。なんでや😭ってなってまた少し調査😅どうやらHost:にはホスト名を入れないといけないということだった。

自分の理解としては、http://dummy-bootcamp-fjord-jp.herokuapp.com/articlesがホスト名と思っていたけど全然違った。URLは下記の構文になっている(参考:Web技術の基本)。

URL.png

構成要素名 説明
スキーム プロトコルを指定する。
ホスト名
(ドメイン名)
接続先のサーバを指定する。IPアドレスでの指定も可
ポート番号 接続先のサーバのポート番号を指定。
プロトコル毎に決まったポート番号(well-knownポート)があるものは省略可能。
パス名 接続先サーバ上のディレクトリやファイルを指定

補足として、ドメイン名とホスト名は厳密には意味が違うらしい。

  • ドメイン名:ネットワークを特定する文字列
  • ホスト名:ネットワーク上のコンピュータにつける識別用文字列

ホスト名とドメイン名をつなげたものをFQDN(Fully Qualified Domain Name:完全修飾ドメイン)といい、こいつでネットワーク上のコンピュータを特定できる。概念的なイメージを載せておく。

domain.png

上記の通りなので、正しくは、dummy-bootcamp-fjord-jp.herokuapp.comがホスト名なので、コマンドを叩いてみる。

ちゃんと取得できました🤗

HTTPのコンテンツサイズ

『POSTも大体同じでしょ』ということで、下記を実施してみた。article[title],articles[body]の部分は、New Articleのページのソースを見て、入れるべき要素を特定した。

POST /articles/new HTTP/1.1
Host: dummy-bootcamp-fjord-jp.herokuapp.com

article[title]=April&article[body]=20220402

すると、コンテンツが存在しないことを示す404が出た。『/articles/newあるのになんでー???』となったが、QAによると、サーバ側で/articles/newにそんなサービスを用意していないということっぽい(と自分としては理解した💦)。なので、/articlesに対して、POSTしてみた。

POST /articles HTTP/1.1
Host: dummy-bootcamp-fjord-jp.herokuapp.com

article[title]=April&article[body]=20220402

そしたら、リクエスト不正の400のエラーコードが発生。ここも調べてみると、コンテンツサイズがないことが原因だった。コンテンツサイズが指定されていない場合、0byteのボディメッセージ扱いになる。POSTはデータを送信するメソッドなのに、データがないため不正リクエストになってしまったと考えられる。

というわけで、コンテンツサイズを入力すれば良い。が、コンテンツサイズってなんぼ入れればいいですか?となってしまった。コンテンツのサイズはメッセージボディなので、article[title]=April&article[body]=20220402のサイズを確認すれば良い。アルファベットと記号は1文字1byteで表現できるので、上の場合43文字なのでコンテンツサイズも43byteにしてあげれば良い。コマンドを叩いてみる。

ちゃんと取得できました🤗

その後に、『コンテンツサイズなんて送るデータよりも大きければ名でもいいじゃね?』と思って色々試してみた。結果として、送りたいデータのサイズをちゃんと送るのが一番という気付きを得られた、、、そりゃそうか😅。理由は下記。

  • 送りたいデータ>Content-Length:送るデータが途中で切られる
  • 送りたいデータ<Content-Length:送るデータがContent-Lengthになるまでメッセージが送信されない

post.png