- 1. Goのnet/httpでサーバーのハンドラー処理を作ってみる
- 2. Goのsql.Openは必ずしも接続を確立しない
- 3. Goのスライスの挙動
- 4. Goの文法を軽く勉強したので、軽くまとめてみる
- 5. GoでJSONデータをメモリ効率よく処理する
- 6. Go言語入門 : goroutineとチャネルを使った簡単並行処理
- 7. GoのテストでDBをモックする
- 8. Goで値渡しをするかポインタ渡しをするかの基準
- 9. Goのerrors.Isとerrors.Asの使いどころ
- 10. Goのエラーチェーンでエラーが起こった流れを把握する
- 11. GoでJSONレスポンスに含めたくないデータがあるときの対処法
- 12. Goのインターフェイスの使い方
- 13. Go言語によるPDFからテーブル情報を抽出するCLIアプリの制作で詰まったこと
- 14. learn-go-with-tests ポインタとエラー
- 15. Goのテストの後処理におけるdeferとt.Cleanupの差
- 16. Go言語でのEchoハンドラテストとContextのモック方法
- 17. Go の Scanner で CSV インポートファイルを読み込む際の改行コードの扱いについて
- 18. 結局doneチャネルパターンって何だったのさ?
- 19. GolangのORM「ent」でクエリビルダーを使い回したらハマった話
- 20. #github.com/go-playground/universal-translatorのエラーの解決
Goのnet/httpでサーバーのハンドラー処理を作ってみる
## はじめに
net/httpパッケージを使って、サーバーのハンドラー処理を書いてみた。handler1、handler2、handler3、handler4、どれも書き方は若干違うが、最終的にhttp.Handle()が呼び出され、ルーティングが登録されるため同義となる。
http.Handle()の第2引数では、http.Handler型が指定されており、http.Handlerはインターフェイスで、ServeHTTPメソッドの実装を期待しているので、それを実装した型を入れてやると動く。
http.HandlerFunc()は、http.ResponseWriterと*http.Requestを引数に持つ関数が指定されており、実行するとhttp.HandlerインターフェイスのServeHTTPメソッドを実装した、http.HandlerFunc型になるので、それをhttp.Handle()に渡す。
“`go
package mainimport (
“net/http”
)type handler3 struct{}
func (h *handler3)
Goのsql.Openは必ずしも接続を確立しない
## はじめに
タイトルのとおり、sql.Openは必ずしも接続を確立しないようです。https://pkg.go.dev/database/sql#Open
> Open may just validate its arguments without creating a connection to the database. To verify that the data source name is valid, call DB.Ping.
DeepL翻訳
> Openは、データベースへの接続を作成せずに引数を検証するだけかもしれません。データ・ソース名が有効かどうかを確認するには、DB.Ping を呼び出します。なので、DBの接続を確実にチェックしたいなら、Ping()を使います。
ヘルスチェックするときにも使えそう。
“`go
package mainimport (
“database/sql”
“fmt”
“os”_ “github.com/go-sql-driver/mysql”
)var (
dbUser = os.Ge
Goのスライスの挙動
## はじめに
Goのスライスの挙動について調べた。容量を超える要素を追加したときは、元の容量を2倍したスライスが作られる。
元のスライスをコピーして新しいスライスが作られるため、メモリ上のアドレスが異なる。
つまり、元のスライスの分だけ、余分にメモリを消費するということ。
“`go
package mainimport “fmt”
func main() {
s1 := make([]int, 1, 5)
fmt.Println(“Len:”, len(s1), “Cap:”, cap(s1)) // Len: 1 Cap: 5
fmt.Printf(“%p\n”, s1) // 0xc00001a150// 容量を超える要素を追加
s1 = append(s1, 1)
s1 = append(s1, 2)
s1 = append(s1, 3)
s1 = append(s1, 4)
s1 = append(s1, 5)
fmt.Println(“Len:”, len(s1), “Cap:”, ca
Goの文法を軽く勉強したので、軽くまとめてみる
# はじめに
A Tour of Goで軽く文法の勉強をしたので、ここに記事として少しまとめてみようと思います。# 構文
### 型定義
JavaやC#等の静的型付け言語は
“`
型 <変数> = …
“`
というように 型名から書き始めると思います。しかし、Goでは
“`
<変数> 型
“`
と逆になっています。例えば、int型の変数xを宣言する場合
– C#
“`
int x;
“`
– Go
“`
x int
“`:::note
後ろに`;`がつきません、気をつけてください
:::### 変数定義
変数を宣言する場合は、先ほどの型宣言に加えて`var`をつける必要があります。
“`
var x int
“`:::note
もし具体的な値を設定しなければ、この場合xは0になります。
bool型の場合は、falseになります。
:::### 定数定義
javascriptと同じように`const`を定義できる。
“`
const x int = 1
“`### 関数宣言
関数は以下のように宣言します。
“`
fu
GoでJSONデータをメモリ効率よく処理する
GoでJSONを扱う場合、”encoding/json”パッケージを使います。
よく使うのは、MarshalとUnmarshalの2つですが、今回は以下のメソッドを使用していきます。
メモリに保存せずストリームとして処理する
– NewEncoder
– NewDecoder“`go
package mainimport (
“encoding/json”
“os”
)type User struct {
Name string `json:”name”`
Age int `json:”age”`
Email string `json:”email”`
}func main() {
user := User{
Name: “Test”,
Age: 25,
Email: “test@example.com”,
}file, err := os.Create(“user.json”)
if err != nil {
panic(err)
}
defer file.Close()// ストリームとし
Go言語入門 : goroutineとチャネルを使った簡単並行処理
Go言語の特徴の一つに、軽量な並行処理を簡単に実現できる「goroutine」と「チャネル」があります。本記事では、goroutineとチャネルの基本概念とその使い方について、学んだことをアウトプットしたいと思います。
# goroutineとは
goroutineは、Go言語で使われる軽量なスレッドのようなもので、並行処理を簡単に実現するための機能です。goroutineを使うと、複数の処理を同時に実行することができます。
# goroutineの基本概念
軽量な並行処理: goroutineは少ないメモリで多くの仕事を同時にこなせます。
簡単な使い方: 関数呼び出しの前に`go` キーワードを使うだけで、簡単にgoroutineをスタートできます。“`
go 関数名()
“`# 実例1: 基本的なgoroutine
以下は、goroutineを使ってメッセージを表示する簡単な例です。
※ [原神](https://genshin.hoyoverse.com/ja/home)でお馴染みの[ナヴィアさん](https://genshin.hoyoverse.com/j
GoのテストでDBをモックする
# はじめに
こんにちは、H×Hのセンリツ大好きエンジニアです。(同担OKです😉)今回はGoにおいてデータベースを扱う関数のテストを行うためにDBのモックを用意する所と、実際に使用したテストを紹介していきます!
# DBをモックしてテストする
### 使用するライブラリ
`github.com/DATA-DOG/go-sqlmock`を使用します。https://github.com/DATA-DOG/go-sqlmock
こちらは、SQLドライバのような振る舞いをしてくれるモックを生成してくれるライブラリです。
モックを使うことで、テスト用のDBを用意しなくても良くなります。### 今回テストする関数
ユーザをEmailから検索するという関数(`UserByEmail`)を用意しました。
この関数は、usersテーブルからクエリとして受け取ったEmailが存在するかチェックする関数となっています。
“` user.go
package persistenceimport (
“backend/domain/model”
“backend/domain/rep
Goで値渡しをするかポインタ渡しをするかの基準
構造体のフィールドの値を変更する必要があるなら、構造体のポインタで渡す。
そうでないなら、値で渡す。
値で渡した場合は、構造体のコピーが作られるため、元のデータが変更されない。
なので、基本的にポインタで渡しておき、フィールドのデータを書き換える必要がない場合や明確に元のデータを書き換えたくない場合には値で渡す。
“`go
package mainimport “fmt”
type mutableUser struct {
ID int
Name string
Age int
}func (u *mutableUser) SetAge(age int) {
u.Age = age
}type immutableUser struct {
ID int
Name string
Age int
}func (u immutableUser) SetAge(age int) {
u.Age = age
}func main() {
// Mutable
mutableUser := mutableUser{ID: 1, Name
Goのerrors.Isとerrors.Asの使いどころ
## errors.Is
エラーチェーンの中で、特定のエラーが存在するか確認する場合に使う。
以下の場合だと、queryDatabase関数内でErrDatabaseエラーが発生していて、それをmain関数内で存在しているか確認している。
もしqueryDatabase関数でエラーがない場合は、convertData関数内のErrConvertエラーの条件に該当する。
“`go
package mainimport (
“errors”
“fmt”
)type queryResult struct{}
// 独自エラーを定義
var (
ErrDatabase = errors.New(“database error”)
ErrConvert = errors.New(“convert error”)
)func queryDatabase() (result *queryResult, err error) {
// なにかしらの処理をしたと仮定
return result, fmt.Errorf(“query failed: %w”, ErrD
Goのエラーチェーンでエラーが起こった流れを把握する
Goにはスタックトレース機構がないので、どういう流れで処理が行われたのかが把握できない。
そのため、一番初めの呼び出し元にエラーをラップして戻す方法で、エラーの流れを把握する。
“`go
package mainimport (
“errors”
“fmt”
)type queryResult struct{}
func queryDatabase() (result *queryResult, err error) {
// なにかしらの処理をしたと仮定
// 一番最初のエラー
return result, errors.New(“database is down”)
}func fetchData() (result *queryResult, err error) {
res, err := queryDatabase()
if err != nil {
// エラーをラップして返す
return &queryResult{}, fmt.Errorf(“fetchData failed: %w”, err)
}
re
GoでJSONレスポンスに含めたくないデータがあるときの対処法
ユーザーデータをJSONで返すとして、その中にパスワードなどの返す必要のないデータが含まれていたときに、簡単に除外する方法がある。
たとえば、User構造体にパスワードが含まれていて、それをJSONレスポンスに含めたくない場合は、構造体のJSONタグに`json:”-“`と書くと除外してくれる。
“`go
package mainimport (
“encoding/json”
“net/http”
)type User struct {
ID int `json:”id”`
Name string `json:”name”`
Age int `json:”age”`
Email string `json:”email”`
Password string `json:”-“`
}func main() {
user := User{
ID: 1,
Name: “Test Test”,
Age: 25,
Email: “test@example.com”,
Goのインターフェイスの使い方
何らかの方法で通知をしたい場合を例に挙げる
1. インターフェイスSenderを宣言し、実装すべきメソッドを定義する
1. 送りたい手段の具体的な構造体を定義する(ここではSlackとメール)
1. その構造体にインターフェイスのメソッドを実装する
1. 使用する“`go
package maintype Sender interface {
Send(msg string) error
}type Slack struct {}
func (s *Slack) Send(msg string) error {
// Slackにメッセージを送信する処理
return nil
}type Mail struct {}
func (m *Mail) Send(msg string) error {
// メールを送信する処理
return nil
}func main() {
var sender Sendersender = &Mail{}
err := sender.Send(“Hello, Mail!”)
if err !=
Go言語によるPDFからテーブル情報を抽出するCLIアプリの制作で詰まったこと
:::note info
この記事は制作途中です.
随時コードブロックなどを追加して見やすくしていきます.
:::# 概要
大学の奨学金一覧のPDFをスクレイピングで取り込んで,それを適切に処理して欲しいデータだけを抽出したいという思いでCLIアプリの制作を始めたが,色々詰まったので備忘録として残しておきます.
成果物(途中) -> https://github.com/Acu4git/pdfxtractor# 当初の構想
* Go言語のみで実装したい(個人的に重要)
* 奨学金一覧のPDFが掲載されている場所は固定されており,PDFファイル名は決まったフォーマットに従っているため,スクレイピングライブラリと正規表現を用いることで取得できそう
* PDFからの表データの抽出は,PDFからテキストを抽出できるライブラリがあればいけそう
* ついでにCSV変換が出来れば嬉しい
* 後は構造体にマッピングして,処理を加えればいけそうスクレイピングは問題なく成功し,GoのPDFライブラリによるCSV変換も無事できたため,幸先が良い...かのように思えた
# 突如PDFライブラリが
learn-go-with-tests ポインタとエラー
# ポインタとエラー
ここでは、Bitcoinを預金するWallet構造体を作成しましょう。## 最初にテストを書く
以下のようにテストを書き実行します。
当然`./wallet_test.go:7:12: undefined: Wallet`で失敗します。
“`wallet_test.go
package bitcoinimport “testing”
func TestWallet(t *testing.T){
wallet := Wallet{}
wallet.Deposit(10)got := wallet.Balance()
want := 10if got != want {
t.Errorf(“got %d want %d”, got , want)
}
}
“`## テストを実行するための最小限のコードを記述し、失敗したテスト出力を確認します
コンパイラはWalletが何であるかを知らないので、それを伝えましょう。`type Wallet struct { }`
これで財布ができました。もう一度テストを実行してください。
`
Goのテストの後処理におけるdeferとt.Cleanupの差
単体テストにおいて前処理と後処理をする場合が多い。
例えばDBのデータを作成/削除する、一時ファイルを作成/削除するなどテスト対象の関数やメソッドの責務のみに集中するためには、テストの前処理と後処理を行う必要がある。
Goのテストにおいての前処理と後処理にはTestMain関数を用いる。
一方で各テスト関数において前処理と後処理を行う方法として、`defer`と`t.Cleanup`がある。
この挙動の差をサンプルコードを用いて確認する。
## deferとt.Cleanupの違い
`defer`は関数の終了時に実行される処理を登録する。
一方で`t.Cleanup`はテスト関数の後処理を登録する関数で、テスト関数が終了するときに登録した関数が実行される。
## サンプルのテストコード
さて問題です。
次のようなテストコードが実行されるとどのように表示されるか。
“`go
package main_testimport (
“fmt”
“testing”
)func TestMain(m *testing.M) {
fmt.Println(“bef
Go言語でのEchoハンドラテストとContextのモック方法
以下では、UserHandler構造体のGetUserメソッドをテストする方法について説明します。特に、echo.Contextをどのようにしてモックするかに焦点を当てます。
UserHandler構造体とそのGetUserメソッドは以下のように定義されています。
“`go
コードをコピーする
type UserHandler struct {
userService service.UserServiceInterface
}func (h *UserHandler) GetUser(c echo.Context) error {
use, err := h.userService.GetUser()
if (err != nil) {
c.String(http.StatusInternalServerError, “error”)
return err
}u := &user.User{
Name: use,
}
c.JSON(http.StatusOK, u)
return nil
}
“`このメソッドをテストするために、`c`と
Go の Scanner で CSV インポートファイルを読み込む際の改行コードの扱いについて
## 問題
Go の Scanner の挙動の違いにより、CSV インポート時のカウントにズレが生じました。例えば **CSV ファイルの中身が「`A \n B`」** のように 1 行の中に改行コードを含む場合。
改行コードも含めて 1 行と見なしたいケースを想定します。下記①の場合ですと、改行コードで分割されて 2 行、
②の場合だと改行コードも含めて 1 行でカウントされます。今回は②の挙動を期待していたのに、①の処理がなされていたために、想定するカウントとズレが発生した、という問題についてです。
## ①.`bufio.NewScanner`における`Scan`メソッドの挙動
### 処理
“`
scanner := bufio.NewScanner(csv)
num := 0
for scanner.Scan() {
num++
}
fmt.Println(num)
“`### 出力結果
“`
2
“`### 説明
– [func NewScanner(r io.Reader) *Scanner](https://pkg.go.dev/b
結局doneチャネルパターンって何だったのさ?
# 1. はじめに
Goのゴルーチン(Goroutine)はだいたいわかったはずなのに、doneチャネルパターンがよくわからない……。(´・ω・`)
数学の問題の解説文がわからなかったときの感覚と似ている……。
こういう時は確か、前提知識となる部分がどこかで抜け落ちているから、理解を阻む壁にぶつかっているのだと思う。
φ(・ω・ )フムフム……(参考書の読み直し中)
あぁ……、そっかぁ~~
(;´д`)=3となったので、その過程や、どうdoneチャネルパターンを理解したかを忘備録がてら共有してみようと思い、この記事を書きました。
# 目次
– [1. はじめに](#1-はじめに)
– [2. doneチャネルパターンとは](#2-doneチャネルパターンとは)
– [3. selectの応用](#3-selectの応用)
– [3-1. キャンセレーション関数](#3-1-キャンセレーション関数)
– [3-2. タイムアウト](#3-2-タイムアウト)
– [3-3. caseの無効化](#3-3-caseの無効化)
– [4.
GolangのORM「ent」でクエリビルダーを使い回したらハマった話
Golangに「ent」というORMがあります。
もともとはFacebookが開発したパッケージで型安全でありクエリ用の関数を自動生成してくれたりと大変使い勝手の良いORMです。そんな「ent」ですが、クエリビルダーの仕様を誤解していて半日ほどハマってしまいました。
下記のようなコードがあったとします。
“`go
query := client.Query()
if limit > 0{
query.Limit(limit)
}ids := []int{1,2,3,4,5}
for _, id := range ids {
query.Where(user.IdIn(id)).All(ctx)
}
“`このコードではAll()メソッドの実行を契機にDBへクエリが投げられるのですが、実はWhere()で設定した条件はイテレーションを跨いで保持されます。
entパッケージで定義されているクエリビルダーは内部に検索条件を保持するスライスを持っている構造体で、All()などでクエリを実行しても検索条件をクリアしません。
なので変数として定義したクエリビルダー
#github.com/go-playground/universal-translatorのエラーの解決
# エラー
#github.com/go-playground/universal-translator
C:\Users\user\go\pkg\mod\github.com\go-playground\universal-translator@v0.18.1\import_export.go:221:12: undefined: ErrMissingLocale
C:\Users\user\go\pkg\mod\github.com\go-playground\universal-translator@v0.18.1\import_export.go:244:12: undefined: ErrBadPluralDefinition
C:\Users\user\go\pkg\mod\github.com\go-playground\universal-translator@v0.18.1\translator.go:101:11: undefined: ErrConflictingTranslation
C:\Users\user\go\pkg\mod\github.com\go