Sinatra(Ruby)とGoの標準パッケージで立てたサーバを見比べる
たまたま同じ時期に0からサーバーを立てる機会に恵まれて、似てるなと思ったので比較してみる。
Sinatra
app.rb
require 'sinatra' get '/' do erb :index end
index.erb
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>タイトル</title> </head> <body> <p>Sinatraだよ</p> </body> </html>
Go
init.go
package main import "net/http" func init() { http.HandleFunc("/", index) }
index.go
package main import ( "html/template" "net/http" ) var indexTmpl = template.Must(template.New("index").Parse(`<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>タイトル</title> </head> <body> {{.}} </body> </html>`)) func index(w http.ResponseWriter, r *http.Request) { fmt.Println(w, "Goだよ") }
改めてGoの標準パッケージすごい。そして html/template
つらい。
selfを理解する
mom0tomo.hateblo.jp 前回の続き。
selfについて、実務であまり使うこともなくちゃんと理解しているのが怪しかったので確認する。
self は、クラス内部の変数やメソッドを明示して使うときに利用する。
class Person attr_accessor :name, :age def initialize self.name = 'もも' self.age = 22 end def cording(programing_language) puts "#{self.name}は#{programing_language}を利用しています。" end end mom0tomo = Person.new mom0tomo.cording('Go言語ʕ ◔ϖ◔ʔ') # ももはGo言語ʕ ◔ϖ◔ʔを利用しています。 p mom0tomo # #<Person:0x00007fa1b684d640 @name="もも", @age=22>
selfは、呼び出されたときのインスタンス (ここではmom0tomo)を指す。
つまり、mom0tomoインスタンスから cording('Go言語ʕ ◔ϖ◔ʔ')メソッドが呼び出されたとき、
mom0tomo.cording('Go言語ʕ ◔ϖ◔ʔ')
mom0tomo.
が、 self.
に代入される。
attr_accessorメソッド
attr_accessor はクラス内で変数を定義するためのメソッド。
下記のように、引数として与えられたシンボルをクラス内の変数として定義する。
class Person attr_accessor :name, :age def initialize self.name = 'もも' self.age = 22 end end mom0tomo = Person.new p mom0tomo
selfについては次回。
Rubyのattr_readerについて
読んだもの
学んだこと
attr_readerメソッドはアクセサメソッドを定義する。 アクセサメソッドとは、クラスやモジュールにインスタンス変数を読み出すためのメソッドである。 引数には、インスタンス変数名をシンボルか文字列で指定する。
下記はBookクラスにtitleメソッドとpriceメソッドを定義する例。beforeはattr_readerを使わない場合の書き方。
before
class Book def title @title end def price @price end end
上の例をattr_readerを書き直すと、下記のようになる。
after
class Book attr_reader :title, :price def initialize(title, price) @title = title; @price = price end end
Rubyのmapとeachの違い
読んだもの
すごくわかりやすかった。
学んだこと
each
eachはレシーバ自身が返る。
def each_sample (1..10).to_a.each do |i| i + 10 end end => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
レシーバ i が返っており、インクリメントはされていない。
map
mapはブロックの戻り値を集めて返す。
def map_sample (1..10).to_a.map do |i| i + 10 end end => [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
Rubyのelsifとガード節について
コードレビューで、if elseは条件が対称な場合にだけ使った方が良いという指摘を受けた。
# if elseを使うのに適しているものの例 if number.odd? # 奇数の場合の処理 else # 偶数の場合の処理 end
上のように分岐の条件に対称性がある場合はif elseを使う。 そうでない場合は、早期リターンを使ったほうが意図が伝わりやすいという指摘だった。
読んだもの
blog.livedoor.jp techracho.bpsinc.jp
学んだこと
例
def display_kanri_fee # 管理費(画面表示用に整形する) if kanri_fee.blank? # 管理費がない場合 '- 円' => elsif kanri_fee_type.blank? # 月額/年額表示がない場合 number_to_currency(kanri_fee, unit: '円') else "#{number_to_currency(kanri_fee, unit: '円')} / #{kanri_type_i18n}" end end
ガード節で書き直す
def display_kanri_fee return '- 円' if kanri_feeblank? return number_to_currency(kanri_fee, unit: '円') if kanri_fee_type.blank? "#{number_to_currency(kanri_fee, unit: '円')} / #{kanri_type_i18n}" end
なるほど、わかりやすい気がする。
Railsのja.ymlでEnumの内容が被ったら
Enumの内容がかぶると、重複エラーになる。
You tried to define an enum named "monthly" on the model "Room", but this will generate a instance method "monthly?", which is already defined by another enum.
こういうときはprefixとsuffixをつけて区別してあげればいい。
読んだもの
学んだこと
修正前
model/Room.rb
enum yachin_type: { monthly: 1, # 月額 yearly: 2 # 年額 } enum kanrihi_type: { monthly: 1, # 月額 yearly: 2 # 年額 } # => 重複エラーになる
修正(prefixをつける場合)
model/Room.rb
enum yachin_type: { monthly: 1, # 月額 yearly: 2 # 年額 }, _prefix: :kanrihi_type enum kanrihi_type: { monthly: 1, # 月額 yearly: 2 # 年額 }
修正(suffixをつける場合)
model/Room.rb
enum yachin_type: { monthly: 1, # 月額 yearly: 2 # 年額 }, _suffix: true enum kanrihi_type: { monthly: 1, # 月額 yearly: 2 # 年額 }
Decorator Helper 違い
Helperに書かれたメソッドをDecoratorに切り出そうとして、「引数どうするんだっけ」と思った。
Helperだけ、Decoratorだけ0-1で書くときはそれほど迷わないことでも、リファクタリングなど2つ同時に作業するとき意外と迷う。
改めてまとめてみた。
読んだもの
tech.misoca.jp qiita.com izumin.hateblo.jp
学んだこと
Helperとは
ViewをよりシンプルにDRYに書くためのモジュール
View以外もhelpできるが、基本的にはViewをhelpする(ViewHelperと言ったりする)
rails g controllerを実施した際に作成される
Helperの例
module MembersHelper # 会員のフルネームを取得する def member_full_name(member) member.first_name + ' ' + member.last_name end end
Decoratorとは
Viewと対応するModelごとに作れる
- モデルの属性にアクセスする際に変数を介する必要がない
Controllerで宣言されたインスタンス変数すべてにdecorateメソッドを適用する
Decoratorの例
moduleMemberDecorator # 会員のフルネームを取得する def full_name first_name + ' ' + last_name end end
Ruby / Railsで日付と日付の差分を求める
読んだもの
学んだこと
Timeクラスか、Dateクラスを使う。 時間まで使いたい場合はTimeクラス、日付だけ使いたい場合はDateクラスを使う。
2つのクラスのオブジェクトに対し、「-」を使って差を取得する(引き算ぽい)。
やりたかったこと
期限日と今日の差分を求めて、期限まであと何日か求めたい。
require 'date' today = Date.current dead_line = Date.new(2018, 7 ,6) remain = (dead_line - today).to_i # -> 3
Date.currentはTime.zoneを、Date.todayはTime.nowを参照している。
また、素のRubyならTimeクラスを使い、 RailsならTimeWithZoneクラスを使うのがいいみたい。
伊藤さんの記事に詳しく書いてあった。
なぜRailsのpartialではインスタンス変数を参照しない方がいいのか
『Ruby on Rails5 アプリケーションプログラミング』 読んだメモ
クエリメソッドはその場でDBにアクセスしない(遅延ロード)
- メソッドチェーン(連鎖して呼び出す)が可能
生成されたクエリはPumaのコンソールに吐き出される
whereメソッドを使うと、プレイスホルダー(任意のパラメータを引き渡せる)が使える
- 動きはfind_byと同じ
パラメータ数が少ない時は名前なしにする (params[:publish]))
- 多い時は名前付きにする (publish: params[:publish])
Active Decoratorメモ
読んだもの
qiita.com GitHub - amatsuda/active_decorator: ORM agnostic truly Object-Oriented view helper for Rails 3, 4 and 5
学んだこと
- テストのとき、ちょっと変わった書き方をする。
- →ActiveDecorator::Decorator.instance.decorate(model_instance)を呼び出す
モデルの例
module OrganizationDecorator def full_name "#{first_name} #{last_name}" end end
テストの例
describe '#full_name' do it 'returns the full organization name' do organization = Organization.new(first_name: 'John', last_name: 'Doe') decorated_organization = ActiveDecorator::Decorator.instance.decorate(organization) expect(decorated_organization.full_name).to eq('John Doe') end end
ADの機能は、Rails組み込みのものだと勘違いしていた。
「使えるRSpec入門・その1 」読んだメモ
読んだもの
学んだこと
原則として「1つの example につき1つのエクスペクテーション」で書く
- テストの保守性が良くなるため
letで宣言できる変数
**インスタンス変数の例** let(:user) { User.new(params) } # 同じ意味 user = User.new(params)
ローカル変数の例
let(:params) { { name: 'たろう' } } # 同じ意味 @params = { name: 'たろう' }
let は遅延評価される
- before + インスタンス変数は遅延評価されない
- 必要になる瞬間まで呼び出されないので効率がよい
it do ... endのことをexampleと呼ぶ
subject { user.greet } を宣言するときの決まり
- expect(user.greet).to eq 'hogehoge'が
- is_expected.to eq 'hodehode'という書き方になる
shared_examples 'foo' do ... end で再利用したいexampleを定義して、 it_behaves_like 'foo' で定義したexampleを呼び出せる
let の遅延評価によってテストが失敗する場合は、かわりに let! を使う
テストコードは「DRYさ」よりも「読みやすさ」を大事にする