ruby_uml_class 0.1.0

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.
@@ -0,0 +1,297 @@
1
+ require "tempfile"
2
+
3
+ CStruct = Struct.new(:type,
4
+ :name,
5
+ :block_count,
6
+ :var_list,
7
+ :method_list,
8
+ :inherit_list,
9
+ :composition_list)
10
+
11
+ def print_uml(out, out_list)
12
+ out_list.each do |o_list|
13
+ if o_list.type == :class_start
14
+ # nop
15
+ elsif o_list.type == :module_start
16
+ out.push "namespace #{o_list.name} {"
17
+ elsif o_list.type == :class_end
18
+ pp o_list if o_list.name == ""
19
+ out.push "class #{o_list.name} {"
20
+ # インスタンス変数の出力
21
+ o_list.var_list.uniq.each do |iv|
22
+ out.push iv
23
+ end
24
+ # メソッドの出力
25
+ o_list.method_list.each do |ml|
26
+ out.push ml
27
+ end
28
+ out.push "}"
29
+ # 継承リストの出力
30
+ o_list.inherit_list.each do |ih|
31
+ out.push "#{o_list.name} --|> #{ih}"
32
+ end
33
+ # compo
34
+ o_list.composition_list.uniq.each do |co|
35
+ out.push "#{o_list.name} *-- #{co}"
36
+ end
37
+ elsif o_list.type == :module_end
38
+ # インスタンス変数がある場合はモジュール名と同じクラスを定義
39
+ if o_list.var_list.size != 0 or
40
+ o_list.method_list.size != 0 or
41
+ o_list.inherit_list.size != 0 or
42
+ o_list.composition_list.size != 0
43
+ pp o_list if o_list.name == ""
44
+ out.push "class #{o_list.name} {"
45
+ # インスタンス変数の出力
46
+ o_list.var_list.uniq.each do |iv|
47
+ out.push iv
48
+ end
49
+ # メソッドの出力
50
+ o_list.method_list.each do |ml|
51
+ out.push ml
52
+ end
53
+ out.push "}"
54
+ # 継承リストの出力
55
+ o_list.inherit_list.each do |ih|
56
+ out.push "#{o_list.name} --|> #{ih}"
57
+ end
58
+ # compo
59
+ o_list.composition_list.uniq.each do |co|
60
+ out.push "#{o_list.name} *-- #{co}"
61
+ end
62
+ end
63
+ out.push "}"
64
+ else
65
+ # error
66
+ puts "error!"
67
+ end
68
+ end
69
+ return out
70
+ end
71
+
72
+ def delete_here_doc(buf)
73
+ new_buf = []
74
+ here_doc = false
75
+ here_word = ""
76
+ buf.each_line do |line|
77
+ if line =~ /(<<|<<~|<<-)[A-Z]+/
78
+ here_doc = true
79
+ here_word = line.match(/(<<|<<~|<<-)[A-Z]+/).to_s.gsub(/[<~-]/, "")
80
+ end
81
+ if here_word != "" and line =~ Regexp.new("^\s*#{here_word}$")
82
+ here_word = ""
83
+ here_doc = false
84
+ pp line
85
+ end
86
+ if here_doc == false
87
+ new_buf.push line
88
+ else
89
+ pp line
90
+ end
91
+ end
92
+ return new_buf.join("")
93
+ end
94
+
95
+ def create_uml_class(in_dir, out_file)
96
+ out = []
97
+ out.push "@startuml"
98
+
99
+ puts "in_dir = #{in_dir}"
100
+ main_composition_list = []
101
+ main_method_list = []
102
+ global_var = []
103
+
104
+ Dir.glob("#{in_dir}/**/*.{rb,ru}") do |f|
105
+ puts f
106
+ buf = ""
107
+ Tempfile.create("rufo") do |tmp_file|
108
+ FileUtils.cp(f, tmp_file.path)
109
+ open("|rufo #{tmp_file.path}") do |f|
110
+ if f.read =~ /error/
111
+ puts "rufo error #{f}"
112
+ return
113
+ else
114
+ buf = File.binread tmp_file.path
115
+ end
116
+ end
117
+ end
118
+
119
+ # コメント削除
120
+ buf.gsub!(/(([\/\"\'].*?[\/\"\'])|([^\/\"\'\)\s]*#.+?$))/) do |m|
121
+ if m[0] == "#" and m[0] != "{"
122
+ #puts "comment #{m}"
123
+ # コメント
124
+ ""
125
+ else
126
+ #puts "not comment #{m}"
127
+ # コメント以外
128
+ m
129
+ end
130
+ end
131
+ # ヒアドキュメント削除
132
+ buf = delete_here_doc(buf)
133
+
134
+ out_list = []
135
+ cstruct_list = []
136
+ block_count = 0
137
+ method_type = :public
138
+ class_name = ""
139
+ # ソースを解析
140
+ buf.each_line do |line|
141
+ next if line =~ /^$/ # 空行は対象外
142
+
143
+ # ブロックの開始/終了
144
+ indent_num = line.match(/^[ ]+/).to_s.size / 2
145
+ if block_count == indent_num
146
+ # 変化なし
147
+ elsif block_count > indent_num
148
+ # ブロックの終了
149
+ block_count = indent_num
150
+ else
151
+ # ブロックの開始
152
+ block_count = indent_num
153
+ end
154
+
155
+ #line.gsub!(/\".+\"/, "\"delete_string\"") # 文字列を削除
156
+ if line =~ /^\s*class\s/
157
+ unless line =~ /<</ # 特異クラスではない
158
+ work = line.gsub(/class\s/, "")
159
+ class_name = work.split("<")[0].to_s.chomp.match(/[A-Z][A-Za-z0-9_:]+/).to_s
160
+ base_name = work.split("<")[1].to_s.chomp.match(/[A-Z][A-Za-z0-9_:]+/).to_s
161
+ class_name.gsub!(/::/, ".")
162
+ if out_list.size != 0 and out_list[-1].type == :class_start # classが連続している
163
+ class_name = out_list[-1].name + "." + class_name
164
+ out_list[-1].name = class_name
165
+ cstruct_list[-1].name = class_name
166
+ else
167
+ out_list.push CStruct.new(:class_start, class_name, block_count, [], [], [], [])
168
+ cstruct_list.push CStruct.new(:class_end, class_name, block_count, [], [], [], [])
169
+ end
170
+ pp line if class_name == ""
171
+ if base_name != ""
172
+ #base_name.gsub!(/::/, ".")
173
+ cstruct_list[-1].inherit_list.push base_name
174
+ end
175
+ end
176
+ next unless line =~ /end\s*$/ # 1行で終了しない場合
177
+ elsif line =~ /^\s*module\s/
178
+ module_name = line.split(" ")[1].to_s.chomp
179
+ module_name.gsub!(/^[:]+/, "")
180
+ module_name.gsub!(/::/, ".")
181
+ out_list.push CStruct.new(:module_start, module_name, block_count, [], [], [], [])
182
+ cstruct_list.push CStruct.new(:module_end, module_name, block_count, [], [], [], [])
183
+ next unless line =~ /end\s*$/ # 1行で終了しない場合
184
+ end
185
+
186
+ if line =~ /^\s*private$/
187
+ method_type = :private
188
+ elsif line =~ /^\s*protected$/
189
+ method_type = :protected
190
+ elsif line =~ /^\s*public$/
191
+ method_type = :public
192
+ end
193
+
194
+ if line =~ /^\s*def\s/
195
+ # 関数名を取り出す
196
+ method = line.chomp.gsub(/\s*def\s*/, "")
197
+ unless method =~ /\(/
198
+ # 関数名にカッコをつける
199
+ sp = method.split(" ")
200
+ if sp.size > 1
201
+ method = sp[0].to_s + "(" + sp[1..-1].to_s + ")"
202
+ else
203
+ method = method + "()"
204
+ end
205
+ end
206
+ if cstruct_list.size != 0
207
+ method_list = cstruct_list[-1].method_list
208
+ case method_type
209
+ when :public
210
+ method_list.push "+ #{method}"
211
+ when :private
212
+ method_list.push "- #{method}"
213
+ when :protected
214
+ method_list.push "# #{method}"
215
+ end
216
+ else
217
+ main_method_list.push "+ #{method}"
218
+ end
219
+ end
220
+
221
+ # composition_list
222
+ line.match(/(([\/\"\')].*?\.new.*?[\/\"\'])|(?![\/\"\'])([a-zA-Z0-9_]+\.new))/) do |m|
223
+ if m.to_s[0] != "/" and m.to_s[0] != "\"" and m.to_s[0] != "'"
224
+ name = m.to_s.gsub(/\.new/, "").match(/[A-Z][A-Za-z0-9_]+/).to_s
225
+ if name != ""
226
+ if cstruct_list.size != 0
227
+ cstruct_list[-1].composition_list.push name
228
+ else
229
+ main_composition_list.push "main *-- #{name}"
230
+ end
231
+ end
232
+ end
233
+ end
234
+
235
+ # インスタンス変数
236
+ if line =~ /\s*@\S+/
237
+ if cstruct_list.size != 0
238
+ line.match(/@[a-zA-Z0-9_]+/) { |m|
239
+ instance_var = cstruct_list[-1].var_list
240
+ val = m.to_s.gsub(/@/, "")
241
+ case method_type
242
+ when :public
243
+ instance_var.push "+ #{val}"
244
+ when :private
245
+ instance_var.push "- #{val}"
246
+ when :protected
247
+ instance_var.push "# #{val}"
248
+ end
249
+ }
250
+ end
251
+ end
252
+
253
+ # 外部変数
254
+ line.match(/\$[a-zA-Z0-9_]+/) { |m|
255
+ global_var.push "+ #{m.to_s}"
256
+ }
257
+
258
+ # クラスの終了
259
+ if cstruct_list.size != 0
260
+ if block_count == cstruct_list[-1].block_count # block_countが一致
261
+ #puts "end of #{cstruct_list[-1].name}"
262
+ out_list.push cstruct_list[-1]
263
+ cstruct_list.slice!(-1) # 最後の要素を削除
264
+ end
265
+ end
266
+ #puts "#{block_count} #{line.chomp}"
267
+ end
268
+ if block_count != 0
269
+ # エラー
270
+ puts f
271
+ return ""
272
+ end
273
+
274
+ # UMLの出力
275
+ out = print_uml(out, out_list)
276
+ end
277
+
278
+ if main_method_list.size != 0 or
279
+ main_composition_list.size != 0 or
280
+ main_method_list.size != 0
281
+ out.push "class main {"
282
+ main_method_list.each do |mml|
283
+ out.push mml
284
+ end
285
+ # グローバル変数の出力
286
+ global_var.uniq.each do |gv|
287
+ out.push gv
288
+ end
289
+ out.push "}"
290
+ main_composition_list.uniq.each do |mcl|
291
+ out.push mcl
292
+ end
293
+ end
294
+
295
+ out.push "@enduml"
296
+ return out.join("\n")
297
+ end
data/lib/css/index.css ADDED
@@ -0,0 +1,178 @@
1
+ body {
2
+ color: #000000;
3
+ background-color: #cac3ec4f;
4
+ overflow: hidden;
5
+ font-size: 12px;
6
+ }
7
+
8
+ hr {
9
+ color: #ffffff;
10
+ background-color: #000000;
11
+ height: 1px;
12
+ /* 線の太さ */
13
+ border: 1px;
14
+ /* 枠の太さ */
15
+ border-style: solid;
16
+ /* 枠の種類 */
17
+ }
18
+
19
+ .error {
20
+ color: red;
21
+ }
22
+
23
+ .outarea {
24
+ background-color: #FFFFFF;
25
+ margin: 5px;
26
+ padding: 5px;
27
+ width: 95vw;
28
+ height: 50vh;
29
+ overflow: auto;
30
+ }
31
+
32
+ .inarea {
33
+ border: thin solid #000000;
34
+ margin: 5px;
35
+ padding: 5px;
36
+ width: 98%;
37
+ }
38
+
39
+ input.long {
40
+ width: 98%;
41
+ background-color: #FAFAFA;
42
+ margin: 5px;
43
+ padding: 5px;
44
+ }
45
+
46
+ textarea.long {
47
+ width: 95vw;
48
+ height: 50vh;
49
+ }
50
+
51
+ .ui-widget {
52
+ font-size: 12px;
53
+ }
54
+
55
+ .ui-autocomplete {
56
+ max-height: 45vh;
57
+ max-width: 90wh;
58
+ overflow-y: auto;
59
+ overflow-x: auto;
60
+ padding-right: 10px;
61
+ }
62
+
63
+ #jquery-ui-autocomplete label {
64
+ float: left;
65
+ margin-right: 0.5em;
66
+ color: black;
67
+ }
68
+
69
+ .ui-autocomplete.ui-front {
70
+ max-height: 250px;
71
+ overflow-y: auto;
72
+ /* prevent horizontal scrollbar */
73
+ overflow-x: hidden;
74
+ /* add padding to account for vertical scrollbar */
75
+ z-index: 1000 !important;
76
+ }
77
+
78
+ .ui-dialog {
79
+ position: absolute;
80
+ top: 0;
81
+ left: 0;
82
+ padding: .2em;
83
+ outline: 0;
84
+ }
85
+
86
+ .long {
87
+ width: 90%;
88
+ }
89
+
90
+ #setting_dialog {
91
+ color: #ffffff;
92
+ background-color: #000000;
93
+ }
94
+
95
+ .setting_name {
96
+ width: 200px;
97
+ color: #ffffff;
98
+ background-color: #000000;
99
+ }
100
+
101
+ .setting_value {
102
+ width: 300px;
103
+ color: #ffffff;
104
+ background-color: #000000;
105
+ }
106
+
107
+ .setting_checkbox {
108
+ color: #ffffff;
109
+ background-color: #000000;
110
+ }
111
+
112
+ ul.log {
113
+ list-style-type: none;
114
+ font-size: 12px;
115
+ color: #000000;
116
+ }
117
+
118
+ input[type="search"] {
119
+ -webkit-appearance: searchfield;
120
+ }
121
+
122
+ input[type="search"]::-webkit-search-cancel-button {
123
+ -webkit-appearance: searchfield-cancel-button;
124
+ }
125
+
126
+ /* menu */
127
+ .menu {
128
+ display: flex;
129
+ justify-content: flex-start;
130
+ list-style-type: none;
131
+ color: #393737;
132
+ padding: 0;
133
+ font-size: 12px;
134
+ font-weight: bold;
135
+ }
136
+
137
+ .menu li {
138
+ position: relative;
139
+ width: 100px;
140
+ margin-left: 1px;
141
+ padding: 5px;
142
+ background: #d8dada;
143
+ list-style-type: none;
144
+ }
145
+
146
+ .menu li a {
147
+ color: rgb(20, 114, 192);
148
+ }
149
+
150
+ .menu li a:hover {
151
+ background: #eba399;
152
+ }
153
+
154
+ .menuSub {
155
+ position: absolute;
156
+ margin-left: -6px;
157
+ padding: 0;
158
+ display: none;
159
+ }
160
+
161
+ /*.openが付与された時、表示の設定*/
162
+ .menuSub.open {
163
+ display: block;
164
+ }
165
+
166
+ .menuSub li a {
167
+ padding: 5px;
168
+ margin-left: -5px;
169
+ margin-right: -5px;
170
+ margin-bottom: -5px;
171
+ display: block;
172
+ color: rgb(20, 114, 192);
173
+ text-decoration: none;
174
+ }
175
+
176
+ .menuSub li a:hover {
177
+ background: #eba399;
178
+ }
@@ -0,0 +1,3 @@
1
+ [
2
+
3
+ ]
@@ -0,0 +1,80 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+
4
+ <head>
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
7
+ <title>RubyUMLClass</title>
8
+ <!-- jQuery -->
9
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
10
+ <!-- jQuery UI -->
11
+ <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
12
+ <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
13
+
14
+ <script src="js/main.js"></script>
15
+ <link rel="stylesheet" href="css/index.css" type="text/css">
16
+ </head>
17
+
18
+ <body>
19
+ <div id="msg_dialog" style="display:none;">
20
+ <div id="msg_text">message</div>
21
+ </div>
22
+ <hr>
23
+ <!-- menu -->
24
+ <div id="setting_dialog" style="display:none;">
25
+ <dl id="wrap"></dl>
26
+ </div>
27
+ <div>
28
+ <ul class="menu">
29
+ <Li><a href="#" id="exec" name="exec">実行</a></li>
30
+ <li><a href="#" id="stop" name="stop">停止</a></li>
31
+ <li>
32
+ 設定
33
+ <ul class="menuSub">
34
+ <li><a href="#" id="setting">設定</a></li>
35
+ <li><a href="#" id="save_setting">設定保存</a></li>
36
+ <li><a href="#" id="load_setting">設定読み込み</a></li>
37
+ </ul>
38
+ </li>
39
+ <!--
40
+ <li>
41
+ menu3
42
+ <ul class="menuSub">
43
+ <li><a href="#">menu3-1</a></li>
44
+ <li><a href="#">menu3-2</a></li>
45
+ </ul>
46
+ </li>
47
+ -->
48
+ </ul>
49
+ </div>
50
+
51
+ <table>
52
+ <tr>
53
+ <td>
54
+ <div id="dialog1" style="display:none;">
55
+ <input class="inarea" type="search" name="search_str" id="search_str">
56
+ </div>
57
+ </td>
58
+ <td class="long"><input class="inarea" type="search" id="inDir" name="inDir"></td>
59
+ <td><input type="button" id="select_dir" value="対象フォルダ" /></td>
60
+ </tr>
61
+ <tr>
62
+ <td>
63
+ <div id="dialog2" style="display:none;">
64
+ <input class="inarea" type="search" name="search_str2" id="search_str2">
65
+ </div>
66
+ </td>
67
+ <td class="long"><input class="inarea" type="search" id="outFile" name="outFile"></td>
68
+ <td><input type="button" id="select_file" value="出力ファイル" /></td>
69
+ <td><input type="button" id="open_file" value="開く" /></td>
70
+ </tr>
71
+ </table>
72
+
73
+ <div class="outarea">
74
+ <ul name="log" id="log" class="log">
75
+ </ul>
76
+ </div>
77
+
78
+ </body>
79
+
80
+ </html>