milkode 0.7.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/Gemfile +17 -4
  2. data/HISTORY.ja.rdoc +27 -0
  3. data/HISTORY.rdoc +27 -0
  4. data/LICENSE.txt +23 -1
  5. data/Rakefile +1 -16
  6. data/VERSION +1 -1
  7. data/bin/gmilk +1 -1
  8. data/bin/milk +1 -1
  9. data/lib/milkode/cdstk/cdstk.rb +171 -168
  10. data/lib/milkode/cdstk/milkode_yaml.rb +1 -1
  11. data/lib/milkode/cdweb/app.rb +11 -3
  12. data/lib/milkode/cdweb/lib/command.rb +43 -0
  13. data/lib/milkode/cdweb/lib/database.rb +58 -210
  14. data/lib/milkode/cdweb/lib/mkurl.rb +10 -1
  15. data/lib/milkode/cdweb/lib/package_list.rb +61 -0
  16. data/lib/milkode/cdweb/lib/search_files.rb +2 -1
  17. data/lib/milkode/cdweb/public/css/bootstrap-responsive.min.css +9 -0
  18. data/lib/milkode/cdweb/public/css/bootstrap.min.css +9 -0
  19. data/lib/milkode/cdweb/public/css/milkode.css +44 -9
  20. data/lib/milkode/cdweb/public/img/glyphicons-halflings-white.png +0 -0
  21. data/lib/milkode/cdweb/public/img/glyphicons-halflings.png +0 -0
  22. data/lib/milkode/cdweb/public/js/bootstrap.min.js +6 -0
  23. data/lib/milkode/cdweb/views/index.haml +52 -16
  24. data/lib/milkode/cdweb/views/packages.haml +11 -0
  25. data/lib/milkode/cli.rb +8 -0
  26. data/lib/milkode/common/array_diff.rb +18 -0
  27. data/lib/milkode/common/util.rb +16 -0
  28. data/lib/milkode/database/document_record.rb +64 -0
  29. data/lib/milkode/database/document_table.rb +341 -0
  30. data/lib/milkode/database/groonga_database.rb +105 -0
  31. data/lib/milkode/database/package_table.rb +98 -0
  32. data/lib/milkode/findgrep/findgrep.rb +20 -124
  33. data/lib/milkode/grep/cli_grep.rb +3 -1
  34. data/milkode.gemspec +32 -2
  35. data/test/data/.gitignore +4 -0
  36. data/test/data/c_project/a.txt +1 -0
  37. data/test/data/c_project/abc.c +2 -0
  38. data/test/data/c_project/abc.h +3 -0
  39. data/test/data/c_project/b.txt +1 -0
  40. data/test/data/c_project/c.txt +1 -0
  41. data/test/data/c_project/cc.txt +1 -0
  42. data/test/data/c_project/to/file.rb +1 -0
  43. data/test/test_cdstk.rb +4 -4
  44. data/test/test_cli.rb +9 -2
  45. data/test/test_database.rb +5 -14
  46. data/test/test_document_record.rb +77 -0
  47. data/test/test_document_table.rb +317 -0
  48. data/test/test_groonga_database.rb +58 -0
  49. data/test/test_mkurl.rb +2 -0
  50. data/test/test_package_list.rb +21 -0
  51. data/test/test_package_table.rb +215 -0
  52. data/test/test_util.rb +8 -0
  53. metadata +57 -5
@@ -74,7 +74,7 @@ EOF
74
74
  def package_root(dir)
75
75
  nd = Util::normalize_filename dir
76
76
  @contents.find do |v|
77
- v if nd =~ /^#{v.directory}/
77
+ v if nd =~ /^#{Regexp.escape(v.directory)}/
78
78
  end
79
79
  end
80
80
 
@@ -15,15 +15,17 @@ require 'milkode/cdweb/lib/database'
15
15
  require 'milkode/cdweb/lib/command'
16
16
  require 'milkode/cdweb/lib/mkurl'
17
17
  require 'milkode/cdweb/lib/web_setting'
18
+ require 'milkode/cdweb/lib/package_list'
18
19
 
19
20
  set :haml, :format => :html5
20
21
 
21
22
  get '/' do
22
23
  @setting = WebSetting.new
23
- @version = "0.7.0"
24
+ @version = "0.8.0"
24
25
  @package_num = Database.instance.yaml_package_num
25
26
  @file_num = Database.instance.totalRecords
26
- haml :index
27
+ @package_list = PackageList.new(Database.instance.grndb)
28
+ haml :index, :layout => false
27
29
  end
28
30
 
29
31
  def package_path(path)
@@ -56,7 +58,13 @@ get '/home*' do |path|
56
58
  path = path.sub(/^\//, "")
57
59
  record = Database.instance.record(path)
58
60
 
59
- if (record)
61
+ if path.empty?
62
+ if (params[:query] and !params[:query].empty?)
63
+ search(path, params, before)
64
+ else
65
+ packages(params, before)
66
+ end
67
+ elsif (record)
60
68
  view(record, params, before)
61
69
  else
62
70
  if (params[:query] and !params[:query].empty?)
@@ -28,6 +28,7 @@ module Milkode
28
28
  @record_content = CodeRayWrapper.new(record.content, record.shortpath).to_html
29
29
  end
30
30
 
31
+ Database.instance.touch_viewtime(@path)
31
32
  @elapsed = Time.now - before
32
33
  haml :view
33
34
  end
@@ -61,12 +62,54 @@ module Milkode
61
62
  @record_content = fileList.map do |v|
62
63
  "<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>"
63
64
  end.join
65
+ Database.instance.touch_viewtime(path)
64
66
  @elapsed = Time.now - before
65
67
  haml :filelist
66
68
  end
67
69
 
70
+ def packages(params, before)
71
+ @setting = WebSetting.new
72
+ @title = "Package List"
73
+ @path = ""
74
+ packages = Database.instance.packages(params["sort"])
75
+ @total_records = packages.size
76
+
77
+ @sort_change_content =
78
+ [
79
+ sort_change_content(params["sort"], '名前'),
80
+ '|',
81
+ sort_change_content(params["sort"], '最近使った', 'viewtime'),
82
+ '|',
83
+ sort_change_content(params["sort"], '追加順' , 'addtime'),
84
+ '|',
85
+ sort_change_content(params["sort"], '更新順' , 'updatetime'),
86
+ '|',
87
+ sort_change_content(params["sort"], 'お気に入り', 'favtime'),
88
+ ].join("\n")
89
+
90
+ @record_content = packages.map do |v|
91
+ "<dt class='result-file'>#{file_or_dirimg(false)}<a href='#{Mkurl.new('/home/' + v, params).inherit_query_shead}'>#{File.basename v}</a></dt>"
92
+ end.join
93
+ @elapsed = Time.now - before
94
+ haml :packages
95
+ end
96
+
97
+ private
98
+
68
99
  def file_or_dirimg(is_file)
69
100
  src = (is_file) ? '/images/file.png' : '/images/directory.png'
70
101
  "<img alt='' style='vertical-align:bottom; border: 0; margin: 1px;' src='#{src}'>"
71
102
  end
103
+
104
+ def sort_change_content(current_value, text, sort_kind = nil)
105
+ if (current_value != sort_kind)
106
+ if (sort_kind)
107
+ "<a href='#{Mkurl.new('/home', params).inherit_query_shead_set_sort(sort_kind)}'>#{text}</a>"
108
+ else
109
+ "<a href='#{Mkurl.new('/home', params).inherit_query_shead}'>#{text}</a>"
110
+ end
111
+ else
112
+ text
113
+ end
114
+ end
72
115
  end
@@ -11,9 +11,11 @@ require 'singleton'
11
11
  require 'groonga'
12
12
  require 'milkode/common/dbdir'
13
13
  require 'milkode/cdstk/yaml_file_wrapper'
14
+ require 'milkode/database/groonga_database'
14
15
  include Milkode
15
16
 
16
17
  module Milkode
18
+ # @todo データベースアクセスは将来的にはGroongaDatabaseに収束させる
17
19
  class Database
18
20
  include Singleton
19
21
 
@@ -28,93 +30,73 @@ module Milkode
28
30
  end
29
31
 
30
32
  attr_reader :yaml
33
+ attr_reader :grndb
31
34
 
32
35
  def initialize
33
- open(Database.dbdir)
36
+ open
34
37
  end
35
38
 
36
39
  def yaml_reload
37
40
  # @yaml = YamlFileWrapper.load_if(@@db_dir || Dbdir.default_dir)
38
41
  end
39
42
 
40
- def open(db_dir)
41
- dbfile = Dbdir.expand_groonga_path(db_dir)
42
-
43
- if File.exist? dbfile
44
- Groonga::Database.open(dbfile)
45
- else
46
- raise "error : #{dbfile} not found!!"
43
+ def open
44
+ if !@grndb || @grndb.closed?
45
+ @grndb = GroongaDatabase.new
46
+ @grndb.open(Database.dbdir)
47
+ @grndb.yaml_sync(yaml_load.contents)
48
+ @documents = @grndb.documents
47
49
  end
48
-
49
- @documents = Groonga::Context.default["documents"]
50
50
  end
51
51
 
52
52
  def record(shortpath)
53
- reopen_patch
54
- table = @documents.select { |record| record.shortpath == shortpath }
55
- return table.records[0]
53
+ DocumentRecord.create @documents.find_shortpath(shortpath)
56
54
  end
57
55
 
58
56
  def search(patterns, packages, current_path, fpaths, suffixs, offset = 0, limit = -1)
59
- # パッケージ名から絶対パスに変換
60
- unless packages.empty?
61
- packages = convert_packages(packages)
62
-
63
- # キーワードがパッケージ名にマッチしなければ検索しない
64
- return [], 0 if packages.empty?
65
- else
66
- # パッケージ名未指定の時は現在位置もfpathsに含める
67
- if current_path != ""
68
- fpaths << path2fpath(current_path)
69
- end
70
- end
71
-
72
- # @todo fpathを厳密に検索するには、検索結果からさらに先頭からのパスではないものを除外する
73
- records, total_records = searchMain(patterns, packages, fpaths, suffixs, offset, limit)
74
- end
75
-
76
- def path2fpath(path)
77
- pa = path.split("/")
78
- File.join(convert_packages([pa[0]])[0], pa[1..-1].join('/'))
79
- end
80
-
81
- def selectAll(offset = 0, limit = -1)
82
- table = @documents.select
57
+ paths = []
83
58
 
84
- # マッチ数
85
- total_records = table.size
86
-
87
- # @todo ここが速度低下の原因?と思ったけど、ここは全て選択の部分だけか・・・
59
+ # パッケージ名未指定の時は現在位置を検索条件に追加
60
+ if packages.empty? && current_path != ''
61
+ package, restpath = Util::divide_shortpath(current_path)
88
62
 
89
- # 2010/10/29 ongaeshi
90
- # 本当はこのようにgroongaAPIでソートしたいのだが上手くいかなかった
91
- # # ファイル名順にソート
92
- # records = table.sort([{:key => "shortpath", :order => "descending"}],
93
- # :offset => offset,
94
- # :limit => limit)
63
+ # PackageRecord#directory をキーに検索する
64
+ directory = @grndb.packages[package].directory
95
65
 
96
- # ソート
97
- if (limit != -1)
98
- records = table.records.sort_by{|record| record.shortpath.downcase }[offset, limit]
99
- else
100
- records = table.records.sort_by{|record| record.shortpath.downcase }[offset..limit]
66
+ if restpath
67
+ paths << File.join(directory, restpath)
68
+ else
69
+ paths << directory
70
+ end
101
71
  end
102
72
 
103
- return records, total_records
73
+ # 検索
74
+ result = @documents.search(
75
+ :patterns => patterns,
76
+ # :keywords => ,
77
+ :paths => paths,
78
+ :packages => packages,
79
+ :restpaths => fpaths,
80
+ :suffixs => suffixs,
81
+ :offset => offset,
82
+ :limit => limit
83
+ )
84
+
85
+ # 結果
86
+ return result.map{|r| DocumentRecord.new(r)}, result.size
104
87
  end
105
88
 
106
- def selectAll2(offset = 0, limit = -1)
107
- records, total_records = selectAll(offset, limit)
108
- records
89
+ def selectAll(offset, limit)
90
+ @documents.select_all_sort_by_shortpath(offset, limit)
109
91
  end
110
-
92
+
111
93
  # レコード数を得る
112
94
  def totalRecords
113
- reopen_patch
114
- @documents.select.size
95
+ @documents.size
115
96
  end
116
97
 
117
98
  # yamlからパッケージの総数を得る
99
+ # @todo PackageTableから取得するように変更する
118
100
  def yaml_package_num
119
101
  yaml_load.contents.size
120
102
  end
@@ -128,17 +110,13 @@ module Milkode
128
110
  if (base_depth == 0)
129
111
  return yaml_load.contents.sort_by{|v| v.name}.map{|v| [v.name, false] }
130
112
  end
131
-
132
- # shortpathにマッチするものだけに絞り込む
133
- if (base == "")
134
- records = @documents.select.records
135
- else
136
- records = @documents.select {|record| record.shortpath =~ base }.to_a
137
- end
113
+
114
+ # base/以下のファイルを全て取得
115
+ records = @documents.find_shortpath_below(base)
138
116
 
139
117
  # ファイルリストの生成
140
118
  paths = records.map {|record|
141
- record.shortpath.split("/")
119
+ DocumentRecord.new(record).shortpath.split("/")
142
120
  }.find_all {|parts|
143
121
  parts.length > base_depth and parts[0, base_depth] == base_parts
144
122
  }.map {|parts|
@@ -151,161 +129,31 @@ module Milkode
151
129
 
152
130
  paths
153
131
  end
154
-
155
- # 指定したfpathにマッチするレコードを削除する
156
- def remove_fpath(fpath)
157
- # データベースを開き直す
158
- reopen_patch
159
-
160
- # 削除したいコンテンツを検索
161
- records, total_records = searchMain([], [], [fpath], [], 0, -1)
162
-
163
- # 検索結果はHashのレコードなので、これを直接deleteしても駄目
164
- # 1. Record#record_idを使って主キー(Groonga#Arrayのレコード)を取り出し
165
- # 2. Record#delete で削除
166
- records.each do |r|
167
- yield r if block_given?
168
- r.record_id.delete
169
- end
170
- end
171
132
 
172
- # 実体の存在しないデータを削除
173
- def cleanup
174
- # データベースを開き直す
175
- reopen_patch
133
+ def packages(sort_kind)
134
+ sorted = nil
176
135
 
177
- # クリーンアップ
178
- records = selectAll2
179
-
180
- records.each do |r|
181
- unless File.exist? r.path
182
- yield r if block_given?
183
- r.record_id.delete
184
- end
185
- end
186
- end
187
-
188
- private
189
-
190
- def reopen_patch
191
- # 削除系のコマンドが上手く動作しないためのパッチ
192
- # 本質的な解決にはなっていないと思う
193
- open(Database.dbdir)
194
- end
195
-
196
- def searchMain(patterns, packages, fpaths, suffixs, offset, limit)
197
- table = @documents.select do |record|
198
- expression = nil
199
-
200
- # キーワード
201
- patterns.each do |word|
202
- sub_expression = record.content =~ word
203
- if expression.nil?
204
- expression = sub_expression
205
- else
206
- expression &= sub_expression
207
- end
208
- end
209
-
210
- # パッケージ(OR)
211
- pe = package_expression(record, packages)
212
- if (pe)
213
- if expression.nil?
214
- expression = pe
215
- else
216
- expression &= pe
217
- end
218
- end
219
-
220
- # ファイルパス
221
- fpaths.each do |word|
222
- sub_expression = record.path =~ word
223
- if expression.nil?
224
- expression = sub_expression
225
- else
226
- expression &= sub_expression
227
- end
228
- end
229
-
230
- # 拡張子(OR)
231
- se = suffix_expression(record, suffixs)
232
- if (se)
233
- if expression.nil?
234
- expression = se
235
- else
236
- expression &= se
237
- end
238
- end
239
-
240
- # 検索式
241
- expression
242
- end
243
-
244
- # スコアとタイムスタンプでソート
245
- # records = table.sort([{:key => "_score", :order => "descending"},
246
- # {:key => "timestamp", :order => "descending"}],
247
- # :offset => offset,
248
- # :limit => limit)
249
-
250
- # ファイル名でソート
251
- records = table.sort([{:key => "shortpath", :order => "ascending"}],
252
- :offset => offset,
253
- :limit => limit)
254
-
255
- # マッチ数
256
- total_records = table.size
257
-
258
- return records, total_records
259
- end
260
- private :searchMain
261
-
262
- def package_expression(record, packages)
263
- sub = nil
264
-
265
- # @todo 専用カラム package が欲しいところ
266
- # でも今でもpackageはORとして機能してるからいいっちゃいい
267
- packages.each do |word|
268
- e = record.path =~ word
269
- if sub.nil?
270
- sub = e
271
- else
272
- sub |= e
273
- end
136
+ if sort_kind == "favtime"
137
+ sorted = @grndb.packages.favs
138
+ elsif (sort_kind)
139
+ sorted = @grndb.packages.sort(sort_kind)
140
+ else
141
+ sorted = @grndb.packages.sort("name", "ascending")
274
142
  end
275
143
 
276
- sub
144
+ sorted.map {|r| r.name}
277
145
  end
278
- private :package_expression
279
146
 
280
- def suffix_expression(record, suffixs)
281
- sub = nil
282
-
283
- suffixs.each do |word|
284
- e = record.suffix =~ word
285
- if sub.nil?
286
- sub = e
287
- else
288
- sub |= e
289
- end
290
- end
291
-
292
- sub
147
+ def touch_viewtime(path)
148
+ package, restpath = Util::divide_shortpath(path)
149
+ @grndb.packages.touch(package, :viewtime) if package
293
150
  end
294
- private :suffix_expression
295
151
 
296
- def convert_packages(packages)
297
- packages.inject([]) {|r, p| r += expand_packages(p)}
298
- end
299
-
300
- def expand_packages(keyword)
301
- yaml_load.match_all(keyword).map{|p| p.directory}
302
- end
152
+ private
303
153
 
304
154
  def yaml_load
305
155
  YamlFileWrapper.load_if(Database.dbdir)
306
156
  end
307
157
 
308
- # --- error ---
309
- class NotFoundPackage < RuntimeError ; end
310
158
  end
311
159
  end
@@ -24,6 +24,14 @@ module Milkode
24
24
  create_url(query_param(true, true, false))
25
25
  end
26
26
 
27
+ def inherit_query_shead_set_sort(sort_kind)
28
+ create_url(query_param(true, true, false, sort_kind))
29
+ end
30
+
31
+ def inherit_query_shead
32
+ create_url(query_param(true, true, false))
33
+ end
34
+
27
35
  def inherit_shead
28
36
  create_url(query_param(false, true, false))
29
37
  end
@@ -42,13 +50,14 @@ module Milkode
42
50
  end
43
51
  end
44
52
 
45
- def query_param(query_inherit, shead_inherit, offset_inherit)
53
+ def query_param(query_inherit, shead_inherit, offset_inherit, sort_kind = nil)
46
54
  qparam = []
47
55
  qparam << "query=#{escape(@params[:query])}" if (query_inherit and @params[:query])
48
56
  qparam << "shead=#{escape(@params[:shead])}" if (shead_inherit and @params[:shead])
49
57
  qparam << "onematch=#{escape(@params[:onematch])}" if (shead_inherit and @params[:onematch])
50
58
  qparam << "offset=#{escape(@params[:offset])}" if (offset_inherit and @params[:offset])
51
59
  qparam << "line=#{escape(@params[:line])}" if (offset_inherit and @params[:line])
60
+ qparam << "sort=#{sort_kind}" if sort_kind
52
61
  qparam.join('&')
53
62
  end
54
63
  end