nested_array 2.4.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0b49067e4c7566810e2689fd293994ae2f8c8b5cb40dfab84e7eb73f440dccd6
4
- data.tar.gz: 3cf05731a72c97d59486f916ac89ce33c3d8c32cd6775e14ceefad498ee11022
3
+ metadata.gz: 1cbdc891f87c006049f2995ac7d0337e6b0c36b3fd18eb9e71ff57725c1b3302
4
+ data.tar.gz: 07ab1ff64ded9400e418042fe8159c82e46db8603947f4da77eaa49a50b16752
5
5
  SHA512:
6
- metadata.gz: c79be9c3ad75d6ee6c6d170fdc7116587e3bd0f0924e8a904ca74f2abb0486c9e748325e66173c1055622106ea6e10a20bd107f6e7e4c56398210d867afa46eb
7
- data.tar.gz: ef2dc1b64710fe0a647f6fd81f0e05cd26ad68abda8a76fb6e4f27c05ccf3be38ca4e5d69bd2d241d692505e619f942df894601dbebdf8b4324ab4e2de7e901f
6
+ metadata.gz: f0cd5151f188b7323a42cddbfd114a645b6677df026933c2254e446022d04206c45d9042d3ae3d8fcada6a76efe00b2ec4184c86c8a9a6d6c2d08f2aed98b731
7
+ data.tar.gz: 49b10c6de7ab78ce83ab61e75653a9564dc9359060f3acf4c7bed9512c916302b0b6a4766516a0890144d373b1fac1207ad4bfce2c870f8c35cd21f76d492d95
data/.gitignore CHANGED
@@ -2,7 +2,6 @@
2
2
  /.yardoc
3
3
  /_yardoc/
4
4
  /coverage/
5
- /doc/
6
5
  /pkg/
7
6
  /spec/reports/
8
7
  /tmp/
data/.rspec CHANGED
@@ -1 +1,4 @@
1
- --require spec_helper
1
+ --color # Вывод в цвете
2
+ --require spec_helper # Подключить файл по умолчанию
3
+ --order rand # В случайном порядке
4
+ --format doc # Вывод в формате
data/.rubocop.yml ADDED
@@ -0,0 +1,125 @@
1
+ # Игнорировать отсутствие магического комментария:
2
+ # frozen_string_literal: true
3
+ #
4
+ # Надо понимать что мы теряем производительность если не используем замороженные
5
+ # строки там где это возможно, применяйте!: ` -"неизменяемая строка" `
6
+ Style/FrozenStringLiteralComment:
7
+ Enabled: false
8
+
9
+ # Надо понимать что есть качественное различие между
10
+ #
11
+ # module Foo
12
+ # class Bar
13
+ # end
14
+ # end
15
+ #
16
+ # и
17
+ #
18
+ # class Foo::Bar
19
+ # end
20
+ #
21
+ # Если где то в модуль вынесен код:
22
+ #
23
+ # module Foo
24
+ # X = 42
25
+ # end
26
+ #
27
+ # то следует инициализировать модуль До класса-потомка, иначе ошибка. Как выход,
28
+ # рубокоп предлагает везде использовать первый синтаксис, а это некрасиво)
29
+ Style/ClassAndModuleChildren:
30
+ Enabled: false
31
+
32
+ # Корячить пустые методы с точка-запятой, нет уж извините!
33
+ Style/EmptyMethod:
34
+ Enabled: false
35
+
36
+ # Не даёт сравнимать с нуль (... == 0), заставляет переписывать на `.zero?`, как-то вообще не согласен!
37
+ Style/NumericPredicate:
38
+ Enabled: false
39
+
40
+ Style/SymbolArray:
41
+ # Предпочитаю полный синтаксис вместо %i[foo bar], пстоянно забываю что
42
+ # такое %i, %w, %g, %Ж, %П, %x, %y, %й, %н, %я.
43
+ # EnforcedStyle: brackets
44
+ # Хотя нет, стиль написания может быть любым, учись понимать и так и сяк.
45
+ Enabled: false
46
+
47
+ # fail обычно бросает исключение, raise пребрасывает после отлова при
48
+ # необходимости.
49
+ Style/SignalException:
50
+ Enabled: false
51
+
52
+ # Что может быть более привычным чем простой if
53
+ Style/NegatedIf:
54
+ Enabled: false
55
+
56
+ Style/SymbolProc:
57
+ # Аргументы отдельно блоки отдельно:
58
+ # something.do_something(foo) { |o| o.bar }
59
+ # AllowMethodsWithArguments: true
60
+ # Отключена проверка стиля передачи блоков в метод, и так и сяк должно быть
61
+ # понятным.
62
+ Enabled: false
63
+
64
+ # Короткий синтаксис для слабаков! =) на мой взгляд, когда в одном хэш смешан
65
+ # короткий и полный синтаксис, тогда код читается рывками (читаем один
66
+ # синтаксис, преключаемся на другой, продолжать читать и потом вдруг обратно).
67
+ # Возможно дело привычки.
68
+ Style/HashSyntax:
69
+ EnforcedShorthandSyntax: never
70
+
71
+ # Разбивать метод или блок на подметоды только ради красоты кода не хочу, если
72
+ # всё что нужно сделать в методе задокумментировано и это не дублирующий код а
73
+ # просто большая отдельная задача, то почему бы и не 2000 строк.
74
+ Metrics/MethodLength:
75
+ Max: 100
76
+ Metrics/BlockLength:
77
+ Max: 300
78
+ Metrics/CyclomaticComplexity:
79
+ Max: 30
80
+ Metrics/PerceivedComplexity:
81
+ Max: 30
82
+
83
+ Metrics/ClassLength:
84
+ Max: 300
85
+ Metrics/ModuleLength:
86
+ Max: 300
87
+
88
+ # Assignment - присвоения, в том числе +=...
89
+ # Branch - явное прямое ветвление программы (вызов методов).
90
+ # Condition - логические выражения
91
+ # AbcSize = Math.sqrt(A * A + B * B + C * C)
92
+ Metrics/AbcSize:
93
+ # Enabled: false
94
+ Max: 100
95
+ CountRepeatedAttributes: false
96
+
97
+ # Я так привык — каждый пробел (отступ) должен быть обоснован, а не нафигачен
98
+ # до «красивого» отступа.
99
+ Layout/ArgumentAlignment:
100
+ EnforcedStyle: with_fixed_indentation
101
+
102
+ Layout/HashAlignment:
103
+ EnforcedHashRocketStyle: table
104
+ EnforcedColonStyle: key
105
+
106
+ Layout/FirstHashElementIndentation:
107
+ EnforcedStyle: consistent
108
+
109
+ # Терпеть не могу пробелы ради выравнивания, предпочитаю устанавливать
110
+ # необходимое количество отступов (отступ в ruby − кратен двум пробелам).
111
+ Layout/MultilineOperationIndentation:
112
+ EnforcedStyle: indented
113
+ Layout/MultilineMethodCallIndentation:
114
+ EnforcedStyle: indented
115
+
116
+ # Эммм, это ж короткий синтаксис, нахуй пробелы! array.map{|x| x.id}
117
+ Layout/SpaceBeforeBlockBraces:
118
+ EnforcedStyle: no_space
119
+ Layout/SpaceInsideBlockBraces:
120
+ EnforcedStyle: no_space
121
+ SpaceBeforeBlockParameters: false
122
+
123
+ # На какой вертикали начался блок, на той и должен закрываться.
124
+ Layout/BlockAlignment:
125
+ EnforcedStyleAlignWith: start_of_block
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.5.3
1
+ 2.7.8
data/CHANGELOG.md ADDED
@@ -0,0 +1,41 @@
1
+ # Changelog
2
+
3
+ ## [2.4.0](https://github.com/Zlatov/nested_array/tree/2.4.0) (2023-01-19)
4
+
5
+ [Full Changelog](https://github.com/Zlatov/nested_array/compare/2.3.0...2.4.0)
6
+
7
+ ## [2.3.0](https://github.com/Zlatov/nested_array/tree/2.3.0) (2023-01-18)
8
+
9
+ [Full Changelog](https://github.com/Zlatov/nested_array/compare/2.2.1...2.3.0)
10
+
11
+ **Security fixes:**
12
+
13
+ - \[Security\] Bump activesupport from 5.2.4.1 to 6.0.3.2 [\#5](https://github.com/Zlatov/nested_array/pull/5) ([dependabot-preview[bot]](https://github.com/apps/dependabot-preview))
14
+
15
+ ## [2.2.1](https://github.com/Zlatov/nested_array/tree/2.2.1) (2023-01-14)
16
+
17
+ [Full Changelog](https://github.com/Zlatov/nested_array/compare/2.2.0...2.2.1)
18
+
19
+ ## [2.2.0](https://github.com/Zlatov/nested_array/tree/2.2.0) (2020-03-10)
20
+
21
+ [Full Changelog](https://github.com/Zlatov/nested_array/compare/1.1.0...2.2.0)
22
+
23
+ ## [1.1.0](https://github.com/Zlatov/nested_array/tree/1.1.0) (2020-02-14)
24
+
25
+ [Full Changelog](https://github.com/Zlatov/nested_array/compare/2.0.4...1.1.0)
26
+
27
+ ## [2.0.4](https://github.com/Zlatov/nested_array/tree/2.0.4) (2020-01-19)
28
+
29
+ [Full Changelog](https://github.com/Zlatov/nested_array/compare/2.0.3...2.0.4)
30
+
31
+ ## [2.0.3](https://github.com/Zlatov/nested_array/tree/2.0.3) (2020-01-19)
32
+
33
+ [Full Changelog](https://github.com/Zlatov/nested_array/compare/2.0.2...2.0.3)
34
+
35
+ ## [2.0.2](https://github.com/Zlatov/nested_array/tree/2.0.2) (2020-01-19)
36
+
37
+ [Full Changelog](https://github.com/Zlatov/nested_array/compare/784a121dd18007e7e7d113f03c665eb39aa9b018...2.0.2)
38
+
39
+
40
+
41
+ \* *This Changelog was automatically generated by [github_changelog_generator] (https://github.com/github-changelog-generator/github-changelog-generator)*
data/Gemfile CHANGED
@@ -3,3 +3,10 @@ source "https://rubygems.org"
3
3
  gemspec
4
4
 
5
5
  gem 'activesupport', '~> 6.0'
6
+
7
+ group :test do
8
+ gem 'rspec', '~> 3.10'
9
+ end
10
+
11
+ # Следим за кодом
12
+ gem 'rubocop', require: false
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nested_array (2.2.1)
4
+ nested_array (3.0.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -12,30 +12,50 @@ GEM
12
12
  minitest (~> 5.1)
13
13
  tzinfo (~> 1.1)
14
14
  zeitwerk (~> 2.2, >= 2.2.2)
15
+ ast (2.4.2)
15
16
  awesome_print (1.8.0)
16
17
  byebug (11.0.1)
17
18
  concurrent-ruby (1.1.6)
18
- diff-lcs (1.3)
19
+ diff-lcs (1.5.0)
19
20
  i18n (1.8.3)
20
21
  concurrent-ruby (~> 1.0)
21
22
  minitest (5.14.1)
23
+ parallel (1.22.1)
24
+ parser (3.2.1.1)
25
+ ast (~> 2.4.1)
26
+ rainbow (3.1.1)
22
27
  rake (10.5.0)
23
- rspec (3.9.0)
24
- rspec-core (~> 3.9.0)
25
- rspec-expectations (~> 3.9.0)
26
- rspec-mocks (~> 3.9.0)
27
- rspec-core (3.9.0)
28
- rspec-support (~> 3.9.0)
29
- rspec-expectations (3.9.0)
28
+ regexp_parser (2.7.0)
29
+ rexml (3.2.5)
30
+ rspec (3.12.0)
31
+ rspec-core (~> 3.12.0)
32
+ rspec-expectations (~> 3.12.0)
33
+ rspec-mocks (~> 3.12.0)
34
+ rspec-core (3.12.1)
35
+ rspec-support (~> 3.12.0)
36
+ rspec-expectations (3.12.2)
30
37
  diff-lcs (>= 1.2.0, < 2.0)
31
- rspec-support (~> 3.9.0)
32
- rspec-mocks (3.9.0)
38
+ rspec-support (~> 3.12.0)
39
+ rspec-mocks (3.12.3)
33
40
  diff-lcs (>= 1.2.0, < 2.0)
34
- rspec-support (~> 3.9.0)
35
- rspec-support (3.9.0)
41
+ rspec-support (~> 3.12.0)
42
+ rspec-support (3.12.0)
43
+ rubocop (1.28.2)
44
+ parallel (~> 1.10)
45
+ parser (>= 3.1.0.0)
46
+ rainbow (>= 2.2.2, < 4.0)
47
+ regexp_parser (>= 1.8, < 3.0)
48
+ rexml
49
+ rubocop-ast (>= 1.17.0, < 2.0)
50
+ ruby-progressbar (~> 1.7)
51
+ unicode-display_width (>= 1.4.0, < 3.0)
52
+ rubocop-ast (1.17.0)
53
+ parser (>= 3.1.1.0)
54
+ ruby-progressbar (1.13.0)
36
55
  thread_safe (0.3.6)
37
56
  tzinfo (1.2.7)
38
57
  thread_safe (~> 0.1)
58
+ unicode-display_width (2.4.2)
39
59
  zeitwerk (2.3.0)
40
60
 
41
61
  PLATFORMS
@@ -48,7 +68,8 @@ DEPENDENCIES
48
68
  byebug (~> 11.0)
49
69
  nested_array!
50
70
  rake (~> 10.0)
51
- rspec (~> 3.9)
71
+ rspec (~> 3.10)
72
+ rubocop
52
73
 
53
74
  BUNDLED WITH
54
75
  2.1.4
data/README-ru-old.md ADDED
@@ -0,0 +1,333 @@
1
+ # NestedArray
2
+
3
+ Предназначен для преобразования в древовидную структуру плоских данных описанных
4
+ по паттерну «Список смежности» (Adjacency List), то есть в нодах указа предок
5
+ `parent_id`. Например:
6
+
7
+ ```ruby
8
+ [
9
+ {id: 1, parent_id: nil, name: 'first', …},
10
+ {id: 2, parent_id: 1, name: 'second', …},
11
+ {id: 3, parent_id: 1, name: 'third', …}
12
+ ]
13
+ # ↓ ↓ ↓
14
+ [
15
+ {id: 1, parent_id: nil, name: 'first', children: [
16
+ {id: 2, parent_id: 1, name: 'second', …},
17
+ {id: 3, parent_id: 1, name: 'third', …}
18
+ ], …}
19
+ ]
20
+ ```
21
+
22
+
23
+
24
+
25
+ ## Установка
26
+
27
+ Добавте строку в _Gemfile_ вашего приложения:
28
+
29
+ ```ruby
30
+ # Работа с древовидными массивами.
31
+ gem "nested_array", "~> 3.0"
32
+ ```
33
+
34
+ И затем выполните `bundle install`.
35
+
36
+ Или установите его как `gem install nested_array`
37
+
38
+ Если вы планируете использовать скромный CSS гема, добавте в
39
+ файл _app/assets/stylesheets/application.scss_:
40
+
41
+ ```css
42
+ /* Отображение древовидных массивов. */
43
+ @import "nested_array";
44
+ ```
45
+
46
+
47
+ ## Использование
48
+
49
+ <a name="methods"></a>
50
+ * Список методов
51
+ * [to_nested](#to_nested) — преобразует плоскую структуру во вложенную;
52
+ * [each_nested](#each_nested) — перебирает вложенную стуктуру;
53
+ * [each_nested!](#each_nested) — перебирает вложенную стуктуру, предоставляя доступ к исходным данным;
54
+ * [nested_to_html](#nested_to_html) — преобразует вложенную структуру в html вёрстку (многоуровневый список `<ul><li>…`);
55
+ * [nested_to_options](#nested_to_options) — преобразует вложенную структуру в массив для формирования опций html-тега `<select>` с псевдографикой;
56
+ * [nested_to_collection_select](#nested_to_collection_select) — преобразует вложенную структуру в плоскую но добавляет псевдографику в тектовое поле для формирования тэга `<select>`;
57
+ * [concat_nested](#concat_nested) — скеивание вложенных структур, ноды склеиваются если путь к ним одинаков.
58
+ * Вывод
59
+ * Вложенный список ul > li
60
+
61
+
62
+ ### Список методов
63
+
64
+ <a name="to_nested"></a>
65
+ #### to_nested [↑](#methods "К методам")
66
+
67
+ Преобразует плоскую структуру во вложенную.
68
+
69
+ ```ruby
70
+ a = [{'id' => 1, 'parent_id' => nil}]
71
+ a = NestedArray::Array.new a
72
+ b = a.to_nested
73
+ ```
74
+
75
+ __Опции__
76
+
77
+ У каждой ноды вложенной структуры есть базовые свойства, такие как
78
+ идентификатор, идентификатор предка и другие. Для доступа к этим данным
79
+ используются ключи, которые можно настроить как в примере ниже. По умолчанию
80
+ используются следующие __строковые__ (_чувствительны к string/symbol_) ключи:
81
+
82
+ ```ruby
83
+ b = a.to_nested({
84
+ id: 'id', # указывает какое свойство ноды является идентификатором;
85
+ parent_id: 'parent_id', # -//- предком;
86
+ children: 'children', # -//- массивом потомков;
87
+ level: 'level' # -//- дополнительным свойством с уровнем вложенности;
88
+ root_id: nil # определяет что является корнем для построения дерева,
89
+ # например, для построения ветви корнем корнем
90
+ # является идентификатор одной из нод.
91
+ })
92
+ ```
93
+
94
+ Дополнительные опции преобразования:
95
+
96
+ ```ruby
97
+ b = a.to_nested({
98
+ hashed: false, # потомки могут храниться не в массиве а в хэше;
99
+ add_level: false, # добавляет в ноду информацию о уровене вложенности ноды;
100
+ })
101
+ ```
102
+
103
+
104
+
105
+
106
+ <a name="each_nested"></a>
107
+ #### each_nested [↑](#methods "К методам")
108
+
109
+ Перебирает вложенную стуктуру.
110
+
111
+ ```ruby
112
+ nested.each_nested do |node, parents, level, is_last_children|
113
+ puts node # > {'id' => ...}
114
+ puts parents # > [{'id' => ...}]
115
+ puts level # > 0
116
+ puts is_last_children # > false
117
+ end
118
+ ```
119
+
120
+
121
+
122
+
123
+ <a name="nested_to_html"></a>
124
+ #### nested_to_html [↑](#methods "К методам")
125
+
126
+ Формирует _html_-код для вывода вложенных структур с использованием вложенных друг в друга списков `<ul>`.
127
+
128
+ __Пример__
129
+
130
+ ```ruby
131
+ [
132
+ {'id' => 1, 'parent_id' => nil, 'name' => 'first'},
133
+ {'id' => 2, 'parent_id' => 1, 'name' => 'second'},
134
+ {'id' => 3, 'parent_id' => 1, 'name' => 'third'}
135
+ ].to_nested.nested_to_html do |node|
136
+ node['name']
137
+ end
138
+ ```
139
+
140
+ Вернёт
141
+
142
+ ```html
143
+ <li>first
144
+ <ul>
145
+ <li>second</li>
146
+ <li>third</li>
147
+ </ul>
148
+ </li>
149
+ ```
150
+
151
+ __Расширенный пример__
152
+
153
+ ```ruby
154
+ .nested_to_html li: '<li class="my">', _ul: '<i></i></ul>' do |node, parents, level|
155
+ block_options = {}
156
+ block_options[:li] = '<li class="my current">' if node['id'] == 2
157
+ [
158
+ "id: #{node['id']}, #{node['name']}, parent name: #{parents[level]&.[]('name')}",
159
+ block_options
160
+ ]
161
+ end
162
+ ```
163
+
164
+ __Опции__
165
+
166
+ Все опции могут быть аргументами метода, и только некоторые опции влияют на результат через блок — на лету (последняя строка блока).
167
+
168
+ ```ruby
169
+ tabulated: true,
170
+ inline: false,
171
+ tab: "\t",
172
+ ul: '<ul>', # может задаваться блоком
173
+ _ul: '</ul>',
174
+ li: '<li>', # может задаваться блоком
175
+ _li: '</li>',
176
+ ```
177
+
178
+
179
+
180
+
181
+ <a name="nested_to_options"></a>
182
+ #### nested_to_options [↑](#methods "К методам")
183
+
184
+ Формирования опций для html-тега &lt;select&gt;
185
+
186
+ Возвращает массив с псевдографикой, позволяющей вывести древовидную структуру.
187
+
188
+ ```ruby
189
+
190
+ [['option_text1', 'option_value1'],['option_text2', 'option_value2'],…]
191
+ ```
192
+
193
+ __Опции__
194
+
195
+ ```ruby
196
+ option_value: 'id', # Что брать в качестве значений при формировании опций селекта.
197
+ option_text: 'name',
198
+ ```
199
+
200
+
201
+
202
+
203
+ <a name="nested_to_collection_select"></a>
204
+ #### nested_to_collection_select [↑](#methods "К методам")
205
+
206
+ Преобразует вложенную структуру данных в плоскую, но добавляет в значение поля
207
+ отвечающего за текстовое представление (:name) псевдографику древовидной
208
+ структуры.
209
+
210
+ Это позволяет вывести тэг select в сносном виде для использования с вложенными
211
+ структурами.
212
+
213
+ Пример с хелпером `collection_select`
214
+
215
+ ```rb
216
+ <%= form.collection_select :catalog_ids,
217
+ Catalog.all.to_a.to_nested.nested_to_collection_select, :id, :name,
218
+ {
219
+ # prompt: true
220
+ # include_blank: true
221
+ },
222
+ {
223
+ multiple: true,
224
+ size: 10
225
+ }
226
+ %>
227
+ ```
228
+
229
+ Пример с хелпером `select` в сочетании с `options_from_collection_for_select`
230
+
231
+ ```erb
232
+ <% catalogs = Catalog.all.to_a %>
233
+ <%= form.select :catalog_ids,
234
+ options_from_collection_for_select(
235
+ catalogs.to_nested.nested_to_collection_select, :id, :name,
236
+ disabled: catalogs.select{|x| x.hidden?}.pluck(:id),
237
+ selected: form.object.catalog_ids
238
+ ),
239
+ {
240
+ # prompt: true
241
+ # include_blank: true
242
+ },
243
+ {
244
+ multiple: true,
245
+ size: 10
246
+ }
247
+ %>
248
+ ```
249
+
250
+
251
+
252
+
253
+ <a name="concat_nested"></a>
254
+ #### concat_nested [↑](#methods "К методам")
255
+
256
+ Скеивание вложенных структур.
257
+
258
+ * Ноды склеиваются если путь к ним одинаков;
259
+ * Путь определяется из сложения Текстов (конфигурируемо через :path_key);
260
+
261
+ __Опции__
262
+
263
+ ```ruby
264
+ path_separator: '-=path_separator=-',
265
+ path_key: 'text',
266
+ ```
267
+
268
+
269
+
270
+ ### Вывод
271
+
272
+ #### Вложенный список ul-li
273
+
274
+ ![Screenshot](doc/images/01.png)
275
+
276
+ ```ruby
277
+ <ul>
278
+ <% @catalogs.to_a.to_nested.each_nested do |node, origin| %>
279
+ <%= node.before -%>
280
+ <%= node.name -%>
281
+ <%= node.after -%>
282
+ <% end %>
283
+ </ul>
284
+ ```
285
+
286
+ ![Screenshot](doc/images/02.png)
287
+
288
+ #### Вложенный расскрывающийся список
289
+
290
+ ```ruby
291
+ <ul class="nested_array-details">
292
+ <% @catalogs.to_a.to_nested.each_nested details: true do |node, origin| %>
293
+ <%= node.before -%>
294
+ <%= node.name -%>
295
+ <%= node.after -%>
296
+ <% end %>
297
+ </ul>
298
+ ```
299
+
300
+ ![Screenshot](doc/images/03.png)
301
+
302
+ Если необходимо открыть некоторые узлы
303
+
304
+ ```ruby
305
+ <ul class="nested_array-details">
306
+ <% @catalogs.to_a.to_nested.each_nested details: true do |node, origin| %>
307
+ <%= node.before -%>
308
+ <%= node.name -%>
309
+ <%= node.after(open: node.is_has_children) -%>
310
+ <% end %>
311
+ </ul>
312
+ ```
313
+
314
+ ![Screenshot](doc/images/04.png)
315
+
316
+
317
+ ## Разработка
318
+
319
+
320
+
321
+
322
+
323
+
324
+ ## Содействие
325
+
326
+
327
+
328
+
329
+
330
+
331
+ ## Лицензия
332
+
333
+ В соответствии с условиями [лицензии MIT](https://opensource.org/licenses/MIT).