quirkey-purse 0.1.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/History.txt ADDED
@@ -0,0 +1,10 @@
1
+ == 0.1.1 2008-10-29
2
+
3
+ * minor enhancements:
4
+ * Error handling for git integration
5
+ * Prompt before creating pockets
6
+
7
+ == 0.1.0 2008-10-28
8
+
9
+ * 1 major enhancement:
10
+ * Initial release
data/License.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Quirkey NYC, LLC
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest.txt ADDED
@@ -0,0 +1,15 @@
1
+ History.txt
2
+ License.txt
3
+ Manifest.txt
4
+ PostInstall.txt
5
+ README.txt
6
+ Rakefile
7
+ bin/purse
8
+ lib/purse.rb
9
+ lib/purse/cli.rb
10
+ lib/purse/error.rb
11
+ lib/purse/note.rb
12
+ lib/purse/pocket.rb
13
+ lib/purse/settings.rb
14
+ lib/purse/version.rb
15
+ lib/purse/help.txt
data/PostInstall.txt ADDED
@@ -0,0 +1,2 @@
1
+
2
+ For more information on purse, see http://quirkey.com/blog
data/README.txt ADDED
@@ -0,0 +1,82 @@
1
+ = purse
2
+
3
+ http://quirkey.rubyforge.org/purse
4
+
5
+ == DESCRIPTION:
6
+
7
+ A simple way of storing private/sensitive info and then sharing it with others using git
8
+
9
+ == SYNOPSIS:
10
+
11
+ Purse stores information in a two level folder system. The basic piece of stored information is a 'note'.
12
+ A 'pocket' is a folder containing one to many notes. A *pocket* can be synced between mutlple users/computers
13
+ using a remote git repository.
14
+
15
+ To configure for the first time:
16
+
17
+ purse
18
+
19
+ and follow the handy on screen instructions.
20
+
21
+ To create a new pocket/note.
22
+
23
+ purse pocketname notename
24
+
25
+ It will prompt you asking if you want to create the note.
26
+ Once you've edited in your favorite editor and saved, purse
27
+ will use Blowfish to encrypt the note with the password you provide.
28
+
29
+ Then you can display the note with:
30
+
31
+ purse pocketname notename
32
+
33
+ or edit with:
34
+
35
+ purse pocketname notename --edit
36
+
37
+ Each time it will ask you for the password you initially provided to encrypt it.
38
+
39
+ For more commands/info run:
40
+
41
+ purse --help
42
+
43
+ == REQUIREMENTS:
44
+
45
+ purse requires a couple of gems, namely
46
+ highline -- for command line tools
47
+ termios
48
+ crypt -- for encryption
49
+ git -- for uh, git
50
+
51
+ == INSTALL:
52
+
53
+ sudo gem install purse
54
+
55
+ then run
56
+
57
+ purse
58
+
59
+ == LICENSE:
60
+
61
+ (The MIT License)
62
+
63
+ Copyright (c) 2008 Quirkey NYC, LLC
64
+
65
+ Permission is hereby granted, free of charge, to any person obtaining
66
+ a copy of this software and associated documentation files (the
67
+ 'Software'), to deal in the Software without restriction, including
68
+ without limitation the rights to use, copy, modify, merge, publish,
69
+ distribute, sublicense, and/or sell copies of the Software, and to
70
+ permit persons to whom the Software is furnished to do so, subject to
71
+ the following conditions:
72
+
73
+ The above copyright notice and this permission notice shall be
74
+ included in all copies or substantial portions of the Software.
75
+
76
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
77
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
78
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
79
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
80
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
81
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
82
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require 'config/requirements'
2
+ require 'config/hoe' # setup Hoe + all gem configuration
3
+
4
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
data/bin/purse ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+
4
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'purse')
5
+
6
+ Purse::Cli.run(ARGV)
data/lib/purse.rb ADDED
@@ -0,0 +1,14 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'rubygems'
5
+ require 'git'
6
+ require 'crypt/blowfish'
7
+ require 'highline'
8
+ require 'yaml'
9
+
10
+ require 'purse/error'
11
+ require 'purse/settings'
12
+ require 'purse/pocket'
13
+ require 'purse/note'
14
+ require 'purse/cli'
data/lib/purse/cli.rb ADDED
@@ -0,0 +1,247 @@
1
+ module Purse
2
+ class Cli
3
+
4
+ ACTION_PREFIX = /^--/
5
+
6
+ # purse pursename #=> initialize or pull
7
+ # purse pursename notename #=> pocket.find(notename) if it doesnt exist first pull then ask if you want to create it
8
+ # purse pursename notename --edit #=> open in EDITOR save and push
9
+ # purse pursename push
10
+ # purse pursename pull
11
+ # purse settings/ purse # rerun settings
12
+ class << self
13
+ def run(args)
14
+ begin
15
+ banner
16
+ settings_exist?
17
+ action = args.detect {|a| a =~ ACTION_PREFIX }
18
+ args.reject! {|a| a == action}
19
+ pocket_name, note_name = args[0], args[1]
20
+ if !action.nil? && action != ''
21
+ action = action.gsub(ACTION_PREFIX,'')
22
+ send(action, *([pocket_name, note_name].compact))
23
+ else
24
+ case note_name
25
+ when 'push'
26
+ push(pocket_name)
27
+ when 'pull'
28
+ pull(pocket_name)
29
+ when ''
30
+ when nil
31
+ list(pocket_name)
32
+ else
33
+ find(pocket_name, note_name)
34
+ end
35
+ end
36
+ rescue MissingFile
37
+ say("Could not find note: #{pocket_name}/#{note_name}")
38
+ rescue Git::GitExecuteError => e
39
+ say(e)
40
+ rescue NoMethodError => e
41
+ say("Sorry, there is no action #{action}.\nTry purse --help for a list of commands. #{e}")
42
+ end
43
+ hr
44
+ end
45
+
46
+ protected
47
+ def settings
48
+ h1('Setup utility')
49
+ hr
50
+ say("loading settings from : #{Settings.path}")
51
+ Settings.load
52
+ settings = {}
53
+ settings[:root_path] = cli.ask("Root Path for pockets? ") {|q| q.default = Settings.get(:root_path) || File.join(File.expand_path('~'), '.purse', 'pockets') }
54
+ settings[:editor] = cli.ask("Editor for notes? ") {|q| q.default = Settings.get(:editor) || 'vim' }
55
+ hr
56
+ say("saving settings to : #{Settings.path}")
57
+ Settings.settings = settings
58
+ end
59
+
60
+ def init(pocket_name)
61
+ pocket = Pocket.new(pocket_path(pocket_name))
62
+ if !pocket.exists?
63
+ say("could not find pocket /#{pocket_name}")
64
+ if cli.agree("would you like to create it? (y/n)")
65
+ pocket.init
66
+ else
67
+ exit
68
+ end
69
+ end
70
+ pocket
71
+ end
72
+
73
+ def find(pocket_name, note_name)
74
+ say("loading note #{pocket_name}/#{note_name}")
75
+ hr
76
+ pocket = init(pocket_name)
77
+ note = pocket.find(note_name)
78
+ display(note)
79
+ rescue MissingFile
80
+ say("could not find note: #{pocket_name}/#{note_name}")
81
+ if cli.agree("would you like to create it? (y/n)")
82
+ edit(pocket_name, note_name)
83
+ end
84
+ end
85
+
86
+ def display(note)
87
+ decrypt(note)
88
+ h1("Note: #{note.name}",:green)
89
+ hr
90
+ say(note.data)
91
+ hr
92
+ end
93
+
94
+ def save(note, password = nil)
95
+ password ||= cli.ask("Password: ") { |q| q.echo = false }
96
+ note.save(password)
97
+ end
98
+
99
+ def decrypt(note)
100
+ return unless note.encrypted?
101
+ password = cli.ask("Password: ") { |q| q.echo = false }
102
+ note.decrypt(password)
103
+ password
104
+ end
105
+
106
+ def edit(pocket_name, note_name)
107
+ say("loading note #{pocket_name}/#{note_name}")
108
+ hr
109
+ pocket = init(pocket_name)
110
+ pocket.edit(note_name) do |note|
111
+ password = decrypt(note)
112
+ tmp_path = empty_temp_path
113
+ File.open(tmp_path, 'w') {|f| f << note.data }
114
+ execute("#{editor} #{tmp_path}")
115
+ sleep(2)
116
+ data = ""
117
+ File.open(tmp_path, 'r') {|f| data << f.read }
118
+ note.data = data
119
+ hr
120
+ say("saving note #{pocket_name}/#{note_name}")
121
+ save(note, password)
122
+ end
123
+ end
124
+
125
+ def delete(pocket_name, note_name = nil)
126
+ pocket = init(pocket_name)
127
+ if note_name
128
+ note = pocket.find(note_name)
129
+ note.delete if cli.agree("are you sure you want to delete #{pocket_name}/#{note_name}? (y/n)")
130
+ else
131
+ pocket.delete if cli.agree("are you sure you want to delete /#{pocket_name}? (y/n)")
132
+ end
133
+ end
134
+
135
+ def push(pocket_name)
136
+ pocket = init(pocket_name)
137
+ say("Committing local changes for #{pocket_name}")
138
+ pocket.commit
139
+ hr
140
+ set_remote(pocket_name)
141
+ say("Pulling changes from remote (#{pocket.remote})")
142
+ pocket.pull
143
+ hr
144
+ say("Pushing changes to remote (#{pocket.remote})")
145
+ pocket.push
146
+ end
147
+
148
+ def pull(pocket_name)
149
+ pocket = init(pocket_name)
150
+ say("Committing local changes for #{pocket_name}")
151
+ pocket.commit
152
+ hr
153
+ set_remote(pocket_name)
154
+ say("Pulling changes from remote (#{pocket.remote})")
155
+ pocket.pull
156
+ end
157
+
158
+ def set_remote(pocket_name)
159
+ pocket = init(pocket_name)
160
+ unless pocket.remote
161
+ remote_url = cli.ask("please enter your remote git url (git@github ...)")
162
+ pocket.set_remote(remote_url)
163
+ say("set remote to #{remote_url}")
164
+ end
165
+ end
166
+
167
+ def help(*args)
168
+ h1("Help", :yellow)
169
+ hr
170
+ help_text = File.open(File.join(File.dirname(__FILE__),'help.txt')) {|f| f.read }
171
+ say(help_text)
172
+ end
173
+
174
+ def list(pocket_name = nil, note_name = nil)
175
+ unless pocket_name
176
+ h1("Listing your Pockets", :green)
177
+ hr
178
+ pockets = Dir[File.join(Settings.get(:root_path), '*')].find_all {|file| File.directory?(file) }
179
+ if pockets.empty?
180
+ say("no pockets yet. create one with 'purse pocketname'")
181
+ else
182
+ pockets.each {|p| say("/#{File.basename(p)}/") }
183
+ end
184
+ else
185
+ pocket = init(pocket_name)
186
+ say("Notes in <%= color '(#{pocket_name})', :red %>")
187
+ hr
188
+ pocket.notes.each do |note|
189
+ say("- #{note.name}")
190
+ end
191
+ end
192
+ end
193
+
194
+ def settings_exist?
195
+ return if Settings.get(:root_path) && Settings.get(:editor)
196
+ h1("you must configure purse before you use it", :red)
197
+ settings
198
+ say("rerun this utility any time with purse --settings")
199
+ exit
200
+ end
201
+
202
+ def pocket_path(pocket_name)
203
+ File.join(Settings.get(:root_path), pocket_name)
204
+ end
205
+
206
+ def editor
207
+ Settings.get(:editor)
208
+ end
209
+
210
+ def banner
211
+ hr
212
+ h1('/ Purse /', :white)
213
+ hr
214
+ end
215
+
216
+ def h1(text, color = :red)
217
+ say("<%= color('#{text}', :#{color}) %>")
218
+ end
219
+
220
+ def hr(color = :magenta)
221
+ say("<%= color('-' * 40, :#{color}) %>")
222
+ end
223
+
224
+ def say(message)
225
+ cli.say(message)
226
+ end
227
+
228
+ def cli
229
+ @cli ||= HighLine.new
230
+ end
231
+
232
+ def execute(command)
233
+ `#{command}`
234
+ end
235
+
236
+ def random_temp_name
237
+ "#{rand Time.now.to_i}-#{rand(1000)}--"
238
+ end
239
+
240
+ def empty_temp_path
241
+ temp_dir = File.join(Settings.get(:root_path), 'tmp')
242
+ FileUtils.mkdir_p(temp_dir)
243
+ File.join(temp_dir, random_temp_name)
244
+ end
245
+ end
246
+ end
247
+ end
@@ -0,0 +1,10 @@
1
+ module Purse
2
+ class Error < RuntimeError; end
3
+
4
+ class MissingParameter < Error; end;
5
+ class MissingFile < Error; end
6
+
7
+ def self.check_for_parameter(name, param)
8
+ raise MissingParameter, "You must set a #{name}" if param.nil? || param.strip == ''
9
+ end
10
+ end
@@ -0,0 +1,54 @@
1
+ -- General --
2
+
3
+ Purse stores information in a two level folder system.
4
+ The basic piece of stored information is a 'note'.
5
+ A 'pocket' is a folder containing one to many notes.
6
+ A *pocket* can be synced between mutlple users/computers using a remote git repository.
7
+
8
+ -- Commands --
9
+
10
+ purse
11
+ purse --list
12
+
13
+ List Pockets
14
+
15
+ purse --help
16
+
17
+ Display this message
18
+
19
+ purse --settings
20
+
21
+ Run the settings utility
22
+
23
+ purse pocketname
24
+
25
+ List the notes in pocket 'pocketname'
26
+
27
+ purse pocketname --delete
28
+
29
+ Destroy the pocket: pocketname
30
+
31
+ purse pocketname --pull
32
+
33
+ Pull the pocket 'pocketname' from a remote git repository.
34
+ Will prompt for URL the first time its run.
35
+
36
+ purse pocketname --push
37
+
38
+ Push the pocket 'pocketname' to a remote git repository.
39
+ Will prompt for URL the first time its run.
40
+
41
+ purse pocketname notename
42
+
43
+ Display or create the note 'notename' in the pocket 'pocketname'.
44
+ Will prompt for password.
45
+
46
+ purse pocketname notename --edit
47
+
48
+ Edit or create the note 'notename' in the pocket 'pocketname'.
49
+ Will prompt for password.
50
+
51
+ purse pocketname notename --delete
52
+
53
+ Delete the note 'notename' in the pocket 'pocketname'.
54
+