milkode 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.
Files changed (70) hide show
  1. data/.document +5 -0
  2. data/Gemfile +12 -0
  3. data/Gemfile.lock +18 -0
  4. data/HISTORY.rdoc +5 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.rdoc +19 -0
  7. data/Rakefile +78 -0
  8. data/VERSION +1 -0
  9. data/bin/cdv +6 -0
  10. data/bin/cdview +10 -0
  11. data/bin/milk +10 -0
  12. data/lib/milkode/cdstk/cdstk.rb +508 -0
  13. data/lib/milkode/cdstk/cdstk_yaml.rb +106 -0
  14. data/lib/milkode/cdstk/cli_cdstk.rb +103 -0
  15. data/lib/milkode/cdstk/cli_cdstksub.rb +102 -0
  16. data/lib/milkode/cdview/cli_cdview.rb +43 -0
  17. data/lib/milkode/cdweb/app.rb +136 -0
  18. data/lib/milkode/cdweb/cli_cdweb.rb +111 -0
  19. data/lib/milkode/cdweb/config.ru +3 -0
  20. data/lib/milkode/cdweb/lib/coderay_wrapper.rb +94 -0
  21. data/lib/milkode/cdweb/lib/command.rb +67 -0
  22. data/lib/milkode/cdweb/lib/database.rb +260 -0
  23. data/lib/milkode/cdweb/lib/grep.rb +68 -0
  24. data/lib/milkode/cdweb/lib/mkurl.rb +56 -0
  25. data/lib/milkode/cdweb/lib/query.rb +81 -0
  26. data/lib/milkode/cdweb/lib/search_contents.rb +117 -0
  27. data/lib/milkode/cdweb/lib/search_files.rb +88 -0
  28. data/lib/milkode/cdweb/public/css/coderay.css +131 -0
  29. data/lib/milkode/cdweb/public/css/gren.css +75 -0
  30. data/lib/milkode/cdweb/public/images/directory.png +0 -0
  31. data/lib/milkode/cdweb/public/images/file.png +0 -0
  32. data/lib/milkode/cdweb/public/images/gren-icon-mini.png +0 -0
  33. data/lib/milkode/cdweb/public/images/gren-icon.png +0 -0
  34. data/lib/milkode/cdweb/views/filelist.haml +14 -0
  35. data/lib/milkode/cdweb/views/help.haml +16 -0
  36. data/lib/milkode/cdweb/views/index.haml +17 -0
  37. data/lib/milkode/cdweb/views/layout.haml +33 -0
  38. data/lib/milkode/cdweb/views/search.haml +12 -0
  39. data/lib/milkode/cdweb/views/view.haml +16 -0
  40. data/lib/milkode/common/dbdir.rb +39 -0
  41. data/lib/milkode/common/display_util.rb +62 -0
  42. data/lib/milkode/common/grenfiletest.rb +19 -0
  43. data/lib/milkode/common/grensnip.rb +37 -0
  44. data/lib/milkode/common/platform.rb +17 -0
  45. data/lib/milkode/common/string_snip.rb +61 -0
  46. data/lib/milkode/common/util.rb +144 -0
  47. data/lib/milkode/findgrep/findgrep.rb +408 -0
  48. data/lib/milkode/findgrep/result.rb +43 -0
  49. data/milkode.gemspec +159 -0
  50. data/test/data/abc.zip +0 -0
  51. data/test/data/nodir_abc.zip +0 -0
  52. data/test/data/nodir_abc_xpi.xpi +0 -0
  53. data/test/file_assert.rb +41 -0
  54. data/test/file_test_utils.rb +59 -0
  55. data/test/rake_test_loader.rb +7 -0
  56. data/test/runner.rb +11 -0
  57. data/test/test_bin_exec.rb +30 -0
  58. data/test/test_cdstk.rb +90 -0
  59. data/test/test_cdstk_yaml.rb +157 -0
  60. data/test/test_coderay_wrapper.rb +22 -0
  61. data/test/test_coderay_wrapper_data.rb +91 -0
  62. data/test/test_database.rb +78 -0
  63. data/test/test_dbdir.rb +60 -0
  64. data/test/test_gren_util.rb +34 -0
  65. data/test/test_helper.rb +2 -0
  66. data/test/test_mkurl.rb +30 -0
  67. data/test/test_query.rb +54 -0
  68. data/test/test_string_snip.rb +31 -0
  69. data/test/test_util.rb +40 -0
  70. metadata +300 -0
@@ -0,0 +1,111 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'rubygems'
3
+ require 'rack'
4
+ require 'launchy'
5
+ require 'optparse'
6
+ require 'milkode/cdweb/lib/database'
7
+
8
+ module Rack
9
+ class Server
10
+ def start
11
+ if options[:warn]
12
+ $-w = true
13
+ end
14
+
15
+ if includes = options[:include]
16
+ $LOAD_PATH.unshift(*includes)
17
+ end
18
+
19
+ if library = options[:require]
20
+ require library
21
+ end
22
+
23
+ if options[:debug]
24
+ $DEBUG = true
25
+ require 'pp'
26
+ p options[:server]
27
+ pp wrapped_app
28
+ pp app
29
+ end
30
+
31
+ # Touch the wrapped app, so that the config.ru is loaded before
32
+ # daemonization (i.e. before chdir, etc).
33
+ wrapped_app
34
+
35
+ daemonize_app if options[:daemonize]
36
+ write_pid if options[:pid]
37
+
38
+ trap(:INT) do
39
+ if server.respond_to?(:shutdown)
40
+ server.shutdown
41
+ else
42
+ exit
43
+ end
44
+ end
45
+
46
+ server.run wrapped_app, options do
47
+ if (options[:LaunchBrowser])
48
+ host = options[:Host] || options[:BindAddress] # options[:BindAddress] for WEBrick
49
+ Launchy.open("http://#{host}:#{options[:Port]}")
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ module Milkode
57
+ class CLI_Cdweb
58
+ def self.execute(stdout, argv)
59
+ options = {
60
+ :environment => ENV['RACK_ENV'] || "development",
61
+ :pid => nil,
62
+ :Port => 9292,
63
+ :Host => "0.0.0.0",
64
+ :AccessLog => [],
65
+ :config => "config.ru",
66
+ # ----------------------------
67
+ :server => "thin",
68
+ :LaunchBrowser => true,
69
+ :DbDir => select_dbdir,
70
+ }
71
+
72
+ opts = OptionParser.new("#{File.basename($0)}")
73
+ opts.on('--db DB_DIR', 'Database dir (default : current_dir)') {|v| options[:DbDir] = v }
74
+ opts.on("-o", "--host HOST", "listen on HOST (default: 0.0.0.0)") {|host| options[:Host] = host }
75
+ opts.on('-p', '--port PORT', 'use PORT (default: 9292)') {|v| options[:Port] = v }
76
+ opts.on("-s", "--server SERVER", "serve using SERVER (default : thin)") {|s| options[:server] = s }
77
+ opts.on('-n', '--no-browser', 'No launch browser.') {|v| options[:LaunchBrowser] = false }
78
+
79
+ # --hostが'-h'を上書きするので、'-h'を再定義してあげる
80
+ opts.on_tail("-h", "-?", "--help", "Show this message") do
81
+ puts opts
82
+ exit
83
+ end
84
+
85
+ opts.parse!(argv)
86
+
87
+ # 実行!!
88
+ execute_with_options(options)
89
+ end
90
+
91
+ def self.execute_with_options(stdout, options)
92
+ # 使用するデータベースの位置設定
93
+ Database.setup(File.expand_path(options[:DbDir]))
94
+
95
+ # サーバースクリプトのある場所へ移動
96
+ FileUtils.cd(File.dirname(__FILE__))
97
+
98
+ # Rackサーバー起動
99
+ Rack::Server.start(options)
100
+ end
101
+
102
+
103
+ def self.select_dbdir
104
+ if (Dbdir.dbdir?('.') || !Dbdir.dbdir?(Dbdir.default_dir))
105
+ '.'
106
+ else
107
+ Dbdir.default_dir
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,3 @@
1
+ require './app'
2
+ run Sinatra::Application
3
+
@@ -0,0 +1,94 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # @file
4
+ # @brief
5
+ # @author ongaeshi
6
+ # @date 2011/07/03
7
+
8
+ require 'rubygems'
9
+ require 'coderay'
10
+ require 'coderay/helpers/file_type'
11
+
12
+ module Milkode
13
+ class CodeRayWrapper
14
+ attr_reader :line_number_start
15
+
16
+ def initialize(content, filename, match_lines = [])
17
+ @content = content
18
+ @filename = filename
19
+ @match_lines = match_lines
20
+ @highlight_lines = match_lines.map{|v|v.index+1}
21
+ @line_number_start = 1
22
+ end
23
+
24
+ def set_range(range)
25
+ content_a = @content.to_a
26
+ range = limit_range(range, content_a)
27
+ @content = content_a[range]
28
+ @line_number_start = range.first + 1
29
+ end
30
+
31
+ def limit_range(range, array)
32
+ Range.new(range.first < 0 ? 0 : range.first,
33
+ range.last >= array.size ? array.size - 1 : range.last)
34
+ end
35
+
36
+ def to_html
37
+ html = CodeRay.scan(@content, file_type).
38
+ html(
39
+ :wrap => nil,
40
+ :line_numbers => :table,
41
+ :css => :class,
42
+ :highlight_lines => @highlight_lines,
43
+ :line_number_start => @line_number_start
44
+ )
45
+
46
+ milkode_ornament(html)
47
+ end
48
+
49
+ def milkode_ornament(html)
50
+ a = html.split("\n")
51
+
52
+ line_number = @line_number_start
53
+ is_code_content = false
54
+
55
+ a.each_with_index do |l, index|
56
+ if (l =~ / <td class="code"><pre (.*?)>(.*)<tt>/)
57
+ a[index] = " <td class=\"code\"><pre #{$1}><span #{line_attr(line_number)}>#{$2}</span><tt>"
58
+ is_code_content = true
59
+ line_number += 1
60
+ next
61
+ elsif (l =~ %r|</tt></pre></td>|)
62
+ is_code_content = false
63
+ end
64
+
65
+ if (is_code_content)
66
+ if (l =~ %r|</tt>(.*)<tt>|)
67
+ a[index] = "</tt><span #{line_attr(line_number)}>#{$1}</span><tt>"
68
+ line_number += 1
69
+ end
70
+ end
71
+ end
72
+
73
+ a.join("\n") + "\n"
74
+ end
75
+
76
+ def file_type
77
+ case File.extname(@filename)
78
+ when ".el"
79
+ :scheme
80
+ else
81
+ CodeRay::FileType.fetch @filename, :plaintext
82
+ end
83
+ end
84
+
85
+ def line_attr(no)
86
+ r = []
87
+ r << "id=\"#{no}\""
88
+ r << "class=\"highlight-line\"" if @highlight_lines.include?(no)
89
+ r.join(" ")
90
+ end
91
+ end
92
+ end
93
+
94
+
@@ -0,0 +1,67 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # @file
4
+ # @brief
5
+ # @author ongaeshi
6
+ # @date 2011/07/11
7
+
8
+ require 'milkode/cdweb/lib/database'
9
+ require 'milkode/cdweb/lib/coderay_wrapper'
10
+ require 'milkode/cdweb/lib/search_contents'
11
+ require 'milkode/cdweb/lib/search_files'
12
+ require 'milkode/cdweb/lib/mkurl'
13
+
14
+ module Milkode
15
+ def view(record, params, before)
16
+ @title = record.shortpath
17
+ @path = record.shortpath
18
+
19
+ q = params[:query] && Query.new(params[:query])
20
+
21
+ if (q and !q.keywords.empty?)
22
+ grep = Grep.new(record.content)
23
+ match_lines = grep.match_lines_and(q.keywords)
24
+ @record_content = CodeRayWrapper.new(record.content, record.shortpath, match_lines).to_html
25
+ else
26
+ @record_content = CodeRayWrapper.new(record.content, record.shortpath).to_html
27
+ end
28
+
29
+ @elapsed = Time.now - before
30
+ haml :view
31
+ end
32
+
33
+ def search(path, params, before)
34
+ @path = path
35
+ query = Query.new(params[:query])
36
+ @title = "'#{query.query_string}' in #{path_title(path)}"
37
+
38
+ if (query.keywords.size > 0)
39
+ searcher = SearchContents.new(path, params, query)
40
+ else
41
+ searcher = SearchFiles.new(path, params, query)
42
+ end
43
+
44
+ @total_records = searcher.total_records
45
+ @range = searcher.data_range
46
+ @record_content = searcher.html_contents + searcher.html_pagination;
47
+ @elapsed = Time.now - before
48
+ haml :search
49
+ end
50
+
51
+ def filelist(path, params, before)
52
+ @title = filelist_title(path)
53
+ @path = path
54
+ fileList = Database.instance.fileList(path)
55
+ @total_records = fileList.size
56
+ @record_content = fileList.map do |v|
57
+ "<dt class='result-file'>#{file_or_dirimg(v[1])}<a href='#{Mkurl.new('/home/' + v[0], params).inherit_query_shead}'>#{File.basename v[0]}</a></dt>"
58
+ end
59
+ @elapsed = Time.now - before
60
+ haml :filelist
61
+ end
62
+
63
+ def file_or_dirimg(is_file)
64
+ src = (is_file) ? '/images/file.png' : '/images/directory.png'
65
+ "<img alt='' style='vertical-align:bottom; border: 0; margin: 1px;' src='#{src}'>"
66
+ end
67
+ end
@@ -0,0 +1,260 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # @file
4
+ # @brief Milkodeで使用するデータベース
5
+ # @author ongaeshi
6
+ # @date 2010/10/17
7
+
8
+ require 'rubygems'
9
+ require 'pathname'
10
+ require 'singleton'
11
+ require 'groonga'
12
+ require 'milkode/common/dbdir'
13
+ include Milkode
14
+
15
+ module Milkode
16
+ class Database
17
+ include Singleton
18
+
19
+ @@db_dir = nil
20
+
21
+ def self.setup(db_dir)
22
+ @@db_dir = db_dir
23
+ end
24
+
25
+ def initialize
26
+ open(@@db_dir || Dbdir.default_dir)
27
+ end
28
+
29
+ def open(db_dir)
30
+ dbfile = Dbdir.expand_groonga_path(db_dir)
31
+
32
+ if File.exist? dbfile
33
+ Groonga::Database.open(dbfile)
34
+ else
35
+ raise "error : #{dbfile} not found!!"
36
+ end
37
+
38
+ @documents = Groonga::Context.default["documents"]
39
+ end
40
+
41
+ def record(shortpath)
42
+ table = @documents.select { |record| record.shortpath == shortpath }
43
+ return table.records[0]
44
+ end
45
+
46
+ def fileNum
47
+ @documents.select.size
48
+ end
49
+
50
+ def search(patterns, packages, fpaths, suffixs, offset = 0, limit = -1)
51
+ # @todo fpathを厳密に検索するには、検索結果からさらに先頭からのパスではないものを除外する
52
+ records, total_records = searchMain(patterns, packages, fpaths, suffixs, offset, limit)
53
+ end
54
+
55
+ def selectAll(offset = 0, limit = -1)
56
+ table = @documents.select
57
+
58
+ # マッチ数
59
+ total_records = table.size
60
+
61
+ # 2010/10/29 ongaeshi
62
+ # 本当はこのようにgroongaAPIでソートしたいのだが上手くいかなかった
63
+ # # ファイル名順にソート
64
+ # records = table.sort([{:key => "shortpath", :order => "descending"}],
65
+ # :offset => page * limit,
66
+ # :limit => limit)
67
+
68
+ # ソート
69
+ if (limit != -1)
70
+ records = table.records.sort_by{|record| record.shortpath.downcase }[offset, limit]
71
+ else
72
+ records = table.records.sort_by{|record| record.shortpath.downcase }[offset..limit]
73
+ end
74
+
75
+ return records, total_records
76
+ end
77
+
78
+ def selectAll2(offset = 0, limit = -1)
79
+ records, total_records = selectAll(offset, limit)
80
+ records
81
+ end
82
+
83
+ # レコード数を得る
84
+ def totalRecords
85
+ reopen_patch
86
+ @documents.select.size
87
+ end
88
+
89
+ # @sample test/test_database.rb:43 TestDatabase#t_fileList
90
+ def fileList(base)
91
+ base_parts = base.split("/")
92
+ base_depth = base_parts.length
93
+
94
+ # shortpathにマッチするものだけに絞り込む
95
+ if (base == "")
96
+ records = @documents.select.records
97
+ else
98
+ records = @documents.select {|record| record.shortpath =~ base }.to_a
99
+ end
100
+
101
+ # ファイルリストの生成
102
+ paths = records.map {|record|
103
+ record.shortpath.split("/")
104
+ }.find_all {|parts|
105
+ parts.length > base_depth and parts[0, base_depth] == base_parts
106
+ }.map {|parts|
107
+ is_file = parts.length == base_depth + 1
108
+ path = parts[0, base_depth + 1].join("/")
109
+ [path, is_file]
110
+ }.uniq
111
+
112
+ paths
113
+ end
114
+
115
+ # コンテンツの削除
116
+ # @todo パッケージ名完全一致になっていないので直す
117
+ def remove(packages)
118
+ # データベースを開き直す
119
+ reopen_patch
120
+
121
+ # 削除したコンテンツをインデックスから削除
122
+ records, total_records = search([], packages, [], [])
123
+
124
+ # 検索結果はHashのレコードなので、これを直接deleteしても駄目
125
+ # 1. Record#record_idを使って主キー(Groonga#Arrayのレコード)を取り出し
126
+ # 2. Record#delete で削除
127
+ records.each do |r|
128
+ yield r if block_given?
129
+ r.record_id.delete
130
+ end
131
+ end
132
+
133
+ # 実体の存在しないデータを削除
134
+ def cleanup
135
+ # データベースを開き直す
136
+ reopen_patch
137
+
138
+ # クリーンアップ
139
+ records = selectAll2
140
+
141
+ records.each do |r|
142
+ unless File.exist? r.path
143
+ yield r if block_given?
144
+ r.record_id.delete
145
+ end
146
+ end
147
+ end
148
+
149
+ private
150
+
151
+ def reopen_patch
152
+ # 削除系のコマンドが上手く動作しないためのパッチ
153
+ # 本質的な解決にはなっていないと思う
154
+ open(@@db_dir || Dbdir.default_dir)
155
+ end
156
+
157
+ def searchMain(patterns, packages, fpaths, suffixs, offset, limit)
158
+ table = @documents.select do |record|
159
+ expression = nil
160
+
161
+ # キーワード
162
+ patterns.each do |word|
163
+ sub_expression = record.content =~ word
164
+ if expression.nil?
165
+ expression = sub_expression
166
+ else
167
+ expression &= sub_expression
168
+ end
169
+ end
170
+
171
+ # パッケージ(OR)
172
+ pe = package_expression(record, packages)
173
+ if (pe)
174
+ if expression.nil?
175
+ expression = pe
176
+ else
177
+ expression &= pe
178
+ end
179
+ end
180
+
181
+ # ファイルパス
182
+ fpaths.each do |word|
183
+ sub_expression = record.path =~ word
184
+ if expression.nil?
185
+ expression = sub_expression
186
+ else
187
+ expression &= sub_expression
188
+ end
189
+ end
190
+
191
+ # 拡張子(OR)
192
+ se = suffix_expression(record, suffixs)
193
+ if (se)
194
+ if expression.nil?
195
+ expression = se
196
+ else
197
+ expression &= se
198
+ end
199
+ end
200
+
201
+ # 検索式
202
+ expression
203
+ end
204
+
205
+ # スコアとタイムスタンプでソート
206
+ records = table.sort([{:key => "_score", :order => "descending"},
207
+ {:key => "timestamp", :order => "descending"}],
208
+ :offset => offset,
209
+ :limit => limit)
210
+
211
+ # パッケージの条件追加
212
+ if (packages.size > 0)
213
+ records.delete_if do |record|
214
+ !packages.any?{|package| record.shortpath.split('/')[0] =~ /#{package}/ }
215
+ end
216
+ end
217
+
218
+ # マッチ数
219
+ total_records = records.size
220
+
221
+ return records, total_records
222
+ end
223
+ private :searchMain
224
+
225
+ def package_expression(record, packages)
226
+ sub = nil
227
+
228
+ # @todo 専用カラム package が欲しいところ
229
+ # でも今でもpackageはORとして機能してるからいいっちゃいい
230
+ packages.each do |word|
231
+ e = record.path =~ word
232
+ if sub.nil?
233
+ sub = e
234
+ else
235
+ sub |= e
236
+ end
237
+ end
238
+
239
+ sub
240
+ end
241
+ private :package_expression
242
+
243
+ def suffix_expression(record, suffixs)
244
+ sub = nil
245
+
246
+ suffixs.each do |word|
247
+ e = record.suffix =~ word
248
+ if sub.nil?
249
+ sub = e
250
+ else
251
+ sub |= e
252
+ end
253
+ end
254
+
255
+ sub
256
+ end
257
+ private :suffix_expression
258
+
259
+ end
260
+ end