fuzzy_notes 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|