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

パソコン、カメラ

【Go】Enumっぽくconstを定義して、その列挙子のリストを簡単に取得したいけどダメそう(微妙記事)

だめそう

stackoverflow.com

Jeremy Fishmanさんの言うように、そういうスライスを保持しておくことになりそう

package main

import (
    "fmt"
)

type Kind int

const (
    _ Kind = iota
    Dog
    Cat
    Horse
    Bird
)

var kinds = []Kind {
    Dog,
    Cat,
    Horse,
    Bird,
}

func main() {
    for _, kind:= range kinds {
            fmt.Println(kind)
      }
 
}

// 1
// 2
// 3
// 4

reflectつかえばでうまいことスライスの生成ができるような気がするんだけど試してない(試したい)

【Go】enumっぽいやつのString()メソッド内でpanicを起こした場合、fmtでprintしようとしても握りつぶされる

String()メソッドというか、fmtでprintする時のフォーマットエラーの話

どういうこと

こういう感じ

enumっぽいことをしようとした時に引っかかりそうな問題な気がする
※個人的にはそもそもこのenumっぽいのに不満足だけど今回の話とは関係ないのでスルー

enum_string_err/enum/enum.go

package enum

type Kind int

const (
    _ Kind = iota
    Dog
    Cat
)

func (k Kind) String() string {
    switch k {
    case Dog:
        return "イヌ"
    case Cat:
        return "ネコ"
    }
    panic("Err")
}

enum_string_err/main.go

package main

import (
    "enum_string_err/enum"
    "fmt"
)

func main() {
    var animal enum.Kind
    animal = enum.Dog
    fmt.Println(animal) // イヌ
    animal = enum.Cat
    fmt.Println(animal) // ネコ

    var animalDefault enum.Kind
    fmt.Println(animalDefault) // %!v(PANIC=String method: Err)

    fmt.Println("---") // --- (上のpanicで止まらず出力される!)

    x := animalDefault.String() // これは普通にエラーで止まる
    fmt.Println(x)
}

ということで以下の箇所がエラーにならない

var animalDefault enum.Kind
fmt.Println(animalDefault) // %!v(PANIC=String method: Err)

これは個人的にはなかなか不便な気がするんだけど、 実務のコードではfmtのprintってよりログ出力することが多いからあんまり問題にならない?

本質

この仕様についてはココに書いてあった(panicとかで検索して原文読んで!)
https://golang.org/pkg/fmt/

一応引用

Format errors:

If an invalid argument is given for a verb, such as providing a string to %d, the generated string will contain a description of the problem, as in these examples:

Wrong type or unknown verb: %!verb(type=value)
    Printf("%d", "hi"):        %!d(string=hi)
Too many arguments: %!(EXTRA type=value)
    Printf("hi", "guys"):      hi%!(EXTRA string=guys)
Too few arguments: %!verb(MISSING)
    Printf("hi%d"):            hi%!d(MISSING)
Non-int for width or precision: %!(BADWIDTH) or %!(BADPREC)
    Printf("%*s", 4.5, "hi"):  %!(BADWIDTH)hi
    Printf("%.*s", 4.5, "hi"): %!(BADPREC)hi
Invalid or invalid use of argument index: %!(BADINDEX)
    Printf("%*[2]d", 7):       %!d(BADINDEX)
    Printf("%.[2]d", 7):       %!d(BADINDEX)

If an Error or String method triggers a panic when called by a print routine, the fmt package reformats the error message from the panic, decorating it with an indication that it came through the fmt package. For example, if a String method calls panic("bad"), the resulting formatted message will look like

%!s(PANIC=bad)

The %!s just shows the print verb in use when the failure occurred. If the panic is caused by a nil receiver to an Error or String method, however, the output is the undecorated string, "\<nil>".

この後半の

If an Error or String method triggers a panic when called by a print routine,

のところで今回の話についてピンポイントで触れている

【Go】C#erが悩んだ構造体の初期化やコンストラクタ(微妙記事)

Goの構造体の初期化方法は色々あってよく迷うので整理した

C# (やさしい)

C#でクラスを初期化する時はこんな感じ

public class Character
{
    public string Name { get; set; }
    public int Age { get; set; }

    public Character() {}

    public Character(string name)
        => Name = name;

    public Character(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

// コンストラクタで初期化
var c  = new Character("キャル", 14);
// デフォルトコンストラクタ + オブジェクト初期化子
var c2 = new Character() { Name = "キャル", Age = 14 };
var c3 = new Character   { Name = "キャル", Age = 14 }; // カッコを省略できる

// これでもいい
var c4 = new Character();
c4.Name = "キャル";
c4.Age = 14;

// これはやらないな...
var ex = new Character("キャル") { Age = 14 };

引数付きのコンストラクタによる初期化を見ることが一番多いかな
オブジェクト初期化子で初期化もできる

コンストラクタというか関数のオーバーロードができるから、
インスタンスを作らせる場合コンストラクタを複数設ける場合が多いと思う

Go(つらく厳しい)

Golangでは所謂クラスの役割を構造体(struct)が担う

なので今回は構造体の初期化の話になる
構造体っていうか構造体の型のポインタ型を初期化する話を考える(意味不明な日本語に遭遇、ここで額に手をあてる)
今回は*Character型のポインタ型の変数を作っていく(天を仰ぐ)

その初期化というかナントカカントカの手法が色々あってC#ほどシンプルではないのでまとめる

type Character struct {
    Name string
    Age int
}

func main() {
    // パターン1:compositeリテラルで初期化(&のやつ、とよく呼ばれる)
    // この一連の処理は c:= &Character... でも同じ
    var c *Character // *Character型のポインタ型 c を宣言
    c = &Character {
        Name: "キャル",
        Age: 14,
    }

    // 端折れる(あんま見ない気がする)
    c2 := &Character {"キャル", 14}

    // パターン2:newで初期化
    c3 := new(Character)
    c3.Name = "キャル"
    c3.Age = 14
}

structそのままだとこんな感じ

compositeリテラルの方はいいけど、newして後から色々と値を格納するのをService層とかでやると無駄に行というか呼び出しが増えて好きじゃない(他の処理と混ざるというか...)

で、よくあるのがここにNewメソッドみたいなのを別途足してそれを使う形
コンストラクタ関数と呼ぶらしい

type Character struct {
    Name string
    Age int
}

func NewCharacter(name string, age int) *Character {
    return &Character {
        Name: name,
        Age: age,
    }
}

func NewCharacterOnlyName(name string) *Character {
    return &Character {
        Name: name,
        // ここで0格納しなくてもintの初期値として0が入るけど書いた方がわかりやすい...
        Age: 0,
    }
}

func main() {
    // パターン3:コンストラクタ関数(Newなんとか)をつくる
    c   := NewCharacter("キャル", 14)
    c2 := NewCharacterOnlyName("キャル")
}

Golangにはオーバーロードがないのでコンストラクタ関数もいちいち名前を変えないといけない
すごく辛そう

でも通例上、このコンストラクタ関数を設ける感じみたい

参考:

bmf-tech - Golangのポインタ概要

Golangのコンストラクタとその利用方法についての考察 - Qiita

【Go】WireでよくあるController, Service周辺をDIするメモ

はじめに

MVCな構成でよくある感じのControllerがServiceに、ServiceがRepositoryに依存している感じの構成をDIする

Wireについてはここらへんが参考になる

参考

Go言語とDependency Injection | リクルートテクノロジーズ メンバーズブログ

GoのDIツールwireで知っておくと良いこと - Carpe Diem

2つ目の記事が大変わかりやすくて、そこの「interfaceに依存している場合」をより自分が使う環境に落とし込んだメモが今回の内容

なので基本的な説明は端折ってる

C#でいうDIコンテナを使わないコードだとこんな感じ
自分的にわかりやすくするためにC#で書いているけど、この話は本質的なところじゃないから読まなくていい

public class Hoge
{
    public static void Main()
    {
        var repo = new UserRepository();
        var service = new UserService(repo);
        var controller = new UserController(service);

        // あんまこういう呼び方はしない気がしてきた
        controller.Service.CreateUser();
    }
}

// Controller
public class UserController
{
    public readonly IUserService Service;
    public UserController(IUserService service) => this.Service = service;
}

// Service
public class UserService : IUserService
{
    private readonly IUserRepository repository;
    public void CreateUser() { repository.Save(); }
    public UserService(IUserRepository repository) => this.repository = repository;
}
public interface IUserService
{
    void CreateUser();
}

// Repository
public class UserRepository : IUserRepository
{
    // ほんとはDBの接続をあれこれするやつをフィールドにもつ
    public void Save() { }
}
public interface IUserRepository
{
    void Save();
}

これをgoでかいて、尚且DIコンテナのWireを使うようにしていくという話

Wireなしで書く

まずWireなしで書くと...

main.go

package main

import (
    "fmt"
)

func main() {
    // ここで依存性の注入
    repository := NewUserRepository()
    service := NewUserService(repository)
    controller := NewUserController(service)

    controller.service.CreateUser()
}

// Controller
type UserController struct {
    service IUserService
}
func NewUserController(service IUserService) *UserController {
    return &UserController{service: service}
}

// Service
type IUserService interface {
    CreateUser()
}
type UserService struct {
    repository IUserRepository
}
func (service *UserService) CreateUser() {
    fmt.Println("run UserService.CreateUser...")
    service.repository.Save()
}
func NewUserService(repository IUserRepository) *UserService {
    return &UserService{repository: repository}
}

// Repository
type IUserRepository interface {
    Save()
}
type UserRepository struct {
}
func (*UserRepository) Save() {
    fmt.Println("run UserRepository.Save...")
}
func NewUserRepository() *UserRepository {
    return &UserRepository{}
}

京都人「オーバーロードがない言語はコンストラクタ関数(Newなんとか)なんて素敵なものを使うんどすなぁ」

Wireありで書くと...

wire.go

//+build wireinject

package main

import (
    "github.com/google/wire"
)

func InitUser() *UserController {
    wire.Build(
        NewUserController,
        NewUserService,
        // IFを使っているとここがめんどくさい
        wire.Bind(new(IUserService), new(*UserService)),
        NewUserRepository,
        // ここも然り
        wire.Bind(new(IUserRepository), new(*UserRepository)),
    )
    return &UserController{}
}

$ wireするとココから以下のファイルが生成される

wire_gen.go

// Code generated by Wire. DO NOT EDIT.

//go:generate wire
//+build !wireinject

package main

// Injectors from injector.go:

func InitUser() *UserController {
    userRepository := NewUserRepository()
    userService := NewUserService(userRepository)
    userController := NewUserController(userService)
    return userController
}

そうするとmain.goの呼び出しはこう変えられる

func main() {
    // 古い方
    // repository := NewUserRepository()
    // service := NewUserService(repository)
    // controller := NewUserController(service)

    // 新しい方
    controller := InitUser()

    controller.service.CreateUser()
}

これで $go run main.go wire_gen.go すれば動く
wire_gen.goも含めてあげることに注意(ここんところもっと上手くやれそう)

マツコの知らない世界 真夏のカレースペシャル (2020.08.11) 自分用まとめ

基本的に過去の放送の再編集版
(正直全部本放送の回見るほうがいいわ)

以下について記述

  • しゃばしゃばカレーの世界
  • ドライカレーの世界
  • カレーパンの世界

以下は個人的に内容がそこまでアツくなかったので割愛

しゃばしゃばカレーの世界

前回の記事を参照

omdwn.hatenablog.com

ドライカレーの世界

魅力は「スパイスが密集。食べた瞬間、脳にガツンと来る快感!」とのこと

神宮前の店が激戦区

BLAKES(国立競技場)

GHEEという1980年代~2005年に村上春樹も通った伝説のカレー店があって、そこの元シェフが2016年に再オープン

MOKUBAZA Curry & Bar(明治神宮前)

ナッツキーマカレー 1170円(税別)

バー。ドライカレーの人気に伴い昼はカレー屋としての営業

インド産の高級ブラックペッパーを使っていて 鼻に抜けるツンとした辛味がポイントとのこと

コーヒーの美味しい店

ドライカレーを美味しい店はコーヒーの美味しい店を探すといいらしい
カレーもコーヒーも匂いがポイントだからだとか

そういう文化人が多く喫茶店が多い新宿が狙い目とのこと

カフェアルル(新宿三丁目)

インドオムラとコーヒー 820円(税別)

ドライカレーをかき混ぜたご飯の上に卵をくるっと
ケチャップの代わりにカレーを掛けている

ビア&カフェ BERG(新宿東口)

五穀米と十種野菜のカレーライス 539円(税込)

名前だけ紹介

カフェ・ハイチ(中野)

ドライカレー 700円(税込)

抜群のコーヒーとドライカレーでマニアの聖地と呼ばれているらしい

元々新宿にあった店が中野に移転したとのこと
1976年オープン2011年に一時閉店→2016年に再オープン

マスターが他のカレーを一切食べていないため昔から味が変わらないとのこと

具材はタマネギ・ニンジン・牛豚合挽肉のみ シンプルなのでスパイスの刺激が引き立つと

食後のコーヒーにブランデー2, 3滴を入れて飲むらしい
スパイスがコーヒーと一緒に広がり再び刺激が楽しめる

若かりしマツコも通ったということで当人も泣きそうに

カレーパンの世界

カトレア(森下駅)

元祖カレーパン238円(税込)

カレーパン発祥の店らしい

ペストリー&ベーカリーGGCo.(北品川駅)

ホテル自家製 TOKYO MARRIOTT カレーパン 350円(税込)

東京マリオットホテルというところの1階

中身がドライカレーではなくホテル自家製のビーフカレー 生地にタピオカを練り込んでいてスゴイもちもちとのこと

ianak!(イアナック)(西日暮里駅)

お豆とれんこんのカレーパン 259円(税込)

レンコン・ひよこ豆・くるみなど和惣菜のようなヘルシー具材が使われている

お酒に合うとのこと

所感

以前GHEE"系"のお店に行ったことがあるけど、僕的には正直そこまでって感じだった
でも今回紹介されていたとこは美味しそう

インド産の高級ブラックペッパーとやらは試してみたい

TOKYO MARRIOTT カレーパンはふらっと寄れるし食べてみようと思う

【C#】Dapperを用いて一対多の関係にある2テーブルのレコードを階層状のクラスにマッピングする

基本的にこれのJeroen Kさんの丸パクリの話を噛み砕いてるだけの記事

dapper - Multi-Mapper to create object hierarchy - Stack Overflow

タイトルだけだと意味分かんないと思う

Itemsテーブルと、それに対して一対多の関係にあるItemRemarksテーブルがあって、
そのレコードを取得する際にList<ItemRemark>型のフィールドをもつItem型に紐付けたい、という話

これでも意味分かんないと思うので、実際のレコード例とかクラスを見たほうがよい

DBとレコード

中間テーブルは設けてない

Items

ID Name
1 カレー
2 ラーメン
3 サラダ

ItemRemarks

ItemId Description
1 おすすめ
3 健康的
3 今なら半額

クラス

~Entityって名前にすべきだったけどC#書くの久しぶりで忘れてた

public class Item
{
    public int Id { get; set; }

    public string Name { get; set; }

    public List<ItemRemark> Remarks { get; set; } = new List<ItemRemark>();
}

public class ItemRemark
{
    public int ItemId { get; set; }

    public string Description { get; set; }
}

ほしいオブジェクトのイメージ

こんな感じのがほしい!

Items: [
    Item: {
        Id: 1,
        Name: 'カレー',
        ItemRemarks: [
            ItemRemark: {
                description: 'おすすめ'
            }
        ]
    },
    Item: {
        Id: 2,
        Name: 'ラーメン',
        ItemRemarks: [ ]
    },
    Item: {
        Id: 3,
        Name: 'サラダ',
        ItemRemarks: [
            ItemRemark: {
                description: '健康的'
            },
            ItemRemark: {
                description: '今なら半額'
            }
        ]
    }
]

実装

ConsoleApplicationでつくって、NugetでDapperとSystem.Data.SQLite.Coreだけインストールしたら下記サンプルが動くはず

static void Main(string[] args)
{
    // sqliteでデータを用意するところから
    var sqliteFile = "testdb.sqlite";
    File.Delete(sqliteFile);
    SQLiteConnection.CreateFile(sqliteFile);
    var config = new SQLiteConnectionStringBuilder(){ DataSource = sqliteFile };
    using (var connection = new SQLiteConnection(config.ToString()))
    {
        connection.Open();
        // データを入れる
        var createDbSql = @"
CREATE TABLE Items (
    id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
    name TEXT NOT NULL
);
CREATE TABLE ItemRemarks (
    itemId INTEGER NOT NULL,
    description TEXT NOT NULL,
    foreign key(itemId) references Items(id)
);
INSERT INTO Items(id, name) VALUES(1, 'カレー');
INSERT INTO Items(id, name) VALUES(2, 'ラーメン');
INSERT INTO Items(id, name) VALUES(3, 'サラダ');
INSERT INTO ItemRemarks(itemId, description) VALUES(1, 'おすすめ');
INSERT INTO ItemRemarks(itemId, description) VALUES(3, '健康的');
INSERT INTO ItemRemarks(itemId, description) VALUES(3, '今なら半額');
";
        connection.Execute(createDbSql);

        // ここからが本題!
        var lookup = new Dictionary<int, Item>();
        connection.Query<Item, ItemRemark, Item>(@"
SELECT
    I.Id
    ,I.Name
    ,IR.ItemId
    ,IR.Description
FROM Items I
LEFT JOIN ItemRemarks IR
    ON IR.itemId = I.id",
            (i, r) => {
                if (!lookup.TryGetValue(i.Id, out Item item)) { lookup.Add(i.Id, item = i); }
                if (r != null) { item.Remarks.Add(r); }
                return item;
            }, splitOn: nameof(ItemRemark.ItemId)
        ).AsQueryable();

        // ここに期待するデータが入る
        IEnumerable<Item> resultList = lookup.Values;
    }
}

ややこしい!

クエリでItemsとItemRemarksをまるっととって、その後ごちゃごちゃマッピングしてるイメージ
必要に応じてsplitOnに気をつけよう
細かいことはDapperのドキュメントから...

もっといいやり方がありそうな気はしつつ取り敢えず動く

これは冒頭に記述したStackoverflowのJeroen Kさんのやり方の通り(ちょっとアレンジはあるけど)

他の人は2テーブルを取得してforeachで回してマッピング、っていう一連の処理をそういうUtilなメソッドにしちゃうといいよ、みたいな感じ

何回も書くならそっちのほうがいいけど、1箇所だけピンポイントで使うなら上のコードでいいと思う でも数ヶ月後に上のコード見たら混乱しそうだし、普通に2テーブルとってService層とかでループしたほうが楽で良い気もする

【AWS】貧しい個人開発者がAWS Clinet VPNの料金を検討するメモ

AWSVPN接続をしたいなぁと思って調べた

が、僕は資本主義経済に搾取される惨めな立ち位置なので利用できるかどうかはお金にかかってくる

結論

貧乏人にはややつらい
だいぶ頑張って仕組みを作ればかなり安くいけそう(でも大変そう)

内容

ウッカリVPN入れたままネットサーフィンしたら通信量で死にそう...
と思いきやそうでもないみたい(2020/08/07時点)

AWS VPN(AWSサイト間でのVPN)には通信量による従量課金要素があるけど、
AWS Client VPNの方では時間単位の課金になっている

じゃあ使う時だけVPN接続して、そうじゃない時は切ればいいだけじゃん!
と思いきや...

AWS Client VPN 接続そのものへの時間単位の課金とは別に、 AWS Client VPN エンドポイントをアクティブにし続けている時間に対する課金も発生する

つまり、ホスト側(エンドポイント側)でVPN受入してますよ~という時間にお金がかかる

ということで、厳密に課金を避けるなら、クライアント側でVPN接続を開始終了するタイミングでエンドポイント側の設定もいじる感じになるはず

AWS側エンドポイントの起動停止 + VPN接続の開始/終了をひとまとめのスクリプトにでもしてしまえば良いようにも思うけど......大変......
※厳密にはVPNエンドポイントの起動停止というか、作成(サブネットへの紐付け)/削除って感じみたい

エンドポイントのみの料金参考

アジアパシフィック(東京)リージョン用の料金
(2020/08/07時点)

項目 料金
AWS Client VPN エンドポイントアソシエーション 0.15USD/時間
AWS Client VPN 接続 0.05USD/時間

ちなみにエンドポイントを1ヶ月ずっとアクティブにした場合は...
0.15USD * 24時間 * 30日 = 108USD ≒ 11,394円

会社だったら許せたかも(1人じゃなくて複数人つないだりするわけだしね)

参考