LINUX.ORG.RU

Какой выдумать или создать формат для конфигов?

 , , ,


1

3

Хочу для своего упражнения использовать yaml. Вроде голанг его умеет читать и писать. Что говорит на эту тему духовенство? Уместен ли такой формат или же лучше взять что-то другое?

json не хочу по той причине, что в него нельзя вставлять комментарии, xml слишком многословен.

==========================================

Решение: взят обычный json, который читает - пишет структуру. Для комментария в структуре предусмотрено специальное поле Comment. Это не так удобно, зато гомоиконно.

Если в конфиге задано поле, отсутствующее в структуре, например, если название поля написано с опечаткой, то будет ошибка чтения конфига. Это важно, поскольку толерантность многих программ к опечаткам в конфигах приводит к тому, что можно часами сидеть и искать ответ на вопрос «ну почему же это дерьмо не работает»?

Код такой:

package main

import (
	"strings"; "fmt"; "os"
	"encoding/json"
	"io/ioutil"
	// "github.com/flynn/json5"
)

type SecretConfigDataStruct struct {
	Comment       string
	RecieverEMail string
	SMTPServer    string
	SMTPUser      string
	SMTPPassword  string
	SenderEMail   string }

var SecretConfigData SecretConfigDataStruct

func (sds *SecretConfigDataStruct) SaveToFile(filename string) (err error) {
	var text []byte
	text, err = json.MarshalIndent(sds,""," ")
	if err != nil { return	}	
	err = ioutil.WriteFile(filename, text, 0600)
	return }

const ConfigFileName = "secret-data.config.json"

// for development
func saveSecretConfigDataExample() {
	sds := SecretConfigDataStruct{
		Comment:       "Example config file. Copy this one to the secret-data.config.json and edit",
		SenderEMail:   "den@example.net",
		RecieverEMail: "world@example.net",
		SMTPServer:    "smtp.example.net",
		SMTPUser:      "Кирилл",
		SMTPPassword:  "bla-bla-bla"}
	err := sds.SaveToFile(ConfigFileName + ".example")
	if err != nil {	panic(err)	}}

func loadSecretConfigData() (err error) {
	sds := &SecretConfigData
	fn := ConfigFileName
	if _, err = os.Stat(fn); os.IsNotExist(err) {
		fmt.Printf("No config file %s found. Create one by copying from %s.example\n",
			fn, fn)
		return	}
	var bytes []byte
	bytes, err = ioutil.ReadFile(fn)
	if err != nil {
		fmt.Printf("Unable to read config %s\n", fn)
		return	}
		dec := json.NewDecoder(strings.NewReader(string(bytes)))
		dec.DisallowUnknownFields() 
		err = dec.Decode(sds)
	if err != nil {
		fmt.Printf("Error reading config file %s: %#v\n", fn, err)
		return	}
	fmt.Printf("playWithSecretConfigData returned %#v\n", sds)
	return }

★★★★★

Последнее исправление: den73 (всего исправлений: 4)

Ответ на: комментарий от den73

Почему?

Ты думаешь, это совпадение, что языки, в которых можно без проблем добавить/удалить statement-строку (C-подобные, Python, …) — используются; а лиспы, паскаль и прочее

function() {
    statement}

— нет?

Cycle_A
()
Ответ на: комментарий от Deleted

UCL уже предлагали. Для моей задачи слишком сложный.

den73 ★★★★★
() автор топика

INI выбор на все времена

А этот ваш ямл говнище еще то. Отступы поедут и привет, ебись-ковыряйся.

anonymous
()
Ответ на: комментарий от Cycle_A

Вообще, мой формат выбора для таких случаев - такой:

a = [1
    ,2
    ,3]
Запятая стоит в начале следующей строки. Те, кто работает с SQL и пишет эти вот ужасные портяночные процедуры со 100 параметрами, обычно приходят именно к такому варианту, совершенно не сговариваясь.

den73 ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Да не буду уже ямл брать. Либо json и в структуру вставлю поле «комментарий», либо json5. Комментарии в конфиге плохи тем, что конфиг перестаёт быть гомоиконным. Поэтому лучше встроить их прямо в структуру данных.

den73 ★★★★★
() автор топика
Ответ на: комментарий от Cycle_A

Во всех случаях использования ЯП, кроме интерактивной работы в REPL, удобство и надёжность при чтении важнее быстроты записи.

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

Кроме того, при поддержке любых приложений добавление в конец списка параметров происходит чаще, чем в начало.

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

Дописал код для yaml, сразу столкнулся с тем, что при записи конфига все имена приводятся в нижний регистр. Ой-ля-ля.

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

удобство

a = [1
    ,2
    ,3]

Очень удобно, ага.

надёжность

Ты, кажется, проигнорировал вопрос; спрошу снова: почему этот ужас надёжнее?

быстроты записи

Ну давай посчитаем.

myvar = [1
<Space><Space><Space><Space><Space><Space><Space><Space>,2
<Space><Space><Space><Space><Space><Space><Space><Space>,3]
vs
myvar = [
<Tab>1,
<Tab>2,
<Tab>3,
]

Cycle_A
()
Ответ на: комментарий от Cycle_A

Зачем ты приплетаешь сюда Tab vs Space?

спрошу снова: почему этот ужас надёжнее?

Вопрос некорректен, это не ужас. Если хочешь ответ - спроси нормально.

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

Зачем ты приплетаешь сюда Tab vs Space?

Эм… потому что, если я хочу выравнивания, то в твоём случае я обязан нажимать <Space>, а в моём — независимо от того, вставляет ли мой редактор пробелы или табы по нажатию <Tab>, я могу нажать <Tab>?

Вопрос некорректен, это не ужас. Если хочешь ответ - спроси нормально.

Уже спросил, см. моё первое сообщение в треде.

Cycle_A
()
Ответ на: комментарий от hyperkahler

Ещё есть hugo, но там, вроде, свой диалект, но могу ошибаться.

mord0d ★★★★★
()
Ответ на: комментарий от Cycle_A

если я хочу выравнивания ... я могу нажать <Tab>

Либо ты не знаешь разницы между выравниваем и отступом, либо тебя полагается расстрелять из говномёта.

anonymous
()
Ответ на: комментарий от Cycle_A

Эм… потому что, если я хочу выравнивания, то в твоём случае я обязан нажимать <Space>

Это можно вынести за скобки. На самом деле, если эта запятая в конце обязательна, то нет проблемы с надёжностью. А вот если запятая опциональна, то забытый элемент списка неотличим от проставленной опциональной запятой. Дальше сам, пожалуйста, подумай.

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

Это можно вынести за скобки.

Нет, нельзя.

На самом деле, если эта запятая в конце обязательна, то нет проблемы с надёжностью. А вот если запятая опциональна, то забытый элемент списка неотличим от проставленной опциональной запятой. Дальше сам, пожалуйста, подумай.

Думаю, что на практике это является гораздо меньшей проблемой, чем те проблемы, на которые указал я. Обязательная запятая неудобна при inline-записи, т.е. enum { A, B, C };

Cycle_A
()
Ответ на: комментарий от Nervous

У меня там голанг. Всем спасибо, я пока взял простой json. json5 не возьму пока что, т.к. гомоиконный конфиг с ограниченными комментариями мне больше нравится, чем кудрявый, но не гомоиконный. Но тема была познавательной.

den73 ★★★★★
() автор топика
Последнее исправление: den73 (всего исправлений: 1)
Ответ на: комментарий от Cycle_A

Насколько я вижу, в голанге при инлайн-записи запятой не должно быть, а при переносе закрывающей на следующую скобку запятая должна быть. Это лучше, чем опциональная запятая, но я бы предпочёл, чтобы запятая в конце списка была всегда запрещена. Например, если списки вложены, то тут опять же можно перепутать и закрыть не тот уровень вложенности.

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

Короче, вот результат:

https://github.com/budden/a/commit/42b4bfa96b643a0972bd7a13b29d9e121c36ba9b

Гора, можно сказать, родила мышь. Засим ставлю звёздочку. Надо бы, конечно, составить индекс из того, что предлагали, но лень. Всем спасибо за участие!

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

Ну а элементы одного уровня-то что мешает форматировать одним отступом?

 func openDb(url string) (db *sqlx.DB, err error, closer func()) {
		db, err = sqlx.Open("postgres", url)
		closer = func() {
			err := db.Close()
			if err != nil {
				fmt.Printf("Error closing db: %v\n",err)
				// Not exiting because this function is called from the defer
			}}
		return }

	func genExpiryDate(db *sqlx.DB) {
		res1, err2 := db.Query(`select current_timestamp + interval '1' day`)
		if err2 != nil {
			fmt.Printf("Wow!"); os.Exit(1)	}
		if !res1.Next() {
			fmt.Printf("No rows here. Why?"); os.Exit(1) }
		var magic time.Time
		res1.Scan(&magic)
		fmt.Printf("Expiry at %s\n", magic.Format("2006-01-02 15:04 -0700"))	}
Cycle_A
()
Ответ на: комментарий от Cycle_A

Спасибо за ревю (как это будет по-русски?) Исправил. Проблема в том, что я не могу применить автоформатирование из-за того, что фигурную скобку ставлю не по писанному. Но нашлась команда «Convert Indentation to tabs»

den73 ★★★★★
() автор топика
Последнее исправление: den73 (всего исправлений: 1)
Ответ на: комментарий от den73

И вот кстати, у меня в этом файле 68 строк стало после того, как я все закрывающие поставил в конце строки. А если применить Format Document, то строк становится 88. Итого, за счёт лиспизации - питонизации количество строк удалось сократить на 22%.

den73 ★★★★★
() автор топика
Ответ на: комментарий от Cycle_A

А если код так писать:

func genExpiryDate(db *sqlx.DB) {


	res1, err2 := db.Query(`select current_timestamp + interval '1' day`)


	if err2 != nil {


		fmt.Printf("Wow!")


		os.Exit(1)


	}


	if !res1.Next() {


		fmt.Printf("No rows here. Why?")


		os.Exit(1)


	}


	var magic time.Time


	res1.Scan(&magic)


	fmt.Printf("Expiry at %s\n", magic.Format("2006-01-02 
15:04 -0700"))


}
то ведь читаемость совсем не страдает, правда? Например, когда функция перестаёт помещаться целиком на экране?

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

то ведь читаемость совсем не страдает, правда?

Нет. Или гораздо меньше, чем при твоей… эм… болезни.

Если я захочу увеличить межстрочный интервал, я настрою редактор, спасибо.

Cycle_A
()
Ответ на: комментарий от Cycle_A

Какие файлы - разные или какой-то конкретный? Мне начхать на самом деле, у меня стоит Tab = 1 пробел. И я нашёл, что это хороший вариант. Я не смотрю их в гитхабе, я их смотрю только в VS Code. В принципе, IDE должна этим заниматься, а не я. Просто IDE не совсем совершенная.

den73 ★★★★★
() автор топика
Ответ на: комментарий от Cycle_A

Ну я так и думал. Ладно, давай не будем терять время. За ревью спасибо, табы когда-нибудь поправлю, когда придумаю технологичный способ это сделать.

den73 ★★★★★
() автор топика
Ответ на: комментарий от Cycle_A

в общем, почитал Библию, пошарил по ключам gofmt и понял, что нет простого способа автоматически поддерживать мои закрывающие скобочки, кроме как форкнуть gofmt. Сейчас я этим заниматься не буду, поэтому придётся пользоваться этим уродским способом форматирования :( Ладно, потерпим.

den73 ★★★★★
() автор топика
Ответ на: комментарий от anonymous

Tcl - это круто но я же пишу на голанге. Соответственно, чтобы сделать конфиг на tcl, придётся сначала реализовать кусок tcl, или впилить его в программу, а это явно перебор по трудоёмкости. Или уже сделано?

den73 ★★★★★
() автор топика

Самый правильный формат — ini. На крайняк пойдет гольный параметр=значение (без секций).

anonymous
()

То он язык выдумыват, то формат конфигов. Может пора остановиться и посмотреть под ноги?

beastie ★★★★★
()
Ответ на: комментарий от den73

Никто не заставляет использовать все возможности.

Pravorskyi ★★★
()
Ответ на: комментарий от system-root

На самом деле есть. Но это больше DSL, чем обобщённый формат.

В частности pf.conf, smtpd.conf, cwmrc и иже с ними.

PS: Познавший yacc о конфигах не плачет.

beastie ★★★★★
()
Последнее исправление: beastie (всего исправлений: 2)
Ответ на: комментарий от den73

Ты всё-таки упорот :). Я был уверен, что возьмёшь tree - детище такого же фанатика NIH. Ну, а на Go есть пучок внедряемых скриптовых языков, любой из которых можно взять для конфигов. Рекомендую tengo.

hbee ★★★★
()

YAML or TOML

Используй один из двух. Сказал бы что это уже стандарты де факто, но пруфов особо нет. Но докер и к8с юзают ямл, в го пакетные менеджеры юзают томл. Собственно они для этого и придумывались, как замена кондовому джейсону. Они обратно с джейсоном совместимы. Томл отличается от ямла более расширенной граматикой что позволяет описывать более сложные условия, но ямл более читаемый, если нет особых причин юзай ямл.

DukeNukem
()

Ещё есть HCL — чуть более продвинутый json. И он даже на GO написан. Правда, там не особо с документацией на сам формат конфига.

anonymous
()
Ответ на: комментарий от hbee

По-моему, я принял самое консервативное решение и очень популярное. В чём упоротость-то? Что есть tengo, меня в гугле забанили.

den73 ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.