python_uml_class 0.2.0 → 0.2.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.
@@ -10,6 +10,12 @@ CStruct = Struct.new(:type,
10
10
  :inherit_list,
11
11
  :composition_list)
12
12
 
13
+ def get_python_path
14
+ return @config["python_path"] if @config["python_path"].to_s != ""
15
+
16
+ "python"
17
+ end
18
+
13
19
  def get_formatter_path
14
20
  return @config["formatter_path"] if @config["formatter_path"].to_s != ""
15
21
 
@@ -24,7 +30,11 @@ def print_uml(out, out_list)
24
30
  out.push "namespace \"#{o_list.name}\" {"
25
31
  elsif o_list.type == :class_end
26
32
  pp o_list if o_list.name == ""
27
- out.push "class \"#{o_list.name}\" {"
33
+ if @config["color_class_name"].to_s != "" && o_list.name.match?(/#{@config["color_class_name"]}/)
34
+ out.push "class \"#{o_list.name}\" ##{@config["class_color"]} {"
35
+ else
36
+ out.push "class \"#{o_list.name}\" {"
37
+ end
28
38
  # インスタンス変数の出力
29
39
  o_list.var_list.uniq.each do |iv|
30
40
  out.push iv
@@ -36,11 +46,29 @@ def print_uml(out, out_list)
36
46
  out.push "}"
37
47
  # 継承リストの出力
38
48
  o_list.inherit_list.each do |ih|
39
- out.push "\"#{o_list.name}\" -[##{@config["inherit_color"]}]-|> \"#{ih}\""
49
+ line_color = @config["inherit_color"]
50
+ if @config["color_class_name"].to_s != ""
51
+ if ih.match?(/#{@config["color_class_name"]}/)
52
+ line_color = @config["class_color"]
53
+ out.push "class \"#{ih}\" ##{@config["class_color"]}"
54
+ elsif o_list.name.match?(/#{@config["color_class_name"]}/)
55
+ line_color = @config["class_color"]
56
+ end
57
+ end
58
+ out.push "\"#{o_list.name}\" -[##{line_color}]-|> \"#{ih}\""
40
59
  end
41
60
  # compo
42
61
  o_list.composition_list.uniq.each do |co|
43
- out.push "\"#{o_list.name}\" *-[##{@config["composition_color"]}]- \"#{co}\""
62
+ line_color = @config["composition_color"]
63
+ if @config["color_class_name"].to_s != ""
64
+ if co.match?(/#{@config["color_class_name"]}/)
65
+ line_color = @config["class_color"]
66
+ out.push "class \"#{co}\" ##{@config["class_color"]}"
67
+ elsif o_list.name.match?(/#{@config["color_class_name"]}/)
68
+ line_color = @config["class_color"]
69
+ end
70
+ end
71
+ out.push "\"#{o_list.name}\" *-[##{line_color}]- \"#{co}\""
44
72
  end
45
73
  elsif o_list.type == :module_end
46
74
  # インスタンス変数がある場合はモジュール名と同じクラスを定義
@@ -49,7 +77,11 @@ def print_uml(out, out_list)
49
77
  o_list.inherit_list.size != 0 or
50
78
  o_list.composition_list.size != 0
51
79
  pp o_list if o_list.name == ""
52
- out.push "class #{o_list.name} {"
80
+ if @config["color_class_name"].to_s != "" && o_list.name.match?(/#{@config["color_class_name"]}/)
81
+ out.push "class #{o_list.name} ##{@config["class_color"]} {"
82
+ else
83
+ out.push "class #{o_list.name} {"
84
+ end
53
85
  # インスタンス変数の出力
54
86
  o_list.var_list.uniq.each do |iv|
55
87
  out.push iv
@@ -61,11 +93,29 @@ def print_uml(out, out_list)
61
93
  out.push "}"
62
94
  # 継承リストの出力
63
95
  o_list.inherit_list.each do |ih|
64
- out.push "\"#{o_list.name}\" -[##{@config["inherit_color"]}]-|> \"#{ih}\""
96
+ line_color = @config["inherit_color"]
97
+ if @config["color_class_name"].to_s != ""
98
+ if ih.match?(/#{@config["color_class_name"]}/)
99
+ line_color = @config["class_color"]
100
+ out.push "class \"#{ih}\" ##{@config["class_color"]}"
101
+ elsif o_list.name.match?(/#{@config["color_class_name"]}/)
102
+ line_color = @config["class_color"]
103
+ end
104
+ end
105
+ out.push "\"#{o_list.name}\" -[##{line_color}]-|> \"#{ih}\""
65
106
  end
66
107
  # compo
67
108
  o_list.composition_list.uniq.each do |co|
68
- out.push "\"#{o_list.name}\" *-[##{@config["composition_color"]}]- \"#{co}\""
109
+ line_color = @config["composition_color"]
110
+ if @config["color_class_name"].to_s != ""
111
+ if co.match?(/#{@config["color_class_name"]}/)
112
+ line_color = @config["class_color"]
113
+ out.push "class \"#{co}\" ##{@config["class_color"]}"
114
+ elsif o_list.name.match?(/#{@config["color_class_name"]}/)
115
+ line_color = @config["class_color"]
116
+ end
117
+ end
118
+ out.push "\"#{o_list.name}\" *-[##{line_color}]- \"#{co}\""
69
119
  end
70
120
  end
71
121
  out.push "}"
@@ -93,9 +143,9 @@ def create_uml_class(in_dir, _out_file)
93
143
 
94
144
  Dir.glob("#{in_dir}/**/*.py") do |f|
95
145
  puts f
96
- puts @config["exclude_path"]
97
- if @config["exclude_path"] != ""
98
- if f =~ Regexp.new(@config["exclude_path"])
146
+ if @config && @config["exclude_path"]
147
+ puts @config["exclude_path"]
148
+ if @config["exclude_path"] != "" && f =~ Regexp.new(@config["exclude_path"])
99
149
  puts "skip #{f}"
100
150
  next
101
151
  end
@@ -106,7 +156,7 @@ def create_uml_class(in_dir, _out_file)
106
156
  # FileUtils.cp(f, tmp_file.path)
107
157
  kernel = Facter.value(:kernel)
108
158
  if kernel == "windows"
109
- open("|#{get_formatter_path} #{f} > #{tmp_file.path}") do |ff|
159
+ open("|#{get_python_path} #{get_formatter_path} #{f} > #{tmp_file.path}") do |ff|
110
160
  if ff.read.to_s != ""
111
161
  puts "pylint error #{ff}"
112
162
  return
@@ -115,8 +165,8 @@ def create_uml_class(in_dir, _out_file)
115
165
  end
116
166
  end
117
167
  else
118
- open("|#{get_formatter_path} #{f} > #{tmp_file.path}") do |ff|
119
- puts "|#{get_formatter_path} #{f} > #{tmp_file.path}"
168
+ open("|#{get_python_path} #{get_formatter_path} #{f} > #{tmp_file.path}") do |ff|
169
+ puts "|#{get_python_path} #{get_formatter_path} #{f} > #{tmp_file.path}"
120
170
  if ff.read.to_s != ""
121
171
  puts "pylint error #{ff}"
122
172
  return
@@ -135,10 +185,71 @@ def create_uml_class(in_dir, _out_file)
135
185
  file_struct_list.push CStruct.new(:class_start, file_name, file_name + ".global", block_count, [], [], [], [])
136
186
  file_struct_list.push CStruct.new(:class_end, file_name, file_name + ".global", block_count, [], [], [], [])
137
187
  is_def = false
188
+ local_imports = {}
189
+ local_classes = []
190
+
191
+ in_multiline_string = false
192
+ multiline_char = nil
193
+
194
+ buf.each_line do |line|
195
+ if line =~ /^\s*class\s+[a-zA-Z0-9_]+/
196
+ work = line.gsub(/class\s/, "")
197
+ c_name = work.split("\(")[0].to_s.gsub(/:/, "").strip
198
+ local_classes.push(c_name)
199
+ end
200
+ end
138
201
 
139
202
  # ソースを解析
140
203
  buf.each_line do |line|
141
- next if line =~ /^[\r\n]*$/ # 空行は対象外
204
+ next if line =~ /^\s*$/ # 空行は対象外
205
+ next if line =~ /^\s*#/ # コメント行は対象外
206
+
207
+ clean_line = line.gsub(/\\"/, '').gsub(/\\'/, '')
208
+ quotes3_double = clean_line.scan(/"""/).size
209
+ quotes3_single = clean_line.scan(/'''/).size
210
+
211
+ if in_multiline_string
212
+ if multiline_char == '"""' && quotes3_double.odd?
213
+ in_multiline_string = false
214
+ elsif multiline_char == "'''" && quotes3_single.odd?
215
+ in_multiline_string = false
216
+ end
217
+ next
218
+ else
219
+ if quotes3_double.odd?
220
+ in_multiline_string = true
221
+ multiline_char = '"""'
222
+ elsif quotes3_single.odd?
223
+ in_multiline_string = true
224
+ multiline_char = "'''"
225
+ end
226
+ end
227
+
228
+ if line =~ /^\s*import\s/
229
+ line.gsub(/import\s/, "").split(",").each do |imp|
230
+ if imp =~ / as /
231
+ short_name = imp.split(" as ")[1].strip
232
+ full_name = imp.split(" as ")[0].strip
233
+ local_imports[short_name] = full_name
234
+ else
235
+ short_name = imp.strip.split(".")[0]
236
+ full_name = imp.strip
237
+ local_imports[short_name] = full_name
238
+ end
239
+ end
240
+ elsif line =~ /^\s*from\s/
241
+ from_module = line.match(/^\s*from\s+([^\s]+)/)[1]
242
+ line.gsub(/^.*import\s/, "").split(",").each do |imp|
243
+ if imp =~ / as /
244
+ short_name = imp.split(" as ")[1].strip
245
+ orig_name = imp.split(" as ")[0].strip
246
+ local_imports[short_name] = "#{from_module}.#{orig_name}"
247
+ else
248
+ short_name = imp.strip
249
+ local_imports[short_name] = "#{from_module}.#{short_name}"
250
+ end
251
+ end
252
+ end
142
253
 
143
254
  line.chomp!
144
255
  # ブロックの開始/終了
@@ -185,7 +296,7 @@ def create_uml_class(in_dir, _out_file)
185
296
  end
186
297
 
187
298
  # クラスの開始
188
- if line =~ /^\s*class.*:/
299
+ if line =~ /^\s*class\s+[a-zA-Z0-9_]+/
189
300
  work = line.gsub(/class\s/, "")
190
301
  class_name = work.split("\(")[0].to_s.gsub(/:/, "")
191
302
  base_name = work.match(/\(.*\)/).to_s.gsub(/[()]/, "")
@@ -198,10 +309,13 @@ def create_uml_class(in_dir, _out_file)
198
309
  if base_name != ""
199
310
  if base_name =~ /,/
200
311
  base_name.split(",").each do |name|
201
- cstruct_list[-1].inherit_list.push name
312
+ name = name.strip
313
+ resolved_name = local_imports[name] || name
314
+ cstruct_list[-1].inherit_list.push resolved_name
202
315
  end
203
316
  else
204
- cstruct_list[-1].inherit_list.push base_name
317
+ resolved_name = local_imports[base_name] || base_name
318
+ cstruct_list[-1].inherit_list.push resolved_name
205
319
  end
206
320
  end
207
321
  next
@@ -247,29 +361,39 @@ def create_uml_class(in_dir, _out_file)
247
361
 
248
362
  # composition_list
249
363
  # クラスの呼び出し箇所
250
- line.match(/\s([A-Z][a-zA-Z]+)\.[a-z]/) do |m|
364
+ line.match(/\s([a-zA-Z][a-zA-Z0-9]+)\.[a-z]/) do |m|
251
365
  c_name = m.to_s.split(".")[0].gsub(/ /, "")
252
366
  puts "compo c_name=#{c_name}"
253
- if cstruct_list.size != 0
254
- cstruct_list[-1].composition_list.push c_name
255
- else
256
- file_struct_list[-1].composition_list.push c_name
367
+
368
+ # importされているものだけを対象とする
369
+ if local_imports.include?(c_name)
370
+ resolved_name = local_imports[c_name] || c_name
371
+ if cstruct_list.size != 0
372
+ cstruct_list[-1].composition_list.push resolved_name
373
+ else
374
+ file_struct_list[-1].composition_list.push resolved_name
375
+ end
257
376
  end
258
377
  end
259
378
 
260
379
  # クラスの初期化箇所
261
- line.match(/[\s\.][A-Z][A-Za-z]+\(/) do |m|
262
- c_name = m.to_s.gsub(/\(/, "").gsub(/[\s\.]/, "")
380
+ line.scan(/\b([A-Z][A-Za-z0-9_]*)\(/).each do |m|
381
+ c_name = m[0]
263
382
  puts "compo c_name=#{c_name}"
264
- if cstruct_list.size != 0
265
- cstruct_list[-1].composition_list.push c_name
266
- else
267
- file_struct_list[-1].composition_list.push c_name
383
+
384
+ # 自分で定義したものとimportされているものだけを対象とする
385
+ if local_imports.include?(c_name) || local_classes.include?(c_name)
386
+ resolved_name = local_imports[c_name] || c_name
387
+ if cstruct_list.size != 0
388
+ cstruct_list[-1].composition_list.push resolved_name
389
+ else
390
+ file_struct_list[-1].composition_list.push resolved_name
391
+ end
268
392
  end
269
393
  end
270
394
 
271
395
  # インスタンス変数
272
- if line =~ /^\s*self\.[a-zA-Z0-9_]+\s+=/ and cstruct_list.size != 0
396
+ if line =~ /^\s*self\.[a-zA-Z0-9_]+(\s*:\s*[^=]+)?\s*=/ and cstruct_list.size != 0
273
397
  line.match(/self\.[a-zA-Z0-9_]+/) do |m|
274
398
  instance_var = cstruct_list[-1].var_list
275
399
  val = m.to_s.gsub(/self\./, "")
@@ -284,19 +408,22 @@ def create_uml_class(in_dir, _out_file)
284
408
  end
285
409
  end
286
410
 
411
+ # クラス変数・外部変数(グローバル変数)の正規表現
412
+ var_regex = /^\s*(?!(?:def|class|if|elif|else|while|for|try|except|finally|with|pass|return|yield|import|from|global|nonlocal|assert|del|raise|break|continue)\b)([a-zA-Z0-9_]+)\s*(?::\s*[^=]+|=(?!=))/
413
+
287
414
  # クラス変数
288
- if line =~ /^\s*[a-zA-Z0-9_]+\s+=/ and cstruct_list.size != 0 and is_def == false
289
- line.match(/[a-zA-Z0-9_]+/) do |m|
415
+ if line =~ var_regex and cstruct_list.size != 0 and is_def == false
416
+ line.match(var_regex) do |m|
290
417
  instance_var = cstruct_list[-1].var_list
291
- val = m.to_s
418
+ val = m[1].to_s
292
419
  instance_var.push "- #{val}"
293
420
  end
294
421
  end
295
422
 
296
423
  # 外部変数
297
- if line =~ /^\s*[a-zA-Z0-9_]+\s+=/ and cstruct_list.size == 0 and is_def == false
298
- line.match(/\$*[a-zA-Z0-9_]+/) do |m|
299
- file_struct_list[-1].var_list.push "+ #{m}"
424
+ if line =~ var_regex and cstruct_list.size == 0 and is_def == false
425
+ line.match(var_regex) do |m|
426
+ file_struct_list[-1].var_list.push "+ #{m[1].to_s}"
300
427
  end
301
428
  end
302
429
  end
data/lib/css/index.css CHANGED
@@ -29,6 +29,12 @@ hr {
29
29
  overflow: auto;
30
30
  }
31
31
 
32
+ .outarea img {
33
+ max-width: 100%;
34
+ height: auto;
35
+ display: block;
36
+ }
37
+
32
38
  .inarea {
33
39
  border: thin solid #000000;
34
40
  margin: 5px;
data/lib/js/main.js CHANGED
@@ -384,10 +384,10 @@ function openFile(file) {
384
384
  $(document).ready(function () {
385
385
 
386
386
  // サーバに接続
387
- server_connect("ws://localhost:45709/wsserver")
387
+ server_connect("ws://localhost:41325/wsserver")
388
388
  window.onload = function (e) {
389
389
  // サーバに接続
390
- //server_connect("ws://localhost:45709/wsserver")
390
+ //server_connect("ws://localhost:41325/wsserver")
391
391
  }
392
392
 
393
393
  // menu
@@ -405,23 +405,37 @@ $(document).ready(function () {
405
405
  });
406
406
 
407
407
  // ウインドウサイズ
408
- var width = 800;
409
- var height = 600;
408
+ function adjustOutareaHeight() {
409
+ var windowHeight = $(window).height();
410
+ var outareaOffset = $(".outarea").offset().top;
411
+ var margin = 20; // 下部の余白
412
+
413
+ // outareaの開始位置からウィンドウの下端までの高さを計算
414
+ var newHeight = windowHeight - outareaOffset - margin;
415
+
416
+ // 最小高さを設定(縮小しすぎないように)
417
+ if (newHeight < 100) {
418
+ newHeight = 100;
419
+ }
420
+
421
+ $(".outarea").height(newHeight);
422
+ $(".outarea").width($(window).width() - 40);
423
+ }
424
+
410
425
  $(window).resize(function () {
411
- $(".outarea").height($(window).height() - 180);
426
+ adjustOutareaHeight();
412
427
  });
428
+
413
429
  // ウインドウの位置
414
430
  $(function () {
415
- //window.resizeTo(width, height);
416
- //window.moveTo((window.screen.width / 2) - (width / 2), (screen.height / 2) - (height / 2));
417
- //window.moveTo(0,0);
418
- $(".outarea").height($(window).height() - 180);
431
+ // DOM要素の高さが確定するのを待ってから調整する
432
+ setTimeout(adjustOutareaHeight, 200);
419
433
  });
420
434
 
421
435
  $('.outarea').scroll(function () {
422
436
  var h = $('.outarea').get(0).scrollHeight - $('.outarea').innerHeight();
423
437
  //console.log("scrollEnd=" + Math.abs($('.outarea').scrollTop() - h));
424
- if (Math.abs($('.outarea').scrollTop() - h) < 30) {
438
+ if (Math.abs($('.outarea').scrollTop() - h) < 50) {
425
439
  // 最後までスクロールしている
426
440
  // 自動スクロールON
427
441
  $auto_scroll = true;
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PythonUmlClassVer
4
- VERSION = "0.2.0"
4
+ VERSION = "0.2.1"
5
5
  end
data/lib/server.rb CHANGED
@@ -44,9 +44,21 @@ class Search < Sinatra::Base
44
44
  end
45
45
  path.gsub!(/[\/]+/, "/")
46
46
  puts path
47
+
48
+ base_dir = path.gsub(/\*$/, "")
49
+ parent_dir = File.expand_path("..", base_dir)
50
+
51
+ if File.directory?(base_dir) && parent_dir != File.expand_path(base_dir)
52
+ data = {}
53
+ data["label"] = "../"
54
+ data["value"] = parent_dir
55
+ res.push data
56
+ end
57
+
47
58
  Dir.glob(path, File::FNM_DOTMATCH).each do |file|
48
59
  data = {}
49
60
  next if File.basename(file) == "."
61
+ next if File.basename(file) == ".."
50
62
  next if kind == "dir" and !File.directory?(file)
51
63
  data["label"] = File.basename(file)
52
64
  data["label"] += "/" if (File.directory?(file))
data/lib/start.rb CHANGED
@@ -2,8 +2,28 @@
2
2
  # -*- coding: utf-8 -*-
3
3
  $LOAD_PATH << File.dirname(File.expand_path(__FILE__))
4
4
 
5
+ require 'rubygems'
6
+ require 'rubygems'
7
+
8
+ begin
9
+ require 'bundler/setup'
10
+ rescue LoadError
11
+ gem 'rack'
12
+ gem 'rackup'
13
+ end
14
+
15
+ require 'rack'
16
+ begin
17
+ require 'rackup'
18
+ require 'rackup/server'
19
+ rescue LoadError
20
+ # Fallback to older rack
21
+ require 'rack/server'
22
+ Object.const_set("Rackup", Rack) unless defined?(Rackup)
23
+ end
24
+
25
+ require 'bundler/setup'
5
26
  require "socket"
6
- require "rack"
7
27
  require "daemons"
8
28
  require "fileutils"
9
29
  require "kconv"
@@ -75,17 +95,17 @@ end
75
95
  port = get_unused_port
76
96
  puts "port=#{port}"
77
97
 
78
- # config.ruの編集
79
- buf = File.binread("config.ru").toutf8
80
- buf.gsub!(/port [0-9]+/, "port #{port}")
81
- File.binwrite("config.ru", buf)
98
+ # config.ruの編集は不要 (Rackup/Server optionsでポートを指定する)
82
99
 
83
100
  # main.jsの編集
84
101
  buf = File.binread("js/main.js").toutf8
85
102
  buf.gsub!(/localhost:[0-9]+\//, "localhost:#{port}/")
103
+ buf.gsub!(/localhost:[0-9]+\/wsserver/, "localhost:#{port}/wsserver")
104
+ # ws://localhost:12345/wsserver のようなパターンの置換をより確実に行う
105
+ buf.gsub!(/ws:\/\/localhost:[0-9]+\/wsserver/, "ws://localhost:#{port}/wsserver")
86
106
  File.binwrite("js/main.js", buf)
87
107
 
88
- # index.htaの編集
108
+ # html/index.htmlの編集
89
109
  buf = File.binread("html/index.html").toutf8
90
110
  buf.gsub!(/localhost:[0-9]+\//, "localhost:#{port}/")
91
111
  File.binwrite("html/index.html", buf)
@@ -122,7 +142,8 @@ begin
122
142
  }
123
143
 
124
144
  # start web server
125
- Rack::Server.start
145
+ server_class = defined?(Rackup::Server) ? Rackup::Server : Rack::Server
146
+ server_class.start(:Port => port, :config => "config.ru", :server => 'thin')
126
147
  rescue
127
148
  puts $!
128
149
  puts $@
data/lib/wsserver.rb CHANGED
@@ -50,8 +50,15 @@ class WsServer < Sinatra::Base
50
50
  end
51
51
  end
52
52
  ws.onmessage do |msg|
53
+ # puts msg # Do not print raw message that might contain binary/non-ascii
54
+ msg = msg.force_encoding("UTF-8") if msg.respond_to?(:force_encoding)
53
55
  puts msg
54
- json = JSON.parse(File.read("#{$home_dir}/config/setting.json"))
56
+ begin
57
+ json = JSON.parse(File.read("#{$home_dir}/config/setting.json"))
58
+ rescue => e
59
+ puts "Error reading setting.json: #{e.message}"
60
+ json = {"setting_list" => []}
61
+ end
55
62
  json_config = config_json_hash(json)
56
63
  $app.set_config(json_config)
57
64
  if msg =~ /^exec:/
@@ -36,6 +36,9 @@ Gem::Specification.new do |spec|
36
36
 
37
37
  spec.add_dependency "browser_app_base", "~> 0.1"
38
38
  spec.add_dependency "facter", "~> 4.2"
39
+ spec.add_dependency "rack", ">= 3.0"
40
+ spec.add_dependency "rackup"
41
+ spec.add_dependency "webrick"
39
42
  #spec.add_dependency "rufo", "~> 0.1"
40
43
 
41
44
  # For more information and examples about making a new gem, check out our