インドカレーファンクラブ

パソコン、カメラ

【Rails】associationを張ったModelを生成する時のmigrationの記述

一人のユーザは一つのプロフィールを有する、というようなAssociationをつくってみる
1:1です

前提

以下のような構成を目指す

User

  • name
  • profile_id

Profile

  • text

モデルはこうなる

class User < ApplicationRecord
  belongs_to :profile
end

class Profile < ApplicationRecord
end

こんな感じのことをしたい

p = Profile.new(text: 'hello')
p.save!
u = User.new(name: 'test user', profile: p)
u.save!
User.first.profile.name # 'hello'

Profileの生成は特に迷うことはない

rails g model profile text:string

db/migrate/yyyyMMddHHmmss_create_profile.rb

class CreateProfiles < ActiveRecord::Migration[5.2]
  def change
    create_table :profiles do |t|
      t.string :text
      t.timestamps
    end
  end
end

で、Userを作っていく時に色々間違えたのでそのメモ

結論からいうと...

上記の

User

  • name
  • profile_id

こんな感じにUserにprofile_idを持たせてあげて、Modelにbelongs_to :profileの記述するだけではダメで、
schema.rbにもbelongs_toが記述されていないと、正しくActiveRecordのassociationが効いてくれない

正しいパターン

rails g model user name:string profile:belongs_to

※belongs_toはreferenceでもよい

参考:

belongs_to is an alias of reference
https://stackoverflow.com/questions/7788188/what-is-the-difference-between-t-belongs-to-and-t-references-in-rails

このgenerateコマンドによって上記のModelと以下のmigrationファイルが生成される

db/migrate/yyyyMMddHHmmss_create_user.rb

class CreateUsers < ActiveRecord::Migration[5.2]
  def change
    create_table :users do |t|
      # ↓が必要な行
      # ついでにprofilesじゃなくて単数形のprofileであることにも注意
      t.belongs_to :profile, foreign_key: true
      t.string :name
      t.timestamps
    end
  end
end

:nameがt.string、つまりstring型であるように
:profileは reference型 という型になる (belongs_to型とはあんまり言わない)

このreference型の:profileが、カラムprofile_idを生成してくれる

メモ:
ソース的には ActiveRecord::ConnectionAdapters::SchemaStatements#add_reference のあたり

add_reference(:table_name, :reference_name):
Adds a new column reference_name_id by default an integer.

ややこしい話はさておき、Migrationを適用するとこうなる

schema.rb

ActiveRecord::Schema.define(version: yyyy_MM_dd_HHmmss) do

  create_table "profiles", force: :cascade do |t|
    t.string "text"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "users", force: :cascade do |t|
    t.integer "profile_id"
    t.string "name"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["profile_id"], name: "index_users_on_profile_id" # ここがポイント!
  end

end

勘違いしてた失敗パターン

こんな感じただUserにprofile_idを持たせてあげて、Modelにbelongs_to :profileの記述するだけではダメで、 schema.rbにもbelongs_toが記述されていないと、正しくActiveRecordのassociationが効いてくれない

これをやってしまったパターン

rails g model user name:string profile_id:integer
# 正しいのは↓
# rails g model cat name:string profile:belongs_to

db/migrate/yyyyMMddHHmmss_create_user.rb

class CreateUsers < ActiveRecord::Migration[5.2]
  def change
    create_table :users do |t|
      t.string :name
      t.integer :profile_id
      # 正しいのは↓
      # t.belongs_to :profile, foreign_key: true
      t.timestamps
    end
  end
end

Migrationを適用するとこうなる

schema.rb

ActiveRecord::Schema.define(version: yyyy_MM_dd_HHmmss) do

  create_table "profiles", force: :cascade do |t|
    t.string "text"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "users", force: :cascade do |t|
    t.integer "profile_id"
    t.string "name"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    # ↓が足りてない
    # t.index ["profile_id"], name: "index_users_on_profile_id"0
  end

end

職場が変わって初めてのスクラム開発が辛い

打ち合わせが長い(多い)

打ち合わせの例・内容

スクラムのイベントとして行う 今の現場ではスプリントの期間は1週間

スプリントのキャパシティを明らかにする方法 – Ryuzee.com

以下は強制での参加 (言い方は悪いが便宜上...)

イベント 頻度 時間 備考
プランニング 週1 2h よく3hまで伸びる。一番辛い
レビュー 週1 1h
レトロスペクティブ 週1 1h
朝打ち合わせ 毎日 0.5-1h 細かい仕様・実装の話になると2hくらいにも
夕打ち合わせ 毎日 0.5-1h 同上

その他、任意参加で仕様や実装の相談などが週に[0.5h|2h]*2程度あったりなかったりする

打ち合わせが長いと何が辛い

  • 開発する時間が少なくなる
    • 残業している人が多いように思えるが
  • 長時間の拘束
  • 会話の内容がわからない
    • これはそもそもまだドメイン知識や技術知識がないからだろうけど

打ち合わせはなぜ長い

  • イベントが多い
  • メンバーが多い?
  • 開発規模が大きい?
    • プランニングほか実装の相談は必然的に増える
  • 込み入った話をしすぎる
    • プランニング然り、朝夕打ち合わせ然り
  • 仕様が固まっていない箇所の実装の話が出る

打ち合わせを短くする・減らすために

  • イベントを減らす・頻度を下げる
    • プランニングは頻度を下げても合計時間は変わらなそう
    • レトロスペクティブは2週間に1回くらいでいいのではないか
      • 不要だと思っている訳ではないけど、毎週じゃなくてもいいと思う
      • それってスクラム的に許されるの?
  • 打ち合わせ中に別件で込み入った話になりそうだったら別途打ち合わせを設ける
    • 打ち合わせを行う前にはゴールを決める
      流れで別の打ち合わせに入るとゴールが不明瞭になりやすい
  • プランニングで込み入った話をしない
    • 現状細かい実装の話までしている
    • 開発の内容(画面がどう変わる)とかはチームに共有すべきだと思うけど詳細な実装まで共有する必要ある?
      • 正直自分が担当すると決まっていない限り実装箇所なんて覚えられない
        • 覚えられない話なら全体にせずメモとして書き残す程度でいいのではないか
  • 打ち合わせが長くなったら休憩をいれる
    • う~ん...

もともと仕様詰め・実装相談・チーム改善などについて打ち合わせをすること自体は嫌いではなかった
でもそれは打ち合わせ自体がそもそも少なくて開発の中でたまに起こるイベントとして楽しみだったから?

上記の話を共有する前に、スクラムの各イベントの目的や前提を勉強しないといけないと思う
もしスクラムの定義を崩壊させるような提案だったなら僕はスクラム開発に向いてないのかもしれない

【Go】interface{}とは一体なんなのか

interfaceじゃなくてinterface{}について

どっちもinterfaceだけど僕的には分けて考えたほうが良いと思った

この人の言っていることが大変しっくりくる
(淡白な学習は僕に毒だから、こういう人間の所感が入っている記事だと同意が得られて嬉しい)

qiita.com

結論

  • 型っぽい
  • Any的なもの

調べる

A Tour of Go曰く

The interface type that specifies zero methods is known as the empty interface:
interface{}
An empty interface may hold values of any type. (Every type implements at least zero methods.)

A Tour of Go

The interface typeという言葉を見るに型ということみたい

全ての型は少なくとも0個のメソッドを実装しているから
empty interface (→interface{}) は任意の型の値を保持できる。ということらしい

実際にGoのreflect.TypeOf()メソッドの引数にこのinterface{}は使われている

// TypeOf returns the reflection Type that represents the dynamic type of i.
// If i is a nil interface value, TypeOf returns nil.
func TypeOf(i interface{}) Type {
    eface := *(*emptyInterface)(unsafe.Pointer(&i))
    return toType(eface.typ)
}

俺たちのオブジェクト指向言語CSharp

つまりこれはC#でいうところのObject型みたいなものなのではないか
と考えると分かりやすい (分かりやすいけど「違う!」と怒られそうなのでここで予防線をきちんと張る)

Supports all classes in the .NET class hierarchy and provides low-level services to derived classes. This is the ultimate base class of all .NET classes; it is the root of the type hierarchy.

Object Class (System) | Microsoft Docs

任意の型の値を保持できるという点ではdynamic型っぽくもあるけど、今回はObject型の方がずれがなさそう

ついでにRubyでも丁度Object型が似たようなもの
詳しくないけどJavaもそんな感じみたい

※Goのinterface{}はC#でいうところのObject型みたいなもの。という雑な理解で逃がすのが楽そうだけど
厳密にC#でinterface{}的なことをやろうとしたらこうなる

interface IHoge {}
public class Test: IHoge {} // IHogeが空だから何も実装しなくていい

まとめ

全ての型における基底となる型がよくあるオブジェクト指向言語ではObject型として用意されている
Goにおいてはinteface{}がそんな感じの役割を果たしている
(役割であってそのものではない!!)

一般的なinterface、つまり「クラスや構造体に決まった振る舞い(メソッド)を定義するもの」はC#でもGoでも似たような理解で概ね良さそうだけど、
Goのinterface{}だけは用途的に切り離して考えたほうがやさしいと思う

interface{}じゃなくてAnyとかだったら嬉しかったなぁ
(それこそtype Any interface{}って感じにエイリアスをつくるような感じで)

おまけ

あまりにinterface{}が理解できず、型なのかどうなのかすら怪しかった時にロシアの詩人フョードル・チュッチェフの詩を思い出したのでここに引用しておく

Умом Россию не понять,
Аршином общим не измерить:
У ней особенная стать —
В Россию можно только верить.

ロシアは頭ではわからぬ
並みの尺度では測れぬ
ロシアならではの特質がある
ロシアは信じることができるのみ。

ロシアは頭ではわからぬ 並みの尺度では測れぬ ロシアならではの特質がある... - The Embassy of the Russian Federation to Japan | Facebook

interface{}は信じることができるのみ。

インドカレーレシピレビュー 01

cookpad.com

レシピについて

自分が作るにはタイトルがやや恥ずかしい(執筆時、僕は26歳であり男子という年齢でもない)が、写真が美味しそうだったので作った

二人前というのがちょっと苦しいところですな

鶏もも肉をマリネしないのが他の一般的なレシピに比べてちょっと楽

油の量が書いてなかったため途中で注ぎ足したりした

工程3でスパイスを用意とあるがその時点では材料のブラックペッパーとクミンは使わない
大変紛らわしいので注意!

所感(個人差あり)

玉ねぎは執拗に焼いたが全体的に味のコク、喉への引っ掛かりが足りなすぎる
→ バターかヨーグルトが必要か?あるいは油を増やす

トマトの酸味をもう少し落としたい

お湯500mlで大味になりすぎているかも

ジャワカレースパイシーブレンドには敵わず

新大久保 THE JANNAT HALAL FOOD 徹底攻略Wiki

新大久保でいい感じにスパイスが安く売っているお店の攻略メモ

公式で通販をやっているらしいがなかなか難易度が高いように思える。それに店頭の方が安いみたい

The Jannat Halal Food

弱い日本人はいきあたりばったりで買えばいいだろうと思っていても外国人に対して萎縮してしまいがちなのでこの手の予習は必須であるといえる

リスト

もっとたくさんあるけど、買ったものを記載

これらのスパイスは店内入って右手、レジカウンターの隣のあたりにある
ビビって出ないでちゃんと確認する胆力が試される

スパイス名 量(g) 金額 販売 原産国 備考
コリアンダー(パウダー) 1000g 850円 サルタージ株式会社 インド
チリ(ホール) 100g 150円 ザ・ジャンナット株式会社 インド
チリ(パウダー) 100g 200円 ザ・ジャンナット株式会社 インド
アフリカンホットぺぺ(パウダー) 100g 350円 ザ・ジャンナット株式会社 アフリカ ペペってなんだよって思ったらペッパー(ガラムマサラの別名とかかと思って適当に買ってしまった)
クミン(パウダー) 100g 200円 ザ・ジャンナット株式会社 インド
クミン(ホール) 100g 150円 ザ・ジャンナット株式会社 インド
パプリカ(パウダー) 100g 200円 ザ・ジャンナット株式会社 インド
ターメリック(パウダー) 100g 200円 ザ・ジャンナット株式会社 インド
ブラックペッパー(パウダー) 100g 350円 ザ・ジャンナット株式会社 インド
ブラックマスタードシード(ホール) 100g 200円 ザ・ジャンナット株式会社 インド
グリーンカルダモン(ホール) 25g 200円 ザ・ジャンナット株式会社 インド パッケージにはカードダムンと書いてあるが...
クローブ(ホール) 50g 200円 ザ・ジャンナット株式会社 インド
フェンネル(ホール) 100g 200円 ザ・ジャンナット株式会社 インド
フェンネル(パウダー) 100g 200円 ザ・ジャンナット株式会社 インド
フェネグリーク(ホール) 100g 200円 ザ・ジャンナット株式会社 インド
フェネグリーク(パウダー) 100g 200円 ザ・ジャンナット株式会社 インド
シナモン(ホール) 50g 200円 ザ・ジャンナット株式会社 インド
シナモン(パウダー) 50g 200円 ザ・ジャンナット株式会社 インド

最終確認日:2020/07/18

メモ

店員さんは日本語で会話してくれるので助かる

店内雰囲気の参考になるブログ

mrs.living.jp

【GO】【WSL】WSL1 + VSCode + Remote-WSLで環境構築する

WSL内にGoをインストールして、WSL内にmain.goファイルを作ったりして、
ホストのVSCodeからRemote-WSLでそれを開いたり実行するための構築手順

WSL1としているのにはそれに起因するバグがあるから

omdwn.hatenablog.com

WSL内にGoをインストール

コレを参考にインストールする

qiita.com

環境変数についてひとつだけ
WSL1では起動時に~/.bash_profileを実行しないみたいなので、
~/.bashrcに書いてよいのではと思うところです

参考

ascii.jp

WSL2では状況が変わってるかもわからん

VSCode側の設定

ここらへんを拡張機能でいれる

  • Remote WSL
  • Remote Development
  • Go

※Remoteなんとか系は必要に応じて適当に入れて

Failed to run "go env" to findなんとかかんとか

ここまできて、適当に拡張子が.goなファイルを開くとこんな感じのことを言われた

Failed to run "go env" to find GOPATH as the "go" binary cannot be found in either GOROOT(undefined) or PATH(......

go envを実行してもgoのバイナリがGOROOTにもPATHにもないよという話で、
いやいやそんなことはないだろうと思ったらホストのWin10の環境変数を見に行っているみたい

エラーメッセージが違うせいで解決に時間がかかったけどこのやり方でOK

qiita.com

もしかしたらついでにgo.gopathも設定しておくといいかも

【Go】【WSL】WSL1 + Go + VSCode + Remote-WSLでデバッグ実行ができない

WSL2が出たばっかり(執筆時2020/06/02)なのに、なんで今頃WSL2じゃなくて1なのかというと、
WSL2に色々不具合があって、面倒くさそうだからWSL1を使っていたところ踏んでしまった問題だから...

ちなみに僕が恐れた不具合

github.com

概要

デバッグの開始」が一向に進まない
厳密に言うと、API server listening at: 127.0.0.1:6594という表示を出してから全く進みなし

でも「デバッグなし」で実行ならできる
こっちに気づかない人は多いかも?

どういうこと

github.com

色々端折って引用すると

Go dlv debugger uses some system calls that are not supported in WSL1.
...
WSL2 contains a fully fledged Linux kernel with full system call compatibility.
...
In WSL2 go debugging works without issues. Also you can run any Linux program natively (Docker containers, kubernetes, etc.)

総括

WSL2つかわないとだめそう

(VisualStudioでC#を開発していた僕の目にこの環境問題は大変恐ろしく映った)