nested_array 2.4.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README-ru.md +448 -178
- data/README.md +510 -30
- data/lib/nested_array/engine.rb +11 -0
- data/lib/nested_array/nested.rb +214 -187
- data/lib/nested_array/version.rb +1 -1
- data/lib/nested_array.rb +3 -4
- data/nested_array.gemspec +13 -3
- data/vendor/assets/stylesheets/nested_array.scss +92 -0
- metadata +5 -27
- data/.gitignore +0 -10
- data/.rspec +0 -1
- data/.ruby-version +0 -1
- data/Gemfile +0 -5
- data/Gemfile.lock +0 -54
- data/Rakefile +0 -2
- data/TODO.md +0 -29
- data/bash/build/gem.sh +0 -9
- data/bash/push/gem.sh +0 -15
- data/bash/test/gem.sh +0 -11
- data/bin/bundle +0 -105
- data/bin/byebug +0 -29
- data/bin/console +0 -14
- data/bin/htmldiff +0 -29
- data/bin/ldiff +0 -29
- data/bin/rake +0 -29
- data/bin/rspec +0 -29
- data/bin/setup +0 -8
- data/dev/nested_options.rb +0 -25
- data/test.old/Gemfile +0 -5
- data/test.old/Gemfile.lock +0 -34
- data/test.old/bash/run/bundle.sh +0 -9
- data/test.old/bash/run/test.sh +0 -9
- data/test.old/test.rb +0 -42
data/README-ru.md
CHANGED
@@ -1,287 +1,557 @@
|
|
1
|
-
#
|
1
|
+
# nested_array
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
`
|
3
|
+
🎉 Мои поздравления! Вышла версия 3.0.
|
4
|
+
|
5
|
+
Гем `nested_array` позволяет преобразовать плоский массив данных древовидной
|
6
|
+
структуры во вложенный массив, а так же помогает отобразить деревья формируя
|
7
|
+
HTML вёрстку или псевдографику.
|
8
|
+
|
9
|
+
Древовидная структура должна быть описана по шаблону Списка смежности
|
10
|
+
(Adjacency List), то есть в каждом узле указан предок.
|
11
|
+
|
12
|
+
__Выбрать язык README.md__
|
13
|
+
|
14
|
+
- en [English](README.md)
|
15
|
+
- ru [Русский](README-ru.md)
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
## <a id="Оглавление"></a>Оглавление
|
21
|
+
- [Установка](#1)
|
22
|
+
- [Использование](#2)
|
23
|
+
- [Преобразование данных методом `.to_nested`](#2.1)
|
24
|
+
- [Исходные данные – массив хэш](#2.1.1)
|
25
|
+
- [Исходные данные – массив ActiveRecord](#2.1.2)
|
26
|
+
- [Опции метода `.to_nested`](#2.1.3)
|
27
|
+
- [`root_id: id`](#2.1.3.1)
|
28
|
+
- [`branch_id: id`](#2.1.3.2)
|
29
|
+
- [Отображение древовидных структур](#2.2)
|
30
|
+
- [В виде многоуровневых списков](#2.2.1)
|
31
|
+
- [Маркированный и нумерованный списки `<ul>`, `<ol>`](#2.2.1.1)
|
32
|
+
- [Использование собственных шаблонов для отображения списка](#2.2.1.2)
|
33
|
+
- [Изменение шаблона в зависимости от данных узла](#2.2.1.3)
|
34
|
+
- [Расскрывающийся список на основе тега `<details></details>`](#2.2.1.4)
|
35
|
+
- [Формирование и вывод собственных шаблонов опираясь на изменение уровня узла `node.level`](#2.2.1.5)
|
36
|
+
- [В виде псевдографики](#2.2.2)
|
37
|
+
- [Добавление псевдографики перед именем модели методом `nested_to_options`](#2.2.2.1)
|
38
|
+
- [Тонкая псевдографика](#2.2.2.2)
|
39
|
+
- [Собственная псевдографика](#2.2.2.3)
|
40
|
+
- [Увеличение отступа в собственной псевдографике](#2.2.2.4)
|
41
|
+
- [В формах](#2.2.3)
|
42
|
+
- [С хелпером `form.select`](#2.2.3.1)
|
43
|
+
- [С хелперами `form.select` и `options_for_select`](#2.2.3.2)
|
44
|
+
- [Раскрывающийся список с переключателями `form.radio_button`](#2.2.3.3)
|
45
|
+
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
## <a id="1"></a>Установка [↑](#Оглавление "К оглавлению")
|
50
|
+
|
51
|
+
1. Добавте строку в файл _Gemfile_ вашего приложения:
|
6
52
|
|
7
53
|
```ruby
|
8
|
-
|
9
|
-
|
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
|
-
]
|
54
|
+
# Работа с древовидными массивами
|
55
|
+
gem "nested_array", "~> 3.0"
|
20
56
|
```
|
21
57
|
|
58
|
+
И выполните `bundle install`.
|
22
59
|
|
60
|
+
2. Если вы планируете использовать скромные CSS стили гема, добавте в
|
61
|
+
файл _app/assets/stylesheets/application.scss_:
|
23
62
|
|
63
|
+
```css
|
64
|
+
/* Отображение древовидных массивов */
|
65
|
+
@import "nested_array";
|
66
|
+
```
|
24
67
|
|
25
|
-
## Установка
|
26
68
|
|
27
|
-
Добавте строку в _Gemfile_ вашего приложения:
|
28
69
|
|
29
|
-
```ruby
|
30
|
-
# Версия не расширяет базовый класс Array методами гема. Для использования необходимо преобразовать данные к новому типу, см ниже.
|
31
70
|
|
32
|
-
|
33
|
-
|
71
|
+
## <a id="2"></a>Использование [↑](#Оглавление "К оглавлению")
|
72
|
+
|
73
|
+
### <a id="2.1"></a>Преобразование данных методом `.to_nested` [↑](#Оглавление "К оглавлению")
|
74
|
+
|
75
|
+
#### <a id="2.1.1"></a>Исходные данные – массив хэш [↑](#Оглавление "К оглавлению")
|
34
76
|
|
35
|
-
|
77
|
+
Допустим, есть массив хэш:
|
36
78
|
|
37
|
-
|
38
|
-
|
79
|
+
```rb
|
80
|
+
flat = [
|
81
|
+
{'id' => 3, 'parent_id' => nil},
|
82
|
+
{'id' => 2, 'parent_id' => 1},
|
83
|
+
{'id' => 1, 'parent_id' => nil}
|
84
|
+
]
|
39
85
|
```
|
40
86
|
|
41
|
-
|
87
|
+
Где каждый хэш это узел дерева, `id` — идентификатор узла,
|
88
|
+
`parent_id` — указатель на родительский узел.
|
42
89
|
|
43
|
-
|
90
|
+
Необходимо преобразовать в массив в котором будут только корневые узлы
|
91
|
+
(`'parent_id' => nil`), а дочерние узлы помещены в поле
|
92
|
+
`children`.
|
44
93
|
|
94
|
+
```rb
|
95
|
+
nested = flat.to_nested
|
96
|
+
puts nested.pretty_inspect
|
97
|
+
```
|
45
98
|
|
99
|
+
Выведет:
|
46
100
|
|
101
|
+
```
|
102
|
+
[#<OpenStruct id=3, parent_id=nil, level=0, origin={"id"=>3, "parent_id"=>nil}>,
|
103
|
+
#<OpenStruct id=1, parent_id=nil, level=0, children=[#<OpenStruct id=2, parent_id=1, level=1, origin={"id"=>2, "parent_id"=>1}>], origin={"id"=>1, "parent_id"=>nil}>]
|
104
|
+
```
|
47
105
|
|
48
|
-
|
106
|
+
В результате узлы представляют собой объекты `OpenStruct` у которых
|
107
|
+
исходные поля `id`, `parent_id` и дополнительные поля
|
108
|
+
`level`, `origin` и `children`.
|
49
109
|
|
50
|
-
|
51
|
-
__Список методов__
|
110
|
+
В качестве исходных узлов могут быть и объекты ActiveRecord.
|
52
111
|
|
53
|
-
* [to_nested](#to_nested) — преобразует плоскую структуру во вложенную;
|
54
|
-
* [each_nested](#each_nested) — перебирает вложенную стуктуру;
|
55
|
-
* [each_nested!](#each_nested) — перебирает вложенную стуктуру, предоставляя доступ к исходным данным;
|
56
|
-
* [nested_to_html](#nested_to_html) — преобразует вложенную структуру в html вёрстку (многоуровневый список `<ul><li>…`);
|
57
|
-
* [nested_to_options](#nested_to_options) — преобразует вложенную структуру в массив для формирования опций html-тега `<select>` с псевдографикой;
|
58
|
-
* [nested_to_collection_select](#nested_to_collection_select) — преобразует вложенную структуру в плоскую но добавляет псевдографику в тектовое поле для формирования тэга `<select>`;
|
59
|
-
* [concat_nested](#concat_nested) — скеивание вложенных структур, ноды склеиваются если путь к ним одинаков.
|
60
112
|
|
61
113
|
|
62
114
|
|
115
|
+
#### <a id="2.1.2"></a>Исходные данные – массив ActiveRecord [↑](#Оглавление "К оглавлению")
|
63
116
|
|
64
|
-
|
65
|
-
|
117
|
+
```rb
|
118
|
+
catalogs = Catalog.all.to_a
|
119
|
+
nested = catalogs.to_nested
|
120
|
+
puts nested.pretty_inspect
|
121
|
+
```
|
66
122
|
|
67
|
-
|
123
|
+
Выведет:
|
68
124
|
|
69
|
-
```
|
70
|
-
|
71
|
-
|
72
|
-
|
125
|
+
```
|
126
|
+
[
|
127
|
+
#<OpenStruct id=1, parent_id=nil, level=0, origin=#<Catalog id: 1, name: "Computer Components", parent_id: nil>, children=[
|
128
|
+
#<OpenStruct id=11, parent_id=1, level=1, origin=#<Catalog id: 11, name: "External Components", parent_id: 1>, children=[
|
129
|
+
#<OpenStruct id=111, parent_id=11, level=2, origin=#<Catalog id: 111, name: "Hard Drives", parent_id: 11>>,
|
130
|
+
#<OpenStruct id=112, parent_id=11, level=2, origin=#<Catalog id: 112, name: "Sound Cards", parent_id: 11>>,
|
131
|
+
#<OpenStruct id=113, parent_id=11, level=2, origin=#<Catalog id: 113, name: "KVM Switches", parent_id: 11>>,
|
132
|
+
#<OpenStruct id=114, parent_id=11, level=2, origin=#<Catalog id: 114, name: "Optical Drives", parent_id: 11>>
|
133
|
+
]>,
|
134
|
+
#<OpenStruct id=12, parent_id=1, level=1, origin=#<Catalog id: 12, name: "Internal Components", parent_id: 1>>
|
135
|
+
]>,
|
136
|
+
#<OpenStruct id=2, parent_id=nil, level=0, origin=#<Catalog id: 2, name: "Monitors", parent_id: nil>>,
|
137
|
+
#<OpenStruct id=3, parent_id=nil, level=0, origin=#<Catalog id: 3, name: "Servers", parent_id: nil>>,
|
138
|
+
#<OpenStruct id=4, parent_id=nil, level=0, origin=#<Catalog id: 4, name: "Networking Products", parent_id: nil>>
|
139
|
+
]
|
73
140
|
```
|
74
141
|
|
75
|
-
|
142
|
+
<sub>Метод `.to_nested` использует метод `object.serializable_hash`, чтобы получить список полей объекта.</sub>
|
76
143
|
|
77
|
-
У каждой ноды вложенной структуры есть базовые свойства, такие как
|
78
|
-
идентификатор, идентификатор предка и другие. Для доступа к этим данным
|
79
|
-
используются ключи, которые можно настроить как в примере ниже. По умолчанию
|
80
|
-
используются следующие __строковые__ (_чувствительны к string/symbol_) ключи:
|
81
144
|
|
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
145
|
|
94
|
-
Дополнительные опции преобразования:
|
95
146
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
147
|
+
#### <a id="2.1.3"></a>Опции метода `.to_nested` [↑](#Оглавление "К оглавлению")
|
148
|
+
|
149
|
+
##### <a id="2.1.3.1"></a>`root_id: id` [↑](#Оглавление "К оглавлению")
|
150
|
+
|
151
|
+
`root_id: 1` — взять потомков узла с `id` равным `1`.
|
152
|
+
|
153
|
+
```erb
|
154
|
+
<% catalogs_of_1 = Catalog.all.to_a.to_nested(root_id: 1) %>
|
155
|
+
<ul>
|
156
|
+
<% catalogs_of_1.each_nested do |node, origin| %>
|
157
|
+
<%= node.before -%>
|
158
|
+
<%= origin.name -%> <small>[<%= origin.id %>, <%= origin.parent_id || :nil %>, <%= node.level %>]</small>
|
159
|
+
<%= node.after -%>
|
160
|
+
<% end %>
|
161
|
+
</ul>
|
101
162
|
```
|
102
163
|
|
164
|
+
Выведет многоуровневый маркированный список потомков узла №1:
|
103
165
|
|
166
|
+

|
104
167
|
|
105
168
|
|
106
|
-
<a name="each_nested"></a>
|
107
|
-
### each_nested [↑](#methods "К методам")
|
108
169
|
|
109
|
-
Перебирает вложенную стуктуру.
|
110
170
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
171
|
+
##### <a id="2.1.3.2"></a>`branch_id: id` [↑](#Оглавление "К оглавлению")
|
172
|
+
|
173
|
+
`branch_id: 1` — взять узел с `id` равным `1` и всех его потомков.
|
174
|
+
|
175
|
+
```erb
|
176
|
+
<% catalogs_from_1 = Catalog.all.to_a.to_nested(branch_id: 1) %>
|
177
|
+
<ul>
|
178
|
+
<% catalogs_from_1.each_nested do |node, origin| %>
|
179
|
+
<%= node.before -%>
|
180
|
+
<%= origin.name -%> <small>[<%= origin.id %>, <%= origin.parent_id || :nil %>, <%= node.level %>]</small>
|
181
|
+
<%= node.after -%>
|
182
|
+
<% end %>
|
183
|
+
</ul>
|
118
184
|
```
|
119
185
|
|
186
|
+
Выведет узел №1 и его потомков:
|
120
187
|
|
188
|
+

|
121
189
|
|
122
190
|
|
123
|
-
<a
|
124
|
-
### nested_to_html [↑](#methods "К методам")
|
191
|
+
### <a id="2.2"></a>Отображение древовидных структур [↑](#Оглавление "К оглавлению")
|
125
192
|
|
126
|
-
|
193
|
+
#### <a id="2.2.1"></a>В виде многоуровневых списков [↑](#Оглавление "К оглавлению")
|
127
194
|
|
128
|
-
|
195
|
+
##### <a id="2.2.1.1"></a>Маркированный и нумерованный списки `<ul>`, `<ol>` [↑](#Оглавление "К оглавлению")
|
129
196
|
|
130
|
-
```
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
197
|
+
```erb
|
198
|
+
<ul>
|
199
|
+
<% @catalogs.to_a.to_nested.each_nested do |node, origin| %>
|
200
|
+
<%= node.before %>
|
201
|
+
<%= link_to origin.name, origin %> <small>[<%= origin.id %>, <%= origin.parent_id || :nil %>, <%= node.level %>]</small>
|
202
|
+
<%= node.after %>
|
203
|
+
<% end %>
|
204
|
+
</ul>
|
205
|
+
|
206
|
+
<ol>
|
207
|
+
<% @catalogs.to_a.to_nested.each_nested ul: '<ol>', _ul: '</ol>' do |node, origin| %>
|
208
|
+
<%= node.before %>
|
209
|
+
<%= link_to origin.name, origin %> <small>[<%= origin.id %>, <%= origin.parent_id || :nil %>, <%= node.level %>]</small>
|
210
|
+
<%= node.after %>
|
211
|
+
<% end %>
|
212
|
+
</ol>
|
138
213
|
```
|
139
214
|
|
140
|
-
|
215
|
+

|
216
|
+
|
217
|
+
|
141
218
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
219
|
+
|
220
|
+
##### <a id="2.2.1.2"></a>Использование собственных шаблонов для отображения списка [↑](#Оглавление "К оглавлению")
|
221
|
+
|
222
|
+
Вместо `<ul><li>`/`<ol><li>`
|
223
|
+
|
224
|
+
```erb
|
225
|
+
<% content_for :head do %>
|
226
|
+
<style>
|
227
|
+
/* Вертикальные отступы узла */
|
228
|
+
div.li { margin: .5em 0; }
|
229
|
+
/* Отступ уровней (children) */
|
230
|
+
div.ul { margin-left: 2em; }
|
231
|
+
</style>
|
232
|
+
<% end %>
|
233
|
+
|
234
|
+
<div class="ul">
|
235
|
+
<%# Переопределение открывающих и закрывающих тегов шаблонов. %>
|
236
|
+
<% @catalogs.to_a.to_nested.each_nested(
|
237
|
+
ul: '<div class="ul">',
|
238
|
+
_ul: '</div>',
|
239
|
+
li: '<div class="li">',
|
240
|
+
_li: '</div>'
|
241
|
+
) do |node, origin| %>
|
242
|
+
<%= node.before -%>
|
243
|
+
<%= origin.name -%> <small>[<%= origin.id %>, <%= origin.parent_id || :nil %>, <%= node.level %>]</small>
|
244
|
+
<%= node.after -%>
|
245
|
+
<% end %>
|
246
|
+
</div>
|
149
247
|
```
|
150
248
|
|
151
|
-
|
249
|
+

|
152
250
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
251
|
+
|
252
|
+
|
253
|
+
|
254
|
+
|
255
|
+
##### <a id="2.2.1.3"></a>Изменение шаблона в зависимости от данных узла [↑](#Оглавление "К оглавлению")
|
256
|
+
|
257
|
+
Для изменения шаблонов вывода в зависимости от данных узла мы можем проверять
|
258
|
+
поля узла `node.li` и `node.ul`. Если поля не пустые, то вместо вывода их
|
259
|
+
содержимого подставлять собственный динамичный html.
|
260
|
+
|
261
|
+
Вывод имеющихся шаблонов узла (`node.li`, `node.ul` и `node._`):
|
262
|
+
|
263
|
+
```erb
|
264
|
+
<ul>
|
265
|
+
<% @catalogs.to_a.to_nested.each_nested do |node, origin| %>
|
266
|
+
<%= node.li -%>
|
267
|
+
<%= origin.name -%> <small>[<%= origin.id %>, <%= origin.parent_id || :nil %>, <%= node.level %>]</small>
|
268
|
+
<%= node.ul -%>
|
269
|
+
<%= node._ -%>
|
270
|
+
<% end %>
|
271
|
+
</ul>
|
162
272
|
```
|
163
273
|
|
164
|
-
|
274
|
+

|
165
275
|
|
166
|
-
|
276
|
+
Замена шаблонов на динамический html:
|
167
277
|
|
168
|
-
```
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
li:
|
175
|
-
|
278
|
+
```erb
|
279
|
+
<% content_for :head do %>
|
280
|
+
<style>
|
281
|
+
li.level-0 {color: red;}
|
282
|
+
li.level-1 {color: green;}
|
283
|
+
li.level-2 {color: blue;}
|
284
|
+
li.has_children {font-weight: bold;}
|
285
|
+
ul.big {border: solid 1px gray;}
|
286
|
+
</style>
|
287
|
+
<% end %>
|
288
|
+
|
289
|
+
<ul>
|
290
|
+
<% @catalogs.to_a.to_nested.each_nested do |node, origin| %>
|
291
|
+
<li class="level-<%= node.level %> <%= 'has_children' if node.is_has_children %>">
|
292
|
+
<%= origin.name %> <small>[<%= origin.id %>, <%= origin.parent_id || :nil %>, <%= node.level %>]</small>
|
293
|
+
<% if node.ul.present? %>
|
294
|
+
<ul class="<%= 'big' if node.children.length > 2 %>">
|
295
|
+
<% end %>
|
296
|
+
<%= node._ -%>
|
297
|
+
<% end %>
|
298
|
+
</ul>
|
176
299
|
```
|
177
300
|
|
301
|
+

|
178
302
|
|
303
|
+
Стоит отметить, что поле `node.li` всегда присутствует в узле, в отличие от
|
304
|
+
`node.ul`.
|
179
305
|
|
180
306
|
|
181
|
-
<a name="nested_to_options"></a>
|
182
|
-
### nested_to_options [↑](#methods "К методам")
|
183
307
|
|
184
|
-
Формирования опций для html-тега <select>
|
185
308
|
|
186
|
-
|
309
|
+
##### <a id="2.2.1.4"></a>Расскрывающийся список на основе тега `<details></details>` [↑](#Оглавление "К оглавлению")
|
187
310
|
|
188
|
-
```
|
311
|
+
```erb
|
312
|
+
<ul class="nested_array-details">
|
313
|
+
<% @catalogs.to_a.to_nested.each_nested details: true do |node, origin| %>
|
314
|
+
<%= node.before %>
|
315
|
+
<%= origin.name %> <small>[<%= origin.id %>, <%= origin.parent_id || :nil %>, <%= node.level %>]</small>
|
316
|
+
<%= node.after %>
|
317
|
+
<% end %>
|
318
|
+
</ul>
|
319
|
+
```
|
189
320
|
|
190
|
-
[
|
321
|
+

|
322
|
+
|
323
|
+
По умолчанию подуровни скрыты, можно управлять отображением подуровней передавая
|
324
|
+
опцию в метод узла: `node.after(open: …)`:
|
325
|
+
|
326
|
+
```erb
|
327
|
+
<ul class="nested_array-details">
|
328
|
+
<% @catalogs.to_a.to_nested.each_nested details: true do |node, origin| %>
|
329
|
+
<%= node.before %>
|
330
|
+
<%= origin.name %> <small>[<%= origin.id %>, <%= origin.parent_id || :nil %>, <%= node.level %>]</small>
|
331
|
+
<%= node.after(open: node.is_has_children) %>
|
332
|
+
<% end %>
|
333
|
+
</ul>
|
191
334
|
```
|
192
335
|
|
193
|
-
|
336
|
+

|
194
337
|
|
195
|
-
|
196
|
-
|
197
|
-
|
338
|
+
|
339
|
+
|
340
|
+
|
341
|
+
##### <a id="2.2.1.5"></a>Формирование и вывод собственных шаблонов опираясь на изменение уровня узла `node.level` [↑](#Оглавление "К оглавлению")
|
342
|
+
|
343
|
+
```erb
|
344
|
+
<% content_for :head do %>
|
345
|
+
<style>
|
346
|
+
div.children {margin-left: 1em;}
|
347
|
+
div.node {position: relative;}
|
348
|
+
div.node::before {
|
349
|
+
position: absolute;
|
350
|
+
content: "";
|
351
|
+
width: 0px;
|
352
|
+
height: 0px;
|
353
|
+
border-top: 5px solid transparent;
|
354
|
+
border-bottom: 5px solid transparent;
|
355
|
+
border-left: 8.66px solid red;
|
356
|
+
left: -9px;
|
357
|
+
top: 3px;
|
358
|
+
}
|
359
|
+
</style>
|
360
|
+
<% end %>
|
361
|
+
|
362
|
+
<div class="children">
|
363
|
+
<% prev_level = nil %>
|
364
|
+
<% @catalogs.to_a.to_nested.each_nested do |node, origin| %>
|
365
|
+
|
366
|
+
<%# Уровень повысился? — открываем подуровень. %>
|
367
|
+
<% if prev_level.present? && prev_level < node.level %>
|
368
|
+
<div class="children">
|
369
|
+
<% end %>
|
370
|
+
|
371
|
+
<%# Уровень тот же? — предыдущий закрываем просто. %>
|
372
|
+
<% if prev_level.present? && prev_level == node.level %>
|
373
|
+
</div>
|
374
|
+
<% end %>
|
375
|
+
|
376
|
+
<%# Уровень понизился? - предыдущий закрываем сложно. %>
|
377
|
+
<% if prev_level.present? && prev_level > node.level %>
|
378
|
+
<% (prev_level - node.level).times do |t| %>
|
379
|
+
</div>
|
380
|
+
</div>
|
381
|
+
<% end %>
|
382
|
+
</div>
|
383
|
+
<% end %>
|
384
|
+
|
385
|
+
<%# Наш узел. %>
|
386
|
+
<div class="node">
|
387
|
+
<%= origin.name %>
|
388
|
+
|
389
|
+
<% prev_level = node.level %>
|
390
|
+
<% end %>
|
391
|
+
|
392
|
+
<%# Учёт предыдущего уровня при выходе из цикла (Уровень понизился). %>
|
393
|
+
<% if !prev_level.nil? %>
|
394
|
+
<% prev_level.times do |t| %>
|
395
|
+
</div>
|
396
|
+
</div>
|
397
|
+
<% end %>
|
398
|
+
</div>
|
399
|
+
<% end %>
|
400
|
+
</div>
|
198
401
|
```
|
199
402
|
|
403
|
+

|
404
|
+
|
200
405
|
|
201
406
|
|
202
407
|
|
203
|
-
<a
|
204
|
-
### nested_to_collection_select [↑](#methods "К методам")
|
408
|
+
#### <a id="2.2.2"></a>В виде псевдографики [↑](#Оглавление "К оглавлению")
|
205
409
|
|
206
|
-
|
207
|
-
отвечающего за текстовое представление (:name) псевдографику древовидной
|
208
|
-
структуры.
|
410
|
+
##### <a id="2.2.2.1"></a>Добавление псевдографики перед именем модели методом `nested_to_options` [↑](#Оглавление "К оглавлению")
|
209
411
|
|
210
|
-
|
211
|
-
|
412
|
+
```erb
|
413
|
+
<% options = @catalogs.to_a.to_nested.nested_to_options(:name, :id) %>
|
414
|
+
<pre><code><%= options.pluck(0).join($/) %>
|
415
|
+
</code></pre>
|
416
|
+
```
|
212
417
|
|
213
|
-
|
418
|
+

|
214
419
|
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
size: 10
|
225
|
-
}
|
226
|
-
%>
|
420
|
+
|
421
|
+
|
422
|
+
|
423
|
+
##### <a id="2.2.2.2"></a>Тонкая псевдографика [↑](#Оглавление "К оглавлению")
|
424
|
+
|
425
|
+
```erb
|
426
|
+
<% options = @catalogs.to_a.to_nested.nested_to_options(:name, :id, thin_pseudographic: true) %>
|
427
|
+
<pre><code><%= options.pluck(0).join($/) %>
|
428
|
+
</code></pre>
|
227
429
|
```
|
228
430
|
|
229
|
-
|
431
|
+

|
432
|
+
|
433
|
+
|
434
|
+
|
435
|
+
|
436
|
+
##### <a id="2.2.2.3"></a>Собственная певдографика [↑](#Оглавление "К оглавлению")
|
230
437
|
|
231
438
|
```erb
|
232
|
-
<%
|
233
|
-
|
234
|
-
|
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
|
-
%>
|
439
|
+
<% options = @catalogs.to_a.to_nested.nested_to_options(:name, :id, pseudographics: %w(┬ ─ ❇ ├ └ │)) %>
|
440
|
+
<pre><code><%= options.pluck(0).join($/).html_safe %>
|
441
|
+
</code></pre>
|
248
442
|
```
|
249
443
|
|
444
|
+

|
445
|
+
|
446
|
+
|
250
447
|
|
251
448
|
|
449
|
+
##### <a id="2.2.2.4"></a>Увеличение отступа в собственной псевдографике [↑](#Оглавление "К оглавлению")
|
252
450
|
|
253
|
-
|
254
|
-
|
451
|
+
```erb
|
452
|
+
<% options = @catalogs.to_a.to_nested.nested_to_options(:name, :id, pseudographics: ['─┬', '──', '─ ', ' ├', ' └', ' ', ' │']) %>
|
453
|
+
<pre><code><%= options.pluck(0).join($/).html_safe %>
|
454
|
+
</code></pre>
|
455
|
+
```
|
255
456
|
|
256
|
-
|
457
|
+

|
257
458
|
|
258
|
-
* Ноды склеиваются если путь к ним одинаков;
|
259
|
-
* Путь определяется из сложения Текстов (конфигурируемо через :path_key);
|
260
459
|
|
261
|
-
__Опции__
|
262
460
|
|
263
|
-
|
264
|
-
|
265
|
-
|
461
|
+
|
462
|
+
#### <a id="2.2.3"></a>В формах [↑](#Оглавление "К оглавлению")
|
463
|
+
|
464
|
+
#### <a id="2.2.3.1"></a>С хелпером `form.select` [↑](#Оглавление "К оглавлению")
|
465
|
+
|
466
|
+
```erb
|
467
|
+
<%= form_with(model: Catalog.find(11), url: root_path, method: :get) do |form| %>
|
468
|
+
<%= form.select :parent_id,
|
469
|
+
@catalogs.to_a.to_nested.nested_to_options(:name, :id),
|
470
|
+
{
|
471
|
+
include_blank: 'None'
|
472
|
+
},
|
473
|
+
{
|
474
|
+
multiple: false,
|
475
|
+
size: 11,
|
476
|
+
class: 'form-select form-select-sm nested_array-select'
|
477
|
+
}
|
478
|
+
%>
|
479
|
+
<% end %>
|
266
480
|
```
|
267
481
|
|
482
|
+

|
268
483
|
|
269
484
|
|
270
485
|
|
271
|
-
## Разработка
|
272
486
|
|
273
|
-
|
487
|
+
#### <a id="2.2.3.2"></a>С хелперами `form.select` и `options_for_select` [↑](#Оглавление "К оглавлению")
|
488
|
+
|
489
|
+
```erb
|
490
|
+
<%= form_with(model: Catalog.find(11), url: root_path, method: :get) do |form| %>
|
491
|
+
<%= form.select :parent_id,
|
492
|
+
options_for_select(
|
493
|
+
@catalogs.to_a.to_nested.nested_to_options(:name, :id).unshift(['None', '']),
|
494
|
+
selected: form.object.parent_id.to_s
|
495
|
+
),
|
496
|
+
{
|
497
|
+
},
|
498
|
+
{
|
499
|
+
multiple: false,
|
500
|
+
size: 11,
|
501
|
+
class: 'nested_array-select'
|
502
|
+
}
|
503
|
+
%>
|
504
|
+
<% end %>
|
505
|
+
```
|
506
|
+
|
507
|
+

|
508
|
+
|
509
|
+
|
274
510
|
|
275
511
|
|
512
|
+
#### <a id="2.2.3.3"></a>Раскрывающийся список с переключателями `form.radio_button` [↑](#Оглавление "К оглавлению")
|
276
513
|
|
514
|
+
```erb
|
515
|
+
<%= form_with(model: nil, url: root_path, method: :get) do |form| %>
|
516
|
+
<ul class="nested_array-details">
|
517
|
+
<% @catalogs.to_a.to_nested.each_nested details: true do |node, origin| %>
|
518
|
+
<%= node.before %>
|
519
|
+
<%= form.radio_button :parent_id, origin.id %>
|
520
|
+
<%= form.label :parent_id, origin.name, value: origin.id %>
|
521
|
+
<small>[<%= origin.id %>, <%= origin.parent_id || :nil %>, <%= node.level %>]</small>
|
522
|
+
<%= node.after(open: node.is_has_children) %>
|
523
|
+
<% end %>
|
524
|
+
</ul>
|
525
|
+
<% end %>
|
526
|
+
```
|
277
527
|
|
278
|
-
|
528
|
+

|
279
529
|
|
280
|
-
…
|
281
530
|
|
282
531
|
|
283
532
|
|
533
|
+
## Разработка
|
284
534
|
|
285
|
-
|
535
|
+
Часто используемые команды
|
536
|
+
|
537
|
+
```sh
|
538
|
+
# rspec управляет загружаемыми гемами, поэтому сам rspec запускается НЕ `bundle
|
539
|
+
# exec rspec`, а просто `rspec` или `./bin/rspec`.
|
540
|
+
rspec ./spec/lib/nested_array_spec.rb
|
541
|
+
rspec ./spec/lib/nested_array/nested_spec.rb
|
542
|
+
rspec # Прогон тестов
|
543
|
+
subl lib/nested_array/version.rb # Обновление версии
|
544
|
+
bundle exec yard doc # Обновление документации в doc/_index.html
|
545
|
+
git … # Git-фиксация в origin/master и тег
|
546
|
+
gem build # Сборка гема
|
547
|
+
gem push ./nested_array-… # Публикация гема
|
548
|
+
```
|
286
549
|
|
287
|
-
|
550
|
+
Для подключения локальной версии гема в rails замените в строке подключения
|
551
|
+
(файл Gemfile) второй аргумент (версию) на опцию path:
|
552
|
+
|
553
|
+
```rb
|
554
|
+
# Gemfile
|
555
|
+
# Работа с древовидными массивами
|
556
|
+
gem "nested_array", path: "../nested_array"
|
557
|
+
```
|