- 1. Amazon Linux 2023にGolangをインストールする
- 2. GORMでPostgresのjsonb型を扱う
- 3. learn-go-with-tests マップ
- 4. GoのSliceとMapをforループで回すときのnilの扱いを動かしながら調べた
- 5. Goのnet/httpでサーバーのハンドラー処理を作ってみる
- 6. Goのsql.Openは必ずしも接続を確立しない
- 7. Goのスライスの挙動
- 8. Goの文法を軽く勉強したので、軽くまとめてみる
- 9. GoでJSONデータをメモリ効率よく処理する
- 10. Go言語入門 : goroutineとチャネルを使った簡単並行処理
- 11. GoのテストでDBをモックする
- 12. Goで値渡しをするかポインタ渡しをするかの基準
- 13. Goのerrors.Isとerrors.Asの使いどころ
- 14. Goのエラーチェーンでエラーが起こった流れを把握する
- 15. GoでJSONレスポンスに含めたくないデータがあるときの対処法
- 16. Goのインターフェイスの使い方
- 17. Go言語によるPDFからテーブル情報を抽出するCLIアプリの制作で詰まったこと
- 18. learn-go-with-tests ポインタとエラー
- 19. Goのテストの後処理におけるdeferとt.Cleanupの差
- 20. Go言語でのEchoハンドラテストとContextのモック方法
Amazon Linux 2023にGolangをインストールする
# はじめに
英語に拒否反応持ってしまう人も多いと思いますので、自分のインストール方法をここに記載しておきます。
2024/7/1時点、現在のバージョンは`go1.22.4`です。
対象OSはAmazonlinux2023を想定しています## 1.バイナリのダウンロード
“`bash
wget https://go.dev/dl/go1.22.4.linux-amd64.tar.gz
“`
## 2.rootユーザにスイッチ
“`bash
sudo su
“`
## 3.不要なディレクトリ削除、バイナリ解凍、配置
“`bash
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.22.4.linux-amd64.tar.gz
“`
※rootで実行すること## 4.rootユーザの終了
“`
exit
“`
## 5.パスを通す(bashrcに追記)
公式だと、.profileに記載するようありますが、
個人で使うだけなのでユーザで良いという判断です。
“`bash
vim ~/.bashrc
“`
.ba
GORMでPostgresのjsonb型を扱う
GORMでPostgresのjsonb型をシンプルな実装で扱う方法についてご紹介します。
# Postgresのjsonb型
PostgresではJSONを扱うデータ型としてjson型の他にjsonb型が用意されています。jsonb型の方が下記の通り、公式でも利用が推奨されていてデータ取得時のパフォーマンスが良くなるなどのメリットが有るようです。https://www.postgresql.jp/document/16/html/datatype-json.html
> json型とjsonb型というデータ型は、ほとんど 同一の入力値セットを受け入れます。 現実的に主要な違いは効率です。 jsonデータ型は入力テキストの正確なコピーで格納し、処理関数を実行するたびに再解析する必要があります。 jsonbデータ型では、分解されたバイナリ形式で格納されます。 格納するときには変換のオーバーヘッドのため少し遅くなりますが、処理するときには、全く再解析が必要とされないので大幅に高速化されます。 また jsonb型の重要な利点はインデックスをサポートしていることです。
> 一般的に、ほ
learn-go-with-tests マップ
# マップ
配列とスライスでは、値を順番に格納する方法を見ました。 では、keyでアイテムを保存し、すばやく検索する方法を見てみましょう。マップを使用すると、辞書と同じようにアイテムを保存できます。 keyは単語、valueは定義と考えることができます。 そして、独自の辞書を構築するよりも、マップについて学ぶより良い方法は何でしょうか?
まず、辞書に定義された単語がすでにあると仮定すると、単語を検索すると、その単語の定義が返されます。
## 最初にテストを書く
まずテストを書きます。実行すると当然パスしません。
`./dictionary_test.go:8:12: undefined: Search`“`dictionary_test.go
package mainimport “testing”
func TestSearch(t *testing.T) {
dictionary := map[string]string{“test”: “this is just a test”}got := Search(dictionary, “test
GoのSliceとMapをforループで回すときのnilの扱いを動かしながら調べた
Goのfor rangeでSliceやMapをループ処理を行うことがたくさんあると思います。
rangeの対象のlengthが0やnilの場合、どのような挙動をするのかが気になったのでサンプルコードを書いて動かしてみました。
## Slice
釈迦に説法かもですが、Sliceとは可変長の配列のことで全ての要素の型は同じです。
“`go
ints := []int{1, 2, 3, 4, 5}
fmt.Println(“[]int{1, 2, 3, 4, 5}のlen:”, len(ints))
fmt.Println(“[]int{1, 2, 3, 4, 5}のrange”)
for i, v := range ints {
fmt.Println(i, v)
}
“`こんなコードを書いたときにlenは5、for文は5回繰り返します。
アウトプットは以下のようになります。
“`
[]int{1, 2, 3, 4, 5}のlen: 5
[]int{1, 2, 3, 4, 5}のrange
0 1
1 2
2 3
3 4
4 5
“`次からSli
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`と