TOMLとは
Tom's Obvious, Minimal Language. の略らしい。
ぐぐってみたら、日本語のエントリがでてきた。
より一部引用。
TOMLとは
https://github.com/mojombo/toml
Tom's Obvious, Minimal Language (TOML) とはgithubの中の人が提案している設定ファイルのためのミニ言語で、以下のような特徴があります。
人間が読み書きしやすい
標準的なデータ型が利用できる
曖昧さが発生する余地が極力排除されている
パーサをかくのが簡単
とのこと。知らなかった。
Golangでサクッとかく設定ファイルとしてのjson
jsonで設定を書いて、Unmarshalして、構造体に突っ込む。
encoding/json パッケージを使えば良いのでお手軽だ。
{
"server": {
"host": "localhost",
"port": "3306",
"slave": [
{"weight": 1, "ip": "10.0.0.1"},
{"weight": 5, "ip": "10.0.0.2"},
{"weight": 3, "ip": "10.0.0.3"},
{"weight": 2, "ip": "10.0.0.4"}
]
},
"db": {
"user": "root",
"pass": "pass"
}
}
以上のようなjsonであれば、以下のようにかけば
Unmarshalし、構造体に突っ込むことができる。
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
)
type Config struct {
Server ServerConfig `json:"server"`
Db DbConfig `json:"db"`
}
type ServerConfig struct {
Host string `json:"host"`
Port string `json:"port"`
Slave []SlaveServer `json:"slave"`
}
type DbConfig struct {
User string `json:"user"`
Pass string `json:"pass"`
}
type SlaveServer struct {
Weight int `json:"weight"`
Ip string `json:"ip"`
}
func main() {
file, err := ioutil.ReadFile("config.json")
if err != nil {
panic(err)
}
var config Config
json.Unmarshal(file, &config)
fmt.Printf("Host is :%s\n", config.Server.Host)
fmt.Printf("Port is :%s\n", config.Server.Port)
for k, v := range config.Server.Slave {
fmt.Printf("Slave %d\n", k)
fmt.Printf(" weight is %d\n", v.Weight)
fmt.Printf(" ip is %s\n", v.Ip)
}
fmt.Printf("DB Username is :%s\n", config.Db.User)
fmt.Printf("DB Password is :%s\n", config.Db.Pass)
}
実行結果は以下となる
> go run json.go
Host is :localhost
Port is :3306
Slave 0
weight is 1
ip is 10.0.0.1
Slave 1
weight is 5
ip is 10.0.0.2
Slave 2
weight is 3
ip is 10.0.0.3
Slave 3
weight is 2
ip is 10.0.0.4
DB Username is :root
DB Password is :pass
きちんとアンシリアライズできている。
けど、ネストしてるとちょっとわかりづらい。
TOMLで書いてみると...
TOMLファイルを用意する
ということで、TOMLで先ほどのjsonと同等のものを書いてみる。
[server]
host = "localhost"
port = "3306"
[[server.slave]]
weight = 1
ip = "10.0.0.1"
[[server.slave]]
weight = 5
ip = "10.0.0.2"
[[server.slave]]
weight = 3
ip = "10.0.0.3"
[[server.slave]]
weight = 2
ip = "10.0.0.4"
[db]
user = "root"
pass = "pass"
デコードをするには
TOMLを構造体にしたい場合は toml.Decode
関数を利用する。
goのパッケージはgithub.com/BurntSushi/tomlを使う。
go get github.com/BurntSushi/toml
をすること
package main
import (
"fmt"
"github.com/BurntSushi/toml"
)
type Config struct {
Server ServerConfig
Db DbConfig
}
type ServerConfig struct {
Host string `toml:"host"`
Port string `toml:"port"`
Slave []SlaveServer `toml:"slave"`
}
type DbConfig struct {
User string `toml:"user"`
Pass string `toml:"pass"`
}
type SlaveServer struct {
Weight int `toml:"weight"`
Ip string `toml:"ip"`
}
func main() {
var config Config
_, err := toml.DecodeFile("config.tml", &config)
if err != nil {
panic(err)
}
fmt.Printf("Host is :%s\n", config.Server.Host)
fmt.Printf("Port is :%s\n", config.Server.Port)
for k, v := range config.Server.Slave {
fmt.Printf("Slave %d\n", k)
fmt.Printf(" weight is %d\n", v.Weight)
fmt.Printf(" ip is %s\n", v.Ip)
}
fmt.Printf("DB Username is :%s\n", config.Db.User)
fmt.Printf("DB Password is :%s\n", config.Db.Pass)
}
出力
出力は、以下となる。
> go run toml.go
Host is :localhost
Port is :3306
Slave 0
weight is 1
ip is 10.0.0.1
Slave 1
weight is 5
ip is 10.0.0.2
Slave 2
weight is 3
ip is 10.0.0.3
Slave 3
weight is 2
ip is 10.0.0.4
DB Username is :root
DB Password is :pass
[[server.slave]] とは何か。
[[slave]]として、列挙すると
Hashの集合(Array)として扱える。
JSONファイルとするならば、こんな形。
{
"slave": [
{ "weight": 3, "ip": "10.0.0.1" },
{ "weight": 10, "ip": "10.0.0.2" },
{ "weight": 1, "ip": "10.0.0.3" }
]
}
このjsonをTOML化するとこうなる。
[[slave]]
weight = 3
ip = "10.0.0.1"
[[slave]]
weight = 10
ip = "10.0.0.2"
[[slave]]
weight = 1
ip = "10.0.0.3"
Array of Tablesを参考。
既存の構造体をTOMLへエンコードするとどうなるか
この構造をTOMLに落とすとき、どう書いていいかわからない、
記法なれてないしなーというときは
toml.Encode
関数を利用すれば良い。
package main
import (
"bytes"
"fmt"
"github.com/BurntSushi/toml"
)
type Config struct {
Server ServerConfig
Db DbConfig
}
type ServerConfig struct {
Host string `toml:"host"`
Port string `toml:"port"`
Slave []SlaveServer `toml:"slave"`
}
type DbConfig struct {
User string `toml:"user"`
Pass string `toml:"pass"`
}
type SlaveServer struct {
Weight int `toml:"weight"`
Ip string `toml:"ip"`
}
func main() {
var config Config
var dbConfig DbConfig
dbConfig.User = "root"
dbConfig.Pass = "pass"
var serverConfig ServerConfig
serverConfig.Host = "localhost"
serverConfig.Port = "3306"
serverConfig.Slave = append(serverConfig.Slave, SlaveServer{Weight: 1, Ip: "10.0.0.1"})
serverConfig.Slave = append(serverConfig.Slave, SlaveServer{Weight: 5, Ip: "10.0.0.2"})
serverConfig.Slave = append(serverConfig.Slave, SlaveServer{Weight: 3, Ip: "10.0.0.3"})
serverConfig.Slave = append(serverConfig.Slave, SlaveServer{Weight: 2, Ip: "10.0.0.4"})
config.Db = dbConfig
config.Server = serverConfig
var buffer bytes.Buffer
encoder := toml.NewEncoder(&buffer)
err := encoder.Encode(config)
if err != nil {
panic(err)
}
fmt.Printf("%v\n", buffer.String())
}
出力結果
以下のようにTOMLファイル化される。便利だ。
[Server]
host = "localhost"
port = "3306"
[[Server.slave]]
weight = 1
ip = "10.0.0.1"
[[Server.slave]]
weight = 5
ip = "10.0.0.2"
[[Server.slave]]
weight = 3
ip = "10.0.0.3"
[[Server.slave]]
weight = 2
ip = "10.0.0.4"
[Db]
user = "root"
pass = "pass"
TOMLのパーサの各言語対応
TOML リポジトリのREADME.mdに書いてあるのだけど、
ある程度の言語はサポートされている。
Rubyもあるし、Pythonもある。
Rubyの場合
Rubyの場合はtoml-rbを使うと良いと思う。
セットアップ
source 'https://rubygems.org/'
gem 'toml-rb'
> bundle ins --path vendor/bundle
デコード
- コード
require 'pp'
require 'toml'
doc = <<-EOF
[server]
host = "localhost"
port = "8080"
[application]
environment = "production"
region = "us-west-1"
EOF
pp TOML.parse(doc)
- 出力結果
{"server"=>{"host"=>"localhost", "port"=>"8080"},
"application"=>{"environment"=>"production", "region"=>"us-west-1"}}
エンコード
- コード
require 'toml'
obj = {
"server" => {
"host"=>"localhost",
"port"=>"8080"
},
"application" => {
"environment"=>"production",
"region"=>"us-west-1"
}
}
puts TOML.dump(obj)
- 出力結果
[application]
environment = "production"
region = "us-west-1"
[server]
host = "localhost"
port = "8080"
設定ファイルのフォーマットの悩み
定数を書いたファイルを管理するとか
YAMLファイルを使うとかあるのだけど、
複数言語向けのパーサが揃っていて(有志が書いてる)、
かつphp.iniとかmy.confのような
configファイル形式ライクにかけるので、
TOMLは良いんじゃないかなと思った。
YAMLとか縦に長くなるし、
セクションの区切りも厳しいしなァ...。