『AWSをはじめよう』で学習①

参考

AWSをはじめよう

物理サーバと仮想サーバ

物理サーバは普通にそこにあるサーバ。

仮想サーバは物理サーバ上のリソースの領域を仮想的に分割してそこを独立したサーバと見なすこと。

オンプレミスとクラウド

住居に例えるとオンプレミスは持ち家。クラウドは賃貸。

VPSクラウド

どちらも仮想サーバ。VPSは定額制が多い。限定されたリソースが前提となっていることが多い。クラウドの場合は従量課金、柔軟なリソースの変更。ただ、厳密な違いはないらしい。VPSクラウドの一種と言えるのかも。

AWSをはじめる

AWSが有利な点は現在シェアの30割を占めており他者に比べて圧倒的にシェアが高いこと。

これにより、対応可能なエンジニアの数も多く、資料の量も最も多いことから有利になっている。AWSの管理画面はマネジメントコンソールと呼ばれている。

IAMユーザ

デフォルトではログインした状態でのユーザはルートになっている。

普段利用する時にルートだと間違って大きな変更を加えてしまう可能性があるのでIAMユーザを作っておいてそれを利用する。IAMではグループを作ってそれぞれ任意の権限を設定した上で運用することができる。グループを作ってユーザを登録するという流れ。IAM は Identity and Access Management の略。リージョンの選択によって立てるサーバの地域も変わってしまうので設定を忘れないようにする。

CloudTrail

サーバやアカウントの管理についていつ誰が何をしたかの記録が確認できるサービス。

EC2

AMI

Amazon Machine Image.

インスタンス(サーバのこと)の作成に必要なOSなどのソフトウェア構成のテンプレートのこと。

インスタンスのタイプ

T や M などスペックやバージョンによって種類がたくさんある。

セキュリティグループ

セキュリティグループとはファイアウォールのこと。セキュリティグループに指定した場所から以外はサーバに入れないようにする。また、セキュリティにはキーペアもあり、これは一度しかダウンロードができない。これがないとサーバに入ることができない。

SSH

SSHとは遠隔地のサーバと自分の目の前のパソコンを安全に繋ぐためのサービスのこと。SSHできるサーバのことをSSHサーバと呼ぶ。SSHではデータを暗号化した上で送受信している。SFTP(SSH File Transfer Protocol) や SCP(Secure Copy) と呼ばれる仕組みもこの機能を使っている。

トランザクション処理

実装

同時に保存すべき項目があったのでトランザクション処理にしてみた。

def create
  Teacher.transaction do
    @teacher = Teacher.new(teacher_params)
    @school = @teacher.build_school(school_params)
    @teacher.save!
    @school.save!
  end
  reset_session
  session[:teacher] = @teacher.id
  redirect_to @teacher
  rescue => e
    render plain: e.message
end

構文

transactionメソッドはモデルクラス経由で呼び出す。

transactionのブロック内にDBにアクセスする処理(例外が起こりうる処理)を書く。

ブロック内の処理が成功したら実行したい処理をブロックの後に続けて書く。

最後に rescue => e に続けてブロック内の例外が発生した場合の処理を書く。

ポイント

  • ブロック内のDBへの保存の処理はsaveでなく、save! にする。

    save の返り値は true/false だけど、save! の場合は失敗すると例外が返る。トランザクション処理のロールバックは例外がトリガーになるので成功以外の場合は例外になるようにする必要がある。

  • 例外処理を用意する

    ruby では bigin ~ rescue ~ end で例外を捕捉する構文があるけどここでも同じことをしている。

    rescue => e で 変数e に例外オブジェクトを格納している。

    例外が起きた時は e.message が rendering される。

追記

今回のパターンだと accepts_nested_attributes_for を使った方が良さそうです。下記のページでまとめました。

accepts_nested_attributes_forの使い方

『DNSをはじめよう』を読む

参考

DNSをはじめよう

route53

route53はAWSの提供するDNSのサービス。ドメインのゾーンを作成できる。

ちなみにec2は Elastic Compute Cloud の略。AWSではサーバのことをインスタンスと呼ぶ。

whois / dig

whois とはドメインの管理の状況や所有者の情報を確認できるサービス。

レジストリによって表示される項目が少しづつ違う。

ターミナルから whois コマンドを使うと、 Whois 情報を調べることができる。 whois コマンドではドメインの持ち主の情報だけでなく、IPアドレスの持ち主の持ち主の情報も調べることができる。

whois IPアドレス

digコマンドでもドメインからその情報を取得できる。

dig ドメイン名 [レコードのオプション] +short

であればそのドメイン名にひもづくIPアドレスを取得することができる。

+short は結果だけを表示させるオプションで、つけなければ付加情報を含んだ結果が返ってくる。

a オプション

a はデフォルトのオプションでたぶんadressの略?

mx オプション

mxはメールの情報。mはたぶんmail。

メールアドレスから実際にどのサーバにメールが送られているのかを紐づけるレコード。これが設定されていない場合はaに紐づけられたサーバにメールが送られる。

ns オプション

nsはネームサーバ。name_server。

ネームサーバを確認することができる。ネームサーバはAWSでいうとroute53で設定できる。

txtオプション

そのドメインのメール送信者の情報が定められてる。例えば、そのドメインが送信者として保証するIPアドレスもそこに記述されてて、もしそのIPアドレス以外からそのドメイン名でメールが届いて入ればそれが不正である可能性が高いとして迷惑メールとして認識されるようになる。

これらはそれぞれのレコードの存在を前提に検索できる。(aならaレコードがあれば検索できる。a レコードにそのドメインIPアドレスの関係が記録されている。)

-xオプション

これはPTR レコードを調べる。PTR レコードはIPアドレスからドメインを逆引きするためのもの。その他のレコードはドメインのネームサーバ側から設定することができるが、このレコードはIPアドレスを持っているサーバ側からでないと設定できない。

dig -x IP アドレス +short

MVCについて

よく見るMVCの図

いつものmvc
いつものmvc
もちろんこれは筋が通ってる。

①ブラウザとかのクライアントサイドからアプリへリクエストを送る。

②それを routes.rb が解釈して該当のコントローラとアクションを探す。

③コントローラはHTTPリクエストのパラメータを受け取ってアクションを実行する。その際必要に応じてモデルにアクセスし、データを渡したり受け取ったりする。

④モデルはDBにアクセスして、各自メソッドを実行する。

⑤コントローラはモデルとのやりとりを通してCRUDなどの処理を実行し、自分のviewを呼び出して、結果の値をインスタンス変数に入れて渡す。

⑥viewがコンパイルされて、htmlとしてブラウザに表示される。

僕がイメージするMVC

私のイメージ
私のイメージ
コントローラの中にモデルが点在していて、それぞれDBにアクセスし、コントローラの任務を実行・手助けする。コントローラ名とそこにいるモデルは必ずしも結びついていない。会社に例えるとコントローラは部署で、その中にアクションという業務がある(アクションは基本的にCRUDのみ)。

インスタンスに仕事を覚えてもらう

社員にあたるのがモデルのインスタンス。社員の行動は社内規定にあたるモデルのクラス定義の制約を受けている。クラス定義にモデルにやってもらいたい仕事を書いておけば、便利な仕事のスキルを社員に予め仕込んでおくことができる。そうすれば、コントローラ内で指示すべきことが減り、インスタンスに「アレやっといて」というだけでキチンと業務をこなしてくれる。つまり、優秀な社員が手に入る。結果的にコントローラが薄くなって混乱が起こりにくくなる。

コントローラの処理もまとめる

ただし、社員に仕込むスキルは彼自身(データ)に結びつくものであるべきで、そうでない処理についてはコントローラ内にプライベートメソッドとしてまとめる。別の部署でも共通の業務であれば ApplicationController にまとめておく。

カンバン方式で生産性向上を図る

カンバン作業の様子

前書き

生産性を上げるためにできることについて考えてみました。今はやっていますが、自分の場合ほっておくと飽きてやらなくなることが目に見えているので未来の自分に宛てて記事にしておきます。

進捗が可視化されてモチベーションを保ちやすい。

プロダクトが形になってくると楽しいですが、目に見えなくて実感が湧きにくい実装というのもあります。タスクを付箋で管理すると結果が可視化されて、やる気が持続しやすくなります。苦労した分前に進んでいるということを自分自身に伝えることも大切だと思います。

タスクの細分化

またこれに関連して、なるべくタスクを細分化して付箋の枚数を増やすことも生産性を上げる上で有効だと思います。付箋一枚を消化するサイクルが早くなると上記で述べたようにやる気が続きやすくなります。またタスクは抽象的であるほど、着手しづらくなるため作業の流れを止める原因になってしまいます。抽象的な課題が思いついたら(「UIを改善する」など)一旦それをそのまま付箋に書いて、あとで複数枚の付箋にバラすのが良いと思います。

スループットを意識しやすくなる。

「コードを書いた時間」や「インプットした時間」がプロダクトを作る時のスループットではない、ということを意識しやすくなります。もちろんインプットしたり、その時の作業のために最適なやり方を探すのはスループットのために重要なことではあるし、試行錯誤することも必要な作業ではあるとは思いますが、往々にして手段は目的と入れ替わり、それに気づかず、肝心のスループットである機能の実装がなされていないのに、充実感だけが得られてしまう、ということが僕の場合かなり頻繁に起こります。当然付箋のタスクは消化されていないので、それがフィードバックになって軌道修正ができます。

今取り組んでいる作業に集中しやすくなる。

タスクを書き出しておくことにはそれ自体に意味があると思います。思いついたやるべきことを付箋に書いておけばとりあえずは頭からそれを出しておくことができるのでやり残したことが気になって集中力が落ちる、ということも起こりにくくなると思います。また、やり残したタスクが目に見えないとやってもやっても終わらないという感覚になり、モチベーションを奪うので付箋の形にするのは有効だと思います。

参考にしたもの

前に読んだ本の考え方を参考にしました。

ザ・ゴール

GTD

i18nの利用

何がしたいか

例えばenumを使うときにはキーとなる文字列をviewで表示したいけど、そのままでは英語表記になってしまう。その場で変換するコードを書いたんではenumの意味がない。そういうときに予め辞書ファイルを作っておいて変換できれば便利。i18nは本来アプリの国際化の対応のための仕組みだと思うけど、今回はプログラム→UIの対応のために利用。

辞書ファイル

my_app/config/locales/ja.yml

のようにja.ymlファイルを作成。

ja:
  school:
    category: 
      general: '普通'
      agricultural: '農業'
      industrial: '工業'
      commercial: '商業'
      fishery: '水産'
      home_economy: '家庭科'
      nursing: '看護'
      information: '情報通信'
      welfare: '福祉'
      other: 'その他'

アプリの設定

下記のファイルを編集

my_app/config/application.rb

このファイルはサーバーを起動するときにしか読まれないようなので、書き換えたら再起動しないと反映されない。

module My_app
  class Application < Rails::Application
    ~略~
    config.i18n.default_locale = :ja
    ~略~      
  end
end

viewでの活用

<%= t'school.category.general' %>

上のようにすれば 「普通」が描画される。

辞書ファイルはヘルパーメソッド t から呼び出すことができる。

<%= t("school.category.#{@school.category}") %>

上のようにすれば動的に辞書を利用することができる。

一つのフォームから複数のモデルを保存

何がしたいのか?

belongs_to/has_manyの関係を持つ2つのモデルのインスタンスをbelongs_to側のformから同時に作成したい。

view

form.fields_for のようにして params を入れ子にすることは避けた。理由はリレーションの親子関係が逆になってややこしくなると思ったため。今回は fields_for @school にして別のパラメータとしてコントローラに送るようにした。

<%= form_with(model: teacher, local: true) do |form| %>

    中略

  <div class="field">
    <%= form.label :name, '名前' %>
    <%= form.text_field :name %>
  </div>

    中略

  <%= fields_for @school do |school_form| %>
    <div class="field">
      <%= school_form.label :name, '学校名' %>
      <%= school_form.text_field :name %>
    </div>
  <% end %>

  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>

コントローラ

belongs_to のリレーションがあると自動的にその属性で reqire :true になる。また、build_〇〇 というメソッドが使えるようになる。

@school = @teacher.build_school(school_params)
@school.save

とすることで @school の生成と @teacher の school_id の取得を同時に行うことができた。

class TeachersController < ApplicationController
  def new
    @teacher = Teacher.new
    @school  = School.new
  end
    
  def create
    @teacher = Teacher.new(teacher_params)
    create_school
    if @teacher.save
      reset_session
      session[:teacher] = @teacher.id
      redirect_to root_path
    else
      redirect_to root_path
    end
  end

  private
    def create_school
      @school = @teacher.build_school(school_params)
      unless @school.save
        redirect_to root_path
      end
    end

    def teacher_params
      params.require(:teacher).permit(:school_id, :name, :password, :password_confirmation, :email)
    end

    def school_params
      params.require(:school).permit(:name)
    end
end

感想

コントローラに別のモデルを保存する処理を書いて良いものか少し迷った。TeachersControllerにSchoolsControllerを継承するという手もあるけどそれはそれで微妙な気がしたのでやめた。

追記

今回のパターンだと accepts_nested_attributes_for を使った方が良さそうです。下記のページでまとめました。

accepts_nested_attributes_forの使い方