qiita_scouter 0.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 +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/.rubocop.yml +5 -0
- data/.travis.yml +8 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +22 -0
- data/README.md +80 -0
- data/Rakefile +8 -0
- data/bin/qiita_scouter +35 -0
- data/lib/models/article.rb +11 -0
- data/lib/models/articles.rb +30 -0
- data/lib/models/user.rb +12 -0
- data/lib/qiita_items_json_loader.rb +43 -0
- data/lib/qiita_scouter/version.rb +4 -0
- data/lib/qiita_scouter_calculator.rb +42 -0
- data/lib/qiita_scouter_core.rb +43 -0
- data/lib/qiita_user_json_loader.rb +29 -0
- data/qiita_scouter.gemspec +27 -0
- data/spec/models/article_spec.rb +57 -0
- data/spec/models/articles_spec.rb +281 -0
- data/spec/models/user_spec.rb +55 -0
- data/spec/qiita_items_json_loader_spec.rb +59 -0
- data/spec/qiita_scouter_calculator_spec.rb +188 -0
- data/spec/qiita_user_json_loader_spec.rb +66 -0
- data/spec/spec_helper.rb +16 -0
- metadata +160 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'qiita_scouter/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'qiita_scouter'
|
8
|
+
spec.version = QiitaScouter::VERSION
|
9
|
+
spec.authors = ['tbpgr']
|
10
|
+
spec.email = ['tbpgr@tbpgr.jp']
|
11
|
+
spec.summary = 'Analyze your qiita power'
|
12
|
+
spec.description = 'Analyze your qiita power'
|
13
|
+
spec.homepage = 'https://github.com/tbpgr/qiita_scouter'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(/^(test|spec|features)\//)
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_runtime_dependency 'thor', '~> 0.18.1'
|
22
|
+
|
23
|
+
spec.add_development_dependency 'rspec', '~> 2.14', '>= 2.14.1'
|
24
|
+
spec.add_development_dependency 'simplecov', '~> 0.8', '>= 0.8.2'
|
25
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
26
|
+
spec.add_development_dependency 'rake', '~> 0'
|
27
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'models/article'
|
4
|
+
|
5
|
+
# rubocop:disable LineLength, UnusedMethodArgument
|
6
|
+
describe QiitaScouter::Article do
|
7
|
+
context :initialize do
|
8
|
+
cases = [
|
9
|
+
{
|
10
|
+
case_no: 1,
|
11
|
+
case_title: 'valid case',
|
12
|
+
init: {
|
13
|
+
'tags' => [
|
14
|
+
{
|
15
|
+
'name' => 'Ruby',
|
16
|
+
'url_name' => 'ruby',
|
17
|
+
'versions' => []
|
18
|
+
}
|
19
|
+
],
|
20
|
+
'stock_count' => 2
|
21
|
+
},
|
22
|
+
expected_tags: ['Ruby'],
|
23
|
+
expected_stock_count: 2
|
24
|
+
}
|
25
|
+
]
|
26
|
+
|
27
|
+
cases.each do |c|
|
28
|
+
it "|case_no=#{c[:case_no]}|case_title=#{c[:case_title]}" do
|
29
|
+
begin
|
30
|
+
case_before c
|
31
|
+
|
32
|
+
# -- given --
|
33
|
+
article = QiitaScouter::Article.new(c[:init])
|
34
|
+
|
35
|
+
# -- when --
|
36
|
+
actual_tags = article.tags
|
37
|
+
actual_stock_count = article.stock_count
|
38
|
+
|
39
|
+
# -- then --
|
40
|
+
expect(actual_tags).to eq(c[:expected_tags])
|
41
|
+
expect(actual_stock_count).to eq(c[:expected_stock_count])
|
42
|
+
ensure
|
43
|
+
case_after c
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def case_before(_c)
|
48
|
+
# implement each case before
|
49
|
+
end
|
50
|
+
|
51
|
+
def case_after(_c)
|
52
|
+
# implement each case after
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
# rubocop:enable LineLength, UnusedMethodArgument
|
@@ -0,0 +1,281 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'models/article'
|
4
|
+
require 'models/articles'
|
5
|
+
|
6
|
+
# rubocop:disable LineLength, UnusedMethodArgument
|
7
|
+
describe QiitaScouter::Articles do
|
8
|
+
context :<< do
|
9
|
+
cases = [
|
10
|
+
{
|
11
|
+
case_no: 1,
|
12
|
+
case_title: '2 list case',
|
13
|
+
appends: [
|
14
|
+
QiitaScouter::Article.new(
|
15
|
+
'tags' => [
|
16
|
+
{ 'name' => 'Ruby' },
|
17
|
+
{ 'name' => 'Graphviz' }
|
18
|
+
],
|
19
|
+
'stock_count' => 25
|
20
|
+
),
|
21
|
+
QiitaScouter::Article.new(
|
22
|
+
'tags' => [
|
23
|
+
{ 'name' => 'Ruby' },
|
24
|
+
{ 'name' => 'Graphviz' }
|
25
|
+
],
|
26
|
+
'stock_count' => 25
|
27
|
+
)
|
28
|
+
],
|
29
|
+
expected: 2
|
30
|
+
},
|
31
|
+
{
|
32
|
+
case_no: 2,
|
33
|
+
case_title: '1 list case',
|
34
|
+
appends: [
|
35
|
+
QiitaScouter::Article.new(
|
36
|
+
'tags' => [
|
37
|
+
{ 'name' => 'Ruby' },
|
38
|
+
{ 'name' => 'Graphviz' }
|
39
|
+
],
|
40
|
+
'stock_count' => 25
|
41
|
+
)
|
42
|
+
],
|
43
|
+
expected: 1
|
44
|
+
}
|
45
|
+
]
|
46
|
+
|
47
|
+
cases.each do |c|
|
48
|
+
it "|case_no=#{c[:case_no]}|case_title=#{c[:case_title]}" do
|
49
|
+
begin
|
50
|
+
case_before c
|
51
|
+
|
52
|
+
# -- given --
|
53
|
+
qiita_scouter_articles = QiitaScouter::Articles.new
|
54
|
+
|
55
|
+
# -- when --
|
56
|
+
c[:appends].each { |e|qiita_scouter_articles << e }
|
57
|
+
actual = qiita_scouter_articles.size
|
58
|
+
|
59
|
+
# -- then --
|
60
|
+
expect(actual).to eq(c[:expected])
|
61
|
+
ensure
|
62
|
+
case_after c
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def case_before(_c)
|
67
|
+
# implement each case before
|
68
|
+
end
|
69
|
+
|
70
|
+
def case_after(_c)
|
71
|
+
# implement each case after
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context :+ do
|
77
|
+
cases = [
|
78
|
+
{
|
79
|
+
case_no: 1,
|
80
|
+
case_title: 'init 0 append 2 list case',
|
81
|
+
init: [],
|
82
|
+
appends: QiitaScouter::Articles.new([
|
83
|
+
QiitaScouter::Article.new(
|
84
|
+
'tags' => [
|
85
|
+
{ 'name' => 'Ruby' },
|
86
|
+
{ 'name' => 'Graphviz' }
|
87
|
+
],
|
88
|
+
'stock_count' => 25
|
89
|
+
),
|
90
|
+
QiitaScouter::Article.new(
|
91
|
+
'tags' => [
|
92
|
+
{ 'name' => 'PHP' },
|
93
|
+
{ 'name' => 'Graphviz' }
|
94
|
+
],
|
95
|
+
'stock_count' => 32
|
96
|
+
)
|
97
|
+
]),
|
98
|
+
expected: 2
|
99
|
+
},
|
100
|
+
{
|
101
|
+
case_no: 2,
|
102
|
+
case_title: 'init 2 append 2 list case',
|
103
|
+
init: [
|
104
|
+
QiitaScouter::Article.new(
|
105
|
+
'tags' => [
|
106
|
+
{ 'name' => 'Ruby' },
|
107
|
+
{ 'name' => 'Graphviz' }
|
108
|
+
],
|
109
|
+
'stock_count' => 25
|
110
|
+
),
|
111
|
+
QiitaScouter::Article.new(
|
112
|
+
'tags' => [
|
113
|
+
{ 'name' => 'Ruby' },
|
114
|
+
{ 'name' => 'Graphviz' }
|
115
|
+
],
|
116
|
+
'stock_count' => 25
|
117
|
+
)
|
118
|
+
],
|
119
|
+
appends: QiitaScouter::Articles.new([
|
120
|
+
QiitaScouter::Article.new(
|
121
|
+
'tags' => [
|
122
|
+
{ 'name' => 'Ruby' },
|
123
|
+
{ 'name' => 'Graphviz' }
|
124
|
+
],
|
125
|
+
'stock_count' => 25
|
126
|
+
),
|
127
|
+
QiitaScouter::Article.new(
|
128
|
+
'tags' => [
|
129
|
+
{ 'name' => 'PHP' },
|
130
|
+
{ 'name' => 'Graphviz' }
|
131
|
+
],
|
132
|
+
'stock_count' => 32
|
133
|
+
)
|
134
|
+
]),
|
135
|
+
expected: 4
|
136
|
+
}
|
137
|
+
]
|
138
|
+
|
139
|
+
cases.each do |c|
|
140
|
+
it "|case_no=#{c[:case_no]}|case_title=#{c[:case_title]}" do
|
141
|
+
begin
|
142
|
+
case_before c
|
143
|
+
|
144
|
+
# -- given --
|
145
|
+
qiita_scouter_articles = QiitaScouter::Articles.new(c[:init])
|
146
|
+
|
147
|
+
# -- when --
|
148
|
+
qiita_scouter_articles += c[:appends]
|
149
|
+
actual = qiita_scouter_articles.size
|
150
|
+
|
151
|
+
# -- then --
|
152
|
+
expect(actual).to eq(c[:expected])
|
153
|
+
ensure
|
154
|
+
case_after c
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def case_before(_c)
|
159
|
+
# implement each case before
|
160
|
+
end
|
161
|
+
|
162
|
+
def case_after(_c)
|
163
|
+
# implement each case after
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
context :size do
|
169
|
+
cases = [
|
170
|
+
{
|
171
|
+
case_no: 1,
|
172
|
+
case_title: '2 list case',
|
173
|
+
articles: [
|
174
|
+
QiitaScouter::Article.new(
|
175
|
+
'tags' => [
|
176
|
+
{ 'name' => 'Ruby' },
|
177
|
+
{ 'name' => 'Graphviz' }
|
178
|
+
],
|
179
|
+
'stock_count' => 25
|
180
|
+
),
|
181
|
+
QiitaScouter::Article.new(
|
182
|
+
'tags' => [
|
183
|
+
{ 'name' => 'Ruby' },
|
184
|
+
{ 'name' => 'Graphviz' }
|
185
|
+
],
|
186
|
+
'stock_count' => 25
|
187
|
+
)
|
188
|
+
],
|
189
|
+
expected: 2
|
190
|
+
},
|
191
|
+
{
|
192
|
+
case_no: 2,
|
193
|
+
case_title: 'empty list case',
|
194
|
+
articles: [],
|
195
|
+
expected: 0
|
196
|
+
}
|
197
|
+
]
|
198
|
+
|
199
|
+
cases.each do |c|
|
200
|
+
it "|case_no=#{c[:case_no]}|case_title=#{c[:case_title]}" do
|
201
|
+
begin
|
202
|
+
case_before c
|
203
|
+
|
204
|
+
# -- given --
|
205
|
+
qiita_scouter_articles = QiitaScouter::Articles.new(c[:articles])
|
206
|
+
|
207
|
+
# -- when --
|
208
|
+
actual = qiita_scouter_articles.size
|
209
|
+
|
210
|
+
# -- then --
|
211
|
+
expect(actual).to eq(c[:expected])
|
212
|
+
ensure
|
213
|
+
case_after c
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def case_before(_c)
|
218
|
+
# implement each case before
|
219
|
+
end
|
220
|
+
|
221
|
+
def case_after(_c)
|
222
|
+
# implement each case after
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
context :each do
|
228
|
+
cases = [
|
229
|
+
{
|
230
|
+
case_no: 1,
|
231
|
+
case_title: '2 list case',
|
232
|
+
articles: [
|
233
|
+
QiitaScouter::Article.new(
|
234
|
+
'tags' => [
|
235
|
+
{ 'name' => 'Ruby' },
|
236
|
+
{ 'name' => 'Graphviz' }
|
237
|
+
],
|
238
|
+
'stock_count' => 1
|
239
|
+
),
|
240
|
+
QiitaScouter::Article.new(
|
241
|
+
'tags' => [
|
242
|
+
{ 'name' => 'Ruby' },
|
243
|
+
{ 'name' => 'Graphviz' }
|
244
|
+
],
|
245
|
+
'stock_count' => 2
|
246
|
+
)
|
247
|
+
],
|
248
|
+
expected: 3
|
249
|
+
}
|
250
|
+
]
|
251
|
+
|
252
|
+
cases.each do |c|
|
253
|
+
it "|case_no=#{c[:case_no]}|case_title=#{c[:case_title]}" do
|
254
|
+
begin
|
255
|
+
case_before c
|
256
|
+
|
257
|
+
# -- given --
|
258
|
+
qiita_scouter_articles = QiitaScouter::Articles.new(c[:articles])
|
259
|
+
|
260
|
+
# -- when --
|
261
|
+
total = 0
|
262
|
+
qiita_scouter_articles.each { |e|total += e.stock_count }
|
263
|
+
|
264
|
+
# -- then --
|
265
|
+
expect(total).to eq(c[:expected])
|
266
|
+
ensure
|
267
|
+
case_after c
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
def case_before(_c)
|
272
|
+
# implement each case before
|
273
|
+
end
|
274
|
+
|
275
|
+
def case_after(_c)
|
276
|
+
# implement each case after
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
# rubocop:enable LineLength, UnusedMethodArgument
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'models/user'
|
4
|
+
|
5
|
+
# rubocop:disable LineLength, UnusedMethodArgument
|
6
|
+
describe QiitaScouter::User do
|
7
|
+
context :initialize do
|
8
|
+
cases = [
|
9
|
+
{
|
10
|
+
case_no: 1,
|
11
|
+
case_title: 'valid case',
|
12
|
+
init: {
|
13
|
+
'url_name' => 'tbpgr',
|
14
|
+
'followers' => 10,
|
15
|
+
'items' => 20
|
16
|
+
},
|
17
|
+
expected_url_name: 'tbpgr',
|
18
|
+
expected_followers: 10,
|
19
|
+
expected_items: 20
|
20
|
+
}
|
21
|
+
]
|
22
|
+
|
23
|
+
cases.each do |c|
|
24
|
+
it "|case_no=#{c[:case_no]}|case_title=#{c[:case_title]}" do
|
25
|
+
begin
|
26
|
+
case_before c
|
27
|
+
|
28
|
+
# -- given --
|
29
|
+
article = QiitaScouter::User.new(c[:init])
|
30
|
+
|
31
|
+
# -- when --
|
32
|
+
actual_url_name = article.url_name
|
33
|
+
actual_followers = article.followers
|
34
|
+
actual_items = article.items
|
35
|
+
|
36
|
+
# -- then --
|
37
|
+
expect(actual_url_name).to eq(c[:expected_url_name])
|
38
|
+
expect(actual_followers).to eq(c[:expected_followers])
|
39
|
+
expect(actual_items).to eq(c[:expected_items])
|
40
|
+
ensure
|
41
|
+
case_after c
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def case_before(_c)
|
46
|
+
# implement each case before
|
47
|
+
end
|
48
|
+
|
49
|
+
def case_after(_c)
|
50
|
+
# implement each case after
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
# rubocop:enable LineLength, UnusedMethodArgument
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'qiita_items_json_loader'
|
4
|
+
require 'json'
|
5
|
+
require 'open-uri'
|
6
|
+
require 'openssl'
|
7
|
+
require 'models/article'
|
8
|
+
require 'models/articles'
|
9
|
+
|
10
|
+
# rubocop:disable LineLength, UnusedMethodArgument
|
11
|
+
describe QiitaScouter::QiitaItemsJsonLoader do
|
12
|
+
context :load do
|
13
|
+
cases = [
|
14
|
+
{
|
15
|
+
case_no: 1,
|
16
|
+
case_title: 'valid case',
|
17
|
+
user: 'user',
|
18
|
+
expected: 150
|
19
|
+
}
|
20
|
+
]
|
21
|
+
|
22
|
+
# DummyForItem
|
23
|
+
class DummyForItem
|
24
|
+
def read
|
25
|
+
'[{"id":133680,"uuid":"69c3d606f47b9d5a2a1e","user":{"id":43480,"url_name":"tbpgr","profile_image_url":"https://pbs.twimg.com/profile_images/378800000650620553/44cda07bda72f68147025c752716b580_normal.png"},"title":"Ruby | Collection Pipeline でランク集計を実装する","created_at":"2014-09-15 20:24:42 +0900","updated_at":"2014-09-15 20:24:42 +0900","created_at_in_words":"約17時間","updated_at_in_words":"約17時間","tags":[{"name":"Ruby","url_name":"ruby","icon_url":"https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/9de6a11d330f5694820082438f88ccf4a1b289b2/medium.jpg?1364837630","versions":[]}],"stock_count":2,"comment_count":0,"url":"http://qiita.com/tbpgr/items/69c3d606f47b9d5a2a1e","created_at_as_seconds":1410780282,"tweet":false,"gist_url":null,"private":false,"raw_body":"# Ruby | Collection Pipeline でランク集計を実装する\n\n## 概要\nCollection Pipeline でランク集計を実装します\n\nMartin Fowler 氏の下記ページを参考にしました。\n[Collection Pipeline](http://martinfowler.com/articles/collection-pipeline/)\n\n## サンプル\n### 仕様\nプログラム言語に関するアンケートを行ったとします。\n解答は言語名で記述されています。\n\n言語でグループ化して、言語ごとの投票数を集計し、\n\n* 言語別投票数の降順、言語名の昇順でソート\n* 結果を Markdown (GitHub Flavored Markdown) の表形式で出力する\n\n### 入力データ\n\n~~~\nRuby\nRuby\nRuby\nJava\nJavaScript\nC++\nRuby\nPython\nBash\nRuby\nRuby\nPerl\nC\nVB\nPHP\nJavaScript\nVB\nJava\nJavaScript\nRuby\nC\nPascal\nPHP\nJava\nPython\nRuby\nPython\nC++\nC\nJava\nJava\nPython\nMathematica\nJavaScript\nPHP\nJava\nC\nScala\nC\nC\n~~~\n\n### プログラム\n\n~~~ruby\nrequire \'tbpgr_utils\'\n\nlangs =\"Ruby\nRuby\nRuby\nJava\nJavaScript\nC++\nRuby\nPython\nBash\nRuby\nRuby\nPerl\nC\nVB\nPHP\nJavaScript\nVB\nJava\nJavaScript\nRuby\nC\nPascal\nPHP\nJava\nPython\nRuby\nPython\nC++\nC\nJava\nJava\nPython\nMathematica\nJavaScript\nPHP\nJava\nC\nScala\nC\nC\n\"\n\ndef group_ranked_list(ary)\n list = ary.group_by.with_index(1) { |e|e }\n .map { |k, v| {language: k, count: v.size} }\n .sort { |a, b|\n (-(a[:count] \u003c=\u003e b[:count])).nonzero? ||\n a[:language] \u003c=\u003e b[:language]\n }.map { |e|e.values }\n rank(list)\nend\n\ndef rank(ary)\n rank_count = 1\n [*0..(ary.size - 1)].each_with_object([]) do |i, memo|\n rank_count += 1 if i.nonzero? \u0026\u0026 (ary[i - 1].last != ary[i].last)\n memo \u003c\u003c [rank_count] + ary[i]\n end\nend\n\ndef headers\n \u003c\u003c-EOS\n|Rank|Language|Count|\n|:--|:--|:--|\n EOS\nend\n\nanswer_langs = langs.each_line.map(\u0026:chomp)\n\nprint headers + group_ranked_list(answer_langs).to_table(:left)\n\n__END__\nArray#to_table は TbpgrUtils gem (自作 gem) のメソッド。\n二次元配列を Markdown のテーブルフォーマットに変換します。\nTbpgrUtils は私の遊び場的ユーティリティ。実用性はない。\n~~~\n\n※多分もっと良い書き方があるでしょう。\n\n## 出力\n\n~~~markdown\n|Rank|Language|Count|\n|:--|:--|:--|\n|1|Ruby |8|\n|2|C |6|\n|2|Java |6|\n|3|JavaScript |4|\n|3|Python |4|\n|4|PHP |3|\n|5|C++ |2|\n|5|VB |2|\n|6|Bash |1|\n|6|Mathematica|1|\n|6|Pascal |1|\n|6|Perl |1|\n|6|Scala |1|\n~~~\n\n## 出力を実際に Markdown として Qiita に貼り付け\n\n|Rank|Language|Count|\n|:--|:--|:--|\n|1|Ruby |8|\n|2|C |6|\n|2|Java |6|\n|3|JavaScript |4|\n|3|Python |4|\n|4|PHP |3|\n|5|C++ |2|\n|5|VB |2|\n|6|Bash |1|\n|6|Mathematica|1|\n|6|Pascal |1|\n|6|Perl |1|\n|6|Scala |1|\n\n## 参照\n* Martin Fowler Collection Pipeline\n http://martinfowler.com/articles/collection-pipeline/\n\n* tbpgr_utils gem\n https://github.com/tbpgr/tbpgr_utils\n","body":"\u003ch1\u003e\n \u003cspan id=\"1-1\" class=\"fragment\"\u003e\u003c/span\u003e\n \u003ca href=\"#1-1\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eRuby | Collection Pipeline でランク集計を実装する\n \u003c/h1\u003e\u003ch2\u003e\n \u003cspan id=\"2-1\" class=\"fragment\"\u003e\u003c/span\u003e\n \u003ca href=\"#2-1\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e概要\n \u003c/h2\u003e\u003cp\u003eCollection Pipeline でランク集計を実装します\u003c/p\u003e\n\u003cp\u003eMartin Fowler 氏の下記ページを参考にしました。\u003cbr\u003e\n\u003ca href=\"http://martinfowler.com/articles/collection-pipeline/\" title=\"\" target=\"_blank\"\u003eCollection Pipeline\u003c/a\u003e\u003c/p\u003e\n\u003ch2\u003e\n \u003cspan id=\"2-2\" class=\"fragment\"\u003e\u003c/span\u003e\n \u003ca href=\"#2-2\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eサンプル\n \u003c/h2\u003e\u003ch3\u003e\n \u003cspan id=\"3-1\" class=\"fragment\"\u003e\u003c/span\u003e\n \u003ca href=\"#3-1\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e仕様\n \u003c/h3\u003e\u003cp\u003eプログラム言語に関するアンケートを行ったとします。\u003cbr\u003e\n解答は言語名で記述されています。\u003c/p\u003e\n\u003cp\u003e言語でグループ化して、言語ごとの投票数を集計し、\u003c/p\u003e\n\n\u003cul\u003e\n\u003cli\u003e言語別投票数の降順、言語名の昇順でソート\u003c/li\u003e\n\u003cli\u003e結果を Markdown (GitHub Flavored Markdown) の表形式で出力する\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3\u003e\n \u003cspan id=\"3-2\" class=\"fragment\"\u003e\u003c/span\u003e\n \u003ca href=\"#3-2\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e入力データ\n \u003c/h3\u003e\u003cdiv class=\"code-frame\" data-lang=\"text\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003eRuby\nRuby\nRuby\nJava\nJavaScript\nC++\nRuby\nPython\nBash\nRuby\nRuby\nPerl\nC\nVB\nPHP\nJavaScript\nVB\nJava\nJavaScript\nRuby\nC\nPascal\nPHP\nJava\nPython\nRuby\nPython\nC++\nC\nJava\nJava\nPython\nMathematica\nJavaScript\nPHP\nJava\nC\nScala\nC\nC\n\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003ch3\u003e\n \u003cspan id=\"3-3\" class=\"fragment\"\u003e\u003c/span\u003e\n \u003ca href=\"#3-3\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eプログラム\n \u003c/h3\u003e\u003cdiv class=\"code-frame\" data-lang=\"ruby\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003cspan class=\"nb\"\u003erequire\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;tbpgr_utils\u0026#39;\u003c/span\u003e\n\n\u003cspan class=\"n\"\u003elangs\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026quot;Ruby\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eRuby\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eRuby\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eJava\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eJavaScript\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eC++\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eRuby\u003c/span\u003e\n\u003cspan class=\"s2\"\u003ePython\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eBash\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eRuby\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eRuby\u003c/span\u003e\n\u003cspan class=\"s2\"\u003ePerl\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eC\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eVB\u003c/span\u003e\n\u003cspan class=\"s2\"\u003ePHP\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eJavaScript\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eVB\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eJava\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eJavaScript\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eRuby\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eC\u003c/span\u003e\n\u003cspan class=\"s2\"\u003ePascal\u003c/span\u003e\n\u003cspan class=\"s2\"\u003ePHP\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eJava\u003c/span\u003e\n\u003cspan class=\"s2\"\u003ePython\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eRuby\u003c/span\u003e\n\u003cspan class=\"s2\"\u003ePython\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eC++\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eC\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eJava\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eJava\u003c/span\u003e\n\u003cspan class=\"s2\"\u003ePython\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eMathematica\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eJavaScript\u003c/span\u003e\n\u003cspan class=\"s2\"\u003ePHP\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eJava\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eC\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eScala\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eC\u003c/span\u003e\n\u003cspan class=\"s2\"\u003eC\u003c/span\u003e\n\u003cspan class=\"s2\"\u003e\u0026quot;\u003c/span\u003e\n\n\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003egroup_ranked_list\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eary\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n \u003cspan class=\"n\"\u003elist\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eary\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003egroup_by\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ewith_index\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n \u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003emap\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ek\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ev\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"ss\"\u003elanguage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003ek\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003ecount\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003ev\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esize\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n \u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esort\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ea\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eb\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\n \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ea\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:count\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eb\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:count\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003enonzero?\u003c/span\u003e \u003cspan class=\"o\"\u003e||\u003c/span\u003e\n \u003cspan class=\"n\"\u003ea\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:language\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;=\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eb\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:language\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n \u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003emap\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003evalues\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n \u003cspan class=\"n\"\u003erank\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003elist\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\n\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003erank\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eary\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n \u003cspan class=\"n\"\u003erank_count\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\n \u003cspan class=\"o\"\u003e[*\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003e.\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eary\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esize\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e].\u003c/span\u003e\u003cspan class=\"n\"\u003eeach_with_object\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e[]\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ememo\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\n \u003cspan class=\"n\"\u003erank_count\u003c/span\u003e \u003cspan class=\"o\"\u003e+=\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003enonzero?\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eary\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"o\"\u003e].\u003c/span\u003e\u003cspan class=\"n\"\u003elast\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"n\"\u003eary\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"o\"\u003e].\u003c/span\u003e\u003cspan class=\"n\"\u003elast\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n \u003cspan class=\"n\"\u003ememo\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003erank_count\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"n\"\u003eary\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\n\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eheaders\u003c/span\u003e\n \u003cspan class=\"o\"\u003e\u0026lt;\u0026lt;-\u003c/span\u003e\u003cspan class=\"no\"\u003eEOS\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e|Rank|Language|Count|\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e|:--|:--|:--|\u003c/span\u003e\n\u003cspan class=\"no\"\u003e EOS\u003c/span\u003e\n\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\n\u003cspan class=\"n\"\u003eanswer_langs\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003elangs\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eeach_line\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003emap\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026amp;\u003c/span\u003e\u003cspan class=\"ss\"\u003e:chomp\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\n\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e \u003cspan class=\"n\"\u003eheaders\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"n\"\u003egroup_ranked_list\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eanswer_langs\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eto_table\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"ss\"\u003e:left\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\n\u003cspan class=\"cp\"\u003e__END__\u003c/span\u003e\n\u003cspan class=\"cp\"\u003eArray#to_table は TbpgrUtils gem (自作 gem) のメソッド。\u003c/span\u003e\n\u003cspan class=\"cp\"\u003e二次元配列を Markdown のテーブルフォーマットに変換します。\u003c/span\u003e\n\u003cspan class=\"cp\"\u003eTbpgrUtils は私の遊び場的ユーティリティ。実用性はない。\u003c/span\u003e\n\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003cp\u003e※多分もっと良い書き方があるでしょう。\u003c/p\u003e\n\u003ch2\u003e\n \u003cspan id=\"2-3\" class=\"fragment\"\u003e\u003c/span\u003e\n \u003ca href=\"#2-3\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e出力\n \u003c/h2\u003e\u003cdiv class=\"code-frame\" data-lang=\"markdown\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e|Rank|Language|Count|\n|:--|:--|:--|\n|1|Ruby |8|\n|2|C |6|\n|2|Java |6|\n|3|JavaScript |4|\n|3|Python |4|\n|4|PHP |3|\n|5|C++ |2|\n|5|VB |2|\n|6|Bash |1|\n|6|Mathematica|1|\n|6|Pascal |1|\n|6|Perl |1|\n|6|Scala |1|\n\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003ch2\u003e\n \u003cspan id=\"2-4\" class=\"fragment\"\u003e\u003c/span\u003e\n \u003ca href=\"#2-4\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e出力を実際に Markdown として Qiita に貼り付け\n \u003c/h2\u003e\n\u003ctable\u003e\u003cthead\u003e\n\u003ctr\u003e\n\u003cth style=\"text-align: left\"\u003eRank\u003c/th\u003e\n\u003cth style=\"text-align: left\"\u003eLanguage\u003c/th\u003e\n\u003cth style=\"text-align: left\"\u003eCount\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd style=\"text-align: left\"\u003e1\u003c/td\u003e\n\u003ctd style=\"text-align: left\"\u003eRuby\u003c/td\u003e\n\u003ctd style=\"text-align: left\"\u003e8\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd style=\"text-align: left\"\u003e2\u003c/td\u003e\n\u003ctd style=\"text-align: left\"\u003eC\u003c/td\u003e\n\u003ctd style=\"text-align: left\"\u003e6\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd style=\"text-align: left\"\u003e2\u003c/td\u003e\n\u003ctd style=\"text-align: left\"\u003eJava\u003c/td\u003e\n\u003ctd style=\"text-align: left\"\u003e6\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd style=\"text-align: left\"\u003e3\u003c/td\u003e\n\u003ctd style=\"text-align: left\"\u003eJavaScript\u003c/td\u003e\n\u003ctd style=\"text-align: left\"\u003e4\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd style=\"text-align: left\"\u003e3\u003c/td\u003e\n\u003ctd style=\"text-align: left\"\u003ePython\u003c/td\u003e\n\u003ctd style=\"text-align: left\"\u003e4\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd style=\"text-align: left\"\u003e4\u003c/td\u003e\n\u003ctd style=\"text-align: left\"\u003ePHP\u003c/td\u003e\n\u003ctd style=\"text-align: left\"\u003e3\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd style=\"text-align: left\"\u003e5\u003c/td\u003e\n\u003ctd style=\"text-align: left\"\u003eC++\u003c/td\u003e\n\u003ctd style=\"text-align: left\"\u003e2\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd style=\"text-align: left\"\u003e5\u003c/td\u003e\n\u003ctd style=\"text-align: left\"\u003eVB\u003c/td\u003e\n\u003ctd style=\"text-align: left\"\u003e2\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd style=\"text-align: left\"\u003e6\u003c/td\u003e\n\u003ctd style=\"text-align: left\"\u003eBash\u003c/td\u003e\n\u003ctd style=\"text-align: left\"\u003e1\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd style=\"text-align: left\"\u003e6\u003c/td\u003e\n\u003ctd style=\"text-align: left\"\u003eMathematica\u003c/td\u003e\n\u003ctd style=\"text-align: left\"\u003e1\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd style=\"text-align: left\"\u003e6\u003c/td\u003e\n\u003ctd style=\"text-align: left\"\u003ePascal\u003c/td\u003e\n\u003ctd style=\"text-align: left\"\u003e1\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd style=\"text-align: left\"\u003e6\u003c/td\u003e\n\u003ctd style=\"text-align: left\"\u003ePerl\u003c/td\u003e\n\u003ctd style=\"text-align: left\"\u003e1\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd style=\"text-align: left\"\u003e6\u003c/td\u003e\n\u003ctd style=\"text-align: left\"\u003eScala\u003c/td\u003e\n\u003ctd style=\"text-align: left\"\u003e1\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\u003c/table\u003e\n\u003ch2\u003e\n \u003cspan id=\"2-5\" class=\"fragment\"\u003e\u003c/span\u003e\n \u003ca href=\"#2-5\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e参照\n \u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cp\u003eMartin Fowler Collection Pipeline\u003cbr\u003e\n\u003ca href=\"http://martinfowler.com/articles/collection-pipeline/\" title=\"http://martinfowler.com/articles/collection-pipeline/\" target=\"_blank\"\u003ehttp://martinfowler.com/articles/collection-pipeline/\u003c/a\u003e\u003c/p\u003e\u003c/li\u003e\n\u003cli\u003e\u003cp\u003etbpgr_utils gem\u003cbr\u003e\n\u003ca href=\"https://github.com/tbpgr/tbpgr_utils\" title=\"https://github.com/tbpgr/tbpgr_utils\" target=\"_blank\"\u003ehttps://github.com/tbpgr/tbpgr_utils\u003c/a\u003e\u003c/p\u003e\u003c/li\u003e\n\u003c/ul\u003e\n","stock_users":["morika-t@github","Reds"]},{"id":132750,"uuid":"7ba662f77b110e9ad0be","user":{"id":43480,"url_name":"tbpgr","profile_image_url":"https://pbs.twimg.com/profile_images/378800000650620553/44cda07bda72f68147025c752716b580_normal.png"},"title":"Ruby | Gvizでインデント構造のテキストをマインドマップに変換する","created_at":"2014-09-12 22:19:05 +0900","updated_at":"2014-09-15 13:09:26 +0900","created_at_in_words":"4日","updated_at_in_words":"1日","tags":[{"name":"Ruby","url_name":"ruby","icon_url":"https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/9de6a11d330f5694820082438f88ccf4a1b289b2/medium.jpg?1364837630","versions":[]},{"name":"Graphviz","url_name":"graphviz","icon_url":"/icons/medium/missing.png","versions":[]}],"stock_count":6,"comment_count":0,"url":"http://qiita.com/tbpgr/items/7ba662f77b110e9ad0be","created_at_as_seconds":1410527945,"tweet":false,"gist_url":null,"private":false,"raw_body":"# Ruby | Gvizでインデント構造のテキストをマインドマップに変換する\n\n## 概要\nGvizでインデント構造のテキストをマインドマップに変換します。\n\n## 利用ライブラリ\n利用ライブラリについては、下記記事参照\n\n[Gvizでインデント構造のテキストをグラフに変換する](http://qiita.com/tbpgr/items/770d56834103efe2b723)\n\n## サンプルコード\n\n~~~ruby\nrequire \'familyable\'\nrequire \'gottani_core\'\nrequire \"gviz\"\n\ntext =\u003c\u003c-EOS\nRuby\n スクリプト言語\n 影響\n From\n Perl\n Lisp\n Smalltalk\n To\n CoffeeScript\n Elixir\n Author\n まつもとゆきひろ\n Matz\n 松江\n Heroku\n Web\n Ruby on Rails\n Author\n DHH\n Products\n Redmine\n GitLab\n Sinatra\n DevOps\n Vagrant\n Author\n M.Hashimoto\n Provider\n Virtual Box\n VMWare\n Workstation\n Fusion\n AWS\n Chef\n Puppet\n 特徴\n 楽しい\n すべてがオブジェクト\n 可読性の高さ\n 動的言語\n 強力なメタプログラミング\n DSL作成の容易さ\nEOS\n\nsp2 = Gottani::Space2.new\nindent_text = sp2.space2_to_common(text)\n\nindexed_indent_text = indent_text.reverse.map.with_index { |e, i|e.merge({id: i}) }\n\nmodule Familyable\n class Person\n attr_accessor :value, :level\n end\nend\n\ni = 0\npersons = indexed_indent_text.reduce([]) do |persons, person|\n parent = indexed_indent_text[(i + 1)..-1]\n .find { |e|e[:level] == person[:level] - 1 }\n parent_ids = parent.nil? ? [] : [parent[:id]]\n person = Familyable::Person.new(id: i, parent_ids: parent_ids).tap do |e|\n e.value = person[:value]\n e.level = person[:level]\n end\n persons \u003c\u003c person\n i += 1\n persons\nend\n\nsorted_persons = persons.reverse\nfamily = Familyable::Family.new(family: persons)\n\nGraph do\n global layout:\'twopi\', overlap:false, splines: :curved\n nodes fontname:\'MS GOTHIC\', shape: :none, style:\'filled\'\n edges dir: :none\n # ノードの定義\n sorted_persons.each { |person|node :\"person#{person.id}\", { label: person.value } }\n\n # エッジの定義\n sorted_persons.each do |person|\n parents = family.get_parents(person)\n next if parents.empty?\n parent = parents.first\n options = {}\n options[:penwidth] = 6 if parent.level == 0\n edge :\"person#{parent.id}_person#{person.id}\", options\n\n end\n\n # *** 根のランク, スタイル定義 ***\n min_id = persons.find { |e|e.level == 0 }.id\n min_person_key = :\"person#{min_id}\"\n rank :min, [min_person_key]\n node min_person_key, { shape: \'pentagon\', fontsize: 80, fillcolor: :red }\n\n # *** 根以外のノードのランク定義 ***\n ranked_persons = sorted_persons.group_by { |e|e.level }.select { |key, value|key.nonzero? }\n ranked_persons.each do |rank, each_rank_persons|\n each_rank_persons.map { |e|node :\"person#{e.id}\", { fontsize: 40 } } if rank == 1\n persons = each_rank_persons.map { |e|\"person#{e.id}\".to_sym }\n rank(:same, persons)\n end\n\n save :\"givz_mindmap\", :png\nend\n~~~\n\n## 出力\n\n\n\n## 参照\n* Gviz gem\n https://github.com/melborne/Gviz\n* tbpgr_utils gem\n https://github.com/tbpgr/tbpgr_utils\n* gottani gem\n https://github.com/tbpgr/gottani\n\n## 関連記事\n* [ryosy383様 Gvizでマインドマップを作成してみた](http://qiita.com/ryosy383/items/c1644031db7da38323bc)\n","body":"\u003ch1\u003e\n \u003cspan id=\"1-1\" class=\"fragment\"\u003e\u003c/span\u003e\n \u003ca href=\"#1-1\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eRuby | Gvizでインデント構造のテキストをマインドマップに変換する\n \u003c/h1\u003e\u003ch2\u003e\n \u003cspan id=\"2-1\" class=\"fragment\"\u003e\u003c/span\u003e\n \u003ca href=\"#2-1\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e概要\n \u003c/h2\u003e\u003cp\u003eGvizでインデント構造のテキストをマインドマップに変換します。\u003c/p\u003e\n\u003ch2\u003e\n \u003cspan id=\"2-2\" class=\"fragment\"\u003e\u003c/span\u003e\n \u003ca href=\"#2-2\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e利用ライブラリ\n \u003c/h2\u003e\u003cp\u003e利用ライブラリについては、下記記事参照\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"http://qiita.com/tbpgr/items/770d56834103efe2b723\" title=\"\"\u003eGvizでインデント構造のテキストをグラフに変換する\u003c/a\u003e\u003c/p\u003e\n\u003ch2\u003e\n \u003cspan id=\"2-3\" class=\"fragment\"\u003e\u003c/span\u003e\n \u003ca href=\"#2-3\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eサンプルコード\n \u003c/h2\u003e\u003cdiv class=\"code-frame\" data-lang=\"ruby\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003cspan class=\"nb\"\u003erequire\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;familyable\u0026#39;\u003c/span\u003e\n\u003cspan class=\"nb\"\u003erequire\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;gottani_core\u0026#39;\u003c/span\u003e\n\u003cspan class=\"nb\"\u003erequire\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026quot;gviz\u0026quot;\u003c/span\u003e\n\n\u003cspan class=\"n\"\u003etext\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026lt;\u0026lt;-\u003c/span\u003e\u003cspan class=\"no\"\u003eEOS\u003c/span\u003e\n\u003cspan class=\"sh\"\u003eRuby\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e スクリプト言語\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e 影響\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e From\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e Perl\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e Lisp\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e Smalltalk\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e To\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e CoffeeScript\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e Elixir\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e Author\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e まつもとゆきひろ\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e Matz\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e 松江\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e Heroku\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e Web\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e Ruby on Rails\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e Author\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e DHH\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e Products\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e Redmine\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e GitLab\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e Sinatra\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e DevOps\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e Vagrant\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e Author\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e M.Hashimoto\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e Provider\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e Virtual Box\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e VMWare\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e Workstation\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e Fusion\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e AWS\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e Chef\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e Puppet\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e 特徴\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e 楽しい\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e すべてがオブジェクト\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e 可読性の高さ\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e 動的言語\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e 強力なメタプログラミング\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e DSL作成の容易さ\u003c/span\u003e\n\u003cspan class=\"no\"\u003eEOS\u003c/span\u003e\n\n\u003cspan class=\"n\"\u003esp2\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"no\"\u003eGottani\u003c/span\u003e\u003cspan class=\"o\"\u003e::\u003c/span\u003e\u003cspan class=\"no\"\u003eSpace2\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003enew\u003c/span\u003e\n\u003cspan class=\"n\"\u003eindent_text\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003esp2\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003espace2_to_common\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etext\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\n\u003cspan class=\"n\"\u003eindexed_indent_text\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eindent_text\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ereverse\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003emap\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ewith_index\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003emerge\u003c/span\u003e\u003cspan class=\"p\"\u003e({\u003c/span\u003e\u003cspan class=\"nb\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e})\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\n\u003cspan class=\"k\"\u003emodule\u003c/span\u003e \u003cspan class=\"nn\"\u003eFamilyable\u003c/span\u003e\n \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003ePerson\u003c/span\u003e\n \u003cspan class=\"kp\"\u003eattr_accessor\u003c/span\u003e \u003cspan class=\"ss\"\u003e:value\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003e:level\u003c/span\u003e\n \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\n\u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\n\u003cspan class=\"n\"\u003epersons\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eindexed_indent_text\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ereduce\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e[]\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003epersons\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eperson\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\n \u003cspan class=\"n\"\u003eparent\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eindexed_indent_text\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003e.\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n \u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efind\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:level\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"n\"\u003eperson\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:level\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n \u003cspan class=\"n\"\u003eparent_ids\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eparent\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003enil?\u003c/span\u003e \u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"o\"\u003e[]\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eparent\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:id\u003c/span\u003e\u003cspan class=\"o\"\u003e]]\u003c/span\u003e\n \u003cspan class=\"n\"\u003eperson\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"no\"\u003eFamilyable\u003c/span\u003e\u003cspan class=\"o\"\u003e::\u003c/span\u003e\u003cspan class=\"no\"\u003ePerson\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nb\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003eparent_ids\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eparent_ids\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etap\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\n \u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003evalue\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eperson\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:value\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n \u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003elevel\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eperson\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:level\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n \u003cspan class=\"n\"\u003epersons\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e \u003cspan class=\"n\"\u003eperson\u003c/span\u003e\n \u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"o\"\u003e+=\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\n \u003cspan class=\"n\"\u003epersons\u003c/span\u003e\n\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\n\u003cspan class=\"n\"\u003esorted_persons\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003epersons\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ereverse\u003c/span\u003e\n\u003cspan class=\"n\"\u003efamily\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"no\"\u003eFamilyable\u003c/span\u003e\u003cspan class=\"o\"\u003e::\u003c/span\u003e\u003cspan class=\"no\"\u003eFamily\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"ss\"\u003efamily\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003epersons\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\n\u003cspan class=\"no\"\u003eGraph\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n \u003cspan class=\"n\"\u003eglobal\u003c/span\u003e \u003cspan class=\"ss\"\u003elayout\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;twopi\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003eoverlap\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"kp\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003esplines\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"ss\"\u003e:curved\u003c/span\u003e\n \u003cspan class=\"n\"\u003enodes\u003c/span\u003e \u003cspan class=\"ss\"\u003efontname\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;MS GOTHIC\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003eshape\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"ss\"\u003e:none\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003estyle\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;filled\u0026#39;\u003c/span\u003e\n \u003cspan class=\"n\"\u003eedges\u003c/span\u003e \u003cspan class=\"ss\"\u003edir\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"ss\"\u003e:none\u003c/span\u003e\n \u003cspan class=\"c1\"\u003e# ノードの定義\u003c/span\u003e\n \u003cspan class=\"n\"\u003esorted_persons\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eeach\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003eperson\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003enode\u003c/span\u003e \u003cspan class=\"ss\"\u003e:\u0026quot;person\u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"n\"\u003eperson\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"ss\"\u003e\u0026quot;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"ss\"\u003elabel\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eperson\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003evalue\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\n \u003cspan class=\"c1\"\u003e# エッジの定義\u003c/span\u003e\n \u003cspan class=\"n\"\u003esorted_persons\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eeach\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003eperson\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\n \u003cspan class=\"n\"\u003eparents\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003efamily\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget_parents\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eperson\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n \u003cspan class=\"k\"\u003enext\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eparents\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eempty?\u003c/span\u003e\n \u003cspan class=\"n\"\u003eparent\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eparents\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efirst\u003c/span\u003e\n \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{}\u003c/span\u003e\n \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:penwidth\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e6\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eparent\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003elevel\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\n \u003cspan class=\"n\"\u003eedge\u003c/span\u003e \u003cspan class=\"ss\"\u003e:\u0026quot;person\u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"n\"\u003eparent\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"ss\"\u003e_person\u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"n\"\u003eperson\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"ss\"\u003e\u0026quot;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eoptions\u003c/span\u003e\n\n \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\n \u003cspan class=\"c1\"\u003e# *** 根のランク, スタイル定義 ***\u003c/span\u003e\n \u003cspan class=\"n\"\u003emin_id\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003epersons\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efind\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003elevel\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eid\u003c/span\u003e\n \u003cspan class=\"n\"\u003emin_person_key\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"ss\"\u003e:\u0026quot;person\u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"n\"\u003emin_id\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"ss\"\u003e\u0026quot;\u003c/span\u003e\n \u003cspan class=\"n\"\u003erank\u003c/span\u003e \u003cspan class=\"ss\"\u003e:min\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003emin_person_key\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n \u003cspan class=\"n\"\u003enode\u003c/span\u003e \u003cspan class=\"n\"\u003emin_person_key\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"ss\"\u003eshape\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;pentagon\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003efontsize\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e80\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003efillcolor\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"ss\"\u003e:red\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\n \u003cspan class=\"c1\"\u003e# *** 根以外のノードのランク定義 ***\u003c/span\u003e\n \u003cspan class=\"n\"\u003eranked_persons\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003esorted_persons\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003egroup_by\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003elevel\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eselect\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ekey\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003evalue\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ekey\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003enonzero?\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n \u003cspan class=\"n\"\u003eranked_persons\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eeach\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003erank\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eeach_rank_persons\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\n \u003cspan class=\"n\"\u003eeach_rank_persons\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003emap\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003enode\u003c/span\u003e \u003cspan class=\"ss\"\u003e:\u0026quot;person\u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"ss\"\u003e\u0026quot;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"ss\"\u003efontsize\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e40\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003erank\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\n \u003cspan class=\"n\"\u003epersons\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eeach_rank_persons\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003emap\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026quot;person\u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026quot;\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eto_sym\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n \u003cspan class=\"n\"\u003erank\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"ss\"\u003e:same\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003epersons\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\n \u003cspan class=\"n\"\u003esave\u003c/span\u003e \u003cspan class=\"ss\"\u003e:\u0026quot;givz_mindmap\u0026quot;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003e:png\u003c/span\u003e\n\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003ch2\u003e\n \u003cspan id=\"2-4\" class=\"fragment\"\u003e\u003c/span\u003e\n \u003ca href=\"#2-4\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e出力\n \u003c/h2\u003e\u003cp\u003e\u003cimg src=\"https://qiita-image-store.s3.amazonaws.com/0/43480/b67aaead-ba36-cd32-3211-e89128d87404.png\" title=\"\" alt=\"givz_mindmap.png\"\u003e\u003c/img\u003e\u003c/p\u003e\n\u003ch2\u003e\n \u003cspan id=\"2-5\" class=\"fragment\"\u003e\u003c/span\u003e\n \u003ca href=\"#2-5\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e参照\n \u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eGviz gem\n\u003ca href=\"https://github.com/melborne/Gviz\" title=\"https://github.com/melborne/Gviz\" target=\"_blank\"\u003ehttps://github.com/melborne/Gviz\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003etbpgr_utils gem\n\u003ca href=\"https://github.com/tbpgr/tbpgr_utils\" title=\"https://github.com/tbpgr/tbpgr_utils\" target=\"_blank\"\u003ehttps://github.com/tbpgr/tbpgr_utils\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003egottani gem\n\u003ca href=\"https://github.com/tbpgr/gottani\" title=\"https://github.com/tbpgr/gottani\" target=\"_blank\"\u003ehttps://github.com/tbpgr/gottani\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2\u003e\n \u003cspan id=\"2-6\" class=\"fragment\"\u003e\u003c/span\u003e\n \u003ca href=\"#2-6\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e関連記事\n \u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"http://qiita.com/ryosy383/items/c1644031db7da38323bc\" title=\"\"\u003eryosy383様 Gvizでマインドマップを作成してみた\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n","stock_users":["kasumani","kumazo@github","makyos","umanoda","tamano","ryosy383"]},{"id":132746,"uuid":"770d56834103efe2b723","user":{"id":43480,"url_name":"tbpgr","profile_image_url":"https://pbs.twimg.com/profile_images/378800000650620553/44cda07bda72f68147025c752716b580_normal.png"},"title":"Ruby | Gvizでインデント構造のテキストをグラフに変換する","created_at":"2014-09-12 22:16:47 +0900","updated_at":"2014-09-15 20:19:28 +0900","created_at_in_words":"4日","updated_at_in_words":"約17時間","tags":[{"name":"Ruby","url_name":"ruby","icon_url":"https://s3-ap-northeast-1.amazonaws.com/qiita-tag-image/9de6a11d330f5694820082438f88ccf4a1b289b2/medium.jpg?1364837630","versions":[]},{"name":"Graphviz","url_name":"graphviz","icon_url":"/icons/medium/missing.png","versions":[]}],"stock_count":4,"comment_count":0,"url":"http://qiita.com/tbpgr/items/770d56834103efe2b723","created_at_as_seconds":1410527807,"tweet":false,"gist_url":null,"private":false,"raw_body":"# Ruby | Gvizでインデント構造のテキストをグラフに変換する\n\n## 概要\nGvizでインデント構造のテキストをグラフに変換します。\n\n## 利用ライブラリ\n* Gviz gem\n* TbpgrUtils gem (自作 gem)\n * 私の遊び場的ユーティリティ。実用性はない。\n * 今回は familyable を利用する。親子関係のデータの取得用。\n* Gottani gem (自作 gem)\n * タブ、スペース、 Markdown の見出し、はてな記法 などツリー構造のテキストの相互変換用ツール\n * 今回は変換時の中間処理用に用意されている、 Array[Hash{:level, :value}] を返却するメソッドを利用\n * level は深さ , value は文字列\n\n## サンプルコード\n\n~~~ruby\nrequire \'familyable\'\nrequire \'gottani_core\'\nrequire \"gviz\"\n\ntext =\u003c\u003c-EOS\nroot\n child1\n child1_1\n child1_1_1\n child1_1_2\n child1_1_2_1\n child1_1_2_2\n child1_2\n child2\n child2_1\n child3\nEOS\n\nsp2 = Gottani::Space2.new\nindent_text = sp2.space2_to_common(text)\n\nindexed_indent_text = indent_text.reverse.map.with_index { |e, i|e.merge({id: i}) }\n# indexed_indent_text はこんなデータが入っています\n# [{:level=\u003e1, :value=\u003e\"child3\", :id=\u003e0},\n# {:level=\u003e2, :value=\u003e\"child2_1\", :id=\u003e1},\n# {:level=\u003e1, :value=\u003e\"child2\", :id=\u003e2},\n# {:level=\u003e2, :value=\u003e\"child1_2\", :id=\u003e3},\n# {:level=\u003e4, :value=\u003e\"child1_1_2_2\", :id=\u003e4},\n# {:level=\u003e4, :value=\u003e\"child1_1_2_1\", :id=\u003e5},\n# {:level=\u003e3, :value=\u003e\"child1_1_2\", :id=\u003e6},\n# {:level=\u003e3, :value=\u003e\"child1_1_1\", :id=\u003e7},\n# {:level=\u003e2, :value=\u003e\"child1_1\", :id=\u003e8},\n# {:level=\u003e1, :value=\u003e\"child1\", :id=\u003e9},\n# {:level=\u003e0, :value=\u003e\"root\", :id=\u003e10}]\n\nmodule Familyable\n class Person\n attr_accessor :value, :level\n end\nend\n\ni = 0\npersons = indexed_indent_text.reduce([]) do |persons, person|\n parent = indexed_indent_text[(i + 1)..-1]\n .find { |e|e[:level] == person[:level] - 1 }\n parent_ids = parent.nil? ? [] : [parent[:id]]\n person = Familyable::Person.new(id: i, parent_ids: parent_ids).tap do |e|\n e.value = person[:value]\n e.level = person[:level]\n end\n persons \u003c\u003c person\n i += 1\n persons\nend\n\nsorted_persons = persons.reverse\nfamily = Familyable::Family.new(family: persons)\n# family にはこんなデータが入っています。\n#\u003cFamilyable::Family:0x00000600291778\n# @family=\n# [#\u003cFamilyable::Person:0x00000600293d70\n# @id=0,\n# @level=1,\n# @parent_ids=[10],\n# @value=\"child3\"\u003e,\n# #\u003cFamilyable::Person:0x00000600293aa0\n# @id=1,\n# @level=2,\n# @parent_ids=[2],\n# @value=\"child2_1\"\u003e,\n# #\u003cFamilyable::Person:0x000006002937f8\n# @id=2,\n# @level=1,\n# @parent_ids=[10],\n# @value=\"child2\"\u003e,\n# #\u003cFamilyable::Person:0x00000600293618\n# @id=3,\n# @level=2,\n# @parent_ids=[9],\n# @value=\"child1_2\"\u003e,\n# #\u003cFamilyable::Person:0x00000600293208\n# @id=4,\n# @level=4,\n# @parent_ids=[6],\n# @value=\"child1_1_2_2\"\u003e,\n# #\u003cFamilyable::Person:0x00000600292d08\n# @id=5,\n# @level=4,\n# @parent_ids=[6],\n# @value=\"child1_1_2_1\"\u003e,\n# #\u003cFamilyable::Person:0x00000600292948\n# @id=6,\n# @level=3,\n# @parent_ids=[8],\n# @value=\"child1_1_2\"\u003e,\n# #\u003cFamilyable::Person:0x000006002925b0\n# @id=7,\n# @level=3,\n# @parent_ids=[8],\n# @value=\"child1_1_1\"\u003e,\n# #\u003cFamilyable::Person:0x00000600292240\n# @id=8,\n# @level=2,\n# @parent_ids=[9],\n# @value=\"child1_1\"\u003e,\n# #\u003cFamilyable::Person:0x00000600291d90\n# @id=9,\n# @level=1,\n# @parent_ids=[10],\n# @value=\"child1\"\u003e,\n# #\u003cFamilyable::Person:0x00000600291a48\n# @id=10,\n# @level=0,\n# @parent_ids=[],\n# @value=\"root\"\u003e]\u003e\n\nGraph do\n global layout:\'dot\'\n # ノードの定義\n sorted_persons.each { |person|node :\"person#{person.id}\", { label: person.value } }\n\n # エッジの定義\n sorted_persons.each do |person|\n parents = family.get_parents(person)\n next if parents.empty?\n parent = parents.first\n edge :\"person#{parent.id}_person#{person.id}\"\n end\n\n # *** 根のランク, スタイル定義 ***\n min_id = persons.find { |e|e.level == 0 }.id\n min_person_key = :\"person#{min_id}\"\n rank :min, [min_person_key]\n node min_person_key, { width: 2, shape: \'doublecircle\' }\n\n # *** 根以外のノードのランク定義 ***\n ranked_persons = sorted_persons.group_by { |e|e.level }.select { |key, value|key.nonzero? }\n ranked_persons.each do |rank, each_rank_persons|\n persons = each_rank_persons.map { |e|\"person#{e.id}\".to_sym }\n rank(:same, persons)\n end\n\n save :\"givz_tree\", :png\nend\n~~~\n\n## 出力\n\n\n\n## 補足1\ngottani は space2 インデント以外に\n\n* space4\n* hard tab\n* markdown 見出し\n* hatena 見出し\n\nなどに対応しているため、サンプルコードの text の内容を\n上記のフォーマットに変更し、 `Gottani::Space2.new` の代わりに\n`Gottani::Tagb.new` などを利用すれば別のフォーマットにも対応可能です。\n\n## 補足2\nもう1段階抽象化すれば、\n\n* ツリー構造のテキスト\n* フォーマット(タブ?、インデント?)\n* 表示スタイル(色、形など)\n\nあたりを設定値として与えるだけで、ツリー構造を画像化する\nツールが作成できそうな・・・。\n\n設定の自由度を考えると、画像の出力まですべて行ってしまうよりも、\nGvizのソースを生成して、そのあと表示スタイル部分を好きに変更してもらう\n「消極的な自動生成」※書籍 達人プログラマ参照\nにしたほうが汎用性がありそう。\n\n内部DSLの設定ファイル\n=> ツリー to Gviz を実行\n=> 生成された Gviz の色・形など設定部を変更\n=> Gviz 実行\n=> 画像が生成される。\n\nオプションとして、デフォルトのレイアウトでよければ\n画像の出力まで一気に行うサブコマンドがあってもよさそう。\n\n内部DSLには\n\n* ツリーフォーマットのテキスト\n* 画像ファイル名\n* Gviz のコードを出力する rb ファイル名\n* 表示スタイル(大まかにスタイル指定。細かくやりたい人は自動生成後に手動で)\n\nを設定できるようにすればいい。\n\n## 参照\n* Gviz gem \n https://github.com/melborne/Gviz\n* tbpgr_utils gem \n https://github.com/tbpgr/tbpgr_utils\n* gottani gem \n https://github.com/tbpgr/gottani\n","body":"\u003ch1\u003e\n \u003cspan id=\"1-1\" class=\"fragment\"\u003e\u003c/span\u003e\n \u003ca href=\"#1-1\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eRuby | Gvizでインデント構造のテキストをグラフに変換する\n \u003c/h1\u003e\u003ch2\u003e\n \u003cspan id=\"2-1\" class=\"fragment\"\u003e\u003c/span\u003e\n \u003ca href=\"#2-1\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e概要\n \u003c/h2\u003e\u003cp\u003eGvizでインデント構造のテキストをグラフに変換します。\u003c/p\u003e\n\u003ch2\u003e\n \u003cspan id=\"2-2\" class=\"fragment\"\u003e\u003c/span\u003e\n \u003ca href=\"#2-2\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e利用ライブラリ\n \u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eGviz gem\u003c/li\u003e\n\u003cli\u003eTbpgrUtils gem (自作 gem)\n\n\u003cul\u003e\n\u003cli\u003e私の遊び場的ユーティリティ。実用性はない。\u003c/li\u003e\n\u003cli\u003e今回は familyable を利用する。親子関係のデータの取得用。\u003c/li\u003e\n\u003c/ul\u003e\u003c/li\u003e\n\u003cli\u003eGottani gem (自作 gem)\n\n\u003cul\u003e\n\u003cli\u003eタブ、スペース、 Markdown の見出し、はてな記法 などツリー構造のテキストの相互変換用ツール\u003c/li\u003e\n\u003cli\u003e今回は変換時の中間処理用に用意されている、 Array[Hash{:level, :value}] を返却するメソッドを利用\u003c/li\u003e\n\u003cli\u003elevel は深さ , value は文字列\u003c/li\u003e\n\u003c/ul\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2\u003e\n \u003cspan id=\"2-3\" class=\"fragment\"\u003e\u003c/span\u003e\n \u003ca href=\"#2-3\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eサンプルコード\n \u003c/h2\u003e\u003cdiv class=\"code-frame\" data-lang=\"ruby\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003cspan class=\"nb\"\u003erequire\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;familyable\u0026#39;\u003c/span\u003e\n\u003cspan class=\"nb\"\u003erequire\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;gottani_core\u0026#39;\u003c/span\u003e\n\u003cspan class=\"nb\"\u003erequire\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026quot;gviz\u0026quot;\u003c/span\u003e\n\n\u003cspan class=\"n\"\u003etext\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026lt;\u0026lt;-\u003c/span\u003e\u003cspan class=\"no\"\u003eEOS\u003c/span\u003e\n\u003cspan class=\"sh\"\u003eroot\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e child1\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e child1_1\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e child1_1_1\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e child1_1_2\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e child1_1_2_1\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e child1_1_2_2\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e child1_2\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e child2\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e child2_1\u003c/span\u003e\n\u003cspan class=\"sh\"\u003e child3\u003c/span\u003e\n\u003cspan class=\"no\"\u003eEOS\u003c/span\u003e\n\n\u003cspan class=\"n\"\u003esp2\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"no\"\u003eGottani\u003c/span\u003e\u003cspan class=\"o\"\u003e::\u003c/span\u003e\u003cspan class=\"no\"\u003eSpace2\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003enew\u003c/span\u003e\n\u003cspan class=\"n\"\u003eindent_text\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003esp2\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003espace2_to_common\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etext\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\n\u003cspan class=\"n\"\u003eindexed_indent_text\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eindent_text\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ereverse\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003emap\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ewith_index\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003emerge\u003c/span\u003e\u003cspan class=\"p\"\u003e({\u003c/span\u003e\u003cspan class=\"nb\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e})\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# indexed_indent_text はこんなデータが入っています\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# [{:level=\u0026gt;1, :value=\u0026gt;\u0026quot;child3\u0026quot;, :id=\u0026gt;0},\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# {:level=\u0026gt;2, :value=\u0026gt;\u0026quot;child2_1\u0026quot;, :id=\u0026gt;1},\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# {:level=\u0026gt;1, :value=\u0026gt;\u0026quot;child2\u0026quot;, :id=\u0026gt;2},\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# {:level=\u0026gt;2, :value=\u0026gt;\u0026quot;child1_2\u0026quot;, :id=\u0026gt;3},\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# {:level=\u0026gt;4, :value=\u0026gt;\u0026quot;child1_1_2_2\u0026quot;, :id=\u0026gt;4},\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# {:level=\u0026gt;4, :value=\u0026gt;\u0026quot;child1_1_2_1\u0026quot;, :id=\u0026gt;5},\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# {:level=\u0026gt;3, :value=\u0026gt;\u0026quot;child1_1_2\u0026quot;, :id=\u0026gt;6},\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# {:level=\u0026gt;3, :value=\u0026gt;\u0026quot;child1_1_1\u0026quot;, :id=\u0026gt;7},\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# {:level=\u0026gt;2, :value=\u0026gt;\u0026quot;child1_1\u0026quot;, :id=\u0026gt;8},\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# {:level=\u0026gt;1, :value=\u0026gt;\u0026quot;child1\u0026quot;, :id=\u0026gt;9},\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# {:level=\u0026gt;0, :value=\u0026gt;\u0026quot;root\u0026quot;, :id=\u0026gt;10}]\u003c/span\u003e\n\n\u003cspan class=\"k\"\u003emodule\u003c/span\u003e \u003cspan class=\"nn\"\u003eFamilyable\u003c/span\u003e\n \u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003ePerson\u003c/span\u003e\n \u003cspan class=\"kp\"\u003eattr_accessor\u003c/span\u003e \u003cspan class=\"ss\"\u003e:value\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003e:level\u003c/span\u003e\n \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\n\u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\n\u003cspan class=\"n\"\u003epersons\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eindexed_indent_text\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ereduce\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e[]\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003epersons\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eperson\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\n \u003cspan class=\"n\"\u003eparent\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eindexed_indent_text\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003e.\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n \u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efind\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:level\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"n\"\u003eperson\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:level\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n \u003cspan class=\"n\"\u003eparent_ids\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eparent\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003enil?\u003c/span\u003e \u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"o\"\u003e[]\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eparent\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:id\u003c/span\u003e\u003cspan class=\"o\"\u003e]]\u003c/span\u003e\n \u003cspan class=\"n\"\u003eperson\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"no\"\u003eFamilyable\u003c/span\u003e\u003cspan class=\"o\"\u003e::\u003c/span\u003e\u003cspan class=\"no\"\u003ePerson\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nb\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003eparent_ids\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eparent_ids\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etap\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\n \u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003evalue\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eperson\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:value\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n \u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003elevel\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eperson\u003c/span\u003e\u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"ss\"\u003e:level\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n \u003cspan class=\"n\"\u003epersons\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u0026lt;\u003c/span\u003e \u003cspan class=\"n\"\u003eperson\u003c/span\u003e\n \u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"o\"\u003e+=\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\n \u003cspan class=\"n\"\u003epersons\u003c/span\u003e\n\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\n\u003cspan class=\"n\"\u003esorted_persons\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003epersons\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ereverse\u003c/span\u003e\n\u003cspan class=\"n\"\u003efamily\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"no\"\u003eFamilyable\u003c/span\u003e\u003cspan class=\"o\"\u003e::\u003c/span\u003e\u003cspan class=\"no\"\u003eFamily\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003enew\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"ss\"\u003efamily\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003epersons\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# family にはこんなデータが入っています。\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e#\u0026lt;Familyable::Family:0x00000600291778\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @family=\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# [#\u0026lt;Familyable::Person:0x00000600293d70\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @id=0,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @level=1,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @parent_ids=[10],\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @value=\u0026quot;child3\u0026quot;\u0026gt;,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# #\u0026lt;Familyable::Person:0x00000600293aa0\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @id=1,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @level=2,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @parent_ids=[2],\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @value=\u0026quot;child2_1\u0026quot;\u0026gt;,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# #\u0026lt;Familyable::Person:0x000006002937f8\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @id=2,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @level=1,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @parent_ids=[10],\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @value=\u0026quot;child2\u0026quot;\u0026gt;,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# #\u0026lt;Familyable::Person:0x00000600293618\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @id=3,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @level=2,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @parent_ids=[9],\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @value=\u0026quot;child1_2\u0026quot;\u0026gt;,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# #\u0026lt;Familyable::Person:0x00000600293208\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @id=4,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @level=4,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @parent_ids=[6],\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @value=\u0026quot;child1_1_2_2\u0026quot;\u0026gt;,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# #\u0026lt;Familyable::Person:0x00000600292d08\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @id=5,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @level=4,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @parent_ids=[6],\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @value=\u0026quot;child1_1_2_1\u0026quot;\u0026gt;,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# #\u0026lt;Familyable::Person:0x00000600292948\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @id=6,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @level=3,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @parent_ids=[8],\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @value=\u0026quot;child1_1_2\u0026quot;\u0026gt;,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# #\u0026lt;Familyable::Person:0x000006002925b0\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @id=7,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @level=3,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @parent_ids=[8],\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @value=\u0026quot;child1_1_1\u0026quot;\u0026gt;,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# #\u0026lt;Familyable::Person:0x00000600292240\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @id=8,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @level=2,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @parent_ids=[9],\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @value=\u0026quot;child1_1\u0026quot;\u0026gt;,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# #\u0026lt;Familyable::Person:0x00000600291d90\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @id=9,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @level=1,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @parent_ids=[10],\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @value=\u0026quot;child1\u0026quot;\u0026gt;,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# #\u0026lt;Familyable::Person:0x00000600291a48\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @id=10,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @level=0,\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @parent_ids=[],\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e# @value=\u0026quot;root\u0026quot;\u0026gt;]\u0026gt;\u003c/span\u003e\n\n\u003cspan class=\"no\"\u003eGraph\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e\n \u003cspan class=\"n\"\u003eglobal\u003c/span\u003e \u003cspan class=\"ss\"\u003elayout\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;dot\u0026#39;\u003c/span\u003e\n \u003cspan class=\"c1\"\u003e# ノードの定義\u003c/span\u003e\n \u003cspan class=\"n\"\u003esorted_persons\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eeach\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003eperson\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003enode\u003c/span\u003e \u003cspan class=\"ss\"\u003e:\u0026quot;person\u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"n\"\u003eperson\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"ss\"\u003e\u0026quot;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"ss\"\u003elabel\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eperson\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003evalue\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\n \u003cspan class=\"c1\"\u003e# エッジの定義\u003c/span\u003e\n \u003cspan class=\"n\"\u003esorted_persons\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eeach\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003eperson\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\n \u003cspan class=\"n\"\u003eparents\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003efamily\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget_parents\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eperson\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n \u003cspan class=\"k\"\u003enext\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eparents\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eempty?\u003c/span\u003e\n \u003cspan class=\"n\"\u003eparent\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eparents\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efirst\u003c/span\u003e\n \u003cspan class=\"n\"\u003eedge\u003c/span\u003e \u003cspan class=\"ss\"\u003e:\u0026quot;person\u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"n\"\u003eparent\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"ss\"\u003e_person\u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"n\"\u003eperson\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"ss\"\u003e\u0026quot;\u003c/span\u003e\n \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\n \u003cspan class=\"c1\"\u003e# *** 根のランク, スタイル定義 ***\u003c/span\u003e\n \u003cspan class=\"n\"\u003emin_id\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003epersons\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efind\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003elevel\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eid\u003c/span\u003e\n \u003cspan class=\"n\"\u003emin_person_key\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"ss\"\u003e:\u0026quot;person\u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"n\"\u003emin_id\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"ss\"\u003e\u0026quot;\u003c/span\u003e\n \u003cspan class=\"n\"\u003erank\u003c/span\u003e \u003cspan class=\"ss\"\u003e:min\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"o\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003emin_person_key\u003c/span\u003e\u003cspan class=\"o\"\u003e]\u003c/span\u003e\n \u003cspan class=\"n\"\u003enode\u003c/span\u003e \u003cspan class=\"n\"\u003emin_person_key\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"ss\"\u003ewidth\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003eshape\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;doublecircle\u0026#39;\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\n \u003cspan class=\"c1\"\u003e# *** 根以外のノードのランク定義 ***\u003c/span\u003e\n \u003cspan class=\"n\"\u003eranked_persons\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003esorted_persons\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003egroup_by\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003elevel\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eselect\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ekey\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003evalue\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ekey\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003enonzero?\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n \u003cspan class=\"n\"\u003eranked_persons\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eeach\u003c/span\u003e \u003cspan class=\"k\"\u003edo\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003erank\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eeach_rank_persons\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\n \u003cspan class=\"n\"\u003epersons\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eeach_rank_persons\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003emap\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e|\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026quot;person\u003c/span\u003e\u003cspan class=\"si\"\u003e#{\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026quot;\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eto_sym\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n \u003cspan class=\"n\"\u003erank\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"ss\"\u003e:same\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003epersons\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n \u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\n \u003cspan class=\"n\"\u003esave\u003c/span\u003e \u003cspan class=\"ss\"\u003e:\u0026quot;givz_tree\u0026quot;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"ss\"\u003e:png\u003c/span\u003e\n\u003cspan class=\"k\"\u003eend\u003c/span\u003e\n\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\u003ch2\u003e\n \u003cspan id=\"2-4\" class=\"fragment\"\u003e\u003c/span\u003e\n \u003ca href=\"#2-4\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e出力\n \u003c/h2\u003e\u003cp\u003e\u003cimg src=\"https://qiita-image-store.s3.amazonaws.com/0/43480/1d1fc4f1-65b3-fedb-f6d9-2f16be0fde26.png\" title=\"\" alt=\"givz_tree.png\"\u003e\u003c/img\u003e\u003c/p\u003e\n\u003ch2\u003e\n \u003cspan id=\"2-5\" class=\"fragment\"\u003e\u003c/span\u003e\n \u003ca href=\"#2-5\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e補足1\n \u003c/h2\u003e\u003cp\u003egottani は space2 インデント以外に\u003c/p\u003e\n\n\u003cul\u003e\n\u003cli\u003espace4\u003c/li\u003e\n\u003cli\u003ehard tab\u003c/li\u003e\n\u003cli\u003emarkdown 見出し\u003c/li\u003e\n\u003cli\u003ehatena 見出し\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eなどに対応しているため、サンプルコードの text の内容を\u003cbr\u003e\n上記のフォーマットに変更し、 \u003ccode\u003eGottani::Space2.new\u003c/code\u003e の代わりに\u003cbr\u003e\n\u003ccode\u003eGottani::Tagb.new\u003c/code\u003e などを利用すれば別のフォーマットにも対応可能です。\u003c/p\u003e\n\u003ch2\u003e\n \u003cspan id=\"2-6\" class=\"fragment\"\u003e\u003c/span\u003e\n \u003ca href=\"#2-6\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e補足2\n \u003c/h2\u003e\u003cp\u003eもう1段階抽象化すれば、\u003c/p\u003e\n\n\u003cul\u003e\n\u003cli\u003eツリー構造のテキスト\u003c/li\u003e\n\u003cli\u003eフォーマット(タブ?、インデント?)\u003c/li\u003e\n\u003cli\u003e表示スタイル(色、形など)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eあたりを設定値として与えるだけで、ツリー構造を画像化する\u003cbr\u003e\nツールが作成できそうな・・・。\u003c/p\u003e\n\u003cp\u003e設定の自由度を考えると、画像の出力まですべて行ってしまうよりも、\u003cbr\u003e\nGvizのソースを生成して、そのあと表示スタイル部分を好きに変更してもらう\u003cbr\u003e\n「消極的な自動生成」※書籍 達人プログラマ参照\u003cbr\u003e\nにしたほうが汎用性がありそう。\u003c/p\u003e\n\u003cp\u003e内部DSLの設定ファイル\u003cbr\u003e\n=> ツリー to Gviz を実行\u003cbr\u003e\n=> 生成された Gviz の色・形など設定部を変更\u003cbr\u003e\n=> Gviz 実行\u003cbr\u003e\n=> 画像が生成される。\u003c/p\u003e\n\u003cp\u003eオプションとして、デフォルトのレイアウトでよければ\u003cbr\u003e\n画像の出力まで一気に行うサブコマンドがあってもよさそう。\u003c/p\u003e\n\u003cp\u003e内部DSLには\u003c/p\u003e\n\n\u003cul\u003e\n\u003cli\u003eツリーフォーマットのテキスト\u003c/li\u003e\n\u003cli\u003e画像ファイル名\u003c/li\u003e\n\u003cli\u003eGviz のコードを出力する rb ファイル名\u003c/li\u003e\n\u003cli\u003e表示スタイル(大まかにスタイル指定。細かくやりたい人は自動生成後に手動で)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eを設定できるようにすればいい。\u003c/p\u003e\n\u003ch2\u003e\n \u003cspan id=\"2-7\" class=\"fragment\"\u003e\u003c/span\u003e\n \u003ca href=\"#2-7\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e参照\n \u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eGviz gem\u003cbr\u003e\n\u003ca href=\"https://github.com/melborne/Gviz\" title=\"https://github.com/melborne/Gviz\" target=\"_blank\"\u003ehttps://github.com/melborne/Gviz\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003etbpgr_utils gem\u003cbr\u003e\n\u003ca href=\"https://github.com/tbpgr/tbpgr_utils\" title=\"https://github.com/tbpgr/tbpgr_utils\" target=\"_blank\"\u003ehttps://github.com/tbpgr/tbpgr_utils\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003egottani gem\u003cbr\u003e\n\u003ca href=\"https://github.com/tbpgr/gottani\" title=\"https://github.com/tbpgr/gottani\" target=\"_blank\"\u003ehttps://github.com/tbpgr/gottani\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n","stock_users":["kasumani","kumazo@github","umanoda","tamano"]}]'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
cases.each do |c|
|
30
|
+
it "|case_no=#{c[:case_no]}|case_title=#{c[:case_title]}" do
|
31
|
+
begin
|
32
|
+
case_before c
|
33
|
+
|
34
|
+
# -- given --
|
35
|
+
qiita_json_loader = QiitaScouter::QiitaItemsJsonLoader.new
|
36
|
+
qiita_json_loader.stub(:open).and_return(DummyForItem.new)
|
37
|
+
|
38
|
+
# -- when --
|
39
|
+
qiita_json_loader.load(c[:user])
|
40
|
+
actual = qiita_json_loader.articles
|
41
|
+
|
42
|
+
# -- then --
|
43
|
+
expect(actual.articles.size).to eq(c[:expected])
|
44
|
+
ensure
|
45
|
+
case_after c
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def case_before(c) # rubocop:disable UnusedMethodArgument
|
50
|
+
# implement each case before
|
51
|
+
end
|
52
|
+
|
53
|
+
def case_after(c) # rubocop:disable UnusedMethodArgument
|
54
|
+
# implement each case after
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
# rubocop:enable LineLength, UnusedMethodArgument
|