「低レイヤを知りたい人のための Cコンパイラ作成入門」を読む -機械語とアセンブラ①-
読んだもの
「CPUとメモリ」から。
プログラムカウンタ
CPUが実行するプログラムと、そのプログラムが読み書きするデータは、どちらもメモリに入っている。
メモリはCPUからみて外部の装置で、それを読み書きするには多少の時間がかかる。
CPUは「現在実行中の命令のアドレス」をCPU内部に保持していて、そのアドレスから命令を読み出して、そこに書かれていることを行い、そして次の命令を読み出して実行する。
その現在実行中の命令のアドレスのことを「プログラムカウンタ」(PC)や「インストラクションポインタ」(IP)という。
CPUが実行するプログラムの形式そのもののことを「機械語」という。
分岐命令
プログラムカウンタは必ずしも直線的に次の命令だけに進んでいくわけではない。
CPUの「分岐命令」と呼ばれる命令を使うと、プログラムカウンタを、次の命令以外の任意のアドレスに設定することができる。
レジスタ
CPUはプログラムカウンタのほかにも、少数のデータ保存領域を持っている。この領域のことを「レジスタ」と呼ぶ。
レジスタはメモリと異なりCPU内部に存在しているので、遅延なしにアクセスすることができる。
プログラムを実行する際には、
このような順序で進む。
次回は「アセンブラとは」から。
Railsのrespond_toの働き
読んだもの
http://railsdoc.com/references/respond_to
学んだこと
respond_toは、クライアントからの要求に応じて、返却するレスポンスのフォーマットを切り替えるメソッド。
respond_to do |format| format.json end
上のように記述し、items.json.slimを用意することで、 /items.json
というURLでJSONでのレスポンスを返すことができる。
Rspecで実行順序に依存して落ちるテストの再現にはseed値を使う
読んだもの
ruby - Rspecのフィーチャーテストが失敗したりしなかったり - スタック・オーバーフロー
学んだこと
テストが落ちたり落ちなかったりする場合、まず問題が出るシード値を特定する。
Randomized with seed 12130
Rspecを実行すると、上のようにSpecの実行順を決める乱数のシード値が最後に表示される。
実行順に依存して失敗するのかどうかはっきりさせるために、同じシード値を使って落ちるテストを再現できるか試してみる。
もし再現したりしなかったりするのであれば、実行順以外の要因があるかもしれない。
FactoryBotとfixturesの比較
Rspecを使ってテストを書いているが、どちらを使うべきかいつもパッと浮かばないのがイヤなので、まとめた。
読んだもの
woshidan.hatenablog.com blog.jnito.com
学んだこと
fixtures
固定のデータ(都道府県など)がほしいときに使う。
コントローラのテストなどで使う。
良い点
- IDが固定化できる
- 誤って同じレコードを複数作成する恐れがない
- 多対多のアソシエーションを簡単に書ける
悪い点
- モデルとは連携しない。バリデーションも通らない
- テーブルのカラムが増えたりすると書き直す必要がある
- テストデータは毎回自動的に全件投入される
- 「このテストケースのときだけここの値を変えたい」ができない
FactoryBot
モデルのメソッドの境界値条件などのテストを書きたいときに使う。
良い点
- 複数のデータを簡単に生成できる
- テストコード側で動的にテストデータを変更できる
- モデルのvalidationを通る
悪い点
- テストデータを投入するコードを書く必要がある
- 親子孫や多対多のアソシエーションを書くときに面倒
インスタンスメソッドとクラスメソッドの違い
読んだもの
学んだこと
あるclass(仮に C とする)があるとして、
である。
違いはこんな感じ。
インスタンスメソッド
class Add def addition(a, b, c, d, e) puts a + b + c + d + e end end add = Add.new() # インスタンスを呼び出している add.addition(1,2,3,4,5)
クラスメソッド
class Add def self.addition(a, b, c, d, e) puts a + b + c + d + e end end Add.addition(1,2,3,4,5)
(コードは上記参照先から抜粋)
【メモ】ストロングパラメータを使うのはどういうときか
読んだもの
学んだこと
結論
基本的にPOSTやPUTでリソースに変更を加えるときに使う。
ストロングパラメータの目的は、Active Modelに不適切な値が入れられ、それを公開してしまうのを防ぐこと。
そのため、POSTやPUT以外はまず使わない。
ストロングパラメータを使うことで、多くの属性を一度に更新したいときに、許可する属性を開発者が明示的に指定できる(ホワイトリスト)。
tryとtry!とぼっち演算子(&.)でレシーバがnilのときのエラーに対処する
読んだもの
学んだこと
tryとtry!とぼっち演算子(&.)は、レシーバがnilのときのエラーに対処する為に使う。
try
user.try(:name)
レシーバであるuserがnilでないなら、メソッドを実行する。
レシーバとは、hoge.methodと書かれている場合のhoge部分のこと。
tryメソッドを使用することで、下記のようにメソッドの記述を簡略化できる。
if @user.nil? @user = @user.find_by(:id) end # tryを使って簡略化する @user.try(:name)
try!
レシーバがnilの場合、 try
も try!
もnilを返す。
違いはメソッドが存在しない場合にある。
try!
はメソッド自体が定義されていない場合にはエラーを返す。(NoMethodErorr)
tryはnilを返す。
NoMethodErorrを捕捉したい場合は try!
を使う。
ぼっち演算子(&.)
try!
と同様の処理をする。
# try!を使う場合 user.try!(:name) # ぼっち演算子を使う場合 user&.(:name)
.try(:[], :hoge)
という書き方について
user = {name: 'taro'} user.try(:[], :name)
こういう書き方ができる。
これは、[]がメソッドであるということを利用している。
下記のコードは上下で同じ挙動になる。
user[:name] # => "taro" user.[] :name # => "taro"
ビジネスロジックはモデルに書く、とはどういうことか
コードレビューで指摘を受けたので改めて調べてまとめた。
読んだもの
https://wa3.i-3-i.info/word13666.html ビジネスロジックとは - IT用語辞典
学んだこと
ビジネスロジックとは
アプリケーション固有の処理やルールを記述したもの。
「ビジネスロジックを分ける」という場合、その本質は
- 使い回しできるところとできないところを分離する
- システムに変更が必要なときに見る必要があるところとないところを分離する
ことにある。
アプリケーション固有の処理と、データベースを操作する処理などの使い回しできる処理を分けておくことで、メンテナンス性・拡張性を高く保てる。
3階層システムという概念においては、プレゼンテーション層(ユーザインターフェース層)とデータアクセス層(データベース層)の中間に位置づけられる。 アプリケーション層と呼ばれることもある。
form_withを使って検索フォームをつくる
form_withを使ってこんな検索フォームを作った。
user/index.html.slim
= form_with model: User.new do |form| = form.label :name, '名前' = form.text_field :name, value: (params[:user][:name] if params[:user]) = form.submit '検索する'
users_controller.rb
class UserController < ApplicationController def index @users = query.order(:id).page(params[:page]) end private def query if params[:user].present? && params[:user][:name] User.where('LOWER(name) LIKE ?', "%#{params[:user][:name].downcase}%") else User.all end end end
検索フォームによくある下記の様な要件を実装しているのでちょっと複雑に見える。
- 検索した文字列を検索した後もフォームに残す (
value: (params[:user][:name] if params[:user])
) - 大文字小文字を区別せずに検索する (
LOWER() 〜 .downcase
) - 検索結果をID順に並べる (
.order(:id)
) - kaminariを使ってページネーションする (
.page(params[:page])
)
この条件を外してシンプルにしてみる。
user/index.html.slim
= form_with model: User.new do |form| = form.label :name, '名前' = form.text_field :name = form.submit '検索する'
users_controller.rb
class UserController < ApplicationController def index @users = query end private def query if params[:user].present? && params[:user][:name] User.where('name LIKE ?', "%#{params[:user][:name]}%") else User.all end end end
こうしてシンプルにしてみるとわかりやすい。
form_withのポイントは、
user/index.html.slim
= form_with model: User.new, method: :get do |form| = form.text_field :name
このようなフォームのパラメータは params[:user][:name]
という形で取得できること(controller参照)。
また、検索フォームとしてのポイントはコントローラの中での検索処理をqueryメソッドに切り出していること。
こうしておくとUserのIDやemailなど他の条件で絞り込みを行いたくなったときに拡張しやすい。
form_forとform_tagはform_withへ統合された(Rails 5.1)
読んだもの
Ruby on Rails 5.1リリースノート | Rails ガイド
学んだこと
なんとRails5.1から統合されていた。マジカヨ...
form_for
: Modelに基づいたformを作るときに使うform_tag
: Modelに基づかないformを作るときに使う(検索フォームなど)
と覚えていたが...
現在は下記のように書く。
form_forのように使う場合
<%= form_with model: Post.new do |form| %> <%= form.text_field :title %> <% end %> # ↓生成されるタグ <form action="/posts" method="post" data-remote="true"> <input type="text" name="post[title]"> </form>
既存のモデルに対してupdate的に使う場合はこちら。
<%= form_with model: Post.first do |form| %> <%= form.text_field :title %> <% end %> # ↓生成されるタグ <form action="/posts/1" method="post" data-remote="true"> <input type="hidden" name="_method" value="patch"> <input type="text" name="post[title]" value="<postのtitle>"> </form>
form_tagのように使う場合
<%= form_with url: posts_path do |form| %> <%= form.text_field :title %> <% end %> # ↓生成されるタグ <form action="/posts" method="post" data-remote="true"> <input type="text" name="title"> </form>
inputフィールド名にスコープを追加する場合はこちら。
<%= form_with scope: :post, url: posts_path do |form| %> <%= form.text_field :title %> <% end %> # ↓生成されるタグ <form action="/posts" method="post" data-remote="true"> <input type="text" name="post[title]"> </form>
結構がんばってform_for / form_tag を覚えていたのでショックだった。便利。
Rubyのattr_accessorとは
読んだもの
Rubyのattr_accessor, attr_reader, attr_writerとは何か -- ぺけみさお
学んだこと
attr_accessor
とは、インスタンス変数にアクセスするためのメソッドを定義するメソッドのこと。
プライベート変数に対するセッタやゲッタを自動的にセットしてくれる。
以下の3種類が使える。
attr_accessor
: セッタとゲッタを共に定義するattr_reader
: ゲッタのみを定義するattr_writer
: セッタのみを定義する
attr_readerは下のメソッドと同じ働きをする。
class User attr_reader :person end
class User def person @person end end
attr_writerは以下のメソッドと同じ働きをする。
class User attr_writer :person end
class User def person=(val) @person = val end end
Railsのバリデーションエラーを日本語化する
読んだもの
学んだこと
下記の手順で設定する。
1.以下のgemをインストールする
gem rails-i18n
2.config/application.rbに設定を追加する
config/application.rb
config.i18n.default_locale = :ja
3.ja.ymlをつくる
ディレクトリは作っても作らなくてもいいが、私は下記のように細かく作る派。
config/locales/models/user/ja.yml
ja: activerecord: models: user: attributes: user: name: 名前
4.config/application.rbにディレクトリを読み込むよう正規表現で設定を追記する
config/application.rb
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.yml').to_s]
挙動を確認する前にサーバーを再起動するのを忘れない。
Railsのモデルと一対多のリレーションのまとめ
読んだもの
Rails4で1対多のリレーションをモデルに実装する - Rails Webook Active Record の関連付け (アソシエーション) | Rails ガイド
学んだこと
下記のようなUser has many Itemsという関係のモデルを考える。
user.rb
class User < ApplicationRecord has_many :items end
item.rb
class Item < ApplicationRecord belongs_to :user validates :user_id, presence: true end
指定できるオプション
has_many / belongs_toメソッドには下記のオプションが指定できる。
- class_name
- 関連するモデルのクラス名を指定する。
- リレーションの名前(has_manyの直後に指定する。上記の例ではitems)https://railsguides.jp/association_basics.htmlと参照先のクラス名を異なるものにできる。
- foreign_key
- 参照する外部キーの名前を指定する。
- 指定しない場合、参照先のモデル名_idになる(上記の例ではuser_id)。
使えるようになるメソッド
has_many と belongs_to で一対多の関係を指定することで、下記のようなメソッドが使えるようになる。
userとitemの作成と保存に関するメソッド
# userを作成し、DBに保存する user = User.create(name: "momo") # item1を作成する item1 = user.items.build(content: 'アイテム1') # item1をDBに保存する item1.save # item2をuserとの関係を示して作成し、保存する item2 = user.items.create(content: 'アイテム2はuserのつくるitem')
userとitemのリレーションに関するメソッド
# itemオブジェクトの配列 user.items # true # itemオブジェクトが存在するか判定する user.items.present? # userオブジェクト(itemを持っているuser) item1.user
userの持つitemから検索するメソッド
# userの持つitemから条件を指定して検索する
user.items.find(...)
user.items.find_by(...)
user.items.where(...)