<?xml version="1.0" encoding="utf-8" ?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:tt="http://teletype.in/" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/"><title>Yet another developer's blog</title><subtitle>Обезличенный личный блог начинающего программиста, не упускающего возможности научиться чему-то новому</subtitle><author><name>Yet another developer's blog</name></author><id>https://teletype.in/atom/yadevblog</id><link rel="self" type="application/atom+xml" href="https://teletype.in/atom/yadevblog?offset=0"></link><link rel="alternate" type="text/html" href="https://teletype.in/@yadevblog?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=yadevblog"></link><link rel="next" type="application/rss+xml" href="https://teletype.in/atom/yadevblog?offset=10"></link><link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></link><updated>2026-04-11T14:48:29.324Z</updated><entry><id>yadevblog:typed-config-dotnet-6</id><link rel="alternate" type="text/html" href="https://teletype.in/@yadevblog/typed-config-dotnet-6?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=yadevblog"></link><title>Подключение валидации к строго типизированной конфигурации в .NET 6+</title><published>2023-02-02T11:49:06.561Z</published><updated>2023-02-02T11:50:43.365Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img1.teletype.in/files/42/96/4296eca6-5217-4ff4-a69a-eb673dfee332.png"></media:thumbnail><category term="dotnet" label="dotnet"></category><summary type="html">&lt;img src=&quot;https://andrewlock.net/content/images/2018/06/unhandled_exception_configuration_validation.png&quot;&gt;Эта статья — перевод статьи Эндрю Лока.</summary><content type="html">
  &lt;p id=&quot;eB0f&quot;&gt;&lt;strong&gt;Эта статья — перевод &lt;a href=&quot;https://andrewlock.net/adding-validation-to-strongly-typed-configuration-objects-in-dotnet-6/&quot; target=&quot;_blank&quot;&gt;статьи Эндрю Лока&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
  &lt;h2 id=&quot;EESt&quot;&gt;Строго типизированная конфигурация в ASP.NET Core&lt;/h2&gt;
  &lt;p id=&quot;K5iH&quot;&gt;Система конфигурации в .NET очень гибкая. Она позволяет загружать параметры из разных мест: &lt;a href=&quot;https://docs.microsoft.com/en-gb/aspnet/core/fundamentals/configuration/index?view=aspnetcore-2.1&amp;tabs=basicconfiguration#json-configuration&quot; target=&quot;_blank&quot;&gt;JSON файлы&lt;/a&gt;, &lt;a href=&quot;https://andrewlock.net/creating-a-custom-iconfigurationprovider-in-asp-net-core-to-parse-yaml/&quot; target=&quot;_blank&quot;&gt;YAML файлы&lt;/a&gt;, &lt;a href=&quot;https://docs.microsoft.com/en-gb/aspnet/core/security/app-secrets?view=aspnetcore-2.1&amp;tabs=windows#environment-variables&quot; target=&quot;_blank&quot;&gt;переменные окружения&lt;/a&gt;, &lt;a href=&quot;https://docs.microsoft.com/en-gb/aspnet/core/security/key-vault-configuration?view=aspnetcore-2.1&amp;tabs=aspnetcore2x&quot; target=&quot;_blank&quot;&gt;Azure Key Vault&lt;/a&gt; и многое другое. В статье предлагается использовать конечный объект &lt;code&gt;IConfiguration&lt;/code&gt; в приложении чтобы настроить строгую типизацию.&lt;/p&gt;
  &lt;p id=&quot;Csj9&quot;&gt;Строго типизированная конфигурация с помощью простых объектов описывает часть вашей конфигурации вместо обычного хранения пар &amp;quot;ключ-значение&amp;quot; в &lt;code&gt;IConfiguration&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;WgJw&quot;&gt;Допустим, вы настраиваете интеграцию со Slack, и для &lt;a href=&quot;https://api.slack.com/incoming-webhooks&quot; target=&quot;_blank&quot;&gt;отправки сообщений  в канал используете вебхуки&lt;/a&gt;. Вам понадобится URL вебхука, и какие-нибудь дополнительные параметры, например имя приложения, которое будет использоваться для отправки сообщений в канал:&lt;/p&gt;
  &lt;pre id=&quot;NMer&quot; data-lang=&quot;clike&quot;&gt;public class SlackApiSettings {
    public string WebhookUrl { get; set; }
    public string DisplayName { get; set; }
    public bool ShouldNotify { get; set; }
}&lt;/pre&gt;
  &lt;p id=&quot;2gdv&quot;&gt;Этот объект можно привязать к вашей конфигурации в &lt;em&gt;Program.cs &lt;/em&gt;используя метод расширения &lt;code&gt;Configure&amp;lt;T&amp;gt;()&lt;/code&gt;. Когда вам понадобится этот объект в контроллере, вы можете внедрить зависимость &lt;code&gt;IOptions&amp;lt;SlackApiSettings&amp;gt;&lt;/code&gt; в его контроллер. Например, чтобы внедрить конфиги в Minimal API эндпоинт и вернуть JSON с ними можно сделать так:&lt;/p&gt;
  &lt;pre id=&quot;M4SR&quot; data-lang=&quot;clike&quot;&gt;using Microsoft.Extensions.Options;

var builder = WebApplication.CreateBuilder(args);

// связка конфигурации с секцией SlackApi
// например SlackApi:WebhookUrl и SlackApi:DisplayName 
builder.Services.Configure&amp;lt;SlackApiSettings&amp;gt;(
    builder.Configuration.GetSection(&amp;quot;SlackApi&amp;quot;)); 

var app = builder.Build();

// вернуть объект конфига
app.MapGet(&amp;quot;/&amp;quot;, (IOptions&amp;lt;SlackApiSettings&amp;gt; options) =&amp;gt; options.Value);

app.Run();&lt;/pre&gt;
  &lt;p id=&quot;UOYk&quot;&gt;Под капотом система конфигурации ASP.NET Core создаёт новый объект &lt;code&gt;SlackApiConfiguration&lt;/code&gt; и пытается сопоставить каждое свойство в объекте со значениями в секции &lt;code&gt;IConfiguration&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;Ruym&quot;&gt;Чтобы получить объект конфигурации, обратитесь к &lt;code&gt;IOptions&amp;lt;T&amp;gt;.Value&lt;/code&gt;, как показано в обработчике эндпоинта.&lt;/p&gt;
  &lt;h2 id=&quot;PBQC&quot;&gt;Избегание зависимости IOptions&lt;/h2&gt;
  &lt;p id=&quot;b09j&quot;&gt;Некоторым людям (мне в том числе) не нравится зависимость эндпоинтов от IOptions вместо объекта конфигурации напрямую. Вы можете избежать зависимости от &lt;code&gt;IOptions&amp;lt;T&amp;gt;&lt;/code&gt; сопоставив объект конфигурации вручную, &lt;a href=&quot;https://www.strathweb.com/2016/09/strongly-typed-configuration-in-asp-net-core-without-ioptionst/&quot; target=&quot;_blank&quot;&gt;как описано здесь&lt;/a&gt;, вместо использования метода расширения &lt;code&gt;Configure&amp;lt;T&amp;gt;&lt;/code&gt;. Более простой подход (по моему мнению) это явно зарегистрировать объект &lt;code&gt;SlackApiSettings &lt;/code&gt;в приложении и делегировать его определение на объект &lt;code&gt;IOptions&lt;/code&gt;. Например:&lt;/p&gt;
  &lt;pre id=&quot;kB4n&quot; data-lang=&quot;clike&quot;&gt;using Microsoft.Extensions.Options;

var builder = WebApplication.CreateBuilder(args);

// Регистрируем объект IOptions
builder.Services.Configure&amp;lt;SlackApiSettings&amp;gt;(
    builder.Configuration.GetSection(&amp;quot;SlackApi&amp;quot;));

// Явно регистрируем объект конфигурации, делегируя определение на IOptions
builder.Services.AddSingleton(resolver =&amp;gt; 
        resolver.GetRequiredService&amp;lt;IOptions&amp;lt;SlackApiSettings&amp;gt;&amp;gt;().Value);

var app = builder.Build();&lt;/pre&gt;
  &lt;p id=&quot;UP47&quot;&gt;Теперь в контроллеры можно внедрять &amp;quot;сырой&amp;quot; объект настроек, без зависимости от пакета Microsoft.Extensions.Options. Я думаю, что это более предпочтительный способ, потому что в этом случае интерфейс &lt;code&gt;IOptions&amp;lt;T&amp;gt;&lt;/code&gt; не нужен.&lt;/p&gt;
  &lt;pre id=&quot;xOgp&quot; data-lang=&quot;clike&quot;&gt;app.MapGet(&amp;quot;/&amp;quot;, (SlackApiSettings options) =&amp;gt; options);

app.Run();&lt;/pre&gt;
  &lt;p id=&quot;q5DB&quot;&gt;Обычно это работает хорошо, хотя тут есть пара нюансов:&lt;/p&gt;
  &lt;ul id=&quot;C35U&quot;&gt;
    &lt;li id=&quot;dSW6&quot;&gt;В примере выше не будет работать &amp;quot;перезагрузка файла&amp;quot; для конфигурации, так как я использовал Singleton (можно использовать Scoped, если вам нужна эта функция)&lt;/li&gt;
    &lt;li id=&quot;CArw&quot;&gt;При регистрации IOption появляется дополнительный уровень косвенности, вместо регистрации объекта SlackApiSettings напрямую в механизме внедрения зависимостей. Лично мне нравится такой подход, но вы можете использовать IOptions. Есть еще один подход, описанный в этом &lt;a href=&quot;https://www.strathweb.com/2016/09/strongly-typed-configuration-in-asp-net-core-without-ioptionst/&quot; target=&quot;_blank&quot;&gt;посте&lt;/a&gt;.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;yfFP&quot;&gt;Наличие отличной поддержки загрузки конфигурации из разных источников это хорошо, но что будет, если вы ошибётесь в конфигурации, например допустите опечатку в JSON файле?&lt;/p&gt;
  &lt;p id=&quot;W9wL&quot;&gt;Чаще всего я сталкивался с проблемой, возникающей из-за того, что секреты необходимо хранить вне системы контроля версий. В таком случае я &lt;em&gt;ожидаю&lt;/em&gt;, что секреты будут доступны на продакшн-сервере, но если они не были корректно настроены, в приложении конфигурация получит значения типа &amp;quot;по умолчанию&amp;quot;. Ошибки конфигурации сложно отловить, ведь их можно воспроизвести только на сервере.&lt;/p&gt;
  &lt;h2 id=&quot;7Wxb&quot;&gt;Что случится, если сопоставление проваливается?&lt;/h2&gt;
  &lt;p id=&quot;1DeP&quot;&gt;Есть несколько случаев, когда что-то может пойти не так при сопоставлении строго типизированных объектов с конфигурацией. Я покажу несколько примеров ошибок в JSON конфигурации, используя пример обработчика, написанный выше.&lt;/p&gt;
  &lt;h3 id=&quot;DfZU&quot;&gt;Опечатка в названии секции&lt;/h3&gt;
  &lt;p id=&quot;u5qc&quot;&gt;При сопоставлении конфигурации вы указываете имя секции, откуда брать значения. Если думать в терминах файла &lt;em&gt;appsettings.json&lt;/em&gt;, секция — это название ключа объекта в JSON. &lt;code&gt;&amp;quot;Logging&amp;quot;&lt;/code&gt; и &lt;code&gt;&amp;quot;SlackApi&amp;quot;&lt;/code&gt; это секции в приведённом ниже &lt;em&gt;.json&lt;/em&gt; файле:&lt;/p&gt;
  &lt;pre id=&quot;EROG&quot; data-lang=&quot;javascript&quot;&gt;{
 &amp;quot;Logging&amp;quot;: {
    &amp;quot;LogLevel&amp;quot;: {
      &amp;quot;Default&amp;quot;: &amp;quot;Warning&amp;quot;
    }
  },
  &amp;quot;AllowedHosts&amp;quot;: &amp;quot;*&amp;quot;,
  &amp;quot;SlackApi&amp;quot;: {
    &amp;quot;WebhookUrl&amp;quot;: &amp;quot;http://example.com/test/url&amp;quot;,
    &amp;quot;DisplayName&amp;quot;: &amp;quot;My fancy bot&amp;quot;,
    &amp;quot;ShouldNotify&amp;quot;: true
  }
}&lt;/pre&gt;
  &lt;p id=&quot;ggyJ&quot;&gt;Чтобы связать &lt;code&gt;SlackApiSettings&lt;/code&gt; с секцией &lt;code&gt;&amp;quot;SlackApi&amp;quot;&lt;/code&gt;, можно сделать:&lt;/p&gt;
  &lt;pre id=&quot;owUZ&quot; data-lang=&quot;clike&quot;&gt;builder.Services.Configure&amp;lt;SlackApiSettings&amp;gt;(
    Configuration.GetSection(&amp;quot;SlackApi&amp;quot;)
); &lt;/pre&gt;
  &lt;p id=&quot;WPT1&quot;&gt;Но что если в названии секции будет допущена опечатка? Например вместо SlackApi укажем SlackApiSettings:&lt;/p&gt;
  &lt;pre id=&quot;u1aM&quot; data-lang=&quot;clike&quot;&gt;builder.Services.Configure&amp;lt;SlackApiSettings&amp;gt;(
    Configuration.GetSection(&amp;quot;SlackApiSettings&amp;quot;)
); &lt;/pre&gt;
  &lt;p id=&quot;mSRj&quot;&gt;Вызов эндпоинта даст:&lt;/p&gt;
  &lt;pre id=&quot;eNVB&quot; data-lang=&quot;clike&quot;&gt;{&amp;quot;webhookUrl&amp;quot;:null,&amp;quot;displayName&amp;quot;:null,&amp;quot;shouldNotify&amp;quot;:false}&lt;/pre&gt;
  &lt;p id=&quot;dyRH&quot;&gt;Все ключи получили значение по умолчанию, но никаких ошибок не произошло. Сопоставление произошло, но с пустой секцией конфигурации. Наверное, это плохо, потому что ваш код ожидает, что в &lt;code&gt;webhookUrl&lt;/code&gt; будет валидный &lt;code&gt;Uri&lt;/code&gt;.&lt;/p&gt;
  &lt;blockquote id=&quot;K93q&quot;&gt;Примечание переводчика: Вообще, чтобы решить эту проблему можно вместо &lt;code&gt;Configuration.GetSection&lt;/code&gt; использовать &lt;code&gt;Configuration.GetRequiredSection&lt;/code&gt;. Тогда при попытке сопоставить объект с несуществующей секцией возникнет исключение.&lt;/blockquote&gt;
  &lt;h3 id=&quot;EoiE&quot;&gt;Опечатка в названии свойства&lt;/h3&gt;
  &lt;p id=&quot;cxMq&quot;&gt;Что произойдёт, если название секции верно, но неверно название свойства?&lt;br /&gt;Например, что если &lt;code&gt;WebhookUrl&lt;/code&gt; будет записан в файле как &lt;code&gt;Url&lt;/code&gt;?&lt;/p&gt;
  &lt;pre id=&quot;wsjo&quot; data-lang=&quot;javascript&quot;&gt;{
  &amp;quot;SlackApi&amp;quot;: {
    &amp;quot;Url&amp;quot;: &amp;quot;http://example.com/test/url&amp;quot;,
    &amp;quot;DisplayName&amp;quot;: &amp;quot;My fancy bot&amp;quot;,
    &amp;quot;ShouldNotify&amp;quot;: true
  }
}&lt;/pre&gt;
  &lt;p id=&quot;xCt1&quot;&gt;Посмотрим на результат:&lt;/p&gt;
  &lt;pre id=&quot;sp78&quot; data-lang=&quot;clike&quot;&gt;{&amp;quot;webhookUrl&amp;quot;:null,&amp;quot;displayName&amp;quot;:&amp;quot;My fancy bot&amp;quot;,&amp;quot;shouldNotify&amp;quot;:true}&lt;/pre&gt;
  &lt;p id=&quot;L1Yz&quot;&gt;Так как название секции правильное, &lt;code&gt;DisplayName&lt;/code&gt; и &lt;code&gt;ShouldNotify&lt;/code&gt; попали в объект конфигурации правильно. Но &lt;code&gt;WebhookUrl&lt;/code&gt; равен &lt;code&gt;null&lt;/code&gt;, так как в конфигурации нет такого поля (&lt;code&gt;Url&lt;/code&gt; вместо него). И снова никаких сообщений о том, что поле не обработалось корректно.&lt;/p&gt;
  &lt;h3 id=&quot;w1jS&quot;&gt;Несвязываемые поля&lt;/h3&gt;
  &lt;p id=&quot;o6SY&quot;&gt;Эта ошибка встречается не слишком часто, но о ней всё же стоит знать. Если вы используете в объекте конфигурации поля без сеттера, они не свяжутся. Например, если мы изменим объект следующим образом:&lt;/p&gt;
  &lt;pre id=&quot;qm3I&quot; data-lang=&quot;clike&quot;&gt;public class SlackApiSettings
{
    public string WebhookUrl { get; }
    public string DisplayName { get; }
    public bool ShouldNotify { get; }
}&lt;/pre&gt;
  &lt;p id=&quot;797M&quot;&gt;и снова посмотрим на ответ эндпоинта, мы получим объект со значениями по умолчанию так как парсер не сможет установить значение в объект:&lt;/p&gt;
  &lt;pre id=&quot;Owz7&quot; data-lang=&quot;clike&quot;&gt;{&amp;quot;webhookUrl&amp;quot;:null,&amp;quot;displayName&amp;quot;:null,&amp;quot;shouldNotify&amp;quot;:false}&lt;/pre&gt;
  &lt;h3 id=&quot;RFR9&quot;&gt;Несовместимые типы данных&lt;/h3&gt;
  &lt;p id=&quot;B41T&quot;&gt;И последняя ошибка в этом посте происходит, когда парсер пытается связать поля с несовместимыми типами данных. В конфигурации всё представлено в виде строк, но парсер может преобразовывать простые типы. Например &lt;code&gt;&amp;quot;true&amp;quot;&lt;/code&gt; или &lt;code&gt;&amp;quot;FALSE&amp;quot;&lt;/code&gt; нормально преобразуется в поле &lt;code&gt;bool ShouldNotify&lt;/code&gt;, но если вы попытаетесь запихать туда что-нибудь ещё, например &lt;code&gt;&amp;quot;THE VALUE&amp;quot;&lt;/code&gt;, вы получите исключение, когда будете дёргать эндпоинт и парсер попытается собрать объект &lt;code&gt;IOptions&amp;lt;T&amp;gt;&lt;/code&gt;:&lt;/p&gt;
  &lt;figure id=&quot;r67p&quot; class=&quot;m_retina&quot;&gt;
    &lt;img src=&quot;https://andrewlock.net/content/images/2018/06/unhandled_exception_configuration_validation.png&quot; width=&quot;667.5&quot; /&gt;
    &lt;figcaption&gt;Скриншот (c) Andrew Lock&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;6iEF&quot;&gt;Факт получения ошибки не очень хороший, но хотя бы парсер вообще кидает исключение, которое чётко даёт понять в чём проблема! Я слишком много раз попадал в ситуации, когда вызовы к внешнему API не отрабатывали только потому, что в объект конфигурации не попадала строка подключения или базовый URL из-за ошибки связывания.&lt;/p&gt;
  &lt;p id=&quot;4VXm&quot;&gt;Об ошибках конфигурации вроде этой лучше всего сообщать как можно раньше. Лучше всего во время компиляции, но и при запуске тоже неплохо. Поэтому нам нужна валидация.&lt;/p&gt;
  &lt;h2 id=&quot;TSyB&quot;&gt;Валидация значений &lt;code&gt;IOptions&lt;/code&gt;&lt;/h2&gt;
  &lt;p id=&quot;BMVZ&quot;&gt;Валидация значений в &lt;code&gt;IOptions&lt;/code&gt; появилась еще в .NET Core 2.2 с методами &lt;code&gt;Validate&amp;lt;&amp;gt;&lt;/code&gt; и &lt;code&gt;ValidateDataAnnotations()&lt;/code&gt;. Их проблема в том, что они не запускаются со стартом приложения, только в момент получения доступа к &lt;code&gt;IOptions&lt;/code&gt;. Это было частичным решением проблемы, поэтому я создал &lt;a href=&quot;https://www.nuget.org/packages/NetEscapades.Configuration.Validation/&quot; target=&quot;_blank&quot;&gt;NuGet пакет&lt;/a&gt;, который запускал валидацию на старте приложения.&lt;/p&gt;
  &lt;p id=&quot;JmFi&quot;&gt;К счастью, в .NET 6 &lt;a href=&quot;https://github.com/dotnet/runtime/issues/36391&quot; target=&quot;_blank&quot;&gt;появился метод&lt;/a&gt; &lt;code&gt;ValidateOnStart()&lt;/code&gt;, который делает в точности то, что нам нужно — запускает валидацию при старте приложения!&lt;/p&gt;
  &lt;blockquote id=&quot;miA3&quot;&gt;Если вам интересно, как это реализовано: Фишка в использовании &lt;code&gt;IHostedService&lt;/code&gt; для валидации. Реализацию можно посмотреть в &lt;a href=&quot;https://github.com/dotnet/runtime/pull/47821/files&quot; target=&quot;_blank&quot;&gt;этом PR&lt;/a&gt;.&lt;/blockquote&gt;
  &lt;p id=&quot;iujj&quot;&gt;Чтобы использовать такую валидацию, нужно сделать четыре вещи:&lt;/p&gt;
  &lt;ul id=&quot;7r2N&quot;&gt;
    &lt;li id=&quot;ko9q&quot;&gt;Переключиться на &lt;code&gt;services.AddOptions&amp;lt;T&amp;gt;().Bind()&lt;/code&gt; вместо &lt;code&gt;services.Configure&amp;lt;T&amp;gt;()&lt;/code&gt;&lt;/li&gt;
    &lt;li id=&quot;GnyE&quot;&gt;Добавить атрибуты валидации к объекту конфигурации&lt;/li&gt;
    &lt;li id=&quot;r4wi&quot;&gt;Вызвать &lt;code&gt;ValidateDateAnnotations()&lt;/code&gt; &lt;code&gt;OptionsBuilder&lt;/code&gt;&amp;#x27;а, возвращённого из &lt;code&gt;AddOptions&amp;lt;T&amp;gt;()&lt;/code&gt;&lt;/li&gt;
    &lt;li id=&quot;RtLi&quot;&gt;Вызвать &lt;code&gt;ValidateOnStart() OptionsBuilder&lt;/code&gt;&amp;#x27;а.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;4rUW&quot;&gt;Метод расширения &lt;code&gt;IServiceCollection.AddOptions&amp;lt;T&amp;gt;()&lt;/code&gt; ведёт себя как альтернативная версия &lt;code&gt;Configure&amp;lt;T&amp;gt;()&lt;/code&gt;: &lt;/p&gt;
  &lt;ul id=&quot;1G80&quot;&gt;
    &lt;li id=&quot;mvQe&quot;&gt;&lt;code&gt;AddOptions&amp;lt;T&amp;gt;()&lt;/code&gt; возвращает объект &lt;code&gt;OptionsBuilder&amp;lt;T&amp;gt;&lt;/code&gt; вместо &lt;code&gt;IServiceCollection&lt;/code&gt;&lt;/li&gt;
    &lt;li id=&quot;25ig&quot;&gt;Нужно вызвать &lt;code&gt;Bind()&lt;/code&gt; объекта &lt;code&gt;OptionsBuilder&amp;lt;T&amp;gt;&lt;/code&gt; чтобы связать конфиг с объектом.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;ideG&quot;&gt;Использование объекта &lt;code&gt;OptionsBuilder&amp;lt;T&amp;gt;&lt;/code&gt; открывает новые возможности для добавления нового функционала вроде валидации.&lt;/p&gt;
  &lt;blockquote id=&quot;NKJy&quot;&gt;Вспомогательное расширение BindConfiguration() было добавлено в OptionsBuilder, чтобы упростить связывание секций конфигураций. В следующем блоке будет показано, как это сделать.&lt;/blockquote&gt;
  &lt;p id=&quot;HH0R&quot;&gt;Добавим атрибуты валидации к SlackApiSettings и настроим валидацию в приложении:&lt;/p&gt;
  &lt;pre id=&quot;rrxj&quot; data-lang=&quot;clike&quot;&gt;using System.ComponentModel.DataAnnotations;
using Microsoft.Extensions.Options;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOptions&amp;lt;SlackApiSettings&amp;gt;()
    .BindConfiguration(&amp;quot;SlackApi&amp;quot;) // 👈 Связать секцию SlackApi
    .ValidateDataAnnotations() // 👈 Включить валидацию
    .ValidateOnStart(); // 👈 Валидировать при старте

// Явно зарегистрируем объект конфигурации,
// делегировав его объекту IOptions
builder.Services.AddSingleton(resolver =&amp;gt; 
        resolver.GetRequiredService&amp;lt;IOptions&amp;lt;SlackApiSettings&amp;gt;&amp;gt;().Value);

var app = builder.Build();

app.MapGet(&amp;quot;/&amp;quot;, (SlackApiSettings options) =&amp;gt; options);

app.Run();

public class SlackApiSettings
{
    [Required, Url]
    public string WebhookUrl { get; set; }
    [Required]
    public string DisplayName { get; set; }
    public bool ShouldNotify { get; set; }
}&lt;/pre&gt;
  &lt;blockquote id=&quot;oi9g&quot;&gt;Обратите внимание, что здесь я использовал DataAnnotations, но можно использовать другие фреймворки для валидации [п/п: У автора есть &lt;a href=&quot;https://andrewlock.net/adding-validation-to-strongly-typed-configuration-objects-using-flentvalidation/&quot; target=&quot;_blank&quot;&gt;статья&lt;/a&gt; про подключение FluentValidation к этому механизму, её перевод выйдет следующим]&lt;/blockquote&gt;
  &lt;h2 id=&quot;mJtG&quot;&gt;Тестирование конфигурации на старте приложения&lt;/h2&gt;
  &lt;p id=&quot;KfSX&quot;&gt;Мы можем проверить валидацию, использовав любой из примеров с ошибками &lt;a href=&quot;#7Wxb&quot;&gt;выше&lt;/a&gt;.  Например, если мы допустим опечатку в названии поля, то при запуске приложения до обработки любых запросов получим исключение:&lt;/p&gt;
  &lt;pre id=&quot;WF0y&quot; data-lang=&quot;clike&quot;&gt;Unhandled exception. Microsoft.Extensions.Options.OptionsValidationException: 
  DataAnnotation validation failed for &amp;#x27;SlackApiSettings&amp;#x27; members: 
    &amp;#x27;DisplayName&amp;#x27; with the error: &amp;#x27;The DisplayName field is required.&amp;#x27;.
   at Microsoft.Extensions.Options.OptionsFactory&amp;#x60;1.Create(String name)
   at Microsoft.Extensions.Options.OptionsMonitor&amp;#x60;1.&amp;lt;&amp;gt;c__DisplayClass10_0.&amp;lt;Get&amp;gt;b__0()&lt;/pre&gt;
  &lt;p id=&quot;xSsp&quot;&gt;Теперь, если в конфиге встретится ошибка, вы узнаете об этом сразу, не дожидаясь того, что приложение упадёт в рантайме. Оно просто не запустится, а если вы используете окружение вроде Kubernetes, проверки состояния не пройдут и на боевом сервере останется рабочая версия, пока вы не почините ошибки конфигурации.&lt;/p&gt;
  &lt;h2 id=&quot;9MvG&quot;&gt;Вывод&lt;/h2&gt;
  &lt;p id=&quot;qOtL&quot;&gt;Система конфигурации в ASP.NET Core очень гибкая и позволяет использовать строгую типизацию. Кроме того, из-за этой гибкости, некоторые ошибки могут возникать только в определённых окружениях. По умолчанию эти ошибки будут появляться только при попытке получить доступ к объекту конфигурации.&lt;/p&gt;
  &lt;p id=&quot;frte&quot;&gt;В этом посте я показал как использовать &lt;code&gt;ValidateOnStart()&lt;/code&gt; метод, появившийся в .NET 6 для того, чтобы проверять конфигурацию на старте приложения. Это позволит как можно раньше убедиться в том, что приложение получило правильную конфигурацию.&lt;/p&gt;

</content></entry><entry><id>yadevblog:developer-windows</id><link rel="alternate" type="text/html" href="https://teletype.in/@yadevblog/developer-windows?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=yadevblog"></link><title>Windows для разработчика</title><published>2023-01-07T19:24:13.213Z</published><updated>2023-01-07T19:24:13.213Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img2.teletype.in/files/52/1c/521c698a-4a79-45cb-aab2-4cb0d9f75743.png"></media:thumbnail><category term="it" label="it"></category><summary type="html">Хэй! Новый год, новая статья на спорную тему! Так вышло, что обретя новый ноут с видеокартой Nvidia при использовании Linux я получил больше страданий, чем удовольствия. То перегрев словлю, то тиринг выбесит. В общем, я решил дать винде шанс и перебраться на неё на постоянку (до этого у меня был дуалбут, потому что периодически мне нужен Windows-специфичный софт)</summary><content type="html">
  &lt;p id=&quot;HEZ2&quot;&gt;Хэй! Новый год, новая статья на спорную тему! Так вышло, что обретя новый ноут с видеокартой Nvidia при использовании Linux я получил больше страданий, чем удовольствия. То перегрев словлю, то тиринг выбесит. В общем, я решил дать винде шанс и перебраться на неё на постоянку (до этого у меня был дуалбут, потому что периодически мне нужен Windows-специфичный софт)&lt;/p&gt;
  &lt;p id=&quot;GUcl&quot;&gt;Сейчас я сижу на Windows 11, Beta Channel, мой стек технологий это .NET и React.js. Да, я из тех людей, что пишут на .NET на линуксе, и им норм. Хотя казалось бы, технология майков, бла-бла-бла... Но на самом деле, когда майки выпустили .NET официально на все платформы и открыли исходники, отпала необходимость в Mono и стало вообще хорошо.&lt;/p&gt;
  &lt;h2 id=&quot;0yKf&quot;&gt;IDE&lt;/h2&gt;
  &lt;p id=&quot;xFLG&quot;&gt;Я человек простой. Обожаю всё, что выпускаетJetbrains, поэтому в этом плане переезд затруднений не вызвал. Rider и Webstorm как работали на линуксе, так и работают здесь.&lt;/p&gt;
  &lt;p id=&quot;i49i&quot;&gt;Искренне презираю Visual Studio. По моему мнению, у нее перегруженный интерфейс, неинтуитивное управление вкладками, убогие комбинации клавиш и паршивый редактор.&lt;/p&gt;
  &lt;p id=&quot;Nupx&quot;&gt;К сожалению, Rider не всесилен и не имеет всех возможностей студии + не сразу получает поддержку новых версий платформы, так что иногда приходится-таки включать студию. Но разрабы молодцы и достаточно быстро вносят нужные изменения.&lt;/p&gt;
  &lt;h2 id=&quot;z9Nj&quot;&gt;CLI&lt;/h2&gt;
  &lt;p id=&quot;dRLY&quot;&gt;Люблю консольные приложения в линуксе за их простоту и очевидность. Вообще мне близка философия Unix &amp;quot;всё есть файл&amp;quot;. Это очень удобно, когда конфиг любого приложения находится там, где ты его ожидаешь увидеть и это просто текстовый файл, который можно открыть чем угодно и прочитать его глазами.&lt;/p&gt;
  &lt;p id=&quot;xEcu&quot;&gt;В винде всё не так. У тебя есть стопицот папок, где приложения оставляют свой мусор, а конфиги порой хранятся в бинарных файлах, которые и открыть-то нечем. А еще есть великий и ужасный реестр, в котором в каком-то только системе понятном формате хранятся килотонны значений. А как это чистить? А как бэкапить? Загадка...&lt;/p&gt;
  &lt;p id=&quot;Oscv&quot;&gt;А как вам синтаксис Powershell?&lt;/p&gt;
  &lt;p id=&quot;5cN3&quot;&gt;Например для того, чтобы в линуксе получить путь до бинарника (иногда бывает нужен в скриптах) используется программа &lt;code&gt;which&lt;/code&gt; (или &lt;code&gt;where&lt;/code&gt;, зависит от дистрибутива). В Powershell для этого нужен целый &lt;code&gt;(get-command binary).Path&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;cjeF&quot;&gt;Или вот еще — достаточно частая встречаемая штука — поиск в текстовом файле. Типичный линуксовый &lt;code&gt;cat&lt;/code&gt; + &lt;code&gt;grep&lt;/code&gt;: &lt;code&gt;cat textfile.txt | grep &amp;#x27;searchquery&amp;#x27;&lt;/code&gt;. Что же нам нужно сделать для того же в Powershell? &lt;code&gt;Select-String -Path &amp;quot;textfile.txt&amp;quot; -Pattern &amp;quot;searchquery&amp;quot;.&lt;/code&gt; Очень удобно, и писать немного нужно...&lt;/p&gt;
  &lt;p id=&quot;Ivko&quot;&gt;Кто-то может сказать мол &amp;quot;Используй WSL&amp;quot;. Если коротко, то я считаю, что это очен странная попытка майков залезть на территорию линя. По факту, это нативная виртуалка с по умолчанию подмонтированным системным диском. Я пользовался этим исключительно в связке с докером (м-м-м, супер, виртуалка в виртуалке) и думаю, что всё это достаточно ненадёжно. Так, например, у меня так и не вышло заставить вебшторм запускать npm&amp;#x27;овские скрипты в WSL, хотя там такая фича есть. Точнее не просто npm&amp;#x27;овские, а еще с системными командами в комплекте (&lt;code&gt;mv&lt;/code&gt;, &lt;code&gt;rm&lt;/code&gt; и вот это всё)&lt;/p&gt;
  &lt;h2 id=&quot;ABcC&quot;&gt;&amp;quot;Горе от ума&amp;quot;&lt;/h2&gt;
  &lt;p id=&quot;YMbg&quot;&gt;Это моя старая шутка про то, что винда считает, что знает, что нужно пользователю, больше чем пользователь. Например, я закрыл студию и проект, который в ней был открыт, иду в проводник, пытаюсь удалить этот проект, а мне говорят, что папка используется каким-то процессом. Хм, странно. Смотрю в &amp;quot;Мониторе ресурсов&amp;quot; — и правда, папка проекта на кой-то чёрт занята процессом System. WTF?! Естественно, это не тот процесс, который можно просто взять и остановить. Спустя несколько выходов из спящего режима, система эту папку так и не отпустила. И зачем она ей нужна, вопрос...&lt;/p&gt;
  &lt;h2 id=&quot;uAK0&quot;&gt;Вместо вывода&lt;/h2&gt;
  &lt;p id=&quot;PSvN&quot;&gt;В целом, винда вполне подходит для разработки, но не для любого стека и с кучей нюансов. Как дотнетщику, мне в принципе без разницы.&lt;/p&gt;
  &lt;p id=&quot;gBQr&quot;&gt;Но вот скоро мне нужно будет писать большой проект на джаве, там я вернусь обратно на линь. Нафиг мне это не сдалось страдать здесь.&lt;/p&gt;
  &lt;p id=&quot;kN08&quot;&gt;Тот же докер лучше работает под линуксом (если это не так, тогда почему авторы докера не стали воротить своих костылей с виртуализацией винды и просто завернулись под WSL?). Поэтому для обучения DevOps я тоже буду использовать линь.&lt;/p&gt;

</content></entry><entry><id>yadevblog:how-i-hated-java</id><link rel="alternate" type="text/html" href="https://teletype.in/@yadevblog/how-i-hated-java?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=yadevblog"></link><title>Как я возненавидел Java</title><published>2022-08-27T09:38:02.954Z</published><updated>2022-08-28T10:58:27.673Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img4.teletype.in/files/f5/19/f519a9c2-e6eb-4ee0-95eb-9ee60d086b94.png"></media:thumbnail><category term="it" label="it"></category><summary type="html">Весьма претенциозный заголовок, не находите? Впрочем, после недавнего проекта для меня он стал близок к истине. Я, если не возненавидел, то точно сильно разочаровался в Java и её экосистеме.</summary><content type="html">
  &lt;p id=&quot;QSpR&quot;&gt;Весьма претенциозный заголовок, не находите? Впрочем, после недавнего проекта для меня он стал близок к истине. Я, если не возненавидел, то точно сильно разочаровался в Java и её экосистеме.&lt;/p&gt;
  &lt;p id=&quot;GKUf&quot;&gt;С Java я и мои &lt;s&gt;ленивые распиз&lt;/s&gt; бравые коллеги познакомились по долгу учёбы. Небольшой проект, размером в семестр, казалось бы, что может пойти не так? Оказалось что многое, и даже очень. &lt;/p&gt;
  &lt;p id=&quot;deV8&quot;&gt;Моя аудитория знает, что до этого проекта я писал по большей части на Python, и, за неимением другого опыта, подсознательно сравнивал Java именно с ним. Но языки эти достаточно разные, чтобы сравнивать их напрямую. Поэтому эта статья выходит лишь сейчас, а не в начале лета, когда проект был только завершён, а эмоции, возникавшие при разработке ещё свежи. Я чувствую, что за лето вырос как разработчик, набрался кое-какого опыта с более &amp;quot;серьёзными&amp;quot; технологиями и подостыл.&lt;/p&gt;
  &lt;p id=&quot;XXWV&quot;&gt;Поэтому здесь не будет громких заявлений в духе &amp;quot;Java — говно, Python — лучше всех!&amp;quot;. Я всё ещё считаю Python — лучшим второстепенным языком, который прекрасно решает многие задачи, но не любые, иногда его лучше заменить на что-то более производительное. Java же, наверняка хороша чем-то по-своему, но просто не для меня.&lt;/p&gt;
  &lt;p id=&quot;Gpin&quot;&gt;В этой статье я постараюсь обстоятельно и безэмоционально описать как мы/я писали этот проект, с какими проблемами столкнулись, и какими костылями в спешке затыкали возникающие дыры в преддверии дедлайна, поэтому заваривайте чашку пельменей и приготовьтесь слушать, нам предстоит долгая история.&lt;/p&gt;
  &lt;h2 id=&quot;YQU4&quot;&gt;А что за проект-то собственно?&lt;/h2&gt;
  &lt;p id=&quot;ChHb&quot;&gt;И правда, что я заладил &amp;quot;проект&amp;quot; да &amp;quot;проект&amp;quot;? Прошу любить, жаловать и не жаловаться — &amp;quot;Student Archive&amp;quot;. Мде, полное название оригинальностью не блещет... &amp;quot;STAR&amp;quot; — во, так-то лучше. Это наша первая попытка в EdTech.&lt;/p&gt;
  &lt;p id=&quot;LyT5&quot;&gt;Мы попытались автоматизировать то, что у нас не получилось сделать вручную — собрать архив лабораторных работ, методических материалов и прочих полезностей студента. Не, ну а что? Оно всё равно годами не устаревает.&lt;/p&gt;
  &lt;p id=&quot;oLaZ&quot;&gt;На этот семестр стояла задача написать бэкенд. Для этого был выбран классический REST API, без всякого хипстерского выпендрёжа вроде GraphQL или gRPC.&lt;/p&gt;
  &lt;p id=&quot;fWW1&quot;&gt;Через семестр к этому мракобесию ещё предстоит вернуться, чтобы написать к нему графический интерфейс.&lt;/p&gt;
  &lt;p id=&quot;FPEm&quot;&gt;Почему Java? Честно, я бы с радостью туда вообще не лез, но предмет &amp;quot;Платформонезависимое программирование &amp;quot; к этому обязывал.&lt;/p&gt;
  &lt;h2 id=&quot;x0bK&quot;&gt;Отвратительная документация&lt;/h2&gt;
  &lt;p id=&quot;RRoH&quot;&gt;Для начала — маленькое лирическое отступление: что я считаю &lt;strong&gt;хорошей &lt;/strong&gt;технической документацией?&lt;/p&gt;
  &lt;ul id=&quot;iEpq&quot;&gt;
    &lt;li id=&quot;zN2J&quot;&gt;Хорошо структурирована&lt;/li&gt;
    &lt;li id=&quot;2sRt&quot;&gt;Снабжена примерами кода (но не состоит из них полностью)&lt;/li&gt;
    &lt;li id=&quot;mHXj&quot;&gt;Отдельные страницы индексируются поисковиками&lt;/li&gt;
    &lt;li id=&quot;ZLla&quot;&gt;Желательно содержит мануал по старту работы с технологией&lt;/li&gt;
    &lt;li id=&quot;OGWt&quot;&gt;Не сгенерирована из комментариев к коду&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;1etN&quot;&gt;Если брать мир Python, то там отличная документация написана к &lt;a href=&quot;https://docs.djangoproject.com/en/4.1/&quot; target=&quot;_blank&quot;&gt;Django&lt;/a&gt;. Несмотря на объемность фреймворка, всё очень хорошо расписано, отлично гуглится, и даже есть несколько пошаговых инструкций по созданию небольших проектов.&lt;/p&gt;
  &lt;p id=&quot;1Nh6&quot;&gt;Что же мы видим, например у Hibernate-ORM?&lt;/p&gt;
  &lt;p id=&quot;TyyZ&quot;&gt;Во-первых: с её помощью можно настраивать подключение к базе данных и описывать таблицы и связи между ними двумя кардинально разными способами&lt;/p&gt;
  &lt;ol id=&quot;pcu1&quot;&gt;
    &lt;li id=&quot;19gW&quot;&gt;Через XML-файл, в котором вручную описываются все таблицы, типы данных  и связи между таблицами, затем поверх этого пишутся POJO (или &amp;quot;простые старые Java-объекты&amp;quot;). По мне, так этот способ дикость. Как минимум потому, что приходится одну и ту же работу выполнять дважды. По неясной причине документация фокусируется именно на этом способе, фактически игнорируя второй, более &amp;quot;новый&amp;quot; способ (мне кажется забавным употреблять слово &amp;quot;новый&amp;quot;, говоря о Java-мире. Сложилось впечатление, что инновации там происходят раз в 15 лет, но существующий софт совершенно игнорирует их чтобы избежать рефактора)&lt;/li&gt;
    &lt;li id=&quot;n5Lb&quot;&gt;&amp;quot;Новый&amp;quot; же способ заключается в описании моделей через обычные классы, на поля которых навешиваются аннотации, обозначающие обязательность, связи и пр. И никакого XML! Увы, но по этой штуке документации практически нет.&lt;/li&gt;
  &lt;/ol&gt;
  &lt;p id=&quot;tIZT&quot;&gt;И тут возникает один вопрос — какого чёрта одна библиотека реализует настолько разные способы описания моделей? В смысле, почему бы не разбить её на две части?&lt;/p&gt;
  &lt;p id=&quot;UmnT&quot;&gt;Во-вторых документация отвратительно структурирована, а страницы совершенно не гуглятся.&lt;/p&gt;
  &lt;p id=&quot;eKgA&quot;&gt;Не знаю, может я один такой ленивый, но когда я работаю с какой-то технологией, я предпочитаю ничего не запоминать, а по мере необходимости гуглить необходимые части библиотеки. Так вот оказывается в Java-мире это так не работает. Гугля что-то, ты попадаешь на список богом забытых форумов, с вопросами, заданными в начале нулевых, ответ на которые даже со свойственной   Java &lt;s&gt;ленью вычищать технический долг&lt;/s&gt; &amp;quot;обратной совместимостью&amp;quot; попросту не работает. Или вопрос настолько специфичный, что даже относительно современный ответ на него не решит твоей проблемы. Ну а желания и времени вызубривать всю документацию ради проекта, который я сделаю один раз и забуду напрочь, у меня нет. Так что прощай Hibernate.&lt;/p&gt;
  &lt;h2 id=&quot;k8vW&quot;&gt;Нерабочие туториалы&lt;/h2&gt;
  &lt;p id=&quot;N2wy&quot;&gt;В моём мире, когда ты заходишь на сайт библиотеки, генерируешь там из их шаблона свой проект, затем по их же туториалу редактируешь его под себя, он должен хотя бы запуститься, а не валиться с непонятным исключением в духе &amp;quot;какая-то моя кишка не умеет работать с какой-то реализацией кишки моей зависимости&amp;quot;. Причём оно даже не гуглится. Вот тебе и сгенерированные шаблоны...&lt;/p&gt;
  &lt;p id=&quot;R7iq&quot;&gt;Напоминает времена, когда я несколько лет назад пытался изучить Vue.js. Там скрипт инициализации проекта начал кидаться ошибками несовместимости версий в ответ на попытки подключить линтер. В общем Spring Boot тоже пошёл лесом.&lt;/p&gt;
  &lt;h2 id=&quot;aPVk&quot;&gt;Платный софт и пакеты &lt;/h2&gt;
  &lt;p id=&quot;tvxc&quot;&gt;Возвращаясь к Hibernate. Одно время я подумывал о том, чтобы писать модели, используя XML.&lt;/p&gt;
  &lt;p id=&quot;EGyY&quot;&gt;Естественно, поскольку я человек &lt;s&gt;крайне ленивый&lt;/s&gt; стремящийся оптимизировать противные задачи, я стал искать хоть какие-то инструменты, которые избавят меня от необходимости писать XML вручную. Что поделать, меня на физическом уровне отворачивает от этого формата. И почему в Java мире его так любят? Весит относительно много, читается тяжело, парсить — больно.&lt;/p&gt;
  &lt;p id=&quot;3PSh&quot;&gt;Подошло бы что угодно: GUI, DSL...&lt;/p&gt;
  &lt;p id=&quot;po6a&quot;&gt;Как-то я наткнулся на одну программу, которая могла бы решить мою проблему (названия не помню, давно это было): С помощью графического интерфейса нужно было рисовать таблицы, соединять их линиями, чтобы на выходе получить Hibernate-совместимый XML файл. Окей, кажется это то, что нужно. Но вместо кнопки &amp;quot;Скачать&amp;quot; вижу лишь &amp;quot;Купить&amp;quot;. Какое разочарование... Не, я конечно не против платить за лицензионный софт, но не такой, который мне понадобится буквально один раз и который стоит две сотни долларов.&lt;/p&gt;
  &lt;p id=&quot;c39g&quot;&gt;Может есть какие-то альтернативы Hibernate? Нахожу парочку каких-то решений с закрытым исходным кодом и опять же платных. Серьёзно? Кажется я слишком привык к Python-миру, который держится на куче энтузиастов, которые выкладывают свои, бесспорно, огромные труды, в открытый доступ. Мде, Даня, добро пожаловать в суровый мир Enterprise-разработки.&lt;/p&gt;
  &lt;h2 id=&quot;GIKU&quot;&gt;Надо что-то делать&lt;/h2&gt;
  &lt;p id=&quot;Vtre&quot;&gt;Пока я экспериментировал, пытаясь сделать &amp;quot;хорошо&amp;quot; неумолимо приближался дедлайн. Поэтому пришлось засунуть перфекционизм подальше и писать как придётся. В конечном счёте в основу лёг Spark, какой-то малоизвестный и минималистичный веб-фреймворк, достаточно неплохая документация которого поместилась на одну страницу. Ну а доступ к базе данных организовали с помощью JDBC, прямо в коде бросив сырые SQL-запросы. Умеем, практикуем!&lt;/p&gt;
  &lt;p id=&quot;zCOm&quot;&gt;За нехваткой времени в итоге многие эндпоинты были брошены возвращать 501 Not Implemented. Только т-с-с-с!&lt;/p&gt;
  &lt;p id=&quot;MM52&quot;&gt;Впрочем какая разница, если в итоге проект был сдан на &amp;quot;отлично&amp;quot;?&lt;/p&gt;
  &lt;h2 id=&quot;ggE6&quot;&gt;Вместо вывода&lt;/h2&gt;
  &lt;p id=&quot;mRy7&quot;&gt;За время разработки я понял одно: Java — это огромная кроличья нора с кучей легаси, в которую невозможно нырнуть с места. Понадобятся годы кропотливого ковыряния в паршивой документации, чтобы получить цельную картину хорошего приложения.&lt;/p&gt;
  &lt;p id=&quot;YOqP&quot;&gt;Гудбай Java, нам с тобой не по пути. А я погнал учить C# для следущего гигантского проекта.&lt;/p&gt;

</content></entry><entry><id>yadevblog:react</id><link rel="alternate" type="text/html" href="https://teletype.in/@yadevblog/react?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=yadevblog"></link><title>React</title><published>2022-08-13T11:55:00.808Z</published><updated>2022-08-13T11:55:00.808Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img4.teletype.in/files/73/8d/738d1854-a9d9-4168-92ac-c69e223fdac3.png"></media:thumbnail><category term="it" label="it"></category><summary type="html">&lt;img src=&quot;https://i1.sndcdn.com/artworks-000526769397-b2oq7x-t500x500.jpg&quot;&gt;Несколько лет назад я зарёкся лезть во фронтенд. И где я оказался?</summary><content type="html">
  &lt;p id=&quot;Gt9M&quot;&gt;Несколько лет назад я зарёкся лезть во фронтенд. И где я оказался?&lt;/p&gt;
  &lt;figure id=&quot;fcu2&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://i1.sndcdn.com/artworks-000526769397-b2oq7x-t500x500.jpg&quot; width=&quot;500&quot; /&gt;
    &lt;figcaption&gt;Картинка со звуком&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;B6DT&quot;&gt;Всему причиной моё стремление пробовать разные штуки.&lt;/p&gt;
  &lt;p id=&quot;xglE&quot;&gt;Я давно хотел поднять свою экосистему сайтов с портфолио, резюме, возможно даже Git-хранилище (хотя не уверен, зачем оно может быть надо)&lt;/p&gt;
  &lt;p id=&quot;1WFC&quot;&gt;Для начала я попробовал вспомнить молодость и написать что-то простенькое без всяких ваших новомодных фреймворков. Это было больно и очень неудобно.&lt;/p&gt;
  &lt;p id=&quot;3pAg&quot;&gt;Тогда я пошёл в ютуб и наткнулся на очень ламповый &lt;a href=&quot;https://www.youtube.com/watch?v=bSMZgXzC9AA&quot; target=&quot;_blank&quot;&gt;ролик&lt;/a&gt; одного японца, который, используя только neovim написал красивый сайт-портфолио на react и next.js. Я взял его за основу и написал почти такой же сайт.&lt;/p&gt;
  &lt;p id=&quot;egqz&quot;&gt;Попытался выгрузить его на Github Pages, где хостился мой предыдущий сайт, оказалось, что для анимации рендерятся бэкендом и на старом хостинге отображалась только шапка с кривой темой.&lt;/p&gt;
  &lt;p id=&quot;Fse4&quot;&gt;Что ж. Значит пришло время строить экосистему. Я купил домен и прикрутил его к Vercel, хостингу от создателей Next.js. Что самое крутое, это интеграция с гитхабом — Vercel автоматически деплоит сайт на привязанные домены на каждый пуш в мастер.&lt;/p&gt;
  &lt;figure id=&quot;Vyy1&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/07/49/0749cb9b-fc80-4deb-9329-26c77f102bce.png&quot; width=&quot;1280&quot; /&gt;
    &lt;figcaption&gt;Вот так &lt;a href=&quot;https://dadyarri.ru&quot; target=&quot;_blank&quot;&gt;сайт&lt;/a&gt; выглядит сейчас&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;YPNw&quot;&gt;Что я могу сказать по итогу трёх недель колупания в современном фронтенде. Честно говоря, впечатления очень смешанные. Местами удобно, местами неприятно.&lt;/p&gt;
  &lt;h3 id=&quot;i73L&quot;&gt;Плюсы&lt;/h3&gt;
  &lt;ul id=&quot;kArw&quot;&gt;
    &lt;li id=&quot;vVNx&quot;&gt;Низкий порог входа (я, практически не зная Javascript, начал понимать, что списываю из видео за два вечера)&lt;/li&gt;
    &lt;li id=&quot;VdCv&quot;&gt;Live Reload (можно просто запустить сервер и писать код, не отвлекаясь на перекомпиляцию)&lt;/li&gt;
    &lt;li id=&quot;bHhL&quot;&gt;Next.js берёт на себя весь геморрой со сборкой и запуском проекта, оптимизацией бандлов (даже вебпак работает под капотом, его конфиг не пишется напрямую)&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;qLgE&quot;&gt;Пока я это всё писал, я осознал, что объективных-то минусов и нет. Думал, что разосру тут весь фронт, а оказалось, что всё это пустые придирки. &lt;strong&gt;Что ж, я поражён, господа.&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;pQGU&quot;&gt;Разве что сюда можно засунуть оверинжиниринг, когда у любого пакета есть туева хуча зависимостей, что приводит к dependency hell, когда разные зависимости зависят от разных версий одного и того же пакета и в итоге всё ломается, но это уже достаточно специфичная штука, которая происходит не всегда.&lt;/p&gt;

</content></entry><entry><id>yadevblog:nim</id><link rel="alternate" type="text/html" href="https://teletype.in/@yadevblog/nim?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=yadevblog"></link><title>Nim</title><published>2022-08-07T22:12:21.737Z</published><updated>2022-08-07T22:12:21.737Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img2.teletype.in/files/d5/15/d5152d4d-01d0-49f8-a3a9-275fcb9b1432.png"></media:thumbnail><category term="it" label="it"></category><summary type="html">Nim — компилируемый, статически типизированный язык программирования… Ай, к чёрту википедию.</summary><content type="html">
  &lt;p id=&quot;FKja&quot;&gt;Nim — компилируемый, статически типизированный язык программирования… Ай, к чёрту википедию.&lt;/p&gt;
  &lt;p id=&quot;TeRW&quot;&gt;Наткнулся я на него совершенно случайно, когда читал статью на realpython про ускорение кода на Python через си-инклюды. Ужаснулся, закрыл статью, ушёл искать что-то еще.&lt;/p&gt;
  &lt;p id=&quot;tzhS&quot;&gt;В итоге наткнулся на эту &lt;a href=&quot;https://habr.com/ru/company/otus/blog/543332/&quot; target=&quot;_blank&quot;&gt;статью&lt;/a&gt;, в которой рассказывается про nimporter в связке с nimpy. Nimpy позволяет nimporter импортировать нимовские модули прямо в питонячий код и работать с ними как обычно. Ну не магия ли это?&lt;/p&gt;
  &lt;p id=&quot;nt2r&quot;&gt;Самый кайф в том, что nim-код компилируется в статический бинарь, которому для запуска не нужны никакие зависимости, скорость на уровне Си, а синтаксис очень простой и сильно напоминает Python (и никаких указателей).&lt;/p&gt;
  &lt;p id=&quot;uZp9&quot;&gt;Правда этим чудо-костылём я так и не воспользовался, потому что не нашёл, чтобы такого в моих микро-проектах можно было бы ускорить и благополучно про nim на какое-то время забыл.&lt;/p&gt;
  &lt;p id=&quot;MMIF&quot;&gt;И тут недавно я переустановил себе Linux (недавно написал статью &lt;a href=&quot;https://blog.dadyarri.ru/dotfiles&quot; target=&quot;_blank&quot;&gt;с обзором конфигов&lt;/a&gt;) и как это у меня обычно бывает — начал смотреть “а чего бы такого автоматизировать?”. Листаю свой конфиг шелла и вижу громозкое описание переменной PATH (не нравится мне идея туеву хучу бинарей пихать в системные каталоги, поэтому кастомных путей у меня там полно).&lt;/p&gt;
  &lt;p id=&quot;tNZZ&quot;&gt;Есть идея — запихать все пути из конфига шелла в отдельный файлик и оттуда его считывать в конфиг.&lt;/p&gt;
  &lt;p id=&quot;pMeL&quot;&gt;Что обычно берут люди, когда нужно что-то заскриптовать в линуксе? Правильно — Bash. Bash это что? Попаболь. Возиться с ним не очень-то хочется, ибо синтаксис неочевидный, дебажить проблематично, один гемор короче.&lt;/p&gt;
  &lt;p id=&quot;2qc2&quot;&gt;Попробовал сначала решить эту задачу на Python. Получилось чертовски медленно, а в конфиге шелла скорость очень важна, ведь пока не отработает скрипт конфига, промпт не отрисуется.&lt;/p&gt;
  &lt;p id=&quot;PINZ&quot;&gt;Хорошо, питон отбрасываем. Что у нас еще есть? Раст? Как из пушки по воробьям стрелять… Нужно что-то простое и быстрое. Вспоминаю про Nim. За пару часов написал маленький скриптик. Получилось красиво, быстро, удобно.&lt;/p&gt;
  &lt;p id=&quot;Mnyc&quot;&gt;Теперь вместо длиннющего &lt;code&gt;export PATH=&amp;quot;$PATH:/home/dadyarri/.nimble/bin:/home/dadyarri/.npm/packages/bin:/home/dadyarri/bin:/home/dadyarri/scripts:/home/dadyarri/.local/bin&amp;quot;&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;QzWt&quot;&gt;стало &lt;code&gt;export PATH=&amp;quot;$PATH:$(/home/dadyarri/bin/getpath)&amp;quot;&lt;/code&gt;&lt;/p&gt;
  &lt;p id=&quot;fKc5&quot;&gt;Ну не супер ли?&lt;/p&gt;
  &lt;p id=&quot;jzxc&quot;&gt;&lt;strong&gt;Плюсы:&lt;/strong&gt;&lt;/p&gt;
  &lt;ul id=&quot;4yo5&quot;&gt;
    &lt;li id=&quot;8YHY&quot;&gt;Красивый, питоноподобный синтаксис;&lt;/li&gt;
    &lt;li id=&quot;l1Ea&quot;&gt;Статическая типизация;&lt;/li&gt;
    &lt;li id=&quot;TGXo&quot;&gt;Высокая скорость запуска;&lt;/li&gt;
    &lt;li id=&quot;vUKu&quot;&gt;Для запуска не нужны зависимости.&lt;/li&gt;
    &lt;li id=&quot;olvD&quot;&gt;Удобный менеджер пакетов nimble (своего репозитория у nim нет, используется git)&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;oP1j&quot;&gt;&lt;strong&gt;Минусы:&lt;/strong&gt;&lt;/p&gt;
  &lt;ul id=&quot;LjcV&quot;&gt;
    &lt;li id=&quot;strq&quot;&gt;Бардак в стандартной библиотеке (&lt;code&gt;std/parsexml&lt;/code&gt;, &lt;code&gt;std/xmlparser&lt;/code&gt;, &lt;code&gt;std/xmltree&lt;/code&gt;, например);&lt;/li&gt;
    &lt;li id=&quot;wkQQ&quot;&gt;Опциональные круглые скобки при вызове фукнкций (это конечно больше придирка);&lt;/li&gt;
    &lt;li id=&quot;KRXj&quot;&gt;Очень странное ООП. Допустим есть &lt;code&gt;let file: File = open(&amp;quot;somefile.txt&amp;quot;, fileMode.FmWrite)&lt;/code&gt;. Можно прочесть его содержимое, вызвав (в моём понимании, как в прочих языках) метод объекта: &lt;code&gt;file.write(&amp;quot;some text&amp;quot;)&lt;/code&gt;, а можно, вызвав функцию: &lt;code&gt;write(file, &amp;quot;some text&amp;quot;)&lt;/code&gt;;&lt;/li&gt;
    &lt;li id=&quot;4xTt&quot;&gt;Паршивая документация, которая обычно сводится к автоматически сгенерированным из кода спискам методов, перечислений и прочего, причём даже не у всех элементов есть описание;&lt;/li&gt;
    &lt;li id=&quot;1T73&quot;&gt;Местами идиотские сообщения об ошибках, которые вообще не дают никаких подсказок о факапе и единственный способ их решить — лезть в код или в ишью.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;75P5&quot;&gt;Энивей, &lt;a href=&quot;https://github.com/nim-lang/Nim&quot; target=&quot;_blank&quot;&gt;nim&lt;/a&gt; классный, есть куда развиваться. Не думаю, что он может стать чем-то серьёзным вроде дотнета (по крайней мере не скоро), но как альтернатива башу для скриптинга вполне себе неплохо.&lt;/p&gt;

</content></entry><entry><id>yadevblog:dotfiles</id><link rel="alternate" type="text/html" href="https://teletype.in/@yadevblog/dotfiles?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=yadevblog"></link><title>Мои настройки Linux</title><published>2022-08-04T09:08:52.495Z</published><updated>2022-08-04T09:09:32.423Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img4.teletype.in/files/33/73/3373c7f1-5ed0-4d83-a7d1-4ec21ca0342b.png"></media:thumbnail><category term="linux" label="linux"></category><summary type="html">&lt;img src=&quot;https://img1.teletype.in/files/41/44/4144c77a-001b-4344-ba8b-a38f3a684a65.png&quot;&gt;Я в очередной раз сменил дистрибутив (на этот раз Fedora с KDE) и пересобрал с нуля конфигурационные файлы многих программ. Мне нравятся минималистичные интерфейсы, но жертвовать удобством ради этого я не хочу. О том, что в итоге получилось — под катом.</summary><content type="html">
  &lt;h2 id=&quot;gord&quot;&gt;Введение&lt;/h2&gt;
  &lt;p id=&quot;fja0&quot;&gt;Я в очередной раз сменил дистрибутив (на этот раз Fedora с KDE) и пересобрал с нуля конфигурационные файлы многих программ. Мне нравятся минималистичные интерфейсы, но жертвовать удобством ради этого я не хочу. О том, что в итоге получилось — под катом.&lt;/p&gt;
  &lt;h2 id=&quot;P9XV&quot;&gt;Шелл&lt;/h2&gt;
  &lt;p id=&quot;5Ect&quot;&gt;В разное время я несколько раз прыгал от zsh к fish и обратно, но в текущей итерации вернулся на &lt;a href=&quot;https://www.zsh.org/&quot; target=&quot;_blank&quot;&gt;z-shell&lt;/a&gt;. Получилась ультимативная штука, которой очень удобно пользоваться. Вот такой набор плагинов я накидал:&lt;/p&gt;
  &lt;ul id=&quot;gE2A&quot;&gt;
    &lt;li id=&quot;SRlE&quot;&gt;&lt;a href=&quot;https://github.com/b4b4r07/enhancd&quot; target=&quot;_blank&quot;&gt;enhancd&lt;/a&gt; — Плагин, который запоминает последние посещённые директории и позволяет переходить по ним по ключевым словам или с помощью &lt;a href=&quot;https://github.com/junegunn/fzf&quot; target=&quot;_blank&quot;&gt;fzf&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;
  &lt;figure id=&quot;acuD&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/41/44/4144c77a-001b-4344-ba8b-a38f3a684a65.png&quot; width=&quot;486&quot; /&gt;
  &lt;/figure&gt;
  &lt;ul id=&quot;D1BP&quot;&gt;
    &lt;li id=&quot;q1gs&quot;&gt;&lt;a href=&quot;https://github.com/zdharma-continuum/fast-syntax-highlighting&quot; target=&quot;_blank&quot;&gt;fast-syntax-highlighting&lt;/a&gt; — Быстрая подсветка синтаксиса. Альтернатива к решению от zsh-users, которым я пользовался долгое время, но лишённый его проблем. Уж не знаю, насколько быстрее, но точно красивее.&lt;/li&gt;
  &lt;/ul&gt;
  &lt;figure id=&quot;YWAF&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/e8/5d/e85dffdb-3b44-4570-b7d0-ad419d823095.png&quot; width=&quot;889&quot; /&gt;
  &lt;/figure&gt;
  &lt;ul id=&quot;1BwP&quot;&gt;
    &lt;li id=&quot;CcQD&quot;&gt;&lt;a href=&quot;https://github.com/zsh-users/zsh-history-substring-search&quot; target=&quot;_blank&quot;&gt;zsh-history-substring-search&lt;/a&gt; — Плагин который добавляет виджет поиска по подстроке в истории команд. По факту это работает просто — вводится часть команды и стрелками вверх-вниз выбирается нужная команда из истории&lt;/li&gt;
  &lt;/ul&gt;
  &lt;figure id=&quot;WNoj&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/06/3b/063bad51-7833-4dbc-afd7-3efe934898cb.png&quot; width=&quot;282&quot; /&gt;
  &lt;/figure&gt;
  &lt;ul id=&quot;yjf5&quot;&gt;
    &lt;li id=&quot;bBBy&quot;&gt;&lt;a href=&quot;https://github.com/zsh-users/zsh-autosuggestions&quot; target=&quot;_blank&quot;&gt;zsh-autosuggestions&lt;/a&gt; — Плагин, который генерирует подсказки автодополнения, основываясь на истории запущенных команд&lt;/li&gt;
  &lt;/ul&gt;
  &lt;figure id=&quot;T57X&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/28/c0/28c0b2ef-05d7-47cc-9c9a-b39dfa18b0c2.png&quot; width=&quot;221&quot; /&gt;
  &lt;/figure&gt;
  &lt;ul id=&quot;kvuz&quot;&gt;
    &lt;li id=&quot;MqQe&quot;&gt;&lt;a href=&quot;https://github.com/romkatv/powerlevel10k&quot; target=&quot;_blank&quot;&gt;powerlevel10k&lt;/a&gt; — красивая тема с множеством настроек и подключаемых виджетов&lt;/li&gt;
  &lt;/ul&gt;
  &lt;figure id=&quot;n5lQ&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/de/dd/dedd2454-3795-4b97-9b64-4238d9d5972f.png&quot; width=&quot;1116&quot; /&gt;
    &lt;figcaption&gt;Так выглядит шелл в папке с моим сайтом-портфолио, написанном на Javascript — текущая папка, ветка в Git, версия node.js и количество активных задач в консольном таск-менеджере&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;ul id=&quot;hvUa&quot;&gt;
    &lt;li id=&quot;plFv&quot;&gt;&lt;a href=&quot;https://github.com/Aloxaf/fzf-tab&quot; target=&quot;_blank&quot;&gt;fzf-tab&lt;/a&gt; — Плагин, заменяющий стандартный механизм отрисовки автоподсказок на fzf. Работает с огромным множеством команд, и даже может показать описание флагов некоторых команд&lt;/li&gt;
  &lt;/ul&gt;
  &lt;figure id=&quot;P82F&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/88/0f/880f3a10-d8a6-4b02-8fc4-50841fec6256.png&quot; width=&quot;336&quot; /&gt;
    &lt;figcaption&gt;Подскажет директории, куда можно перейти, если нажать &amp;lt;Tab&amp;gt; после команды &amp;#x60;cd&amp;#x60;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;figure id=&quot;kl37&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/d4/ba/d4bae62e-fc84-4c0f-a562-abf995e0b89f.png&quot; width=&quot;1109&quot; /&gt;
    &lt;figcaption&gt;Подскажет флаги команды &amp;#x60;git branch&amp;#x60;, если нажать &amp;lt;Tab&amp;gt; после команды и знака &amp;quot;-&amp;quot;&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;ul id=&quot;aaul&quot;&gt;
    &lt;li id=&quot;a0HV&quot;&gt;&lt;a href=&quot;https://github.com/hlissner/zsh-autopair&quot; target=&quot;_blank&quot;&gt;autopair&lt;/a&gt; — Плагин, который автоматически закрывает парные скобки при вводе открывающей. Прямо как взрослые IDE!&lt;/li&gt;
    &lt;li id=&quot;R8FA&quot;&gt;&lt;a href=&quot;https://github.com/joshskidmore/zsh-fzf-history-search&quot; target=&quot;_blank&quot;&gt;fzf-history-search&lt;/a&gt; — И еще один плагин для работы с историей. Этот открывает fzf со историей запуска команд. Благодаря ему можно быстро по неточному поиску найти нужную команду, вместо того, чтобы вечность тыкать стрелку вверх&lt;/li&gt;
  &lt;/ul&gt;
  &lt;figure id=&quot;xw9W&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/9f/b3/9fb35f3b-154a-4433-9a82-ea7b2de9fa7c.jpeg&quot; width=&quot;500&quot; /&gt;
  &lt;/figure&gt;
  &lt;figure id=&quot;yH4t&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/95/73/95737b70-c4cc-413e-9231-cc8328a40d17.png&quot; width=&quot;1121&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;F2mP&quot;&gt;Управляет всем этим великолепием &lt;a href=&quot;https://github.com/rossmacarthur/sheldon&quot; target=&quot;_blank&quot;&gt;Sheldon&lt;/a&gt; — универсальный менеджер плагинов, который работает как с bash, так и с zsh. Кстати, написан на Rust.&lt;/p&gt;
  &lt;h2 id=&quot;3v5n&quot;&gt;Менеджер конфигов&lt;/h2&gt;
  &lt;p id=&quot;ksVU&quot;&gt;Все конфиги у меня лежат на &lt;a href=&quot;https://github.com/dadyarri/dotfiles&quot; target=&quot;_blank&quot;&gt;гитхабе&lt;/a&gt;. Ими нужно как-то управлять, и чтобы не держать в домашней папке гитовый репозиторий (неудобно, есть риск добавить лишнее), я использую &lt;a href=&quot;https://chezmoi.io&quot; target=&quot;_blank&quot;&gt;chezmoi&lt;/a&gt;. Он даёт возможность в одну команду без установки сторонних программ быстро перенести все конфиги на новую машину (потому что распространяется маленьким бинарником, который легко скачать), запустить все скрипты и получить готовую систему.&lt;/p&gt;
  &lt;pre id=&quot;uH24&quot;&gt;sh -c &amp;quot;$(curl -fsLS https://chezmoi.io/get)&amp;quot; -- init --apply dadyarri&lt;/pre&gt;
  &lt;p id=&quot;8xCb&quot;&gt;(Сработает только на федоре, потому что там есть автоматически запускаемый скрипт, который устанавливает кучу пакетов через dnf)&lt;/p&gt;
  &lt;h2 id=&quot;npJ9&quot;&gt;Скрипты&lt;/h2&gt;
  &lt;p id=&quot;lGk5&quot;&gt;Последнее время пишу разные скриптики разной степени замороченности, чтобы упростить себе жизнь (и заодно не скиснуть от безделия).&lt;/p&gt;
  &lt;h3 id=&quot;crun&quot;&gt;ex&lt;/h3&gt;
  &lt;p id=&quot;Cm8Q&quot;&gt;Скрипт, изначально стыренный с реддита, но немного докрученный для повышения удобства. Автоматически определяет тип архива и распаковывывает его нужной программой. Опционально можно указать путь назначения, и, если программа поддерживает такое колдунство содержимое архива попадёт в указанную папку.&lt;/p&gt;
  &lt;figure id=&quot;y6l2&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/16/58/165860c7-3173-4c20-825e-2c71d6930f3e.png&quot; width=&quot;999&quot; /&gt;
  &lt;/figure&gt;
  &lt;h3 id=&quot;SzMo&quot;&gt;fdups&lt;/h3&gt;
  &lt;p id=&quot;31nq&quot;&gt;Самописный скрипт, ищущий дубликаты файлов в указанной директории (имя не имеет значения, поиск ведётся по хеш-сумме)&lt;/p&gt;
  &lt;figure id=&quot;W27W&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/fd/f8/fdf84ef1-de5f-47fa-9b00-d061ef62dab2.png&quot; width=&quot;899&quot; /&gt;
  &lt;/figure&gt;
  &lt;h3 id=&quot;uvyA&quot;&gt;upd&lt;/h3&gt;
  &lt;p id=&quot;8uyH&quot;&gt;Самописный скрипт, автоматизирующий обновление системы и всевозможных пакетов из кучи разных источников (сейчас доступен dnf, flatpak, neovim, мои бинарники и глобальные npm пакеты)&lt;/p&gt;
  &lt;figure id=&quot;9wnX&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/91/15/91156543-77c1-4ef0-b149-7496abd12557.png&quot; width=&quot;1265&quot; /&gt;
  &lt;/figure&gt;
  &lt;h3 id=&quot;OBU4&quot;&gt;addpath&lt;/h3&gt;
  &lt;p id=&quot;RO07&quot;&gt;Переменную &lt;code&gt;$PATH&lt;/code&gt; я не храню статически в конфиге, а держу все нужные мне пути в отдельном файлике и специальным скриптом читаю оттуда и передаю в &lt;code&gt;.zshrc&lt;/code&gt;. Этот скрипт (в отличие от всех предыдущих, написанный на nim) записывает новый путь в указанный файл (примерно как &lt;code&gt;fish_add_path&lt;/code&gt; только текущий шел не изменяется).&lt;/p&gt;
  &lt;figure id=&quot;S34b&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/cf/3c/cf3cb98b-d3ba-4b4b-ad7d-ceec680ae645.png&quot; width=&quot;893&quot; /&gt;
  &lt;/figure&gt;
  &lt;h3 id=&quot;20Dy&quot;&gt;getpath&lt;/h3&gt;
  &lt;p id=&quot;70rc&quot;&gt;Тот скрипт, который собирает все пути из файла &lt;code&gt;.paths&lt;/code&gt; и печатает их в формате, необходимом для &lt;code&gt;$PATH&lt;/code&gt;, заодно проверяя, существует ли путь, чтобы лишнее не копилось в переменных окружения.&lt;/p&gt;
  &lt;figure id=&quot;2jRS&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/25/b0/25b0554d-a75d-4bb4-bb4a-716f778ea14b.png&quot; width=&quot;1136&quot; /&gt;
  &lt;/figure&gt;
  &lt;h3 id=&quot;7MEi&quot;&gt;dotversion&lt;/h3&gt;
  &lt;p id=&quot;09LX&quot;&gt;Еще один самописный скрипт на nim. Я тут начал активно переходить на .NET и оказалось, что у них нет встроенного инструмента для управления версиями приложений (типа &lt;code&gt;npm version&lt;/code&gt;). В итоге я потратил пару вечеров и написал свой такой инструмент, в чём-то даже превосходящий упомянутый джаваскриптовый.&lt;/p&gt;
  &lt;p id=&quot;CZb7&quot;&gt;В основе находится &lt;a href=&quot;https://semver.org&quot; target=&quot;_blank&quot;&gt;semver&lt;/a&gt;, поддерживается генерация мажор, минор, патч, альфа, бета, релиз-кандидат, альфа-{мажор, минор, патч}, бета-{мажор, минор, патч}, релиз-кандидат-{мажор, минор, патч} версий. Приложение автоматически найдёт файл csproj возьмет оттуда версию и запишет новую (есть флаг, запускающий вычисление новой версии, без перезаписи файла)&lt;/p&gt;
  &lt;figure id=&quot;Hps2&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/01/17/01177123-e79b-4645-9c32-b5dc6692ba5a.png&quot; width=&quot;1132&quot; /&gt;
  &lt;/figure&gt;
  &lt;figure id=&quot;C8TJ&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/fd/1c/fd1c1a7e-3a05-43a4-9b06-309b4d5b580b.png&quot; width=&quot;352&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;ZT3A&quot;&gt;&lt;strong&gt;Ну, вроде бы и всё. Stay in touch!&lt;/strong&gt;&lt;/p&gt;

</content></entry><entry><id>yadevblog:control</id><link rel="alternate" type="text/html" href="https://teletype.in/@yadevblog/control?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=yadevblog"></link><title>Control</title><published>2022-03-03T19:02:44.568Z</published><updated>2022-07-05T13:58:42.178Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img4.teletype.in/files/72/2d/722da094-7bde-4b85-82c5-8c5a74ae80df.png"></media:thumbnail><category term="games" label="games"></category><summary type="html">Ну, раз у меня теперь есть мощности для запуска нормальных игр, значит теперь будут появляться обзоры всякого разного. Я не считаю себя игрожуром и всего лишь высказываю собственное мнение. Поэтому считаю, что имею право что-то говорить не допройдя игру до конца.</summary><content type="html">
  &lt;p id=&quot;EvVR&quot;&gt;Ну, раз у меня теперь есть мощности для запуска нормальных игр, значит теперь будут появляться обзоры всякого разного. Я не считаю себя игрожуром и всего лишь высказываю собственное мнение. Поэтому считаю, что имею право что-то говорить не допройдя игру до конца.&lt;/p&gt;
  &lt;p id=&quot;fynm&quot;&gt;Ахтунг, много текста и нет картинок, потому что пишу я это, когда удалил игру, потому что она мне осточертела.&lt;/p&gt;
  &lt;p id=&quot;oYd3&quot;&gt;Итак, Control. Шутер с элементами RPG от третьего лица, выпущенный несколько лет назад финской Remedy Entertainment (та, что создала Alan Wake и еще кучу неплохих игр).&lt;/p&gt;
  &lt;p id=&quot;vSXp&quot;&gt;Всю игру мы находимся в одном огромном здании, называющимся Федеральное Бюро Контроля или же Старейший Дом. У меня огромные проблемы с ориентированием в играх, и Control не стал исключением. Большое количество связанных помещений, уйма бектрекинга и убогая карта явно не улучшают ситуацию.&lt;/p&gt;
  &lt;p id=&quot;tS5L&quot;&gt;Проблема в навигации заключается в вертикальности уровней, но карта при этом всего одна и разные уровни высоты наложены друг на друга&lt;/p&gt;
  &lt;h2 id=&quot;VWTF&quot;&gt;Сюжет&lt;/h2&gt;
  &lt;p id=&quot;Yy41&quot;&gt;Концепция Старейшего Дома мне сильно напоминает фонд SCP (коллективный сборник статей про разнообразные паранормальные объекты). ФБК тоже изучает различные объекты, которые здесь называются Предметами Силы, по всему зданию разбросаны Исследования с пометками &lt;em&gt;[Данные засекречены].&lt;/em&gt;&lt;/p&gt;
  &lt;p id=&quot;J4Xl&quot;&gt;Главная героиня Джесси Фейден приходит в Фонд искать своего брата, которого много лет назад забрали агенты Фонда и в процессе поиска хоть каких-то признаков живых людей натыкается на труп человека, застреленного из лежащего рядом пистолета, подбирает его, и становится Директором Фонда (да, в самом начале игры).&lt;/p&gt;
  &lt;p id=&quot;CyII&quot;&gt;Джесси находит помощницу доктора Дарлинга, главного дядьки в Секторе Исследнований Эмили Поуп, которая порционно ей рассказывает о том, что произошло в Фонде и почему везде под потолком висят люди.&lt;/p&gt;
  &lt;p id=&quot;xm8x&quot;&gt;Оказывается, здание и людей в нём заразили некие астральные существа (Джесси назвала их Шумами, и всё Бюро, включая людей, которые закрыты в других секторах, начали их так называть), из-за которых люди обретают красное свечение и некоторые спецспособности, вроде метания осколков стен и левитирования.&lt;/p&gt;
  &lt;p id=&quot;UKCn&quot;&gt;Мы пробуем очистить одного человека от заражения, но похоже, что это необратимый процесс и человек просто растворяется в воздухе.&lt;/p&gt;
  &lt;p id=&quot;9KTT&quot;&gt;Наша глобальная задача (помимо поиска брата), кто бы сомневался?, спасти Бюро от нашествия Шумов.&lt;/p&gt;
  &lt;h2 id=&quot;D2C6&quot;&gt;Боёвка и способности&lt;/h2&gt;
  &lt;p id=&quot;gdpc&quot;&gt;Было бы странно отправлять человека с одним лишь пистолетом сражаться с Шумами. Поэтому нам постепенно (как через основные, так и через побочные квесты) выдают новые способности.&lt;/p&gt;
  &lt;p id=&quot;w3pU&quot;&gt;Я прошёл не всю игру (полагаю, что я даже не близко к середине), но уже получил Метание (способность хватать практически любые предметы и кидать их во врагов), Уклонение (быстрый рывок в любую сторону) и Щит (поднятие впереди себя кусков пола, которое даёт некоторую, но не абсолютную защиту героине).&lt;/p&gt;
  &lt;p id=&quot;iIcB&quot;&gt;Использование любой способности тратит энергию, которая автоматически восполняется в периоды неактивности.&lt;/p&gt;
  &lt;p id=&quot;3kRC&quot;&gt;Табельное оружие — пистолет, для которого можно открывать разные формы, благодаря которым он становится дробовиком, автоматом, и еще каким-то типами оружия, которые я еще не успел открыть. “Патроны” к табельному — это, видимо, какая-то энергия, которая тоже пополняется автоматически и ничего по локациям искать не нужно, она просто берётся из воздуха.&lt;/p&gt;
  &lt;p id=&quot;KV62&quot;&gt;Стрельба приятная, казуальная, отдачи нет вообще.&lt;/p&gt;
  &lt;p id=&quot;RUQu&quot;&gt;Оружие модифицируется через Моды, которые выпадают из противников или лежат в заныканных ящиках (их действительно приходится иногда поискать, потому что стоят они не на виду)&lt;/p&gt;
  &lt;h2 id=&quot;8Sqh&quot;&gt;Квесты&lt;/h2&gt;
  &lt;p id=&quot;DxUP&quot;&gt;Их в игре приличное количество, но все они сводятся к одному — сходи в далёкий сектор, перестреляй несколько волн спавнящихся из воздуха Шумов. Иногда нужно запитать какие-нибудь механизмы, с помощью Левитации кинув валяющиеся рядом &amp;quot;батареи&amp;quot; (понятия не имею что это) в предусмотренные слоты.&lt;/p&gt;
  &lt;p id=&quot;i6hD&quot;&gt;Есть еще странная механика &amp;quot;Уведомлений Бюро&amp;quot;. Это квесты, время выполнения которых ограничено. За 20 минут нужно добраться в другой сектор и убить всех врагов. Cмерть сбрасывает такие квесты, а появляются они нечасто и обычно во время замеса, что сильно отвлекает от игрового процесса.&lt;/p&gt;
  &lt;p id=&quot;Mj3I&quot;&gt;Даже побочные квесты здесь очень нудные (те, что выдают неписи по ходу развития сюжета) — собери несколько летающих по дурацкой траектории писем или уничтожь огромный якорь, за которые дают лишь несколько очков прокачки&lt;/p&gt;
  &lt;h2 id=&quot;Q8oL&quot;&gt;Противники&lt;/h2&gt;
  &lt;p id=&quot;mzMR&quot;&gt;Вот чего в игре с избытком, это видов противников. Тупые автоматчики, рейнджеры (вооружены автоматами, но походу у них есть какая-то тактика, и они её придерживаются), летающие, взрывающиеся, кидающие осколки окружения, снайперы и еще уйма видов.&lt;/p&gt;
  &lt;p id=&quot;KOq9&quot;&gt;Это вносило бы неплохое разнообразие, если бы не отвратительная механика спавна. Ты просто проходишь по помещению, которое когда-то уже зачищал и игра тебе в случайном порядке может дать (а может и не дать) волну-другую мобов. Причем количество и их виды тоже рандомное. То есть могут кинуть парочку базовых врагов, с которыми легко разберешься за пару секунд, а могут — жирного пулемётчика и толпу летающих ублюдков, которые наврядли раскидаются так же легко. Но при этом с какой-то вероятностью просто никого не будет. И такая вариативность сильно раздражает.&lt;/p&gt;
  &lt;h2 id=&quot;oMZW&quot;&gt;Сохранения&lt;/h2&gt;
  &lt;p id=&quot;7Yg8&quot;&gt;В игре очень часто загорается иконка автосохранения (даже во время боя), но что  в это время сохраняется — для меня осталось загадкой, потому что смерть возрождает на последнем посещенном &amp;quot;костре&amp;quot; и все мобы, которых ты уничтожил до смерти появляются там же.  &lt;/p&gt;
  &lt;h2 id=&quot;zkqS&quot;&gt;Итог&lt;/h2&gt;
  &lt;p id=&quot;UuAi&quot;&gt;Ух, что-то я разогнался. В общем, Control  — это красивая игра, которую я бросил и никогда не вернусь, о чём вообще не жалею. Визуально — замечательная, музыка &amp;quot;Старых богов Асгарда&amp;quot; — потрясающая, локализация от GameVoice — отличная, но слишком много спорных геймдизайнерских решений, которые меня оттолкнули.&lt;/p&gt;

</content></entry><entry><id>yadevblog:second-mind-ed2</id><link rel="alternate" type="text/html" href="https://teletype.in/@yadevblog/second-mind-ed2?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=yadevblog"></link><title>Второй мозг. Reinvented edition</title><published>2022-01-01T20:51:49.901Z</published><updated>2022-01-01T20:56:10.699Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img4.teletype.in/files/b7/e6/b7e6582e-af7c-473f-b15a-19f457ec7e94.png"></media:thumbnail><summary type="html">&lt;img src=&quot;https://i.ytimg.com/vi/zdpVUvQpgw4/maxresdefault.jpg&quot;&gt;Прошел год с моего прошлого поста о том, как я использую Notion в своем рабочем процессе и с тех пор многое поменялось. Notion сильно изменился, появились классные плюшки, упрощающие автоматизацию workflow. Поменялось и мое окружение в нем. Подробное в меру моей лени описание с кучей скриншотов описание - под катом.  </summary><content type="html">
  &lt;p id=&quot;y4Uu&quot;&gt;Прошел год с моего прошлого поста о том, как я использую Notion в своем рабочем процессе и с тех пор многое поменялось. Notion сильно изменился, появились классные плюшки, упрощающие автоматизацию workflow. Поменялось и мое окружение в нем. Подробное в меру моей лени описание с кучей скриншотов описание - под катом.  &lt;/p&gt;
  &lt;h2 id=&quot;4ln7&quot;&gt;Модификация интерфейса&lt;/h2&gt;
  &lt;p id=&quot;w9yf&quot;&gt;В последнее время я все чаще сижу на винде, ибо огромная куча работ в универе зависит от проприетарного софта (mathcad, visual studio, excel). Раньше я писал, что использую notion-enhancer, но когда я начал активно переезжать на винду, понял, что не так уж оно и необходимо, notion сам по себе неплох. (ага, мне просто лень было его ставить, потому что он зависит от node.js)&lt;/p&gt;
  &lt;p id=&quot;hq0L&quot;&gt;Сегодня я попробовал обновившийся прямо перед новым годом notion-enhancer, и знаете, прям хорошо стало. Если раньше нужно было устанавливать node.js, notion-enhancer через терминал (мне в принципе пофиг, я привык работать в консоли, но представьте на месте меня обычного &lt;s&gt;хомячка&lt;/s&gt; пользователя), то сейчас стало совсем хорошо. Они взяли официальную сборку Notion для Windows, запаковали в нее notion-enhancer со всеми зависимостями и сделали из этого маленький экзешник. Ну не красота ли?&lt;/p&gt;
  &lt;figure id=&quot;nAcF&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://i.ytimg.com/vi/zdpVUvQpgw4/maxresdefault.jpg&quot; width=&quot;1280&quot; /&gt;
    &lt;figcaption&gt;Красота-то какая... Ляпота...&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;figure id=&quot;laiL&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/f2/d4/f2d46c4f-82e8-4d6d-a061-61ef948c7055.png&quot; width=&quot;1243&quot; /&gt;
    &lt;figcaption&gt;Интерфейс нового notion-enhancer&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;y8BV&quot;&gt;Я не буду рассказывать о том, какие твики выбрал (тем более что нынешний список практически не отличается от прошлого), просто дам ссылку на документацию: &lt;a href=&quot;https://notion-enhancer.github.io/&quot; target=&quot;_blank&quot;&gt;https://notion-enhancer.github.io/&lt;/a&gt;&lt;/p&gt;
  &lt;h2 id=&quot;q9vr&quot;&gt;Дашборд&lt;/h2&gt;
  &lt;figure id=&quot;krE8&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/4e/21/4e21b416-8a8d-4802-b165-1e5d14bf63fc.png&quot; width=&quot;785&quot; /&gt;
    &lt;figcaption&gt;Главная воркспейса&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;9Fe8&quot;&gt;На этой странице просто содержатся ссылки на другие странице, тут нет ничего интересного&lt;/p&gt;
  &lt;h2 id=&quot;SmEj&quot;&gt;Учебный дашборд&lt;/h2&gt;
  &lt;h3 id=&quot;6euK&quot;&gt;Ссылки, недели, счётчик&lt;/h3&gt;
  &lt;figure id=&quot;m9MB&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/2b/36/2b366eeb-719e-46af-b28d-ca972b195b4b.png&quot; width=&quot;615&quot; /&gt;
    &lt;figcaption&gt;Учебный дашборд. Тут содержится всякое разное, связанное с универом&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;3XBo&quot;&gt;Слева расположены ссылки на учебные ресурсы и таблица, в которой автоматически вычисляется текущая учебная неделя (какой же у Notion мерзкий синтаксис формул).&lt;/p&gt;
  &lt;figure id=&quot;ZuHg&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/ea/07/ea071f2f-43d3-4082-a55a-69e8b56717e5.png&quot; width=&quot;485&quot; /&gt;
    &lt;figcaption&gt;Формула для вычисления учебной недели. Почему она работает именно так - не спрашивайте, я сам уже не знаю 😂&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;0REg&quot;&gt;Справа - счетчик дней до конца учебы, сделанный с помощью &lt;a href=&quot;https://indify.co&quot; target=&quot;_blank&quot;&gt;https://indify.co&lt;/a&gt; &lt;/p&gt;
  &lt;h3 id=&quot;Scfk&quot;&gt;Информация о семестрах&lt;/h3&gt;
  &lt;figure id=&quot;wzgj&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/99/66/9966ef2c-ffd9-4e3c-8c71-1404a565d22c.png&quot; width=&quot;1000&quot; /&gt;
    &lt;figcaption&gt;Блок со ссылками на информацию о семестрах&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;2N38&quot;&gt;В этом блоке находятся страницы с подробным описанием семестра (об этом ниже) и кнопка &amp;quot;новый год&amp;quot;, по которой можно продублировать страницы&lt;/p&gt;
  &lt;h3 id=&quot;frD4&quot;&gt;Список заданий&lt;/h3&gt;
  &lt;figure id=&quot;DNe6&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/62/cd/62cd65ef-f141-4dee-8cf5-08326d029313.png&quot; width=&quot;1064&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;YdWx&quot;&gt;Слева перечислены актуальные задания (не выполненные, и с дедлайном меньше чем, две недели), справа - календарное представление всех созданных заданий.&lt;/p&gt;
  &lt;h3 id=&quot;6fn1&quot;&gt;Страница семестра&lt;/h3&gt;
  &lt;figure id=&quot;Oz17&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/5f/de/5fdeed0f-f231-43d8-b0a2-cae407c7dcce.png&quot; width=&quot;382&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;frDL&quot;&gt;Вверху страницы дублируется блок со ссылками и учебной неделей (это встроенная функция Notion — Synced block, которая автоматически синхронизирует блоки между разными страницами).&lt;/p&gt;
  &lt;h3 id=&quot;Zf05&quot;&gt;Расписание&lt;/h3&gt;
  &lt;figure id=&quot;CZmh&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/17/ea/17ea933c-15f4-45d0-8db3-30019334e407.png&quot; width=&quot;1275&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;l9c4&quot;&gt;Блок с расписанием (формируется вручную и не меняется в зависимости от учебной недели). Внизу под каждой колонкой находится кнопка &amp;quot;Добавить пару&amp;quot;, при нажатии на которую создается шаблон пары&lt;/p&gt;
  &lt;figure id=&quot;BJ36&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/ef/67/ef672628-adbd-4a11-99f6-33e6606f98f9.png&quot; width=&quot;197&quot; /&gt;
    &lt;figcaption&gt;Шаблон пары&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;h3 id=&quot;EqMh&quot;&gt;Предметы&lt;/h3&gt;
  &lt;figure id=&quot;3vok&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/d9/93/d9932276-a765-4b81-8ff3-8713ecff64e4.png&quot; width=&quot;566&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;jBGF&quot;&gt;Таблица со списком предметов в семестре. Кроме того здесь отображается количество заданий, которые осталось сделать по каждому предмету&lt;/p&gt;
  &lt;figure id=&quot;H1NP&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/fc/b5/fcb5bed1-aa4d-448c-9e4b-02719a42fd8e.png&quot; width=&quot;730&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;Bh7l&quot;&gt;Есть блок с возможностью добавить лекции по предметам (он пустой, потому что мне лень таскать ноут в универ)&lt;/p&gt;
  &lt;figure id=&quot;AgKW&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/5e/f5/5ef56057-5c36-4e1c-8ada-04e851f82dcc.png&quot; width=&quot;1319&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;wwev&quot;&gt;И блок, в который можно складировать полезные ссылки по семестру&lt;/p&gt;
  &lt;h2 id=&quot;brJ2&quot;&gt;Предмет&lt;/h2&gt;
  &lt;figure id=&quot;mS14&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/77/cc/77ccd9a4-4642-49ad-add5-f262ad931d15.png&quot; width=&quot;1312&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;PPzH&quot;&gt;Вверху страницы с предметом указаны контакты преподавателя и вставлен документ с учебным планом по предмету&lt;/p&gt;
  &lt;figure id=&quot;k01m&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/fb/89/fb89a31e-588e-483e-b03c-234e0b152cc9.png&quot; width=&quot;1294&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;rAaF&quot;&gt;Затем список заданий по этому предмету и таблица с лекциями.&lt;/p&gt;
  &lt;h2 id=&quot;L4p6&quot;&gt;Список фильмов/сериалов&lt;/h2&gt;
  &lt;p id=&quot;WJ9y&quot;&gt;Этот шаблон я нашел на реддите (потрясающая штука для поиска &lt;s&gt;чего бы такого прихватизировать&lt;/s&gt; вдохновения). &lt;a href=&quot;https://www.reddit.com/user/flxp49/&quot; target=&quot;_blank&quot;&gt;u/flxp49&lt;/a&gt; написал приложение, которое подключается к вашей базе данных, и автоматически подхватывает всю информацию о фильме/сериале из TMDB. Работает быстро, доступно бесплатно. Один минус - TMDB - англоязычный ресурс и поэтому искать иностранные фильмы на русском не получится, хотя русские фильмы по оригинальному названию находит и вставляет английское. В целом это не мешает использованию, так что и минусом назвать это не получается.&lt;/p&gt;
  &lt;figure id=&quot;RXqs&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img3.teletype.in/files/e7/ff/e7ff82dc-95e9-4e9d-a64e-16d7ffdae69a.png&quot; width=&quot;1309&quot; /&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;25KT&quot;&gt;Все, что нужно сделать для того, чтобы получить информацию о фильме — просто написать его название и поставить точку с запятой. Интеграция автоматически подгрузит первый подходящий релиз и заполнит все поля. Магия!&lt;/p&gt;
  &lt;p id=&quot;5IjI&quot;&gt;Конечно, есть фильтры, позволяющие указать год, выбрать между сериалом или фильмом и пр. Также можно искать по iMDB id.&lt;/p&gt;
  &lt;h2 id=&quot;sGYb&quot;&gt;Итог&lt;/h2&gt;
  &lt;p id=&quot;tk3x&quot;&gt;Это был краткий обзор моего рабочего процесса в Notion, надеюсь он поможет вам вдохновиться и сделать что-то свое. Думаю, у меня дойдут руки и я сделаю шаблон из своего учебного дашборда и расскажу о нём на реддите, но это уже совсем другая история...&lt;/p&gt;

</content></entry><entry><id>yadevblog:nov21-news</id><link rel="alternate" type="text/html" href="https://teletype.in/@yadevblog/nov21-news?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=yadevblog"></link><title>Проекты и жизня (ноябрь 2021 edition)</title><published>2021-10-26T23:17:05.122Z</published><updated>2021-10-26T23:59:40.931Z</updated><summary type="html">Не так давно наткнулся на классный проект plashiki.su - сайт, агрегирующий разные озвучки аниме и позволяющий в один клик отмечать серии как просмотренные на Шикимори (огромная база знаний об аниме/манге/ранобэ).</summary><content type="html">
  &lt;h2 id=&quot;8gXS&quot;&gt;Истлевшие идеи...&lt;/h2&gt;
  &lt;p id=&quot;r4En&quot;&gt;Не так давно наткнулся на классный проект &lt;a href=&quot;http://plashiki.su&quot; target=&quot;_blank&quot;&gt;plashiki.su&lt;/a&gt; - сайт, агрегирующий разные озвучки аниме и позволяющий в один клик отмечать серии как просмотренные на Шикимори (огромная база знаний об аниме/манге/ранобэ).&lt;/p&gt;
  &lt;p id=&quot;UKKJ&quot;&gt;Увы, автор объявил о его закрытии, объяснив это тем, что сама перестала им пользоваться, да и есть полно альтернатив. Исходный код сайта уже выложен в открытый доступ на Github, дамп базы данных появился в день закрытия.&lt;/p&gt;
  &lt;p id=&quot;laaV&quot;&gt;Вот только ни одна из приведённых автором альтернатив не умеет отмечать серии просмотренными, а мне как ленивому человеку это чертовски важно.&lt;/p&gt;
  &lt;p id=&quot;EBPw&quot;&gt;Первая пришедшая в голову идея — написать свой аналог плашики. Я потратил на это где-то часов 10 (за два дня) и ничего рабочего не получил (&lt;a href=&quot;http://github.com/shiki-watch&quot; target=&quot;_blank&quot;&gt;github.com/shiki-watch&lt;/a&gt;). В целом, идея интересная, наверное, когда-нибудь я возьмусь за неё серьёзнее.&lt;/p&gt;
  &lt;h2 id=&quot;WyUq&quot;&gt;Jacob Reborn (v4.x)&lt;/h2&gt;
  &lt;p id=&quot;TpHb&quot;&gt;После очередной безуспешной попытки отрефакторить код Якоба, я начал всё с начала: &lt;a href=&quot;https://github.com/uni-jacob/jacob/tree/dev-4x&quot; target=&quot;_blank&quot;&gt;https://github.com/uni-jacob/jacob/tree/dev-4x&lt;/a&gt;. Готова система регистрации, в разработке модуль расписания. Планируется наладить сотрудничество с администрацией ВлГУ для получения свежего расписания в режиме реального времени для любой группы университета.&lt;/p&gt;
  &lt;p id=&quot;RC4K&quot;&gt;Основной упор сделаю на автоматические линтеры, чтобы обеспечить высокое качество кода, своевременное избавление от технического долга (поменьше &amp;quot;ай, и так сойдет&amp;quot;) и написание тестов&lt;/p&gt;
  &lt;h2 id=&quot;mTxi&quot;&gt;Учеба&lt;/h2&gt;
  &lt;p id=&quot;GJ3U&quot;&gt;Закрыл долг по математике (Ура!), пытаюсь наверстать лабы, которые не делал за время подготовки. Чертовски муторное занятие...&lt;/p&gt;
  &lt;h2 id=&quot;JXzw&quot;&gt;Работа&lt;/h2&gt;
  &lt;p id=&quot;0ekK&quot;&gt;Прошёл собеседование в Hawking Bros., и как мне показалось, проявил себя неплохо. Сказали, что работы сейчас нет и предложили подождать декабря/января, когда запустится стажировка по моему направлению. Дали список тем и технологий, которые мне нужно наверстать. Пока нарабатываю портфолио, делая для себя всякие проекты.&lt;/p&gt;
  &lt;h3 id=&quot;ioCN&quot;&gt;CRM для работы с шоколадом&lt;/h3&gt;
  &lt;p id=&quot;AyiS&quot;&gt;Я во Владимире занимаюсь продажей шоколада в килограммовых брикетах. Мне надоели постоянные рассинхроны по остаткам в гугл табличке и фактически на складе. Поэтому пишу систему (на базе бота ВКонтакте) для автоматизации управления этим делом. Вот только в API контача нет функционала, который критически необходим для этой системы. Они где-то с полгода назад (а может и больше) добавили поддержку остатков в карточках товаров в маркете сообщества. А методы API для получения / редактирования этой циферки не обновили. Так что пока оно работает очень неполноценно, изменяя значения только во внутреннем хранилище системы.&lt;/p&gt;
  &lt;p id=&quot;8mQ0&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;EPG1&quot; data-align=&quot;center&quot;&gt;&lt;strong&gt;Впереди куча планов и новостей, stay tuned!&lt;/strong&gt;&lt;/p&gt;

</content></entry><entry><id>yadevblog:second_mind</id><link rel="alternate" type="text/html" href="https://teletype.in/@yadevblog/second_mind?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=yadevblog"></link><title>Второй мозг</title><published>2020-10-11T14:34:41.272Z</published><updated>2020-10-11T14:41:16.965Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://teletype.in/files/ff/63/ff63423c-0461-40c1-8ebd-b0ba10ca86e6.png"></media:thumbnail><summary type="html">&lt;img src=&quot;https://teletype.in/files/3f/26/3f265d37-9847-4882-8c15-0dcc55865fe2.png&quot;&gt;Меня попросили рассказать о том, как я использую Notion и как изменил его для большего удобства (Ахтунг, много картинок).</summary><content type="html">
  &lt;p&gt;Меня попросили рассказать о том, как я использую Notion и как изменил его для большего удобства &lt;em&gt;(Ахтунг, много картинок)&lt;/em&gt;.&lt;/p&gt;
  &lt;h2&gt;Структура&lt;/h2&gt;
  &lt;h3&gt;Dashboard&lt;/h3&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/3f/26/3f265d37-9847-4882-8c15-0dcc55865fe2.png&quot; width=&quot;1280&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Так выглядит главная страница моего воркспейса (чёрт, какой бы аналог подобрать). Слева ссылки на учебные сайты и ссылки на отдельные страницы с образовательными дисцилинами по семестрам, справа — личные страницы, не связанные с универом; внизу текущий список задач. На скрине например, предстоящие домашние задания. В этой базе данных есть еще несколько представлений:&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/e8/ef/e8efdb45-9ce7-41cd-a956-be75a3e68fc2.png&quot; width=&quot;771&quot; /&gt;
    &lt;figcaption&gt;Представление &amp;quot;Ближайшие задачи&amp;quot;. Показывает все задачи, дедлайн которых не дальше, чем через неделю&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/ee/8f/ee8fb7be-ac49-4c77-bfd9-0e8349c2b801.png&quot; width=&quot;748&quot; /&gt;
    &lt;figcaption&gt;Представление &amp;quot;weekly&amp;quot;. Показывает календарь на неделю с отмеченными на нем задачи с дедлайном. Реализовано через notion-enhancer&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;h3&gt;Универ&lt;/h3&gt;
  &lt;p&gt;Как я и говорил, блок &amp;quot;Универ&amp;quot; состоит из нескольких страниц с семестрами. На странице семестра есть список с изучаемыми дисциплинами, каждая с пометкой о типе аттестации по итогам семестра:&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/3c/26/3c265ed0-4f5a-462a-aba9-61005e445cb7.png&quot; width=&quot;1280&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Под спойлером &amp;quot;Экзамены, зачёты&amp;quot; находится таблица с дисциплинами, по которым будет аттестация в том или ином формате. Каждая запись это отдельная страница, куда, например, можно собрать материалы для подготовки к аттестации:&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/93/23/9323d722-86a5-4215-96a3-6b9d6497eb08.png&quot; width=&quot;714&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;На странице дисциплины собрана информация о преподавателях, их контактах, расписании и домашних работах по этой дисциплине (зеркало таблицы с дашборда, в Notion называется &amp;quot;Linked Database&amp;quot;)&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/17/84/17847a5e-191f-4852-bdd9-36c09effb5db.png&quot; width=&quot;1280&quot; /&gt;
  &lt;/figure&gt;
  &lt;h3&gt;Списки&lt;/h3&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/e7/5b/e75b0363-d1ed-47ea-a188-d07ae0a2bcff.png&quot; width=&quot;1280&quot; /&gt;
  &lt;/figure&gt;
  &lt;p&gt;Это отдельная страница, где я храню все, что можно категоризировать — фильмы, сериалы, игры и прочее.&lt;/p&gt;
  &lt;p&gt;Например — страница &amp;quot;Фильмы&amp;quot;, другие показывать не буду, они все +- аналогичны&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/67/dc/67dc2de7-0343-42f5-84ef-5cc760962208.png&quot; width=&quot;1280&quot; /&gt;
    &lt;figcaption&gt;В представление &amp;quot;Посмотреть&amp;quot; я собираю фильмы, которые хотел бы посмотреть в будущем&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/86/32/863297d2-0f55-4c72-a9b9-b21e04e1ad14.png&quot; width=&quot;1280&quot; /&gt;
    &lt;figcaption&gt;В представлении &amp;quot;Просмотрено&amp;quot; - фильмы, которые я досмотрел с моей личной оценкой&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/53/29/5329989e-5e43-402e-9351-4f104f6bc635.png&quot; width=&quot;1280&quot; /&gt;
    &lt;figcaption&gt;И в &amp;quot;Брошено&amp;quot; фильмы, которые я по тем или иным причинам не досмотрел&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;h2&gt;Организация списка дел&lt;/h2&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/a4/28/a4283664-02c4-46d9-976b-4f27821a5cd9.png&quot; width=&quot;1280&quot; /&gt;
    &lt;figcaption&gt;Вот так без прикрас выглядит мой список дел&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;h3&gt;Статус выполнения&lt;/h3&gt;
  &lt;p&gt;Это поле с выбором одного варианта из &amp;quot;Сделать&amp;quot;, &amp;quot;В процессе&amp;quot; или &amp;quot;Выполнено&amp;quot;.&lt;/p&gt;
  &lt;h3&gt;Проект&lt;/h3&gt;
  &lt;p&gt;Это поле нужно только для удобства поиска задач, на постоянной основе оно нигде не используется&lt;/p&gt;
  &lt;h3&gt;Дисциплина&lt;/h3&gt;
  &lt;p&gt;Основное фильтрующее поле на страницах дисциплин&lt;/p&gt;
  &lt;h3&gt;Блокирующий фактор&lt;/h3&gt;
  &lt;p&gt;Бывает такое, что выполнить задачу мешает что-то извне, например отстутствие знаний о чем-либо. В этом текстовом поле можно описать причину, почему задачу нельзя выполнить сейчас.&lt;/p&gt;
  &lt;h3&gt;Дедлайн&lt;/h3&gt;
  &lt;p&gt;Дата крайнего срока выполнения задачи&lt;/p&gt;
  &lt;p&gt;И самое интересное:&lt;/p&gt;
  &lt;h3&gt;Просрочено?&lt;/h3&gt;
  &lt;p&gt;Это вычисляемое поле, значение которого зависит от каких-то условий, например, от значения других полей. Там находится не очень сложная формула, которая выводит &amp;quot;Ахтунг!&amp;quot;, если до дедлайна осталось меньше двух дней или &amp;quot;Просрочено&amp;quot;, если дедлайн прошел. Но синтаксис у Notion&amp;#x60;а ужасен:&lt;/p&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/81/f4/81f4c084-742d-4c4a-aff6-49aae791572d.png&quot; width=&quot;640&quot; /&gt;
  &lt;/figure&gt;
  &lt;h2&gt;Notion-enhancer&lt;/h2&gt;
  &lt;p&gt;Это плагин, который работает с Windows, Mac или Linux/AUR версией приложения Notion и предоставляет набор улучшений для интерфейса Notion.&lt;/p&gt;
  &lt;p&gt;&lt;a href=&quot;https://github.com/dragonwocky/notion-enhancer&quot; target=&quot;_blank&quot;&gt;Ссылка на проект&lt;/a&gt;&lt;/p&gt;
  &lt;p&gt;Из этого пакета я использую несколько улучшений:&lt;/p&gt;
  &lt;ul&gt;
    &lt;li&gt;Close window to the tray — вместо закрытия окна сворачивает его в трей&lt;/li&gt;
    &lt;li&gt;Выключил по умолчанию включенный Integrated titlebar, он отнимает слишком много места&lt;/li&gt;
    &lt;li&gt;Integrated scrollbars — полосы прокрутки выглядят согласно выбранной теме&lt;/li&gt;
    &lt;li&gt;Bypass preview — выключает открытие страницы в виде небольшого превью и сразу разворачивает ее на полный экран&lt;/li&gt;
    &lt;li&gt;Font Chooser — изменяет шрифты приложения. Когда я установил шрифт Times New Roman для написания отчетов в универ, Notion решил, что нужно использовать этот шрифт во всем приложении. &lt;/li&gt;
  &lt;/ul&gt;
  &lt;figure class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/7e/7b/7e7b2955-7de8-4788-8227-81f83855611a.jpeg&quot; width=&quot;480&quot; /&gt;
  &lt;/figure&gt;
  &lt;ul&gt;
    &lt;li&gt;Neutral — на мой взгляд, самая приятная темная тема из всех в notion-enhancer. Ее можно видеть на всех скриншотах в этом посте.&lt;/li&gt;
    &lt;li&gt;Property layout — по умолчанию скрывает список свойств страницы под спойлер&lt;/li&gt;
  &lt;/ul&gt;
  &lt;figure class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://teletype.in/files/a6/6b/a66b5de5-6591-4464-ad03-fb16acb13c1a.png&quot; width=&quot;304&quot; /&gt;
  &lt;/figure&gt;
  &lt;ul&gt;
    &lt;li&gt;Weekly view — расширение, дающее способ создания представления с календарем на неделю, вместо месяца по умолчанию. Для этого достаточно создать представление Calendar и дать ему имя weekly, что на мой взгляд не самое удачное решение не-англоязычной аудитории&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p&gt;Спасибо за прочтение, надеюсь эта статья была вам полезна. Я думаю, что буду иногда писать такие большие статьи с рассуждениями на разные темы, так что stay tuned!&lt;/p&gt;
  &lt;p&gt;&lt;a href=&quot;http://t.me/dadyarriscorner&quot; target=&quot;_blank&quot;&gt;(c) Dadyarri&amp;#x27;s corner&lt;/a&gt;&lt;/p&gt;

</content></entry></feed>