honyomi 0.2.0 → 1.0.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.
- 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