milkode 0.9.2 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -22,7 +22,7 @@ set :haml, :format => :html5
22
22
 
23
23
  get '/' do
24
24
  @setting = WebSetting.new
25
- @version = "0.9.2"
25
+ @version = "0.9.3"
26
26
  @package_num = Database.instance.yaml_package_num
27
27
  @file_num = Database.instance.totalRecords
28
28
  @package_list = PackageList.new(Database.instance.grndb)
@@ -62,6 +62,20 @@ post '/search*' do
62
62
  end
63
63
  end
64
64
 
65
+ post '/command' do
66
+ case params[:kind]
67
+ when 'update'
68
+ before = Time.now
69
+ if (params[:name] == '')
70
+ result = Database.instance.update_all
71
+ update_result_str(result, before)
72
+ else
73
+ result = Database.instance.update(params[:name])
74
+ update_result_str(result, before)
75
+ end
76
+ end
77
+ end
78
+
65
79
  get '/home*' do |path|
66
80
  before = Time.now
67
81
  path = path.sub(/^\//, "")
@@ -158,11 +172,35 @@ EOF
158
172
  def create_headmenu(path, query, flistpath = '')
159
173
  href = Mkurl.new('/home/' + path, params).inherit_query_shead
160
174
  flist = File.join("/home/#{path}", flistpath)
175
+
176
+ package_name = ""
177
+ modal_body = "全てのパッケージを更新しますか?"
178
+
179
+ if (path != "")
180
+ package_name = path.split('/')[0]
181
+ modal_body = "#{package_name} を更新しますか?"
182
+ end
183
+
161
184
  <<EOF
162
- #{headicon('go-home-5.png')} <a href="/home" class="headmenu">全てのパッケージ</a>
185
+ #{headicon('go-home-5.png')} <a href="/home" class="headmenu">ホーム</a>
163
186
  #{headicon('document-new-4.png')} <a href="#{href}" class="headmenu" onclick="window.open('#{href}'); return false;">新しい検索</a>
164
187
  #{headicon('directory.png')} <a href="#{flist}" class="headmenu">ディレクトリ</a>
188
+ #{headicon('view-refresh-4.png')} <a href="#updateModal" class="headmenu" data-toggle="modal">パッケージを更新</a>
165
189
  #{headicon('help.png')} <a href="/help" class="headmenu">ヘルプ</a>
190
+
191
+ <div id="updateModal" class="modal hide fade">
192
+ <div class="modal-header">
193
+ <a href="#" class="close" data-dismiss="modal">&times;</a>
194
+ <h3>パッケージを更新</h3>
195
+ </div>
196
+ <div class="modal-body">
197
+ <h4>#{modal_body}</h4>
198
+ </div>
199
+ <div class="modal-footer">
200
+ <a href="#" id="updateCancel" class="btn" data-dismiss="modal">Cancel</a>
201
+ <a href="#" id="updateOk" class="btn btn-primary" data-loading-text="Updating..." milkode-package-name="#{package_name}"">OK</a>
202
+ </div>
203
+ </div>
166
204
  EOF
167
205
  end
168
206
 
@@ -224,6 +262,15 @@ EOF
224
262
  def filelist_title(path)
225
263
  (path == "") ? "Package List" : path
226
264
  end
265
+
266
+ def update_result_str(result, before)
267
+ r = []
268
+ r << "#{result.package_count} packages" if result.package_count > 1
269
+ r << "#{result.file_count} records"
270
+ r << "#{result.add_count} add"
271
+ r << "#{result.update_count} update"
272
+ "#{r.join(', ')} (#{Time.now - before} sec)"
273
+ end
227
274
  end
228
275
 
229
276
  class Array
@@ -13,6 +13,7 @@ require 'milkode/common/dbdir'
13
13
  require 'milkode/cdstk/yaml_file_wrapper'
14
14
  require 'milkode/database/groonga_database'
15
15
  require 'milkode/common/util'
16
+ require 'milkode/database/updater'
16
17
  include Milkode
17
18
 
18
19
  module Milkode
@@ -168,6 +169,20 @@ module Milkode
168
169
  package, restpath = Util::divide_shortpath(path)
169
170
  @grndb.packages.touch_if(package, :viewtime) if package
170
171
  end
172
+
173
+ def update(name)
174
+ result = Updater::ResultAccumulator.new
175
+ result << update_in(yaml_load.find_name(name))
176
+ result
177
+ end
178
+
179
+ def update_all
180
+ result = Updater::ResultAccumulator.new
181
+ yaml_load.contents.each do |package|
182
+ result << update_in(package)
183
+ end
184
+ result
185
+ end
171
186
 
172
187
  private
173
188
 
@@ -175,5 +190,14 @@ module Milkode
175
190
  YamlFileWrapper.load_if(Database.dbdir)
176
191
  end
177
192
 
193
+ def update_in(package)
194
+ updater = Updater.new(@grndb, package.name)
195
+ updater.set_package_ignore IgnoreSetting.new("/", package.ignore)
196
+ updater.enable_no_auto_ignore if package.options[:no_auto_ignore]
197
+ updater.enable_update_with_git_pull if package.options[:update_with_git_pull]
198
+ updater.exec
199
+ updater.result
200
+ end
201
+
178
202
  end
179
203
  end
@@ -84,5 +84,30 @@ $(document).ready(function(){
84
84
  selectedList: 1,
85
85
  height: 350
86
86
  }).multiselectfilter();
87
+
88
+ $("#updateOk").click(function (e) {
89
+ update_package($("#updateOk").attr("milkode-package-name"));
90
+ return false;
91
+ });
87
92
  });
88
93
 
94
+ function update_package(package_name)
95
+ {
96
+ // click button
97
+ $("#updateModal .modal-body").html("<h4>更新中... <img src='/images/waiting.gif'/></h4>");
98
+ $("#updateCancel").addClass("hide");
99
+ $("#updateOk").button('loading').off('click');
100
+
101
+ // update end
102
+ $.post(
103
+ '/command',
104
+ {
105
+ kind: 'update',
106
+ name: package_name
107
+ },
108
+ function (data) {
109
+ $("#updateModal .modal-body").html("<h4>実行結果</h4>" + "<p>" + data + "</p>");
110
+ $("#updateOk").button('reset').attr("data-dismiss", "modal").text("Close").on('click', function () { location.reload(); });
111
+ }
112
+ );
113
+ }
@@ -20,6 +20,7 @@
20
20
  %script(type='text/javascript' src='/js/jquery-ui-1.8.22.custom.min.js')
21
21
  %script(type='text/javascript' src='/js/jquery.multiselect.min.js')
22
22
  %script(type='text/javascript' src='/js/jquery.multiselect.filter.min.js')
23
+ %script(type='text/javascript' src='/js/bootstrap.min.js')
23
24
  %script(type='text/javascript' src='/js/milkode.js')
24
25
 
25
26
  -# GoogleAnalyticsを使いたい場合はここにコードを挿入して下さい。
@@ -32,7 +32,7 @@ Samples:
32
32
  milk add git://github.com/ongaeshi/milkode.git
33
33
  EOF
34
34
  option :ignore, :type => :array, :aliases => '-i', :desc => 'Ignore path.'
35
- option :no_auto_ignore, :type => :boolean, :desc => 'Disable auto ignore (.gitignore).'
35
+ option :no_auto_ignore, :type => :boolean, :aliases => '-n', :desc => 'Disable auto ignore (.gitignore).'
36
36
  option :verbose, :type => :boolean, :aliases => '-v', :desc => 'Be verbose.'
37
37
 
38
38
  def add(*args)
@@ -108,9 +108,15 @@ EOF
108
108
  cdstk.mcd(options)
109
109
  end
110
110
 
111
- desc "info", "Information of milkode status"
112
- def info
113
- cdstk.info
111
+ desc "info [package]", "Display package information"
112
+ option :all, :type => :boolean, :aliases => '-a', :desc => 'Display all packages'
113
+ option :detail, :type => :boolean, :aliases => '-d', :desc => 'Detail format'
114
+ option :table, :type => :boolean, :aliases => '-t', :desc => 'Table format'
115
+ option :breakdown, :type => :boolean, :aliases => '-b', :desc => 'Breakdown format'
116
+ option :unknown, :type => :boolean, :aliases => '-u', :desc => 'Display unknown files'
117
+
118
+ def info(*args)
119
+ cdstk.info(args_or_pipe(args, $stdin), options)
114
120
  end
115
121
 
116
122
  desc "ignore [path ...]", "Ignore a file or directory"
@@ -169,10 +175,16 @@ EOF
169
175
  $stdout.puts <<EOF
170
176
  Gitomb https://github.com/tomykaira/gitomb
171
177
  redmine_milkode https://github.com/suer/redmine_milkode
178
+ Milkode_Sublime https://github.com/tsurushuu/Milkode_Sublime
172
179
  emacs-milkode https://github.com/ongaeshi/emacs-milkode
173
180
  EOF
174
181
  end
175
182
 
183
+ desc "files", "Display package files"
184
+ def files(*args)
185
+ cdstk.files(args)
186
+ end
187
+
176
188
  # --------------------------------------------------------------------------
177
189
 
178
190
  no_tasks do
@@ -208,5 +220,16 @@ EOF
208
220
  end
209
221
  end
210
222
 
223
+ # 引数が空の場合はパイプ(標準入力)を受け取る
224
+ def args_or_pipe(args, stdin)
225
+ if !args.empty?
226
+ args
227
+ elsif File.pipe?(stdin)
228
+ stdin.readlines.map{|v| v.chomp}
229
+ else
230
+ []
231
+ end
232
+ end
233
+
211
234
  end
212
235
  end
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  module GrenFileTest
4
- IGNORE_FILE = /(\A#.*#\Z)|(~\Z)|(\A\.#)|(\.d\Z)|(\.map\Z)|(\.MAP\Z)/
4
+ IGNORE_FILE = /(\A#.*#\Z)|(~\Z)|(\A\.#)|(\.d\Z)|(\.map\Z)|(\.MAP\Z)|(\.xbm\Z)|(\.ppm\Z)|(\.ai\Z)|(\.png\Z)|(\.webarchive\Z)/
5
5
  IGNORE_DIR = /(\A\.svn\Z)|(\A\.git\Z)|(\ACVS\Z)/
6
6
 
7
7
  def self.ignoreDir?(fpath)
@@ -0,0 +1,111 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # @file
4
+ # @brief Programming Language Detector
5
+ # @author ongaeshi
6
+ # @date 2012/09/29
7
+
8
+ module Milkode
9
+ class PlangDetector
10
+ LANGUAGES =
11
+ [
12
+ # high priority
13
+ { :name => 'README' , :filepatterns => ['README', 'readme'] },
14
+
15
+ # normal priority
16
+ { :name => 'ActionScript' , :suffixs => ['as'] },
17
+ { :name => 'Autotools' , :suffixs => ['am', 'in'] },
18
+ { :name => 'AWK' , :suffixs => ['awk'] },
19
+ { :name => 'Batch File' , :suffixs => ['bat'] },
20
+ { :name => 'Bundler' , :filenames => ['Gemfile', 'Gemfile.lock'] },
21
+ { :name => 'C' , :suffixs => ['c', 'h'] },
22
+ { :name => 'C#' , :suffixs => ['cs'] },
23
+ { :name => 'C++' , :suffixs => ['cc', 'cpp', 'hpp'] },
24
+ { :name => 'CGI' , :suffixs => ['cgi'] },
25
+ { :name => 'ChangeLog' , :filenames => ['ChangeLog'] },
26
+ { :name => 'Common Lisp' , :suffixs => ['cl'] },
27
+ { :name => 'CSS' , :suffixs => ['css'] },
28
+ { :name => 'CSV' , :suffixs => ['csv'] },
29
+ { :name => 'Diff' , :suffixs => ['diff'] },
30
+ { :name => 'Emacs Lisp' , :suffixs => ['el'] },
31
+ { :name => 'Erlang' , :suffixs => ['erl'] },
32
+ { :name => 'eRuby' , :suffixs => ['erb', 'rhtml'] },
33
+ { :name => 'gitignore' , :filenames => ['.gitignore'] },
34
+ { :name => 'Haml' , :suffixs => ['haml'] },
35
+ { :name => 'Haskell' , :suffixs => ['hs'] },
36
+ { :name => 'HTML' , :suffixs => ['html'] },
37
+ { :name => 'Java' , :suffixs => ['java'] },
38
+ { :name => 'JavaScript' , :suffixs => ['js'] },
39
+ { :name => 'JSON' , :suffixs => ['json'] },
40
+ { :name => 'Lua' , :suffixs => ['lua'] },
41
+ { :name => 'Makefile' , :suffixs => ['mk'] , :filenames => ['Makefile', 'makefile'] },
42
+ { :name => 'Markdown' , :suffixs => ['md', 'markdown'] },
43
+ { :name => 'M4' , :suffixs => ['m4'] },
44
+ { :name => 'Objective-C' , :suffixs => ['m', 'mm'] },
45
+ { :name => 'PEM' , :suffixs => ['pem'] },
46
+ { :name => 'Perl' , :suffixs => ['pl', 'PL', 'pm', 't'] },
47
+ { :name => 'POD' , :suffixs => ['pod'] },
48
+ { :name => 'PHP' , :suffixs => ['php'] },
49
+ { :name => 'Python' , :suffixs => ['py'] },
50
+ { :name => 'Rackup' , :suffixs => ['ru'] },
51
+ { :name => 'Rakefile' , :suffixs => ['rake'] , :filenames => ['Rakefile'] },
52
+ { :name => 'RD' , :suffixs => ['rd'] , :filepatterns => [/rd.ja\Z/] },
53
+ { :name => 'RDoc' , :suffixs => ['rdoc'] },
54
+ { :name => 'Ruby' , :suffixs => ['rb'] },
55
+ { :name => 'RubyGems' , :suffixs => ['gemspec'] },
56
+ { :name => 'Scheme' , :suffixs => ['scm'] },
57
+ { :name => 'sed' , :suffixs => ['sed'] },
58
+ { :name => 'Shell' , :suffixs => ['sh'] },
59
+ { :name => 'SVG' , :suffixs => ['svg'] },
60
+ { :name => 'Tcl' , :suffixs => ['tcl'] },
61
+ { :name => 'Text' , :suffixs => ['txt'] },
62
+ { :name => 'XML' , :suffixs => ['xml'] },
63
+ { :name => 'Yaml' , :suffixs => ['yml', 'yaml'] },
64
+ # { :name => '' , :suffixs => [] , :filenames => [] },
65
+ ]
66
+
67
+ UNKNOWN = 'unknown'
68
+ UNKNOWN_LANGUAGE = {:name => UNKNOWN}
69
+
70
+ def initialize(filename)
71
+ suffix = File.extname(filename)
72
+ suffix = suffix[1..-1]
73
+
74
+ filename = File.basename(filename)
75
+
76
+ @lang = LANGUAGES.find {|v|
77
+ is_found = false
78
+
79
+ if v[:suffixs]
80
+ is_found = v[:suffixs].include?(suffix)
81
+ end
82
+
83
+ if !is_found && v[:filenames]
84
+ is_found = v[:filenames].include?(filename)
85
+ end
86
+
87
+ if !is_found && v[:filepatterns]
88
+ v[:filepatterns].each do |pattern|
89
+ if filename.match pattern
90
+ is_found = true
91
+ break
92
+ end
93
+ end
94
+ end
95
+
96
+ is_found
97
+ }
98
+
99
+ @lang ||= UNKNOWN_LANGUAGE
100
+ end
101
+
102
+ def name
103
+ @lang[:name]
104
+ end
105
+
106
+ def unknown?
107
+ name == UNKNOWN
108
+ end
109
+ end
110
+ end
111
+
@@ -89,7 +89,11 @@ module Milkode
89
89
  end
90
90
 
91
91
  def larger_than_oneline(content)
92
- content && content.count($/) > 1
92
+ begin
93
+ content && content.count($/) > 1
94
+ rescue ArgumentError
95
+ true
96
+ end
93
97
  end
94
98
 
95
99
  def normalize_filename(str)
@@ -174,6 +178,11 @@ module Milkode
174
178
  def git_url?(src)
175
179
  (src =~ /^(:?git[:@])|(:?ssh:)/) != nil
176
180
  end
181
+
182
+ # StringIO patch
183
+ def pipe?(io)
184
+ !io.instance_of?(IO) || !File.pipe?(io)
185
+ end
177
186
  end
178
187
  end
179
188
 
@@ -9,9 +9,9 @@ require 'rubygems'
9
9
  require 'groonga'
10
10
  require 'milkode/common/dbdir'
11
11
  require 'fileutils'
12
- require 'milkode/database/package_table.rb'
13
- require 'milkode/database/document_table.rb'
14
- require 'milkode/database/document_record.rb'
12
+ require 'milkode/database/package_table'
13
+ require 'milkode/database/document_table'
14
+ require 'milkode/database/document_record'
15
15
 
16
16
  module Milkode
17
17
  class GroongaDatabase
@@ -91,7 +91,7 @@ EOF
91
91
  true
92
92
  end
93
93
  end
94
-
94
+
95
95
  private
96
96
 
97
97
  def define_schema
@@ -0,0 +1,242 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # @file
4
+ # @brief
5
+ # @author ongaeshi
6
+ # @date 2012/09/15
7
+
8
+ require 'milkode/database/groonga_database'
9
+ require 'milkode/common/grenfiletest'
10
+ require 'milkode/common/ignore_checker'
11
+ require 'kconv'
12
+
13
+ module Milkode
14
+ class Updater
15
+ attr_reader :result
16
+
17
+ def initialize(grndb, package_name)
18
+ @grndb = grndb
19
+ @package_name = package_name
20
+ @package = @grndb.packages[@package_name]
21
+ @result = Result.new
22
+ @current_ignore = IgnoreChecker.new
23
+ @options = {}
24
+ @out = $stdout
25
+ end
26
+
27
+ def exec
28
+ # git pull
29
+ if @options[:update_with_git_pull]
30
+ Dir.chdir(@package.directory) { system("git pull") }
31
+ end
32
+
33
+ # cleanup
34
+ unless @options[:no_clean]
35
+ @grndb.documents.cleanup_package_name(@package_name)
36
+ end
37
+
38
+ # update
39
+ update_dir(@package.directory)
40
+
41
+ # 更新時刻の更新
42
+ @grndb.packages.touch(@package_name, :updatetime)
43
+ end
44
+
45
+ def set_package_ignore(ignore_setting)
46
+ @current_ignore.add ignore_setting
47
+ end
48
+
49
+ def enable_no_auto_ignore
50
+ @options[:no_auto_ignore] = true
51
+ end
52
+
53
+ def enable_silent_mode
54
+ @options[:silent_mode] = true
55
+ end
56
+
57
+ def enable_display_info
58
+ @options[:display_info] = true
59
+ end
60
+
61
+ def enable_update_with_git_pull
62
+ @options[:update_with_git_pull] = true
63
+ end
64
+
65
+ def enable_no_clean
66
+ @options[:no_clean] = true
67
+ end
68
+
69
+ class Result
70
+ attr_reader :file_count
71
+ attr_reader :add_count
72
+ attr_reader :update_count
73
+
74
+ def initialize
75
+ @file_count = 0
76
+ @add_count = 0
77
+ @update_count = 0
78
+ end
79
+
80
+ def inc_file_count
81
+ @file_count += 1
82
+ end
83
+
84
+ def inc_add_count
85
+ @add_count += 1
86
+ end
87
+
88
+ def inc_update_count
89
+ @update_count += 1
90
+ end
91
+ end
92
+
93
+ class ResultAccumulator
94
+ attr_reader :package_count
95
+ attr_reader :file_count
96
+ attr_reader :add_count
97
+ attr_reader :update_count
98
+
99
+ def initialize
100
+ @package_count = 0
101
+ @file_count = 0
102
+ @add_count = 0
103
+ @update_count = 0
104
+ end
105
+
106
+ def <<(result)
107
+ @package_count += 1
108
+ @file_count += result.file_count
109
+ @add_count += result.add_count
110
+ @update_count += result.update_count
111
+ end
112
+ end
113
+
114
+ # ---------------------------------------------------------
115
+ private
116
+
117
+ def update_dir(dir)
118
+ if (!FileTest.exist?(dir))
119
+ warning_alert("#{dir} (Not found, skip)")
120
+ elsif (FileTest.directory? dir)
121
+ db_add_dir(dir)
122
+ else
123
+ db_add_file(File.dirname(dir), File.basename(dir), File.basename(dir)) # .bashrc/.bashrc のようになる
124
+ end
125
+ end
126
+
127
+ def db_add_dir(dir)
128
+ searchDirectory(dir, @package_name, "/", 0)
129
+ end
130
+
131
+ def db_add_file(package_dir, restpath, package_name = nil)
132
+ # サイレントモード
133
+ return if @options[:silent_mode]
134
+
135
+ # データベースには先頭の'/'を抜いて登録する
136
+ # 最初から'/'を抜いておけば高速化の余地あり?
137
+ # ignore設定との互換性保持が必要
138
+ restpath = restpath.sub(/^\//, "")
139
+
140
+ # パッケージ名を設定
141
+ package_name = package_name || File.basename(package_dir)
142
+
143
+ # レコードの追加
144
+ result = @grndb.documents.add(package_dir, restpath, package_name)
145
+
146
+ # メッセージの表示
147
+ case result
148
+ when :newfile
149
+ @result.inc_add_count
150
+ alert_info("add_record", File.join(package_dir, restpath))
151
+ when :update
152
+ # @grndb.packages.touch(package_name, :updatetime)
153
+ @result.inc_update_count
154
+ alert_info("update", File.join(package_dir, restpath))
155
+ end
156
+ end
157
+
158
+ def searchDirectory(dirname, packname, path, depth)
159
+ # 現在位置に.gitignoreがあれば無視設定に加える
160
+ add_current_gitignore(dirname, path) unless @options[:no_auto_ignore]
161
+
162
+ # 子の要素を追加
163
+ Dir.foreach(File.join(dirname, path)) do |name|
164
+ next if (name == '.' || name == '..')
165
+
166
+ next_path = File.join(path, name)
167
+ fpath = File.join(dirname, next_path)
168
+ shortpath = File.join(packname, next_path)
169
+
170
+ # 除外ディレクトリならばパス
171
+ next if ignoreDir?(fpath, next_path)
172
+
173
+ # 読み込み不可ならばパス
174
+ next unless FileTest.readable?(fpath)
175
+
176
+ # ファイルならば中身を探索、ディレクトリならば再帰
177
+ case File.ftype(fpath)
178
+ when "directory"
179
+ searchDirectory(dirname, packname, next_path, depth + 1)
180
+ when "file"
181
+ unless ignoreFile?(fpath, next_path)
182
+ db_add_file(dirname, next_path) # shortpathの先頭に'/'が付いているのが気になる
183
+ @result.inc_file_count
184
+ # @out.puts "file_count : #{@file_count}" if (@file_count % 100 == 0)
185
+ end
186
+ end
187
+ end
188
+ end
189
+
190
+ def ignoreDir?(fpath, mini_path)
191
+ FileTest.directory?(fpath) &&
192
+ (GrenFileTest::ignoreDir?(fpath) ||
193
+ package_ignore?(fpath, mini_path))
194
+ end
195
+
196
+ def ignoreFile?(fpath, mini_path)
197
+ GrenFileTest::ignoreFile?(fpath) ||
198
+ GrenFileTest::binary?(fpath) ||
199
+ package_ignore?(fpath, mini_path)
200
+ end
201
+
202
+ def package_ignore?(fpath, mini_path)
203
+ if @current_ignore.ignore?(mini_path)
204
+ alert_info("ignore", fpath)
205
+ true
206
+ else
207
+ false
208
+ end
209
+ end
210
+
211
+ def add_current_gitignore(dirname, path)
212
+ git_ignore = File.join(dirname, path, ".gitignore")
213
+
214
+ if File.exist? git_ignore
215
+ alert_info("add_ignore", git_ignore)
216
+
217
+ open(git_ignore) do |f|
218
+ @current_ignore.add IgnoreSetting.create_from_gitignore(path, f.read)
219
+ end
220
+ end
221
+ end
222
+
223
+ def alert_info(title, msg)
224
+ alert(title, msg) if @options[:display_info]
225
+ end
226
+
227
+ def alert(title, msg)
228
+ if (Util::platform_win?)
229
+ @out.puts "#{title.ljust(10)} : #{Kconv.kconv(msg, Kconv::SJIS)}"
230
+ else
231
+ @out.puts "#{title.ljust(10)} : #{msg}"
232
+ end
233
+ end
234
+
235
+ def warning_alert(msg)
236
+ @out.puts "[warning] #{msg}"
237
+ end
238
+
239
+ end
240
+ end
241
+
242
+