fuzzy_notes 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
data/bin/fnote CHANGED
@@ -16,12 +16,14 @@ optparse = OptionParser.new do |opts|
16
16
 
17
17
  opts.on("-c", "--config [CONFIG]", "Specify config file") { |opt| options[:config] = opt}
18
18
  opts.on("-p", "--print", "Dump matching notes to stdout") { |opt| options[:print] = true }
19
- opts.on("-l", "--list", "List statistics for matching notes") { |opt| options[:list] = true }
20
- opts.on("-i", "--info", "Alias for 'list'") { |opt| options[:info] = true }
19
+ opts.on("-l", "--list", "List matching notes") { |opt| options[:list] = true }
20
+ opts.on("-i", "--info", "Show statistics for matching notes") { |opt| options[:info] = true }
21
21
  opts.on("-s", "--search", "Perform a full text search when matching notes") { |opt| options[:search] = true }
22
22
  opts.on("-v", "--verbose", "Enable debug output") { |opt| options[:verbose] = true }
23
23
  opts.on("-e", "--encrypt", "Encrypt matching notes") { |opt| options[:encrypt] = true }
24
24
  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 }
25
27
  opts.on("-h", "--help", "Show this message") {
26
28
  puts opts
27
29
  exit
@@ -47,23 +49,25 @@ end
47
49
  # fetch config
48
50
  #
49
51
  config_path = \
50
- options[:config] && File.exists?(options[:config]) && options[:config] ||
52
+ File.exists?(options[:config].to_s) && options[:config] ||
51
53
  File.exists?(CONFIG_PATH) && CONFIG_PATH
52
54
 
53
55
  config = config_path ? YAML::load_file(config_path) : {}
54
- puts "Warning: config file not found, using defaults" if config.empty?
56
+ log.info("config file not found, using defaults") if config.empty?
55
57
 
56
- # find matching notes
57
- #
58
58
  notes = FuzzyNotes::Notes.new(:editor => config[:editor],
59
59
  :note_paths => config[:note_paths],
60
+ :evernote_params => config[:evernote],
60
61
  :full_text_search => options[:search] || config[:full_text_search],
61
- :log_level => (options[:verbose] || config[:verbose]) ? 0 : 1,
62
+ :log_level => (options[:verbose] || config[:verbose]) ? :debug : :info,
63
+ :color => (options[:color] == false || config[:color] == false) ? false : true,
62
64
  :keywords => ARGV)
63
65
 
64
- # perform action on matching notes
65
- #
66
- if options[:list] || options[:info]
66
+ notes.evernote_sync if options[:evernote_update]
67
+
68
+ if options[:list]
69
+ notes.list
70
+ elsif options[:info]
67
71
  notes.info
68
72
  elsif options[:print]
69
73
  notes.cat
@@ -2,6 +2,7 @@ require 'gibberish'
2
2
 
3
3
  class FuzzyNotes::Cipher
4
4
  include FuzzyNotes::Logger
5
+ include FuzzyNotes::PasswordProtected
5
6
 
6
7
  PLAINTEXT_EXT = 'txt'
7
8
  CIPHERTEXT_EXT = 'enc'
@@ -43,10 +44,10 @@ private
43
44
  dirname = File.dirname(path)
44
45
  filename = File.basename(path, '.*')
45
46
 
46
- log.debug "writing #{decrypt? ? 'un' : ''}encrypted content to: #{dirname}/#{filename}.#{extension}"
47
+ log.info "#{Colors::CREATE} writing #{decrypt? ? 'un' : ''}encrypted file: #{Colors::PATH} #{dirname}/#{filename}.#{extension}"
47
48
  File.open("#{dirname}/#{filename}.#{extension}", 'w') { |f| f << contents }
48
49
 
49
- log.debug "deleting #{decrypt? ? '' : 'un'}encrypted file: #{path}"
50
+ log.info "#{Colors::DELETE} deleting #{decrypt? ? '' : 'un'}encrypted file: #{Colors::PATH} #{path}"
50
51
  File.delete(path)
51
52
  end
52
53
 
@@ -58,12 +59,4 @@ private
58
59
  @action == :dec
59
60
  end
60
61
 
61
- def get_password
62
- printf 'Enter password (will not be shown):'
63
- `stty -echo`; password = STDIN.gets.strip;`stty echo`; puts "\n\n"
64
- log.debug "entered password: #{password.inspect}"
65
- password
66
- end
67
-
68
-
69
62
  end
@@ -1,23 +1,127 @@
1
1
  require 'evernote'
2
- user_store_url = 'https://sandbox.evernote.com/edam/user'
3
-
4
- config = {
5
- :username => 'rut216',
6
- :password => 'password',
7
- :consumer_key => 'rut216',
8
- :consumer_secret => '160988912605c36c' }
9
-
10
- user_store = Evernote::UserStore.new(user_store_url, config)
11
- auth_result = user_store.authenticate
12
- user = auth_result.user
13
- @token = auth_result.authenticationToken
14
- puts "Authentication was successful for #{user.username}"
15
- puts "Authentication token = #{@token}"
16
-
17
- note_store_url = "http://sandbox.evernote.com/edam/note/#{user.shardId}"
18
- @note_store = Evernote::NoteStore.new(note_store_url)
19
-
20
- #notebooks = note_store.listNotebooks(auth_token)
21
- #puts "Found #{notebooks.size} notebooks:"
22
- #default_notebook = notebooks[0]
23
- #notebooks.each { |notebook| puts " * #{notebook.name}"}
2
+ require 'fileutils'
3
+ require 'sanitize'
4
+ require 'digest/md5'
5
+
6
+ class FuzzyNotes::EvernoteSync
7
+ include FuzzyNotes::Logger
8
+ include FuzzyNotes::PasswordProtected
9
+
10
+ USER_STORE_URL = 'https://evernote.com/edam/user'
11
+ NOTE_STORE_URL = 'http://evernote.com/edam/note'
12
+ NOTE_EXT = 'html'
13
+ MAX_NOTES = 1000
14
+
15
+ # opts should be a hash with the following keys:
16
+ # :username, :password, :consumer_key, :consumer_secret
17
+ #
18
+ def initialize(params = {})
19
+ params.merge!(:password => get_password)
20
+ user_store = Evernote::UserStore.new(USER_STORE_URL, params)
21
+ begin
22
+ auth_result = user_store.authenticate
23
+ rescue Evernote::UserStore::AuthenticationFailure
24
+ log.error "Evernote authentication failed for #{Colors::USER} #{params[:username]}"
25
+ return
26
+ end
27
+
28
+ @path = params[:note_path]
29
+ user = auth_result.user
30
+ @token = auth_result.authenticationToken
31
+ note_store_url = "#{NOTE_STORE_URL}/#{user.shardId}"
32
+ @note_store = Evernote::NoteStore.new(note_store_url)
33
+ log.info "Evernote authentication was successful for #{Colors::USER} #{params[:username]}"
34
+ end
35
+
36
+ def sync
37
+ return unless authenticated?
38
+ unless File.directory?(@path)
39
+ log.error("#{@path}' is not a directory!")
40
+ return
41
+ end
42
+
43
+ log.info "synchronizing with Evernote account..."
44
+ log.indent(2) do
45
+ # create notebook directories
46
+ fetch_notebooks.each do |notebook|
47
+ notebook_path = get_notebook_path(notebook[:name])
48
+ FileUtils.mkdir(notebook_path) unless File.exists?(notebook_path)
49
+
50
+ # write notes to files
51
+ notebook[:notes].each do |note|
52
+ note_path = get_note_path(notebook_path, note[:title])
53
+ File.open(note_path, 'w') { |f| f << note[:content] }
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ def self.sanitize_evernote(path)
60
+ html = File.read(path)
61
+ Sanitize.clean(html)
62
+ end
63
+
64
+ private
65
+
66
+ def fetch_notebooks
67
+ notebooks = @note_store.listNotebooks(@token) || []
68
+ log.info "checking for updates..."
69
+ log.indent(2) do
70
+ notebooks.map { |notebook| { :name => notebook.name,
71
+ :guid => notebook.guid,
72
+ :notes => fetch_notes(:name => notebook.name, :guid => notebook.guid) } }
73
+ end
74
+ end
75
+
76
+ def fetch_notes(notebook_params)
77
+ filter = Evernote::EDAM::NoteStore::NoteFilter.new
78
+ filter.notebookGuid = notebook_params[:guid]
79
+ notes = @note_store.findNotes(@token, filter, nil, MAX_NOTES).notes || []
80
+ log.indent(2) do
81
+ notes.inject([]) do |notes, note|
82
+ if needs_update?(notebook_params[:name], note)
83
+ notes << { :title => note.title, :guid => note.guid, :content => fetch_note_content(note.guid) }
84
+ else
85
+ notes
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ def fetch_note_content(note_guid)
92
+ note = @note_store.getNote(@token, note_guid, true, nil, nil, nil)
93
+ log.info "updating note #{Colors::NOTE} #{note.title} #{Colors::DEFAULT} with content length #{Colors::NUMBER} #{note.contentLength}"
94
+ note.content
95
+ end
96
+
97
+ def needs_update?(notebook_name, note)
98
+ local_note_path = get_note_path(get_notebook_path(notebook_name), note.title)
99
+ return true unless File.exists?(local_note_path)
100
+
101
+ evernote_hash = note.contentHash
102
+ local_note_hash = Digest::MD5.digest(File.read(local_note_path))
103
+ log.debug "evernote_hash: #{evernote_hash}"
104
+ log.debug "local_hash: #{local_note_hash}"
105
+ return evernote_hash != local_note_hash
106
+ end
107
+
108
+ def get_notebook_path(notebook_name)
109
+ "#{@path}/#{sanitize_filename(notebook_name)}"
110
+ end
111
+
112
+ def get_note_path(notebook_path, note_title)
113
+ "#{notebook_path}/#{sanitize_filename(note_title)}.#{NOTE_EXT}"
114
+ end
115
+
116
+ def authenticated?
117
+ !@token.nil?
118
+ end
119
+
120
+ def sanitize_filename(filename)
121
+ name = filename.strip
122
+ name.gsub!(/[^0-9A-Za-z-]/, '_')
123
+ name.gsub!(/_+/, '_')
124
+ name
125
+ end
126
+
127
+ end
@@ -5,8 +5,9 @@ class FuzzyNotes::Log
5
5
  LOG_LEVEL = 1
6
6
 
7
7
 
8
- def self.init_log(log_level)
8
+ def self.init_log(log_level, color)
9
9
  @log = BufferedLogger.new(STDOUT, log_level || LOG_LEVEL, default_format)
10
+ log.disable_color unless color
10
11
  end
11
12
 
12
13
  def self.log
@@ -16,16 +17,25 @@ class FuzzyNotes::Log
16
17
  private
17
18
 
18
19
  def self.default_format
19
- { :debug => "$negative DEBUG: $white %s",
20
- :info => "$green INFO: $white %s",
21
- :warn => "$yellow WARNING: $white %s",
22
- :error => "$red ERROR: $white %s" }
20
+ { :debug => "$negative DEBUG: $reset %s",
21
+ :warn => "$yellow WARNING: $reset %s",
22
+ :error => "$red ERROR: $reset %s" }
23
23
  end
24
24
 
25
25
  end
26
26
 
27
27
 
28
28
  module FuzzyNotes::Logger
29
+ module Colors
30
+ PATH = "$blue"
31
+ USER = "$green"
32
+ NOTE = "$cyan"
33
+ NUMBER = "$red"
34
+ CREATE = "$green"
35
+ DELETE = "$red"
36
+ DEFAULT = "$reset"
37
+ end
38
+
29
39
  def log
30
40
  FuzzyNotes::Log.log
31
41
  end
@@ -2,22 +2,26 @@ class FuzzyNotes::Notes
2
2
  include FuzzyNotes::Logger
3
3
 
4
4
  module Defaults
5
- LOG_LEVEL = 1
6
- EDITOR='vim'
7
- NOTE_PATHS=[ "#{ENV['HOME']}/notes" ]
8
- VALID_EXTENSIONS=%w( txt enc )
9
- KEYWORDS = []
5
+ LOG_LEVEL = 1
6
+ EDITOR = 'vim'
7
+ COLOR = true
8
+ KEYWORDS = []
9
+ NOTE_PATHS = [ "#{ENV['HOME']}/notes" ]
10
+ VALID_EXTENSIONS = [ 'txt',
11
+ FuzzyNotes::Cipher::CIPHERTEXT_EXT,
12
+ FuzzyNotes::Cipher::PLAINTEXT_EXT,
13
+ FuzzyNotes::EvernoteSync::NOTE_EXT ]
10
14
 
11
15
  def self.const_missing(*args); end
12
16
  end
13
17
 
14
- OPTS = [:log_level, :editor, :note_paths, :valid_extensions, :keywords].freeze
18
+ OPTS = [:log_level, :color, :editor, :note_paths, :valid_extensions, :keywords, :evernote_params].freeze
15
19
 
16
20
  attr_reader :matching_notes, :all_notes
17
21
 
18
22
  def initialize(params = {})
19
23
  parse_init_params(params)
20
- FuzzyNotes::Log.init_log(@log_level)
24
+ FuzzyNotes::Log.init_log(@log_level, @color)
21
25
  log.debug "init attributes: \n#{inspect_instance_vars}"
22
26
 
23
27
  unless note_paths_valid?
@@ -39,7 +43,7 @@ end
39
43
  klass = self.class
40
44
  klass.send(:attr_reader, param)
41
45
  default_const = param.to_s.upcase
42
- instance_variable_set("@#{param}", params[param] || Defaults.const_get(default_const) )
46
+ instance_variable_set("@#{param}", params.include?(param) ? params[param] : Defaults.const_get(default_const) )
43
47
  end
44
48
  end
45
49
  private :parse_init_params
@@ -50,14 +54,18 @@ end
50
54
  matching_notes.each do |note_path|
51
55
  contents = \
52
56
  if encrypted?(note_path)
53
- puts "decrypting #{note_path}"
57
+ log.info "decrypting note #{Colors::PATH} #{note_path}"
54
58
  FuzzyNotes::Cipher.new.decrypt(note_path)
59
+ elsif evernote?(note_path)
60
+ FuzzyNotes::EvernoteSync.sanitize_evernote(note_path)
55
61
  else
56
62
  File.read(note_path)
57
63
  end
58
64
 
59
- puts "=== #{note_path} ===\n\n"
60
- puts "#{contents}\n"
65
+ unless contents.blank?
66
+ log.info "=== #{note_path} ===\n\n"
67
+ puts "#{contents}\n"
68
+ end
61
69
  end
62
70
  end
63
71
 
@@ -70,13 +78,19 @@ end
70
78
  # encrypt matching notes
71
79
  #
72
80
  def encrypt
73
- FuzzyNotes::Cipher.new.encrypt(matching_notes, :replace => true)
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) }
74
85
  end
75
86
 
76
87
  # decrypt matching notes
77
88
  #
78
89
  def decrypt
79
- FuzzyNotes::Cipher.new.decrypt(matching_notes, :replace => true)
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) }
80
94
  end
81
95
 
82
96
  # view WC info for all/matching notes
@@ -86,11 +100,26 @@ end
86
100
  puts `wc $(find #{paths} -type f)`
87
101
  end
88
102
 
103
+ def list
104
+ print_notes(:all => true)
105
+ end
106
+
107
+ 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]}"
114
+ FuzzyNotes::EvernoteSync.new(@evernote_params).sync
115
+ log.print_blank_line
116
+ end
117
+
89
118
  private
90
119
 
91
120
  def note_paths_valid?
92
121
  @note_paths.any? do |p|
93
- File.exists?(p) || log.info("Warning: note path '#{p}' not found")
122
+ File.directory?(p) || log.warn("note path #{Colors::PATH} #{p} does not exist")
94
123
  end
95
124
  end
96
125
 
@@ -108,5 +137,13 @@ private
108
137
  File.extname(path)[1..-1] == FuzzyNotes::Cipher::CIPHERTEXT_EXT
109
138
  end
110
139
 
140
+ def evernote?(path)
141
+ File.extname(path)[1..-1] == FuzzyNotes::EvernoteSync::NOTE_EXT
142
+ end
143
+
144
+ def print_notes(params = {})
145
+ notes = (matching_notes.empty? && params[:all]) ? all_notes : matching_notes
146
+ log.indent(2) { notes.each { |note| log.info "#{Colors::PATH} #{note}" } }
147
+ end
111
148
 
112
149
  end
@@ -0,0 +1,10 @@
1
+ module FuzzyNotes::PasswordProtected
2
+
3
+ def get_password
4
+ printf 'Enter password (will not be shown):'
5
+ `stty -echo`; password = STDIN.gets.strip;`stty echo`; puts
6
+ log.debug "entered password: #{password.inspect}"
7
+ password
8
+ end
9
+
10
+ end
data/lib/fuzzy_notes.rb CHANGED
@@ -1,8 +1,11 @@
1
+ require 'pp'
1
2
  require 'rubygems'
2
3
 
3
4
  module FuzzyNotes; end
4
5
 
5
6
  require 'fuzzy_notes/logger'
7
+ require 'fuzzy_notes/password_protected'
6
8
  require 'fuzzy_notes/cipher'
7
- require 'fuzzy_notes/notes'
9
+ require 'fuzzy_notes/evernote_sync'
8
10
  require 'fuzzy_notes/fuzzy_finder'
11
+ require 'fuzzy_notes/notes'
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fuzzy_notes
3
3
  version: !ruby/object:Gem::Version
4
- hash: 19
4
+ hash: 17
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 6
10
- version: 0.0.6
9
+ - 7
10
+ version: 0.0.7
11
11
  platform: ruby
12
12
  authors:
13
13
  - Alex Skryl
@@ -15,25 +15,27 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-07-25 00:00:00 -05:00
18
+ date: 2011-07-26 00:00:00 -05:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
- name: gibberish
22
+ name: buffered_logger
23
23
  prerelease: false
24
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
25
  none: false
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- hash: 3
29
+ hash: 31
30
30
  segments:
31
31
  - 0
32
- version: "0"
32
+ - 1
33
+ - 2
34
+ version: 0.1.2
33
35
  type: :runtime
34
36
  version_requirements: *id001
35
37
  - !ruby/object:Gem::Dependency
36
- name: buffered_logger
38
+ name: gibberish
37
39
  prerelease: false
38
40
  requirement: &id002 !ruby/object:Gem::Requirement
39
41
  none: false
@@ -46,7 +48,35 @@ dependencies:
46
48
  version: "0"
47
49
  type: :runtime
48
50
  version_requirements: *id002
49
- description: A note manager with fuzzy search, full text search, evernote support, and encryption capabilities
51
+ - !ruby/object:Gem::Dependency
52
+ name: evernote
53
+ prerelease: false
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ type: :runtime
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ name: sanitize
67
+ prerelease: false
68
+ requirement: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ hash: 3
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ type: :runtime
78
+ version_requirements: *id004
79
+ description: A note manager with fuzzy path search, full text search, evernote sync, and encryption capabilities
50
80
  email: rut216@gmail.com
51
81
  executables:
52
82
  - fnote
@@ -60,6 +90,7 @@ files:
60
90
  - lib/fuzzy_notes/fuzzy_finder.rb
61
91
  - lib/fuzzy_notes/logger.rb
62
92
  - lib/fuzzy_notes/notes.rb
93
+ - lib/fuzzy_notes/password_protected.rb
63
94
  - lib/fuzzy_notes.rb
64
95
  - bin/fnote
65
96
  - README
@@ -97,6 +128,6 @@ rubyforge_project:
97
128
  rubygems_version: 1.6.2
98
129
  signing_key:
99
130
  specification_version: 3
100
- summary: A plain text note manager
131
+ summary: A cli note manager
101
132
  test_files: []
102
133