honyomi 0.2.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/HISTORY.md +25 -0
- data/lib/honyomi/cli.rb +11 -5
- data/lib/honyomi/core.rb +5 -1
- data/lib/honyomi/database.rb +63 -2
- data/lib/honyomi/query.rb +92 -0
- data/lib/honyomi/util.rb +34 -47
- data/lib/honyomi/version.rb +1 -1
- data/lib/honyomi/web/app.rb +262 -38
- data/lib/honyomi/web/public/css/honyomi.css +58 -2
- data/lib/honyomi/web/public/image/bookmark-01.png +0 -0
- data/lib/honyomi/web/public/image/bookmark-02.png +0 -0
- data/lib/honyomi/web/public/image/bookmark-03.png +0 -0
- data/lib/honyomi/web/public/image/star.png +0 -0
- data/lib/honyomi/web/public/js/honyomi.js +155 -0
- data/lib/honyomi/web/views/help.haml +65 -0
- data/lib/honyomi/web/views/index.haml +43 -1
- data/lib/honyomi/web/views/layout.haml +2 -1
- data/test/test_database.rb +3 -3
- data/test/test_query.rb +79 -0
- data/test/test_util.rb +13 -0
- metadata +40 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e2f44bb420e77ab9fbef9b1508052aca915ffbd7
|
4
|
+
data.tar.gz: d443c1e385c34c9f67f25cffc201070a49503dd2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b9dd0c9422a025fefa25e36d64bfd20b5f0083d16595219b920acb6ce67529adda81624cefb6801714f50a51765b999024fd0b77121c1acbd3c6daedd5956c4b
|
7
|
+
data.tar.gz: da02decc4acb9741aae5492378f520b3c0940026f2717d3a7b4a3ed6051d84b120470f99a57807211b9446914016cb9e1b443adb3482905995a0ba7662799590
|
data/HISTORY.md
CHANGED
@@ -1,5 +1,30 @@
|
|
1
1
|
# HISTORY - Honyomi
|
2
2
|
|
3
|
+
## 1.0 - 2014-11-15
|
4
|
+
|
5
|
+
* Bookmark a page
|
6
|
+
* Click star
|
7
|
+
* Is also attached comments
|
8
|
+
* Link page no (P111)
|
9
|
+
* Auto link http://...
|
10
|
+
* Ctrl+Enter: Shortcut to Update
|
11
|
+
|
12
|
+
* Edit a book information on web
|
13
|
+
* Title, Author, URL
|
14
|
+
|
15
|
+
* Convenient search query
|
16
|
+
* `123`: Jump to page
|
17
|
+
* `b:11 hello`: Book Id
|
18
|
+
* `-b:11 hello`: Book Id (NOT)
|
19
|
+
* `t:hello world`: Book Title
|
20
|
+
* `p:>100 p:<200 world`: Page Number
|
21
|
+
|
22
|
+
* Help
|
23
|
+
|
24
|
+
* Clear Button
|
25
|
+
|
26
|
+
* Impliment Util.highlight_keywords using Groonga::PatriciaTrie#tag_keys instead of Pure Ruby
|
27
|
+
|
3
28
|
## 0.2 - 2014-10-07
|
4
29
|
|
5
30
|
* Improve search result
|
data/lib/honyomi/cli.rb
CHANGED
@@ -62,6 +62,8 @@ module Honyomi
|
|
62
62
|
|
63
63
|
desc "edit book_id [options]", "Edit book info"
|
64
64
|
option :title, :aliases => '-t', :type => :string, :desc => 'Change title'
|
65
|
+
option :author, :aliases => '-a', :type => :string, :desc => 'Change author'
|
66
|
+
option :url, :aliases => '-u', :type => :string, :desc => 'Change url'
|
65
67
|
option :path, :type => :string, :desc => 'Change file path'
|
66
68
|
option :strip, :type => :boolean, :desc => 'Remove spaces'
|
67
69
|
option :no_strip, :type => :boolean, :desc => 'Not remove spaces'
|
@@ -70,9 +72,14 @@ module Honyomi
|
|
70
72
|
core = Core.new
|
71
73
|
core.load_database
|
72
74
|
|
73
|
-
|
74
|
-
|
75
|
-
|
75
|
+
begin
|
76
|
+
book_id = args[0].to_i
|
77
|
+
core.edit(book_id, options)
|
78
|
+
puts core.list([book_id])
|
79
|
+
rescue HonyomiError => e
|
80
|
+
puts e
|
81
|
+
exit -1
|
82
|
+
end
|
76
83
|
end
|
77
84
|
|
78
85
|
desc "remove book_id1 [book_id2 ...]", "Remove books"
|
@@ -91,8 +98,7 @@ module Honyomi
|
|
91
98
|
core = Core.new
|
92
99
|
core.load_database
|
93
100
|
|
94
|
-
results = core.search(args.join(" "))
|
95
|
-
snippet = GrnMini::Util.text_snippet_from_selection_results(results)
|
101
|
+
results, snippet = core.search(args.join(" "))
|
96
102
|
|
97
103
|
puts "#{results.size} matches"
|
98
104
|
results.map do |page|
|
data/lib/honyomi/core.rb
CHANGED
@@ -41,6 +41,8 @@ module Honyomi
|
|
41
41
|
def edit(book_id, options)
|
42
42
|
opts = {}
|
43
43
|
opts[:title] = options[:title] if options[:title]
|
44
|
+
opts[:author] = options[:author] if options[:author]
|
45
|
+
opts[:url] = options[:url] if options[:url]
|
44
46
|
opts[:path] = options[:path] if options[:path]
|
45
47
|
opts[:timestamp] = Time.parse(options[:timestamp]) if options[:timestamp]
|
46
48
|
|
@@ -58,7 +60,7 @@ module Honyomi
|
|
58
60
|
end
|
59
61
|
|
60
62
|
def search(query)
|
61
|
-
@database.search(query)
|
63
|
+
@database.search(Query.new(query), cli: true)
|
62
64
|
end
|
63
65
|
|
64
66
|
def list(args = [], options = {})
|
@@ -81,6 +83,8 @@ module Honyomi
|
|
81
83
|
results << <<EOF
|
82
84
|
id: #{book.id.to_s}
|
83
85
|
title: #{book.title}
|
86
|
+
author: #{book.author}
|
87
|
+
url: #{book.url}
|
84
88
|
path: #{book.path}
|
85
89
|
pages: #{book.page_num}
|
86
90
|
timestamp: #{book.timestamp}
|
data/lib/honyomi/database.rb
CHANGED
@@ -1,25 +1,36 @@
|
|
1
1
|
require 'honyomi'
|
2
|
+
require 'honyomi/query'
|
2
3
|
require 'grn_mini'
|
3
4
|
|
4
5
|
module Honyomi
|
6
|
+
class HonyomiError < Exception ; end
|
7
|
+
|
5
8
|
class Database
|
6
9
|
attr_reader :books
|
7
10
|
attr_reader :pages
|
11
|
+
attr_reader :bookmarks
|
8
12
|
|
9
13
|
def initialize
|
10
14
|
@books = GrnMini::Array.new("Books")
|
11
15
|
@pages = GrnMini::Hash.new("Pages")
|
16
|
+
@bookmarks = GrnMini::Hash.new("Bookmarks")
|
12
17
|
|
13
18
|
@books.setup_columns(path: "",
|
14
19
|
title: "",
|
15
20
|
author: "",
|
21
|
+
url: "",
|
16
22
|
page_num: 0,
|
17
23
|
timestamp: Time.new,
|
18
24
|
)
|
19
25
|
@pages.setup_columns(book: @books,
|
20
26
|
text: "",
|
21
27
|
page_no: 0,
|
28
|
+
bookmark: @bookmarks,
|
22
29
|
)
|
30
|
+
@bookmarks.setup_columns(page: @pages,
|
31
|
+
comment: "",
|
32
|
+
timestamp: Time.new,
|
33
|
+
)
|
23
34
|
end
|
24
35
|
|
25
36
|
def add_book(path, pages, options = {})
|
@@ -40,6 +51,8 @@ module Honyomi
|
|
40
51
|
|
41
52
|
book = @books.add(path: path,
|
42
53
|
title: title,
|
54
|
+
author: "",
|
55
|
+
url: "",
|
43
56
|
page_num: pages.size,
|
44
57
|
timestamp: timestamp,
|
45
58
|
)
|
@@ -54,8 +67,11 @@ module Honyomi
|
|
54
67
|
|
55
68
|
def change_book(book_id, options = {})
|
56
69
|
book = @books[book_id]
|
70
|
+
raise HonyomiError, "Invalid book id: #{book_id}" unless book.valid_id?
|
57
71
|
|
58
72
|
book.title = options[:title] if options[:title]
|
73
|
+
book.author = options[:author] if options[:author]
|
74
|
+
book.url = options[:url] if options[:url]
|
59
75
|
book.path = options[:path] if options[:path]
|
60
76
|
book.timestamp = options[:timestamp] if options[:timestamp]
|
61
77
|
|
@@ -83,8 +99,26 @@ module Honyomi
|
|
83
99
|
book.delete
|
84
100
|
end
|
85
101
|
|
86
|
-
def search(query)
|
87
|
-
@pages.select(query, default_column: "text")
|
102
|
+
def search(query, options = {})
|
103
|
+
match_pages = @pages.select(query.page_query, default_column: "text")
|
104
|
+
|
105
|
+
if options[:cli]
|
106
|
+
snippet = match_pages.expression.snippet([ ['<<',
|
107
|
+
'>>'] ],
|
108
|
+
{normalize: true})
|
109
|
+
else
|
110
|
+
snippet = match_pages.expression.snippet([["<span class=\"highlight\">", "</span>"]], {html_escape: true, normalize: true, max_results: 5})
|
111
|
+
end
|
112
|
+
|
113
|
+
match_bookmarks = @bookmarks.select do |record|
|
114
|
+
record.match(query.bookmark_query) do |target|
|
115
|
+
target.comment * 10
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
group_by_page = match_bookmarks.group("page")
|
120
|
+
|
121
|
+
return group_by_page.union!(match_pages), snippet
|
88
122
|
end
|
89
123
|
|
90
124
|
def book_pages(book_id)
|
@@ -100,5 +134,32 @@ module Honyomi
|
|
100
134
|
nil
|
101
135
|
end
|
102
136
|
end
|
137
|
+
|
138
|
+
def add_bookmark(page)
|
139
|
+
@bookmarks["#{page.book.id}:#{page.page_no}"] = { page: page, timestamp: Time.now }
|
140
|
+
end
|
141
|
+
|
142
|
+
def delete_bookmark(page)
|
143
|
+
@bookmarks.delete { |bookmark| bookmark.page == page }
|
144
|
+
end
|
145
|
+
|
146
|
+
def update_bookmark_comment(id, page_no, comment)
|
147
|
+
bm = @bookmarks["#{id}:#{page_no}"]
|
148
|
+
bm.comment = comment
|
149
|
+
bm.timestamp = Time.now
|
150
|
+
bm
|
151
|
+
end
|
152
|
+
|
153
|
+
def bookmark?(page)
|
154
|
+
@bookmarks["#{page.book.id}:#{page.page_no}"]
|
155
|
+
end
|
156
|
+
|
157
|
+
def bookmark_from_page(page)
|
158
|
+
@bookmarks["#{page.book.id}:#{page.page_no}"]
|
159
|
+
end
|
160
|
+
|
161
|
+
def books_bookmark(book)
|
162
|
+
@bookmarks.select { |record| record.page.book == book }
|
163
|
+
end
|
103
164
|
end
|
104
165
|
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module Honyomi
|
4
|
+
class Query
|
5
|
+
attr_reader :src
|
6
|
+
attr_reader :page_query
|
7
|
+
attr_reader :bookmark_query
|
8
|
+
attr_reader :jump_page_no
|
9
|
+
attr_reader :key
|
10
|
+
|
11
|
+
OPTIONS = [
|
12
|
+
['book' , 'b'],
|
13
|
+
['title' , 't'],
|
14
|
+
['page' , 'p'],
|
15
|
+
]
|
16
|
+
|
17
|
+
def initialize(src)
|
18
|
+
@src = src
|
19
|
+
init_hash
|
20
|
+
parse
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def init_hash
|
26
|
+
@key = {}
|
27
|
+
|
28
|
+
OPTIONS.flatten.each do |key|
|
29
|
+
@key[key] = []
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def parse
|
34
|
+
kp = OPTIONS.flatten.join('|')
|
35
|
+
parts = @src.scan(/(-)?(?:(#{kp}):)?(?:"(.+)"|(\S+))/)
|
36
|
+
|
37
|
+
page_query = []
|
38
|
+
bookmark_query = []
|
39
|
+
|
40
|
+
parts.each do |minus, key, quoted_value, value|
|
41
|
+
if quoted_value
|
42
|
+
text = %Q|"#{quoted_value}"|
|
43
|
+
else
|
44
|
+
text = value
|
45
|
+
end
|
46
|
+
|
47
|
+
unless (key)
|
48
|
+
begin
|
49
|
+
@jump_page_no = Integer(text)
|
50
|
+
page_query << make_query(minus, text, "page_no")
|
51
|
+
bookmark_query << make_query(minus, text, "page.page_no")
|
52
|
+
rescue ArgumentError
|
53
|
+
page_query << make_query(minus, text)
|
54
|
+
bookmark_query << make_query(minus, text)
|
55
|
+
end
|
56
|
+
else
|
57
|
+
case key
|
58
|
+
when 'book', 'b'
|
59
|
+
@key['book'] << text
|
60
|
+
page_query << make_query(minus, text, "book")
|
61
|
+
bookmark_query << make_query(minus, text, "page.book")
|
62
|
+
when 'title', 't'
|
63
|
+
@key['title'] << text
|
64
|
+
page_query << make_query(minus, text, "book.title:@")
|
65
|
+
bookmark_query << make_query(minus, text, "page.book.title:@")
|
66
|
+
when 'page', 'p'
|
67
|
+
@key['page'] << text
|
68
|
+
page_query << make_query(minus, text, "page_no")
|
69
|
+
bookmark_query << make_query(minus, text, "page.book.page_no")
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
@page_query = page_query.join(" ")
|
75
|
+
@bookmark_query = bookmark_query.join(" ")
|
76
|
+
end
|
77
|
+
|
78
|
+
def make_query(minus, text, key = nil)
|
79
|
+
m = minus ? "-" : ""
|
80
|
+
|
81
|
+
if key
|
82
|
+
if key[/@$/]
|
83
|
+
"#{m}#{key}#{text}"
|
84
|
+
else
|
85
|
+
"#{m}#{key}:#{text}"
|
86
|
+
end
|
87
|
+
else
|
88
|
+
"#{m}#{text}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
data/lib/honyomi/util.rb
CHANGED
@@ -39,60 +39,28 @@ module Honyomi
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def highlight_keywords(src, keywords, css_class)
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
keywords.each do |keyword|
|
46
|
-
pos = 0
|
47
|
-
|
48
|
-
loop do
|
49
|
-
r = src.match(/#{Regexp.escape(keyword)}/i, pos) do |m|
|
50
|
-
s = m.begin(0)
|
51
|
-
l = keyword.length
|
52
|
-
e = s+l
|
53
|
-
(s...e).each {|i| hightlight_map[i] = 1 }
|
54
|
-
pos = e
|
55
|
-
end
|
42
|
+
return "" if src.nil?
|
43
|
+
return src if keywords.nil?
|
56
44
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
index = 0
|
63
|
-
in_tag = false
|
64
|
-
src.each_char do |char|
|
65
|
-
in_tag = true if char == '<'
|
66
|
-
hightlight_map[index] = nil if in_tag
|
67
|
-
in_tag = false if char == '>'
|
68
|
-
index += 1
|
45
|
+
words = nil
|
46
|
+
if Groonga::VERSION[0] >= 3
|
47
|
+
words = Groonga::PatriciaTrie.create(key_type: "ShortText", normalizer: "NormalizerAuto")
|
48
|
+
else
|
49
|
+
words = Groonga::PatriciaTrie.create(key_type: "ShortText", key_normalize: true)
|
69
50
|
end
|
51
|
+
keywords.each { |keword| words.add(keword) }
|
70
52
|
|
71
|
-
|
72
|
-
|
53
|
+
other_text_handler = Proc.new { |string| ERB::Util.h(string) }
|
54
|
+
options = { other_text_handler: other_text_handler }
|
73
55
|
|
74
|
-
|
75
|
-
|
76
|
-
src.each_char do |char|
|
77
|
-
current = hightlight_map[index]
|
78
|
-
|
79
|
-
if prev.nil? && current
|
80
|
-
result += "<span class='#{css_class}'>"
|
81
|
-
elsif prev && current.nil?
|
82
|
-
result += "</span>"
|
83
|
-
end
|
84
|
-
|
85
|
-
result += char
|
86
|
-
|
87
|
-
index += 1
|
88
|
-
prev = current
|
56
|
+
words.tag_keys(src, options) do |record, word|
|
57
|
+
"<span class='#{css_class}'>#{ERB::Util.h(word)}</span>"
|
89
58
|
end
|
90
|
-
result += "</span>" if prev
|
91
|
-
|
92
|
-
result
|
93
59
|
end
|
94
60
|
|
95
61
|
def extract_keywords(query)
|
62
|
+
return [] if query.nil?
|
63
|
+
|
96
64
|
query.split.reduce([]) do |a, e|
|
97
65
|
e = e.gsub(/^\(|\)|AND|OR$/, "")
|
98
66
|
|
@@ -103,9 +71,28 @@ module Honyomi
|
|
103
71
|
elsif e =~ /:/
|
104
72
|
a
|
105
73
|
else
|
106
|
-
|
74
|
+
if e.empty?
|
75
|
+
a
|
76
|
+
else
|
77
|
+
a + [e]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def render_bookmark_comment_to_html(bookmark)
|
84
|
+
comment = CGI.escape_html(bookmark.comment || "")
|
85
|
+
|
86
|
+
URI.extract(comment, %w{http https}).uniq.each do |uri|
|
87
|
+
unless uri.match(/(\.jpg|\.jpeg|\.png)/)
|
88
|
+
comment.gsub!(uri, %Q{<a href="#{uri}">#{uri}</a>})
|
107
89
|
end
|
108
90
|
end
|
91
|
+
|
92
|
+
comment.gsub!(/P([1-9][0-9]*)/, %Q(<a href="/v/#{bookmark.page.book.id}?page=\\1">P\\1</a>))
|
93
|
+
|
94
|
+
comment.gsub("\n", "<br/>")
|
109
95
|
end
|
96
|
+
|
110
97
|
end
|
111
98
|
end
|
data/lib/honyomi/version.rb
CHANGED