honyomi 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8b8abf6e1fd0398fd884d5303704b4002f65f09a
4
+ data.tar.gz: 755277443d0d80ce650ac5276383313c36fa244d
5
+ SHA512:
6
+ metadata.gz: 56be1e2715f5a5a79566b3150fe09a827f936bf1bc0d6c03855eb6d72ed43630b5439f6c06f26ced20174373a35a1030ee5b40e0194d80e6fa43987908759e94
7
+ data.tar.gz: e8553166f084fb3e560854ba0579e69ac01e5294a10c116d5b4014b7b6f4d9e5e55228f00630e186de3006244963d0a9d79e7d13323f14698228978173b9cdc6
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in honyomi.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 ongaeshi
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Honyomi
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'honyomi'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install honyomi
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ end
7
+
8
+ task :default => :test
data/bin/honyomi ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'honyomi/cli'
4
+
5
+ Honyomi::CLI.start
data/honyomi.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'honyomi/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "honyomi"
8
+ spec.version = Honyomi::VERSION
9
+ spec.authors = ["ongaeshi"]
10
+ spec.email = ["ongaeshi0621@gmail.com"]
11
+ spec.summary = %q{e-book (pdf) search engine, command line interface, and web application}
12
+ spec.description = %q{honyomi is a e-book (pdf) search engine. It have command line interface and web application. It will accelerate the e-book of your life.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'grn_mini', "~> 0.5"
22
+ spec.add_dependency 'haml'
23
+ spec.add_dependency 'launchy'
24
+ spec.add_dependency 'rack'
25
+ spec.add_dependency 'sinatra'
26
+ spec.add_dependency 'thin', "< 1.7"
27
+ spec.add_dependency 'thor'
28
+
29
+ spec.add_development_dependency "bundler", "~> 1.3"
30
+ spec.add_development_dependency "rake"
31
+ spec.add_development_dependency "minitest"
32
+ spec.add_development_dependency 'sinatra-reloader'
33
+ end
data/lib/honyomi.rb ADDED
@@ -0,0 +1,10 @@
1
+ require "honyomi/version"
2
+
3
+ require "honyomi/core"
4
+ require "honyomi/database"
5
+ require "honyomi/pdf"
6
+ require "honyomi/util"
7
+
8
+ module Honyomi
9
+ # Your code goes here...
10
+ end
@@ -0,0 +1,142 @@
1
+ require 'honyomi'
2
+ require 'thor'
3
+
4
+ module Honyomi
5
+ class CLI < Thor
6
+ class_option :help, :type => :boolean, :aliases => '-h', :desc => 'Help message'
7
+
8
+ desc "init", "Create database in ENV['HONYOMI_DATABASE_DIR'] or '~/.honyomi'"
9
+ def init
10
+ begin
11
+ core = Core.new
12
+ core.init_database
13
+ puts "Create database to \"#{core.db_path}\""
14
+ rescue Groonga::FileExists
15
+ puts "Database already exists in \"#{core.db_path}\""
16
+ end
17
+ end
18
+
19
+ desc "add file1 [file2 ...]", "Add pdf files"
20
+ option :title, :aliases => '-t', :type => :string, :desc => 'Specify title'
21
+ option :strip, :type => :boolean, :desc => 'Remove spaces'
22
+ def add(*args)
23
+ core = Core.new
24
+ core.load_database
25
+
26
+ if options[:title] && args.size > 1
27
+ puts "Arguments is specified more than once, but there is --title option"
28
+ return
29
+ end
30
+
31
+ args.each do |arg|
32
+ begin
33
+ book, status = core.add(arg, options)
34
+ rescue Errno::ENOENT => e
35
+ puts "Not found 'pdftotext' command (poppler, xpdf)"
36
+ exit -1
37
+ end
38
+
39
+ unless book
40
+ puts "Not exist: #{arg}"
41
+ next
42
+ end
43
+
44
+ case status
45
+ when :update
46
+ puts "U #{book.id.to_s} #{book.title} (#{book.page_num} pages)"
47
+ when :add
48
+ puts "A #{book.id.to_s} #{book.title} (#{book.page_num} pages)"
49
+ else
50
+ raise
51
+ end
52
+ end
53
+ end
54
+
55
+ # desc "update [book_id1 book_id2 ...]", "Update pdf files"
56
+ # option :all, :type => :boolean, :desc => 'Update all pdf'
57
+ # def update(*args)
58
+ # core = Core.new
59
+ # core.load_database
60
+ # core.update(args[0], options)
61
+ # end
62
+
63
+ desc "edit book_id [options]", "Edit book info"
64
+ option :title, :aliases => '-t', :type => :string, :desc => 'Change title'
65
+ option :path, :type => :string, :desc => 'Change file path'
66
+ option :strip, :type => :boolean, :desc => 'Remove spaces'
67
+ option :no_strip, :type => :boolean, :desc => 'Not remove spaces'
68
+ option :timestamp, :type => :string, :desc => 'Change timestamp'
69
+ def edit(*args)
70
+ core = Core.new
71
+ core.load_database
72
+
73
+ book_id = args[0].to_i
74
+ core.edit(book_id, options)
75
+ puts core.list([book_id])
76
+ end
77
+
78
+ desc "remove book_id1 [book_id2 ...]", "Remove books"
79
+ def remove(*args)
80
+ core = Core.new
81
+ core.load_database
82
+
83
+ args.each do |id|
84
+ puts core.list([id.to_i])
85
+ core.remove(id.to_i)
86
+ end
87
+ end
88
+
89
+ desc "search query", "Search pages"
90
+ def search(*args)
91
+ core = Core.new
92
+ core.load_database
93
+
94
+ results = core.search(args.join(" "))
95
+ snippet = GrnMini::Util.text_snippet_from_selection_results(results)
96
+
97
+ puts "#{results.size} matches"
98
+ results.map do |page|
99
+ puts "--- #{page.book.title} (#{page.page_no} page) ---"
100
+ snippet.execute(page.text).each do |segment|
101
+ puts segment.gsub("\n", "")
102
+ end
103
+ end
104
+ end
105
+
106
+ desc "list [book_id1 book_id2 ...]", "List books"
107
+ option :title, :aliases => '-t', :type => :string, :desc => 'Filter title'
108
+ def list(*args)
109
+ core = Core.new
110
+ core.load_database
111
+
112
+ puts core.list(args.map{|v| v.to_i }, options)
113
+ end
114
+
115
+ desc "web", "Web search interface"
116
+ option :no_browser, :type => :boolean, :default => false, :aliases => '-n', :type => :boolean, :desc => 'Do not launch browser.'
117
+ option :host, :default => '127.0.0.1', :aliases => '-o', :desc => 'Listen on HOST.'
118
+ option :port, :default => 9295, :aliases => '-p', :desc => 'Use PORT.'
119
+ option :server, :default => 'thin', :aliases => '-s', :desc => 'Use SERVER.'
120
+ def web
121
+ core = Core.new
122
+ core.load_database
123
+ core.web(options)
124
+ end
125
+
126
+ no_tasks do
127
+ # Override method for support -h
128
+ # defined in /lib/thor/invocation.rb
129
+ def invoke_command(task, *args)
130
+ if task.name == "help" && args == [[]]
131
+ print "honyomi #{Honyomi::VERSION}\n\n"
132
+ end
133
+
134
+ if options[:help]
135
+ CLI.task_help(shell, task.name)
136
+ else
137
+ super
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,144 @@
1
+ require 'honyomi'
2
+ require 'fileutils'
3
+ require 'launchy'
4
+ require 'rack'
5
+
6
+ module Honyomi
7
+ class Core
8
+ attr_reader :database
9
+
10
+ def initialize(opts = {})
11
+ @opts = opts
12
+ end
13
+
14
+ def init_database
15
+ FileUtils.mkdir_p(db_dir)
16
+ Groonga::Database.create(path: db_path)
17
+ @database = Database.new
18
+ end
19
+
20
+ def load_database
21
+ Groonga::Database.open(db_path)
22
+ @database = Database.new
23
+ end
24
+
25
+ def add(filename, options = {})
26
+ if File.exist?(filename)
27
+ options = options.dup
28
+ pages = Pdf.new(filename).pages
29
+ pages = pages.map { |page| Util.strip_page(page) } if options[:strip]
30
+ options[:timestamp] = File.stat(filename).mtime
31
+ @database.add_book(filename, pages, options)
32
+ else
33
+ nil
34
+ end
35
+ end
36
+
37
+ def update(book_id, options)
38
+ end
39
+
40
+ def edit(book_id, options)
41
+ opts = {}
42
+ opts[:title] = options[:title] if options[:title]
43
+ opts[:path] = options[:path] if options[:path]
44
+ opts[:timestamp] = Time.parse(options[:timestamp]) if options[:timestamp]
45
+
46
+ if options[:strip] || options[:no_strip]
47
+ filename = @database.books[book_id].path
48
+ opts[:pages] = Pdf.new(filename).pages
49
+ opts[:pages] = opts[:pages].map { |page| Util.strip_page(page) } if options[:strip]
50
+ end
51
+
52
+ @database.change_book(book_id, opts)
53
+ end
54
+
55
+ def remove(book_id)
56
+ @database.delete_book(book_id)
57
+ end
58
+
59
+ def search(query)
60
+ @database.search(query)
61
+ end
62
+
63
+ def list(args = [], options = {})
64
+ books = @database.books
65
+ books = books.select("title:@#{options[:title]}").map {|record| record.key} if options[:title]
66
+
67
+ if args.empty?
68
+ id_length = books.max { |book| book.id.to_s.length }
69
+ id_length = id_length ? id_length.id.to_s.length : 0
70
+
71
+ books.map do |book|
72
+ # "#{book.id} #{book.title} (#{book.page_num} pages) #{book.path}"
73
+ "#{book.id.to_s.rjust(id_length)} #{book.title} (#{book.page_num} pages)"
74
+ end
75
+ else
76
+ results = []
77
+
78
+ books.each do |book|
79
+ if args.include?(book.id)
80
+ results << <<EOF
81
+ id: #{book.id.to_s}
82
+ title: #{book.title}
83
+ path: #{book.path}
84
+ pages: #{book.page_num}
85
+ timestamp: #{book.timestamp}
86
+
87
+ EOF
88
+ end
89
+ end
90
+
91
+ results
92
+ end
93
+ end
94
+
95
+ def web(options)
96
+ rack_options = {
97
+ :environment => ENV['RACK_ENV'] || "development",
98
+ :pid => nil,
99
+ :Port => options[:port],
100
+ :Host => options[:host],
101
+ :AccessLog => [],
102
+ :config => "config.ru",
103
+ # ----------------------------
104
+ :server => options[:server],
105
+ }
106
+
107
+ # Move to the location of the server script
108
+ FileUtils.cd(File.join(File.dirname(__FILE__), 'web'))
109
+
110
+ # Create Rack Server
111
+ rack_server = Rack::Server.new(rack_options)
112
+
113
+ # Start Rack
114
+ rack_server.start do
115
+ unless options[:no_browser]
116
+ Launchy.open("http://#{options[:host]}:#{options[:port]}")
117
+ end
118
+ end
119
+ end
120
+
121
+ def db_dir
122
+ File.join(home_dir, 'db')
123
+ end
124
+
125
+ def db_path
126
+ File.join(db_dir, 'honyomi.db')
127
+ end
128
+
129
+ private
130
+
131
+ def home_dir
132
+ unless @home_dir
133
+ @home_dir = @opts[:home_dir] || ENV['HONYOMI_DATABASE_DIR'] || File.join(default_home, '.honyomi')
134
+ FileUtils.mkdir_p(@home_dir) unless File.exist?(@home_dir)
135
+ end
136
+
137
+ @home_dir
138
+ end
139
+
140
+ def default_home
141
+ File.expand_path '~'
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,104 @@
1
+ require 'honyomi'
2
+ require 'grn_mini'
3
+
4
+ module Honyomi
5
+ class Database
6
+ attr_reader :books
7
+ attr_reader :pages
8
+
9
+ def initialize
10
+ @books = GrnMini::Array.new("Books")
11
+ @pages = GrnMini::Hash.new("Pages")
12
+
13
+ @books.setup_columns(path: "",
14
+ title: "",
15
+ author: "",
16
+ page_num: 0,
17
+ timestamp: Time.new,
18
+ )
19
+ @pages.setup_columns(book: @books,
20
+ text: "",
21
+ page_no: 0,
22
+ )
23
+ end
24
+
25
+ def add_book(path, pages, options = {})
26
+ book = book_from_path(path)
27
+
28
+ if book
29
+ # Already exist
30
+ opts = options.dup
31
+ opts[:pages] = pages
32
+ change_book(book.id, opts)
33
+ return book, :update
34
+
35
+ else
36
+ # New book
37
+ path = Util.filename_to_utf8(path)
38
+ title = options[:title] || File.basename(path, File.extname(path))
39
+ timestamp = options[:timestamp] || Time.now
40
+
41
+ book = @books.add(path: path,
42
+ title: title,
43
+ page_num: pages.size,
44
+ timestamp: timestamp,
45
+ )
46
+
47
+ pages.each_with_index do |page, index|
48
+ @pages["#{book.id}:#{index+1}"] = { book: book, text: page, page_no: index+1 }
49
+ end
50
+
51
+ return book, :add
52
+ end
53
+ end
54
+
55
+ def change_book(book_id, options = {})
56
+ book = @books[book_id]
57
+
58
+ book.title = options[:title] if options[:title]
59
+ book.path = options[:path] if options[:path]
60
+ book.timestamp = options[:timestamp] if options[:timestamp]
61
+
62
+ if options[:pages]
63
+ pages = options[:pages]
64
+ book.page_num = pages.size
65
+
66
+ @pages.delete do |page|
67
+ page.book == book
68
+ end
69
+
70
+ pages.each_with_index do |page, index|
71
+ @pages["#{book.id}:#{index+1}"] = { book: book, text: page, page_no: index+1 }
72
+ end
73
+ end
74
+ end
75
+
76
+ def delete_book(book_id)
77
+ book = @books[book_id]
78
+
79
+ @pages.delete do |page|
80
+ page.book == book
81
+ end
82
+
83
+ book.delete
84
+ end
85
+
86
+ def search(query)
87
+ @pages.select(query, default_column: "text")
88
+ end
89
+
90
+ def book_pages(book_id)
91
+ @pages.select("book._id:\"#{book_id}\"").sort(["page_no"])
92
+ end
93
+
94
+ def book_from_path(path)
95
+ r = @books.select("path:\"#{path}\"").first
96
+
97
+ if r
98
+ r.key
99
+ else
100
+ nil
101
+ end
102
+ end
103
+ end
104
+ end