fuzzy_notes 0.0.6 → 0.0.7

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/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