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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 68f371dc5130aa8dc7f02f3464902d28628ba38a
4
- data.tar.gz: dceceb65a54d4944993a91ae8d8a75b7576d9a0e
3
+ metadata.gz: e2f44bb420e77ab9fbef9b1508052aca915ffbd7
4
+ data.tar.gz: d443c1e385c34c9f67f25cffc201070a49503dd2
5
5
  SHA512:
6
- metadata.gz: 7ca2111c247ee0bea1a11e74a3b6d57479e361bd148b76fb8f3873c3e349c079fbf39e3c14ceb6744053b1007b5cb2e6b0e287edbaf2dd7d8a825f2a3bc17ebd
7
- data.tar.gz: c733a8d9b8420a5b92fb0df4b317931a10ac1bd03cf336bf265e4521ab08abbd26ff1d6711a812cefdbf5b4977dd05c7150921cedef13ebe8a51bb7f07fb330e
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
- book_id = args[0].to_i
74
- core.edit(book_id, options)
75
- puts core.list([book_id])
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}
@@ -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
- # Init highlight_map
43
- hightlight_map = Array.new(src.length, nil)
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
- break if r.nil?
58
- end
59
- end
60
-
61
- # Delete html tag
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
- # Output
72
- result = ""
53
+ other_text_handler = Proc.new { |string| ERB::Util.h(string) }
54
+ options = { other_text_handler: other_text_handler }
73
55
 
74
- index = 0
75
- prev = nil
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
- a + [e]
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
@@ -1,3 +1,3 @@
1
1
  module Honyomi
2
- VERSION = "0.2.0"
2
+ VERSION = "1.0.0"
3
3
  end