fuzzy_notes 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.
- data/README +1 -0
- data/TODO +2 -0
- data/bin/fnote +63 -0
- data/lib/fuzzy_notes.rb +7 -0
- data/lib/fuzzy_notes/fuzzy_finder.rb +42 -0
- data/lib/fuzzy_notes/logger.rb +21 -0
- data/lib/fuzzy_notes/notes.rb +116 -0
- metadata +100 -0
data/README
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
README
|
data/bin/fnote
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'yaml'
|
5
|
+
require 'rubygems'
|
6
|
+
require 'fuzzy_notes'
|
7
|
+
|
8
|
+
CONFIG_PATH = "#{ENV['HOME']}/.fuzzy_notes"
|
9
|
+
|
10
|
+
options = {}
|
11
|
+
need_extra_args = [:print, :edit, :encrypt, :decrypt]
|
12
|
+
optparse = OptionParser.new do |opts|
|
13
|
+
opts.banner = "Usage: fnote [options] [keyword1, keyword2...]"
|
14
|
+
|
15
|
+
opts.on("-c", "--config [CONFIG]", "Specify config file") { |opt| options[:config] = opt}
|
16
|
+
opts.on("-p", "--print", "Dump matching notes to stdout") { |opt| options[:print] = true }
|
17
|
+
opts.on("-l", "--list", "List statistics for matching notes") { |opt| options[:list] = true }
|
18
|
+
opts.on("-i", "--info", "Alias for 'list'") { |opt| options[:info] = true }
|
19
|
+
opts.on("-s", "--search", "Perform a full text search when matching notes") { |opt| options[:search] = true }
|
20
|
+
opts.on("-v", "--verbose", "Enable debug output") { |opt| options[:verbose] = true }
|
21
|
+
opts.on("-e", "--encrypt", "Encrypt matching notes") { |opt| options[:encrypt] = true }
|
22
|
+
opts.on("-d", "--decrypt", "Decrypt matching notes") { |opt| options[:decrypt] = true }
|
23
|
+
opts.on("-h", "--help", "Show this message") {
|
24
|
+
puts opts
|
25
|
+
exit
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
begin
|
30
|
+
optparse.parse!(ARGV)
|
31
|
+
options[:edit] = true if options.values.compact.empty?
|
32
|
+
# check for extra args if necessary
|
33
|
+
if need_extra_args.any? {|opt| options[opt]} && ARGV.empty?
|
34
|
+
puts optparse
|
35
|
+
exit
|
36
|
+
end
|
37
|
+
rescue OptionParser::ParseError => e
|
38
|
+
puts optparse
|
39
|
+
end
|
40
|
+
|
41
|
+
config_path = \
|
42
|
+
options[:config] && File.exists?(options[:config]) && options[:config] ||
|
43
|
+
File.exists?(CONFIG_PATH) && CONFIG_PATH
|
44
|
+
|
45
|
+
config = config_path ? YAML::load_file(config_path) : {}
|
46
|
+
puts "Warning: config file not found, using defaults" if config.empty?
|
47
|
+
|
48
|
+
notes = FuzzyNotes::Notes.new(:log_level => (options[:verbose] || config[:verbose]) ? 0 : 1,
|
49
|
+
:note_paths => config[:note_paths],
|
50
|
+
:full_text_search => options[:search] || config[:full_text_search],
|
51
|
+
:keywords => ARGV)
|
52
|
+
|
53
|
+
if options[:list] || options[:info]
|
54
|
+
notes.info
|
55
|
+
elsif options[:print]
|
56
|
+
notes.cat
|
57
|
+
elsif options[:encrypt]
|
58
|
+
notes.encrypt
|
59
|
+
elsif options[:decrypt]
|
60
|
+
notes.decrypt
|
61
|
+
else
|
62
|
+
notes.edit
|
63
|
+
end
|
data/lib/fuzzy_notes.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'find'
|
2
|
+
|
3
|
+
class FuzzyNotes::FuzzyFinder
|
4
|
+
extend FuzzyNotes::Logger
|
5
|
+
private_class_method :new
|
6
|
+
|
7
|
+
def self.find(path, params = {})
|
8
|
+
keywords, extensions, search = params.values_at(:keywords, :extensions, :full_text_search)
|
9
|
+
match_proc = method(search ? :full_text_match? : :file_name_match?)
|
10
|
+
log.debug "[debug] search path: #{path}"
|
11
|
+
|
12
|
+
all_files, matching_files = [], []
|
13
|
+
Find.find(*path) do |file_path|
|
14
|
+
if extension_match?(file_path, extensions)
|
15
|
+
all_files << file_path
|
16
|
+
matching_files << file_path if match_proc.call(file_path, keywords)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
[all_files.sort, matching_files.sort]
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def self.extension_match?(file_path, extensions)
|
26
|
+
file_name = File.basename(file_path)
|
27
|
+
!extensions || extensions.any? {|ext| /\.#{ext}$/ === file_name }
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.file_name_match?(file_path, keywords)
|
31
|
+
keywords ? keywords.any? { |name| /#{name}/ === file_path } : false
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.full_text_match?(file_path, keywords)
|
35
|
+
if keywords && !keywords.empty?
|
36
|
+
file_contents = File.read(file_path)
|
37
|
+
keywords.any? { |key| /#{key}/m === file_contents }
|
38
|
+
else false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'buffered_logger'
|
2
|
+
|
3
|
+
class FuzzyNotes::Log
|
4
|
+
LOG_LEVEL = 1
|
5
|
+
|
6
|
+
private_class_method :new
|
7
|
+
|
8
|
+
def self.init_log(log_level)
|
9
|
+
@log = BufferedLogger.new(STDOUT, log_level)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.log
|
13
|
+
@log ||= BufferedLogger.new(STDOUT, LOG_LEVEL)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module FuzzyNotes::Logger
|
18
|
+
def log
|
19
|
+
FuzzyNotes::Log.log
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'gibberish'
|
2
|
+
|
3
|
+
class FuzzyNotes::Notes
|
4
|
+
include FuzzyNotes::Logger
|
5
|
+
|
6
|
+
INIT_PARAMS = :log_level, :editor, :note_paths, :valid_extensions, :keywords
|
7
|
+
|
8
|
+
# param defaults
|
9
|
+
#
|
10
|
+
LOG_LEVEL = 1
|
11
|
+
EDITOR='vim'
|
12
|
+
NOTE_PATHS=[ "#{ENV['HOME']}/notes" ]
|
13
|
+
VALID_EXTENSIONS=%w( txt enc )
|
14
|
+
KEYWORDS = []
|
15
|
+
|
16
|
+
attr_reader :notes, :all_notes
|
17
|
+
|
18
|
+
def initialize(params = {})
|
19
|
+
parse_init_params(params)
|
20
|
+
|
21
|
+
unless @note_paths.any? { |p| File.exists?(p) }
|
22
|
+
log.error "ERROR: no valid note paths found"
|
23
|
+
exit
|
24
|
+
end
|
25
|
+
|
26
|
+
@all_notes, @notes = \
|
27
|
+
FuzzyNotes::FuzzyFinder.find(@note_paths, { :keywords => @keywords,
|
28
|
+
:extensions => @extensions,
|
29
|
+
:full_text_search => params[:full_text_search] })
|
30
|
+
end
|
31
|
+
|
32
|
+
def parse_init_params(params)
|
33
|
+
INIT_PARAMS.each do |param|
|
34
|
+
klass = self.class
|
35
|
+
klass.send(:attr_reader, param)
|
36
|
+
const_name = param.to_s.upcase
|
37
|
+
instance_variable_set("@#{param}", params[param] ||
|
38
|
+
(klass.const_defined?(const_name) ? klass.const_get(const_name) : nil) )
|
39
|
+
end
|
40
|
+
FuzzyNotes::Log.init_log(@log_level)
|
41
|
+
|
42
|
+
ivar_values = instance_variables.inject("") { |s, ivar| s << " #{ivar} => #{eval(ivar).inspect}\n" }
|
43
|
+
log.debug "[debug] init attributes: \n#{ivar_values}"
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
# cat all matching notes to stdout
|
48
|
+
def cat
|
49
|
+
notes.each do |n|
|
50
|
+
puts "=== #{n} ===\n\n"
|
51
|
+
puts "#{File.read(n)}\n"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
# edit all matching notes in EDITOR
|
57
|
+
def edit
|
58
|
+
exec("#{editor} #{bashify_paths(notes)}") if !notes.empty?
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
def encrypt
|
63
|
+
apply_cipher
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def decrypt
|
68
|
+
apply_cipher(true)
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
# view WC info for all/matching notes
|
73
|
+
def info
|
74
|
+
paths = bashify_paths(notes.empty? ? all_notes : notes)
|
75
|
+
puts `wc $(find #{paths} -type f)`
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
|
82
|
+
def apply_cipher(decrypt = false)
|
83
|
+
extension, action = decrypt ? ['.txt', 'dec'] : ['.enc', 'enc']
|
84
|
+
password = get_password
|
85
|
+
cipher = Gibberish::AES.new(password)
|
86
|
+
notes.each do |note|
|
87
|
+
log.info "#{action} '#{note}'"
|
88
|
+
pathname = File.dirname(note)
|
89
|
+
filename = File.basename(note, '.*')
|
90
|
+
begin
|
91
|
+
ciphertext = cipher.send(action, File.read(note))
|
92
|
+
log.debug "[debug] writing encrypted content to: #{pathname}/#{filename}#{extension}"
|
93
|
+
File.open("#{pathname}/#{filename}#{extension}", 'w') { |f| f << ciphertext }
|
94
|
+
log.debug "[debug] deleting unencrypted file: #{note}"
|
95
|
+
File.delete(note)
|
96
|
+
rescue OpenSSL::Cipher::CipherError => e
|
97
|
+
log.error "ERROR: #{e}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
def get_password
|
104
|
+
printf 'Enter password (will not be shown):'
|
105
|
+
`stty -echo`; password = STDIN.gets.strip;`stty echo`; puts
|
106
|
+
log.debug "[debug] entered password: #{password.inspect}"
|
107
|
+
password
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
# lists matching note paths in bash style, space seperated fashion
|
112
|
+
def bashify_paths(paths)
|
113
|
+
paths.map {|n| "\"#{n}\""}.join(' ')
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
metadata
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fuzzy_notes
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Alex Skryl
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-06-24 00:00:00 -05:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: gibberish
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: buffered_logger
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :runtime
|
48
|
+
version_requirements: *id002
|
49
|
+
description: A note manager with fuzzy search, full text search, and encryption capabilities
|
50
|
+
email: rut216@gmail.com
|
51
|
+
executables:
|
52
|
+
- fnote
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
extra_rdoc_files: []
|
56
|
+
|
57
|
+
files:
|
58
|
+
- lib/fuzzy_notes/fuzzy_finder.rb
|
59
|
+
- lib/fuzzy_notes/logger.rb
|
60
|
+
- lib/fuzzy_notes/notes.rb
|
61
|
+
- lib/fuzzy_notes.rb
|
62
|
+
- bin/fnote
|
63
|
+
- README
|
64
|
+
- TODO
|
65
|
+
has_rdoc: true
|
66
|
+
homepage: http://github.com/skryl
|
67
|
+
licenses: []
|
68
|
+
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
hash: 3
|
80
|
+
segments:
|
81
|
+
- 0
|
82
|
+
version: "0"
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
hash: 3
|
89
|
+
segments:
|
90
|
+
- 0
|
91
|
+
version: "0"
|
92
|
+
requirements: []
|
93
|
+
|
94
|
+
rubyforge_project:
|
95
|
+
rubygems_version: 1.6.2
|
96
|
+
signing_key:
|
97
|
+
specification_version: 3
|
98
|
+
summary: A plain text note manager
|
99
|
+
test_files: []
|
100
|
+
|