fuzzy_notes 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/TODO CHANGED
@@ -1,3 +1,8 @@
1
1
  TODO
2
+ - README
3
+ - ask for missing params during evernote AUTH
4
+ - debugging output
5
+ - figure out good output spacing
2
6
  - test suite
3
7
  - 1.9 testing
8
+ - add a per-note password option for enc/dec
data/bin/fnote CHANGED
@@ -9,36 +9,38 @@ require 'rubygems'
9
9
  require 'fuzzy_notes'
10
10
 
11
11
  CONFIG_PATH = "#{ENV['HOME']}/.fuzzy_notes"
12
+ OPT_ACTIONS = [:print, :list, :info, :encrypt, :decrypt, :update_evernotes].freeze
13
+ OPT_KEYWORDS_REQUIRED = [:print, :edit, :encrypt, :decrypt].freeze
12
14
 
13
15
  options = {}
14
16
  optparse = OptionParser.new do |opts|
15
17
  opts.banner = "Usage: fnote [options] [keyword1, keyword2...]"
16
18
 
17
19
  opts.on("-c", "--config [CONFIG]", "Specify config file") { |opt| options[:config] = opt}
20
+ opts.on("-n", "--note-path [PATH]", "Specify config file") { |opt| options[:note_path] = opt}
21
+ opts.on("--editor [EDITOR]", "Editor of choice") { |opt| options[:editor] = opt}
22
+ opts.on("-s", "--search", "Perform a full text search when matching notes") { |opt| options[:search] = true}
18
23
  opts.on("-p", "--print", "Dump matching notes to stdout") { |opt| options[:print] = true }
19
- opts.on("-l", "--list", "List matching notes") { |opt| options[:list] = true }
24
+ opts.on("-l", "--list", "List all or matching notes") { |opt| options[:list] = true }
20
25
  opts.on("-i", "--info", "Show statistics for matching notes") { |opt| options[:info] = true }
21
- opts.on("-s", "--search", "Perform a full text search when matching notes") { |opt| options[:search] = true }
22
- opts.on("-v", "--verbose", "Enable debug output") { |opt| options[:verbose] = true }
23
26
  opts.on("-e", "--encrypt", "Encrypt matching notes") { |opt| options[:encrypt] = true }
24
27
  opts.on("-d", "--decrypt", "Decrypt matching notes") { |opt| options[:decrypt] = true }
25
- opts.on("-n", "--no-color", "Turn off ANSI color") { |opt| options[:color] = false}
26
- opts.on("-u", "--evernote-update", "Synchronize evernote directory") { |opt| options[:evernote_update] = true }
27
- opts.on("-h", "--help", "Show this message") {
28
+ opts.on("-v", "--verbose", "Enable debug output") { |opt| options[:verbose] = true }
29
+ opts.on("--no-color", "Turn off ANSI color") { |opt| options[:no_color] = true}
30
+ opts.on("-u", "--update-evernotes", "Synchronize evernote directory") { |opt| options[:update_evernotes] = true }
31
+ opts.on("-h", "--help", "Show usage") {
28
32
  puts opts
29
33
  exit
30
34
  }
31
35
  end
32
36
 
33
- need_extra_args = [:print, :edit, :encrypt, :decrypt]
34
-
37
+ # parse args
38
+ #
35
39
  begin
36
40
  optparse.parse!(ARGV)
37
- # edit is the default action
38
- options[:edit] = true if options.values.compact.empty?
39
-
41
+ options[:edit] = true if options.values_at(*OPT_ACTIONS).compact.empty?
40
42
  # check for required args
41
- if need_extra_args.any? {|opt| options[opt]} && ARGV.empty?
43
+ if OPT_KEYWORDS_REQUIRED.any? {|opt| options[opt]} && ARGV.empty?
42
44
  puts optparse
43
45
  exit
44
46
  end
@@ -51,22 +53,25 @@ end
51
53
  config_path = \
52
54
  File.exists?(options[:config].to_s) && options[:config] ||
53
55
  File.exists?(CONFIG_PATH) && CONFIG_PATH
54
-
55
56
  config = config_path ? YAML::load_file(config_path) : {}
56
57
  log.info("config file not found, using defaults") if config.empty?
57
58
 
58
- notes = FuzzyNotes::Notes.new(:editor => config[:editor],
59
- :note_paths => config[:note_paths],
59
+ # grab notes
60
+ #
61
+ notes = FuzzyNotes::Notes.new(:editor => options[:editor] || config[:editor],
62
+ :note_paths => [options[:note_path] || config[:note_paths]].flatten,
60
63
  :evernote_params => config[:evernote],
61
64
  :full_text_search => options[:search] || config[:full_text_search],
62
65
  :log_level => (options[:verbose] || config[:verbose]) ? :debug : :info,
63
- :color => (options[:color] == false || config[:color] == false) ? false : true,
66
+ :color => (options[:no_color] || config[:no_color]) ? false : true,
64
67
  :keywords => ARGV)
65
68
 
66
- notes.evernote_sync if options[:evernote_update]
67
-
69
+ # perform action
70
+ #
68
71
  if options[:list]
69
72
  notes.list
73
+ elsif options[:update_evernotes]
74
+ notes.evernote_sync
70
75
  elsif options[:info]
71
76
  notes.info
72
77
  elsif options[:print]
@@ -4,7 +4,7 @@ require 'rubygems'
4
4
  module FuzzyNotes; end
5
5
 
6
6
  require 'fuzzy_notes/logger'
7
- require 'fuzzy_notes/password_protected'
7
+ require 'fuzzy_notes/authentication'
8
8
  require 'fuzzy_notes/cipher'
9
9
  require 'fuzzy_notes/evernote_sync'
10
10
  require 'fuzzy_notes/fuzzy_finder'
@@ -1,4 +1,9 @@
1
- module FuzzyNotes::PasswordProtected
1
+ module FuzzyNotes::Authentication
2
+
3
+ def get_username
4
+ printf 'Enter username: '
5
+ gets
6
+ end
2
7
 
3
8
  def get_password
4
9
  printf 'Enter password (will not be shown):'
@@ -2,48 +2,63 @@ require 'gibberish'
2
2
 
3
3
  class FuzzyNotes::Cipher
4
4
  include FuzzyNotes::Logger
5
- include FuzzyNotes::PasswordProtected
5
+ include FuzzyNotes::Authentication
6
6
 
7
7
  PLAINTEXT_EXT = 'txt'
8
8
  CIPHERTEXT_EXT = 'enc'
9
+ TMP_FILE_PREFIX = 'fuzzy_notes'
10
+ ENCRYPT_METHOD_MATCHER = /\Aencrypt_(data|file|files|from_tempfile|from_tempfiles)\Z/
11
+ DECRYPT_METHOD_MATCHER = /\Adecrypt_(data|file|files|to_tempfile|to_tempfiles)\Z/
9
12
 
10
- def encrypt(file_paths, opts = {})
11
- @action = :enc
12
- apply_cipher(file_paths, opts)
13
- end
13
+ private
14
+
15
+ def method_missing(method_sym, *args)
16
+ method_name = method_sym.to_s
17
+ log.debug "args: #{args.inspect}"
18
+ last_arg = args.last
19
+ opts = last_arg.is_a?(Hash) ? last_arg : {}
20
+
21
+ case method_name
22
+ when ENCRYPT_METHOD_MATCHER
23
+ @action = :enc
24
+ when DECRYPT_METHOD_MATCHER
25
+ @action = :dec
26
+ else super
27
+ end
14
28
 
15
- def decrypt(file_paths, opts = {})
16
- @action = :dec
17
- apply_cipher(file_paths, opts)
29
+ case $1
30
+ when 'data'
31
+ process_data(args.first, Gibberish::AES.new(get_password))
32
+ when 'file'
33
+ process_file(args.first, Gibberish::AES.new(get_password), opts)
34
+ when 'files'
35
+ cipher = Gibberish::AES.new(get_password)
36
+ args.first.map { |path| process_file(path, cipher, opts) }
37
+ when 'to_tempfile', 'from_tempfile'
38
+ self.send(method_sym, args.first, Gibberish::AES.new(get_password))
39
+ when 'to_tempfiles', 'from_tempfiles'
40
+ cipher = Gibberish::AES.new(get_password)
41
+ args.first.map { |file_path| self.send(method_name[0..-2], *file_path, cipher) }
42
+ end
18
43
  end
19
44
 
20
- private
45
+ def process_file(path, cipher, opts = {})
46
+ return unless path && valid_filename?(path)
47
+ content = File.read(path)
48
+ return unless valid_content?(content)
49
+ log.info "#{decrypt? ? 'de' : 'en'}crypting file #{PATH_COLOR} #{path}"
21
50
 
22
- def apply_cipher(file_paths, opts)
23
- cipher = Gibberish::AES.new(get_password)
24
- case file_paths
25
- when Array
26
- file_paths.each do |path|
27
- if encrypt? && self.class.encrypted?(path) ||
28
- decrypt? && !self.class.encrypted?(path)
29
- log.warn "#{Colors::PATH} #{path} #{Colors::DEFAULT} is #{encrypt? ? 'already' : 'not'} encrypted, skipping"
30
- next
31
- end
32
- process_file(path, cipher, opts)
33
- end
34
- when String
35
- process_file(file_paths, cipher, opts)
36
- end
51
+ processed_content = process_data(content, cipher)
52
+ replace_file!(path, processed_content) if processed_content && opts[:replace]
53
+ processed_content
37
54
  end
38
55
 
39
- def process_file(path, cipher, opts)
56
+ def process_data(data, cipher)
40
57
  begin
41
- log.debug "#{@action} '#{path}'"
42
- content = cipher.send(@action, File.read(path))
43
- replace_file!(path, content) if opts[:replace]
44
- content
58
+ cipher.send(@action, data) unless data.blank?
45
59
  rescue OpenSSL::Cipher::CipherError => e
46
60
  log.error e
61
+ nil
47
62
  end
48
63
  end
49
64
 
@@ -51,13 +66,49 @@ private
51
66
  dirname = File.dirname(path)
52
67
  filename = File.basename(path, '.*')
53
68
 
54
- log.info "#{Colors::CREATE} writing #{decrypt? ? 'un' : ''}encrypted file: #{Colors::PATH} #{dirname}/#{filename}.#{extension}"
69
+ log.info "#{CREATE_COLOR} writing #{decrypt? ? 'un' : ''}encrypted file: #{PATH_COLOR} #{dirname}/#{filename}.#{extension}"
55
70
  File.open("#{dirname}/#{filename}.#{extension}", 'w') { |f| f << contents }
56
71
 
57
- log.info "#{Colors::DELETE} deleting #{decrypt? ? '' : 'un'}encrypted file: #{Colors::PATH} #{path}"
72
+ log.info "#{DELETE_COLOR} deleting #{decrypt? ? '' : 'un'}encrypted file: #{PATH_COLOR} #{path}"
58
73
  File.delete(path)
59
74
  end
60
75
 
76
+ def decrypt_to_tempfile(file_path, cipher)
77
+ return unless file_path
78
+ content = process_file(file_path, cipher)
79
+ content && Tempfile.open(TMP_FILE_PREFIX) do |tmp_file|
80
+ tmp_file << content
81
+ tmp_file.path
82
+ end
83
+ end
84
+
85
+ def encrypt_from_tempfile(tmp_file, file_path, cipher)
86
+ return unless tmp_file && file_path
87
+ content = File.read(tmp_file)
88
+ return unless valid_content?(content)
89
+
90
+ log.info "#{CREATE_COLOR} writing encrypted file: #{PATH_COLOR} #{file_path}"
91
+ File.open(file_path, 'w') do |file|
92
+ file << process_data(content, cipher)
93
+ end
94
+ end
95
+
96
+ def valid_filename?(path)
97
+ if encrypt? && self.class.encrypted?(path) || decrypt? && !self.class.encrypted?(path)
98
+ log.warn "#{PATH_COLOR} #{path} #{DEFAULT_COLOR} is #{encrypt? ? 'already' : 'not'} encrypted, skipping"
99
+ return false
100
+ end
101
+ true
102
+ end
103
+
104
+ def valid_content?(content)
105
+ if content.blank?
106
+ log.warn "file is empty, skipping"
107
+ return false
108
+ end
109
+ true
110
+ end
111
+
61
112
  def extension
62
113
  decrypt? ? PLAINTEXT_EXT : CIPHERTEXT_EXT
63
114
  end
@@ -74,5 +125,4 @@ private
74
125
  File.extname(path)[1..-1] == CIPHERTEXT_EXT
75
126
  end
76
127
 
77
-
78
128
  end
@@ -6,13 +6,12 @@ require 'ostruct'
6
6
 
7
7
  class FuzzyNotes::EvernoteSync
8
8
  include FuzzyNotes::Logger
9
- include Colors
10
- include FuzzyNotes::PasswordProtected
9
+ include FuzzyNotes::Authentication
11
10
 
12
- USER_STORE_URL = 'https://evernote.com/edam/user'
13
- NOTE_STORE_URL = 'http://evernote.com/edam/note'
14
- NOTE_EXT = 'html'
15
- MAX_NOTES = 1000
11
+ USER_STORE_URL = 'http://evernote.com/edam/user'
12
+ NOTE_STORE_URL = 'http://evernote.com/edam/note'
13
+ NOTE_EXT = 'html'
14
+ MAX_NOTES = 1000
16
15
 
17
16
  # opts should be a hash with the following keys:
18
17
  # :username, :password, :consumer_key, :consumer_secret
@@ -26,16 +25,17 @@ MAX_NOTES = 1000
26
25
 
27
26
  def sync
28
27
  return unless authenticated? && valid_sync_path?
29
- log.info "#{IMPORT} synchronizing with Evernote account..."
30
- log.indent(2) { log.info "#{IMPORT} checking for updates..." }
28
+ log.info "syncing evernote directory #{PATH_COLOR} #{@path}"
29
+ log.info "#{IMPORT_COLOR} synchronizing with Evernote account..."
30
+ log.indent(2) { log.info "#{IMPORT_COLOR} checking for updates..." }
31
31
  log.indent(4) do
32
32
  notebook_structs = fetch_notebooks
33
- log.info "#{IMPORT} syncing Evernote deletions..."
33
+ log.info "#{IMPORT_COLOR} syncing Evernote deletions..."
34
34
  log.indent(2) do
35
35
  propagate_evernote_deletions(notebook_structs)
36
36
  end
37
37
  notebook_structs.each do |notebook_struct|
38
- log.info "#{IMPORT} syncing notebook #{NOTE} #{notebook_struct.name}"
38
+ log.info "#{IMPORT_COLOR} syncing notebook #{NOTE_COLOR} #{notebook_struct.name}"
39
39
  log.indent(2) do
40
40
  create_local_notebook(notebook_struct)
41
41
  sync_notes(notebook_struct)
@@ -75,26 +75,33 @@ private
75
75
  def propagate_evernote_deletions(notebook_structs)
76
76
  evernote_dir_entries = Dir["#{@path}/*"]
77
77
  evernote_dir_entries.each do |notebook_path|
78
- notebook_name = File.basename(notebook_path)
79
- notebook_match = notebook_structs.find { |ns| sanitize_filename(ns.name) == notebook_name }
78
+ notebook_match = notebook_structs.find { |ns| sanitize_filename(ns.name) == File.basename(notebook_path) }
80
79
  unless notebook_match
81
- log.info "#{DELETE} notebook #{NOTE} #{notebook_name} #{DELETE} has been deleted from Evernote"
82
- verify_deletion(notebook_path)
80
+ delete_notebook!(notebook_path)
83
81
  else
84
82
  note_entries = Dir["#{notebook_path}/*"]
85
83
  note_entries.each do |note_path|
86
- note_title = File.basename(note_path, '.*')
87
- unless notebook_match.notes.any? { |n| sanitize_filename(n.title) == note_title }
88
- log.info "#{DELETE} note #{NOTE} #{note_title} #{DELETE} has been deleted from Evernote"
89
- verify_deletion(note_path)
84
+ unless notebook_match.notes.any? { |n| sanitize_filename(n.title) == File.basename(note_path, '.*') }
85
+ delete_note!(note_path)
90
86
  end
91
87
  end
92
88
  end
93
89
  end
94
90
  end
95
91
 
92
+ def delete_notebook!(notebook_path)
93
+ log.info "#{DELETE_COLOR} notebook #{NOTE_COLOR} #{File.basename(notebook_path)} #{DELETE_COLOR} has been deleted from Evernote"
94
+ verify_deletion(notebook_path)
95
+ end
96
+
97
+ def delete_note!(note_path)
98
+ log.info "#{DELETE_COLOR} note #{NOTE_COLOR} #{File.basename(note_path, '.*')} #{DELETE_COLOR} has been deleted from Evernote"
99
+ verify_deletion(note_path)
100
+ end
101
+
96
102
  def sync_notes(notebook_struct)
97
- note_updates = notebook_struct.notes.inject([[],[]]) do |(import, export), note|
103
+ note_updates = \
104
+ notebook_struct.notes.inject([[],[]]) do |(import, export), note|
98
105
  if needs_import?(notebook_struct, note)
99
106
  import << fetch_note_with_content(note)
100
107
  elsif needs_export?(notebook_struct, note)
@@ -109,7 +116,7 @@ private
109
116
  def import_notes(notebook_struct, notes)
110
117
  notes.each do |note|
111
118
  note_path = get_note_path(notebook_struct, note)
112
- log.info "#{IMPORT} importing note #{NOTE} #{note.title} #{DEFAULT} with content length #{NUMBER} #{note.contentLength}"
119
+ log.info "#{IMPORT_COLOR} importing note #{NOTE_COLOR} #{note.title} #{DEFAULT_COLOR} with content length #{NUMBER_COLOR} #{note.contentLength}"
113
120
  File.open(note_path, 'w') { |f| f << note.content }
114
121
  end
115
122
  end
@@ -117,7 +124,7 @@ private
117
124
  def export_notes(notebook_struct, notes)
118
125
  notes.each do |note|
119
126
  note.content = local_note_content(notebook_struct, note)
120
- log.info "#{EXPORT} exporting note #{NOTE} #{note.title} #{DEFAULT} with content length #{NUMBER} #{note.content.length}"
127
+ log.info "#{EXPORT_COLOR} exporting note #{NOTE_COLOR} #{note.title} #{DEFAULT_COLOR} with content length #{NUMBER_COLOR} #{note.content.length}"
121
128
  begin
122
129
  @note_store.updateNote(@token, note)
123
130
  rescue Evernote::EDAM::Error::EDAMUserException => e
@@ -194,7 +201,7 @@ private
194
201
  def verify_deletion(path)
195
202
  r = nil
196
203
  until r =~ /(Y|y|N|n)/ do
197
- printf "Are you sure you want to delete #{path}? (Y/N) "
204
+ log.info "Are you sure you want to delete #{path}? (Y/N) "
198
205
  r = gets
199
206
  end
200
207
 
@@ -210,18 +217,23 @@ private
210
217
  name
211
218
  end
212
219
 
220
+ def self.evernote?(path)
221
+ File.extname(path)[1..-1] == NOTE_EXT
222
+ end
223
+
213
224
  # authentication helpers
214
225
 
226
+ #TODO: ask for all missing params
215
227
  def authenticate(params)
216
228
  params.merge!(:password => get_password)
217
229
  user_store = Evernote::UserStore.new(USER_STORE_URL, params)
218
230
  begin
219
- user_store.authenticate
231
+ token = user_store.authenticate
232
+ log.info "Evernote authentication was successful for #{USER_COLOR} #{params[:username]}"
233
+ token
220
234
  rescue Evernote::UserStore::AuthenticationFailure
221
- log.error "Evernote authentication failed for #{USER} #{params[:username]}"
235
+ log.error "Evernote authentication failed for #{USER_COLOR} #{params[:username]}"
222
236
  return
223
- ensure
224
- log.info "Evernote authentication was successful for #{USER} #{params[:username]}"
225
237
  end
226
238
  end
227
239
 
@@ -3,7 +3,7 @@ require 'find'
3
3
  class FuzzyNotes::FuzzyFinder
4
4
  include FuzzyNotes::Logger
5
5
 
6
- attr_reader :path, :all_files, :matching_files, :keywords, :extensions, :full_text_search
6
+ attr_reader :path, :all_files, :files_matching_extension, :files_matching_all, :keywords, :extensions, :full_text_search
7
7
 
8
8
  def initialize(path, params = {})
9
9
  @path = path
@@ -16,9 +16,12 @@ class FuzzyNotes::FuzzyFinder
16
16
  def refresh
17
17
  clear_results
18
18
  Find.find(*@path) do |file_path|
19
- if !File.directory?(file_path) && extension_match?(file_path)
20
- @all_files << file_path
21
- @matching_files << file_path if file_match_proc.call(file_path)
19
+ if !File.directory?(file_path)
20
+ @all_files << file_path
21
+ if extension_match?(file_path)
22
+ @files_matching_extension << file_path
23
+ @files_matching_all << file_path if file_match_proc.call(file_path)
24
+ end
22
25
  end
23
26
  end
24
27
  end
@@ -26,11 +29,7 @@ class FuzzyNotes::FuzzyFinder
26
29
  private
27
30
 
28
31
  def clear_results
29
- @all_files, @matching_files = [], []
30
- end
31
-
32
- def file_match_proc
33
- method(@search_type ? :full_text_match? : :file_name_match?)
32
+ @all_files, @files_matching_extension, @files_matching_all = [], [], []
34
33
  end
35
34
 
36
35
  def extension_match?(file_path)
@@ -38,17 +37,27 @@ private
38
37
  !@extensions || @extensions.any? {|ext| /\.#{ext}$/ === file_name }
39
38
  end
40
39
 
40
+ def file_match_proc
41
+ method(@search_type ? :full_text_match? : :file_name_match?)
42
+ end
43
+
44
+ # search procs
45
+
41
46
  def file_name_match?(file_path)
42
- @keywords ? @keywords.any? { |name| /#{name}/ === file_path } : false
47
+ @keywords ? @keywords.any? { |keyword| /#{keyword}/ === file_path } : false
43
48
  end
44
49
 
45
50
  def full_text_match?(file_path)
46
- if @keywords && !@keywords.empty?
51
+ unless @keywords.blank? || encrypted_file?(file_path)
47
52
  file_contents = File.read(file_path)
48
53
  @keywords.any? { |key| /#{key}/m === file_contents }
49
54
  else false
50
55
  end
51
56
  end
52
57
 
58
+ def encrypted_file?(path)
59
+ /\.#{FuzzyNotes::Cipher::CIPHERTEXT_EXT}$/ === path
60
+ end
61
+
53
62
 
54
63
  end
@@ -27,16 +27,17 @@ end
27
27
 
28
28
  module FuzzyNotes::Logger
29
29
  module Colors
30
- PATH = "$blue"
31
- USER = "$green"
32
- NOTE = "$cyan"
33
- NUMBER = "$red"
34
- CREATE = "$green"
35
- DELETE = "$red"
36
- IMPORT = "$green"
37
- EXPORT = "$red"
38
- DEFAULT = "$reset"
30
+ PATH_COLOR = "$blue"
31
+ USER_COLOR = "$green"
32
+ NOTE_COLOR = "$cyan"
33
+ NUMBER_COLOR = "$red"
34
+ CREATE_COLOR = "$green"
35
+ DELETE_COLOR = "$red"
36
+ IMPORT_COLOR = "$green"
37
+ EXPORT_COLOR = "$red"
38
+ DEFAULT_COLOR = "$reset"
39
39
  end
40
+ include Colors
40
41
 
41
42
  def log
42
43
  FuzzyNotes::Log.log
@@ -1,10 +1,11 @@
1
+ require 'tempfile'
2
+
1
3
  class FuzzyNotes::Notes
2
4
  include FuzzyNotes::Logger
3
5
 
4
6
  module Defaults
5
- LOG_LEVEL = 1
7
+ LOG_LEVEL = :info
6
8
  EDITOR = 'vim'
7
- COLOR = true
8
9
  KEYWORDS = []
9
10
  NOTE_PATHS = [ "#{ENV['HOME']}/notes" ]
10
11
  VALID_EXTENSIONS = [ 'txt',
@@ -15,54 +16,43 @@ module Defaults
15
16
  def self.const_missing(*args); end
16
17
  end
17
18
 
18
- OPTS = [:log_level, :color, :editor, :note_paths, :valid_extensions, :keywords, :evernote_params].freeze
19
+ VALID_PARAMS = [:log_level, :color, :editor, :note_paths, :valid_extensions, :keywords, :evernote_params].freeze
19
20
 
20
21
  attr_reader :matching_notes, :all_notes
21
22
 
22
23
  def initialize(params = {})
23
24
  parse_init_params(params)
24
25
  FuzzyNotes::Log.init_log(@log_level, @color)
25
- log.debug "init attributes: \n#{inspect_instance_vars}"
26
-
27
- unless note_paths_valid?
28
- log.error "no valid note paths found, exiting"
29
- exit
30
- end
26
+ log.debug "init params: \n#{inspect_instance_vars}"
27
+ @note_paths = prune_invalid_note_paths!
31
28
 
32
29
  finder = FuzzyNotes::FuzzyFinder.new(@note_paths,
33
30
  { :keywords => @keywords,
34
- :extensions => @extensions,
31
+ :extensions => @valid_extensions,
35
32
  :full_text_search => params[:full_text_search] })
36
- @all_notes, @matching_notes = finder.all_files, finder.matching_files
33
+ @all_notes, @matching_notes = finder.files_matching_extension, finder.files_matching_all
34
+ @cipher = FuzzyNotes::Cipher.new
37
35
  end
38
36
 
39
- # initialize params or use defaults
40
- #
41
- def parse_init_params(params)
42
- OPTS.each do |param|
43
- klass = self.class
44
- klass.send(:attr_reader, param)
45
- default_const = param.to_s.upcase
46
- instance_variable_set("@#{param}", params.include?(param) ? params[param] : Defaults.const_get(default_const) )
47
- end
48
- end
49
- private :parse_init_params
50
-
51
37
  # dump all matching notes to stdout
52
38
  #
53
39
  def cat
40
+ unless encrypted_notes.empty?
41
+ print_notes(:encrypted => true)
42
+ decrypted_notes = @cipher.decrypt_files(encrypted_notes)
43
+ end
44
+
54
45
  matching_notes.each do |note_path|
55
46
  contents = \
56
47
  if FuzzyNotes::Cipher.encrypted?(note_path)
57
- log.info "decrypting note #{Colors::PATH} #{note_path}"
58
- FuzzyNotes::Cipher.new.decrypt(note_path)
59
- elsif evernote?(note_path)
48
+ decrypted_notes.shift
49
+ elsif FuzzyNotes::EvernoteSync.evernote?(note_path)
60
50
  FuzzyNotes::EvernoteSync.sanitize_evernote(note_path)
61
51
  else
62
52
  File.read(note_path)
63
53
  end
64
54
 
65
- unless contents.blank?
55
+ if contents
66
56
  log.info "=== #{note_path} ===\n\n"
67
57
  puts "#{contents}\n"
68
58
  end
@@ -72,74 +62,155 @@ end
72
62
  # edit all matching notes in EDITOR
73
63
  #
74
64
  def edit
75
- exec("#{editor} #{bashify_paths(matching_notes)}") unless matching_notes.empty?
65
+ notes_to_edit = \
66
+ unless encrypted_notes.empty?
67
+ print_notes(:encrypted => true)
68
+ decrypted_tempfiles = @cipher.decrypt_to_tempfiles(encrypted_notes)
69
+ successfully_decrypted_files = decrypted_tempfiles.compact
70
+ plaintext_notes + successfully_decrypted_files
71
+ else plaintext_notes
72
+ end
73
+
74
+ # edit decrypted files
75
+ unless notes_to_edit.empty?
76
+ system("#{editor} #{bashify_note_paths(notes_to_edit)}")
77
+ end
78
+
79
+ # reencrypt decrypted notes
80
+ unless encrypted_notes.empty? || successfully_decrypted_files.empty?
81
+ log.info "#{CREATE_COLOR} re-encrypting edited notes:"
82
+ tempfiles_notes = decrypted_tempfiles.zip(encrypted_notes)
83
+ log.indent do
84
+ tempfiles_notes.each do |(tmpfile, note_path)|
85
+ log.info "#{PATH_COLOR} #{note_path}" if note_path
86
+ end
87
+ end
88
+ log.indent { @cipher.encrypt_from_tempfiles(tempfiles_notes) }
89
+ end
76
90
  end
77
91
 
78
92
  # encrypt matching notes
79
93
  #
80
94
  def encrypt
81
- return if matching_notes.empty?
82
- log.info "encrypting matching notes:"
83
- print_notes
84
- log.indent(2) { FuzzyNotes::Cipher.new.encrypt(matching_notes, :replace => true) }
95
+ return if plaintext_notes.empty?
96
+ print_notes(:plaintext => true)
97
+ log.indent do
98
+ @cipher.encrypt_files(plaintext_notes, :replace => true)
99
+ end
85
100
  end
86
101
 
87
102
  # decrypt matching notes
88
103
  #
89
104
  def decrypt
90
- return if matching_notes.empty?
91
- log.info "decrypting matching notes:"
92
- print_notes
93
- log.indent(2) { FuzzyNotes::Cipher.new.decrypt(matching_notes, :replace => true) }
105
+ return if encrypted_notes.empty?
106
+ print_notes(:encrypted => true)
107
+ log.indent do
108
+ @cipher.decrypt_files(encrypted_notes, :replace => true)
109
+ end
94
110
  end
95
111
 
96
112
  # view WC info for all/matching notes
97
113
  #
98
114
  def info
99
- paths = bashify_paths(matching_notes.empty? ? all_notes : matching_notes)
115
+ paths = bashify_note_paths(matching_notes(:all_if_empty => true))
100
116
  puts `wc $(find #{paths} -type f)`
101
117
  end
102
118
 
103
119
  def list
104
- print_notes(:all => true)
120
+ print_notes(:all_if_empty => true)
105
121
  end
106
122
 
107
123
  def evernote_sync
108
- unless @evernote_params
109
- log.error("no evernote configuration found!")
110
- return
111
- end
112
-
113
- log.info "syncing evernote directory #{Colors::PATH} #{@evernote_params[:note_path]}"
124
+ return unless evernote_params_found?
114
125
  FuzzyNotes::EvernoteSync.new(@evernote_params).sync
115
- log.print_blank_line
116
126
  end
117
127
 
118
128
  private
119
129
 
120
- def note_paths_valid?
121
- @note_paths.any? do |p|
122
- File.directory?(p) || log.warn("note path #{Colors::PATH} #{p} does not exist")
130
+ def encrypted_notes
131
+ @encrypted_notes ||= matching_notes.select { |note_path| FuzzyNotes::Cipher.encrypted?(note_path) }
132
+ end
133
+
134
+ def evernote_notes
135
+ @evernote_notes ||= matching_notes.select { |note_path| FuzzyNotes::EvernoteSync.evernote?(note_path) }
136
+ end
137
+
138
+ def plaintext_notes
139
+ @plaintext_notes ||= matching_notes.select { |note_path| !FuzzyNotes::Cipher.encrypted?(note_path) &&
140
+ !FuzzyNotes::EvernoteSync.evernote?(note_path) }
141
+ end
142
+
143
+ def prune_invalid_note_paths!
144
+ valid_paths = []
145
+ @note_paths.each do |path|
146
+ if File.directory?(path)
147
+ valid_paths << path
148
+ else
149
+ log.warn("note path #{PATH_COLOR} #{path} #{DEFAULT_COLOR} does not exist")
150
+ end
123
151
  end
152
+ if valid_paths.empty?
153
+ log.error "no valid note paths found, exiting"
154
+ exit
155
+ end
156
+ valid_paths
124
157
  end
125
158
 
126
- # bash style, space seperated fashion
159
+ # TODO: grab creds from user
160
+ def evernote_params_found?
161
+ unless @evernote_params
162
+ log.error("required evernote configuration not found!")
163
+ false
164
+ else true
165
+ end
166
+ end
167
+
168
+ # bash helpers
127
169
  #
128
- def bashify_paths(paths)
170
+ def bashify_note_paths(paths)
129
171
  paths.map {|n| "\"#{n}\""}.join(' ')
130
172
  end
131
173
 
132
- def inspect_instance_vars
133
- instance_variables.inject("") { |s, ivar| s << " #{ivar} => #{eval(ivar.to_s).inspect}\n" }
174
+ def print_notes(params = {})
175
+ notes = []
176
+
177
+ notes << encrypted_notes if params[:encrypted]
178
+ notes << evernote_notes if params[:evernote]
179
+ notes << plaintext_notes if params[:plaintext]
180
+ if notes.empty?
181
+ notes << matching_notes(:all_if_empty => true)
182
+ end
183
+
184
+ notes.flatten!
185
+ keys = params.keys.reject { |k| k == :all_if_empty }
186
+ log.info "#{keys.join(',')} notes:" unless keys.empty?
187
+ log.indent { notes.each { |note| log.info "#{PATH_COLOR} #{note}" } }
134
188
  end
135
189
 
136
- def evernote?(path)
137
- File.extname(path)[1..-1] == FuzzyNotes::EvernoteSync::NOTE_EXT
190
+ def matching_notes(params = {})
191
+ (@matching_notes.empty? && params[:all_if_empty]) ? @all_notes : @matching_notes
138
192
  end
139
193
 
140
- def print_notes(params = {})
141
- notes = (matching_notes.empty? && params[:all]) ? all_notes : matching_notes
142
- log.indent(2) { notes.each { |note| log.info "#{Colors::PATH} #{note}" } }
194
+ # initialize to params or use defaults
195
+ #
196
+ def parse_init_params(params)
197
+ VALID_PARAMS.each do |param|
198
+ klass = self.class
199
+ klass.send(:attr_reader, param)
200
+ default_const = param.to_s.upcase
201
+
202
+ value = \
203
+ if params.include?(param)
204
+ params[param]
205
+ else Defaults.const_get(default_const)
206
+ end
207
+ instance_variable_set("@#{param}", value)
208
+ end
143
209
  end
144
210
 
211
+ def inspect_instance_vars
212
+ instance_variables.inject("") { |s, ivar| s << " #{ivar} => #{eval(ivar.to_s).inspect}\n" }
213
+ end
214
+
215
+
145
216
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fuzzy_notes
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-08-01 00:00:00.000000000Z
12
+ date: 2011-09-26 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: buffered_logger
16
- requirement: &70150343147260 !ruby/object:Gem::Requirement
16
+ requirement: &70119415052580 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 0.1.2
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70150343147260
24
+ version_requirements: *70119415052580
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: gibberish
27
- requirement: &70150343146760 !ruby/object:Gem::Requirement
27
+ requirement: &70119415052080 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70150343146760
35
+ version_requirements: *70119415052080
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: evernote
38
- requirement: &70150343146280 !ruby/object:Gem::Requirement
38
+ requirement: &70119415051600 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70150343146280
46
+ version_requirements: *70119415051600
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: sanitize
49
- requirement: &70150343145800 !ruby/object:Gem::Requirement
49
+ requirement: &70119415051120 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,7 +54,7 @@ dependencies:
54
54
  version: '0'
55
55
  type: :runtime
56
56
  prerelease: false
57
- version_requirements: *70150343145800
57
+ version_requirements: *70119415051120
58
58
  description: A note manager with fuzzy path search, full text search, evernote sync,
59
59
  and encryption capabilities
60
60
  email: rut216@gmail.com
@@ -63,12 +63,12 @@ executables:
63
63
  extensions: []
64
64
  extra_rdoc_files: []
65
65
  files:
66
+ - lib/fuzzy_notes/authentication.rb
66
67
  - lib/fuzzy_notes/cipher.rb
67
68
  - lib/fuzzy_notes/evernote_sync.rb
68
69
  - lib/fuzzy_notes/fuzzy_finder.rb
69
70
  - lib/fuzzy_notes/logger.rb
70
71
  - lib/fuzzy_notes/notes.rb
71
- - lib/fuzzy_notes/password_protected.rb
72
72
  - lib/fuzzy_notes.rb
73
73
  - bin/fnote
74
74
  - README