kaboom 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,117 @@
1
+ ## This is the rakegem gemspec template. Make sure you read and understand
2
+ ## all of the comments. Some sections require modification, and others can
3
+ ## be deleted if you don't need them. Once you understand the contents of
4
+ ## this file, feel free to delete any comments that begin with two hash marks.
5
+ ## You can find comprehensive Gem::Specification documentation, at
6
+ ## http://docs.rubygems.org/read/chapter/20
7
+ Gem::Specification.new do |s|
8
+ s.specification_version = 2 if s.respond_to? :specification_version=
9
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
10
+ s.rubygems_version = '1.3.5'
11
+
12
+ ## Leave these as is they will be modified for you by the rake gemspec task.
13
+ ## If your rubyforge_project name is different, then edit it and comment out
14
+ ## the sub! line in the Rakefile
15
+ s.name = 'kaboom'
16
+ s.version = '0.3.1'
17
+ s.date = '2012-03-17'
18
+ s.rubyforge_project = 'boom'
19
+
20
+ ## Make sure your summary is short. The description may be as long
21
+ ## as you like.
22
+ s.summary = "boom lets you access text snippets over your command line."
23
+ s.description = "God it's about every day where I think to myself, gadzooks,
24
+ I keep typing *REPETITIVE_BORING_TASK* over and over. Wouldn't it be great if
25
+ I had something like boom to store all these commonly-used text snippets for
26
+ me? Then I realized that was a worthless idea since boom hadn't been created
27
+ yet and I had no idea what that statement meant. At some point I found the
28
+ code for boom in a dark alleyway and released it under my own name because I
29
+ wanted to look smart."
30
+
31
+ ## List the primary authors. If there are a bunch of authors, it's probably
32
+ ## better to set the email to an email list or something. If you don't have
33
+ ## a custom homepage, consider using your GitHub URL or the like.
34
+ s.authors = ["Zach Holman", "Mark Burns"]
35
+ s.email = 'markthedeveloper@gmail.com'
36
+ s.homepage = 'https://github.com/markburns/kaboom'
37
+
38
+ ## This gets added to the $LOAD_PATH so that 'lib/NAME.rb' can be required as
39
+ ## require 'NAME.rb' or'/lib/NAME/file.rb' can be as require 'NAME/file.rb'
40
+ s.require_paths = %w[lib]
41
+
42
+ ## This sections is only necessary if you have C extensions.
43
+ #s.require_paths << 'ext'
44
+ #s.extensions = %w[ext/extconf.rb]
45
+
46
+ ## If your gem includes any executables, list them here.
47
+ s.executables = ["boom", "kaboom"]
48
+ s.default_executable = 'boom'
49
+
50
+ ## Specify any RDoc options here. You'll want to add your README and
51
+ ## LICENSE files to the extra_rdoc_files list.
52
+ s.rdoc_options = ["--charset=UTF-8"]
53
+ s.extra_rdoc_files = %w[README.markdown LICENSE.markdown]
54
+
55
+ ## List your runtime dependencies here. Runtime dependencies are those
56
+ ## that are needed for an end user to actually USE your code.
57
+ s.add_dependency('multi_json', "~> 1.0.3")
58
+ s.add_dependency('json_pure', "~> 1.5.3")
59
+
60
+ ## List your development dependencies here. Development dependencies are
61
+ ## those that are only needed during development
62
+ s.add_development_dependency('mocha', "~> 0.9.9")
63
+ s.add_development_dependency('rake', "~> 0.9.2")
64
+
65
+ ## Leave this section as-is. It will be automatically generated from the
66
+ ## contents of your Git repository via the gemspec task. DO NOT REMOVE
67
+ ## THE MANIFEST COMMENTS, they are used as delimiters by the task.
68
+ # = MANIFEST =
69
+ s.files = %w[
70
+ CHANGELOG.markdown
71
+ Gemfile
72
+ Gemfile.lock
73
+ LICENSE.markdown
74
+ README.markdown
75
+ Rakefile
76
+ bin/boom
77
+ bin/kaboom
78
+ completion/README.md
79
+ completion/boom.bash
80
+ completion/boom.zsh
81
+ kaboom.gemspec
82
+ lib/kaboom.rb
83
+ lib/kaboom/color.rb
84
+ lib/kaboom/command.rb
85
+ lib/kaboom/config.rb
86
+ lib/kaboom/core_ext/symbol.rb
87
+ lib/kaboom/item.rb
88
+ lib/kaboom/list.rb
89
+ lib/kaboom/output.rb
90
+ lib/kaboom/platform.rb
91
+ lib/kaboom/remote.rb
92
+ lib/kaboom/storage.rb
93
+ lib/kaboom/storage/base.rb
94
+ lib/kaboom/storage/gist.rb
95
+ lib/kaboom/storage/json.rb
96
+ lib/kaboom/storage/keychain.rb
97
+ lib/kaboom/storage/mongodb.rb
98
+ lib/kaboom/storage/redis.rb
99
+ test/examples/config_json.json
100
+ test/examples/test_json.json
101
+ test/examples/urls.json
102
+ test/helper.rb
103
+ test/output_interceptor.rb
104
+ test/test_color.rb
105
+ test/test_command.rb
106
+ test/test_config.rb
107
+ test/test_item.rb
108
+ test/test_list.rb
109
+ test/test_platform.rb
110
+ test/test_remote.rb
111
+ ]
112
+ # = MANIFEST =
113
+
114
+ ## Test files will be grabbed from the file list. Make sure the path glob
115
+ ## matches what you actually use.
116
+ s.test_files = s.files.select { |path| path =~ /^test\/test_.*\.rb/ }
117
+ end
@@ -0,0 +1,59 @@
1
+ # coding: utf-8
2
+
3
+ begin
4
+ require 'rubygems'
5
+ rescue LoadError
6
+ end
7
+
8
+ require 'fileutils'
9
+ require 'multi_json'
10
+
11
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
12
+
13
+ require 'kaboom/output'
14
+ require 'kaboom/color'
15
+ require 'kaboom/platform'
16
+ require 'kaboom/command'
17
+ require 'kaboom/config'
18
+ require 'kaboom/item'
19
+ require 'kaboom/list'
20
+
21
+ require 'kaboom/storage'
22
+ require 'kaboom/storage/base'
23
+ require 'kaboom/storage/json'
24
+ require 'kaboom/storage/redis'
25
+ require 'kaboom/storage/mongodb'
26
+ require 'kaboom/storage/keychain'
27
+ require 'kaboom/storage/gist'
28
+
29
+ require 'kaboom/core_ext/symbol'
30
+ require 'kaboom/remote'
31
+
32
+ module Boom
33
+ VERSION = '0.3.1'
34
+
35
+ extend self
36
+
37
+ def storage
38
+ @storage ||= Boom::Storage.backend
39
+ end
40
+
41
+ # Public: tell Boom to use the storage specified in
42
+ # ~/.boom.remote.conf
43
+ # Returns a Config instance.
44
+ def use_remote remote=true
45
+ @config = Boom::Config.new remote
46
+ end
47
+
48
+ def config
49
+ @config ||= Boom::Config.new
50
+ end
51
+
52
+ def remote?
53
+ config.remote
54
+ end
55
+
56
+ def local?
57
+ !remote?
58
+ end
59
+ end
@@ -0,0 +1,52 @@
1
+ module Boom
2
+ # Color collects some methods for colorizing terminal output.
3
+ module Color
4
+ extend self
5
+
6
+ CODES = {
7
+ :reset => "\e[0m",
8
+
9
+ :cyan => "\e[36m",
10
+ :magenta => "\e[35m",
11
+ :red => "\e[31m",
12
+ :yellow => "\e[33m"
13
+ }
14
+
15
+ # Tries to enable Windows support if on that platform.
16
+ #
17
+ # Returns nothing.
18
+ def self.included(other)
19
+ if RUBY_PLATFORM =~ /win32/ || RUBY_PLATFORM =~ /mingw32/
20
+ require 'Win32/Console/ANSI'
21
+ end
22
+ rescue LoadError
23
+ # Oh well, we tried.
24
+ end
25
+
26
+ # Wraps the given string in ANSI color codes
27
+ #
28
+ # string - The String to wrap.
29
+ # color_code - The String representing he ANSI color code
30
+ #
31
+ # Examples
32
+ #
33
+ # colorize("Boom!", :magenta)
34
+ # # => "\e[35mBoom!\e[0m"
35
+ #
36
+ # Returns the wrapped String unless the the platform is windows and
37
+ # does not have Win32::Console, in which case, returns the String.
38
+ def colorize(string, color_code)
39
+ if !defined?(Win32::Console) && !!(RUBY_PLATFORM =~ /win32/ || RUBY_PLATFORM =~ /mingw32/)
40
+ # looks like this person doesn't have Win32::Console and is on windows
41
+ # just return the uncolorized string
42
+ return string
43
+ end
44
+ "#{CODES[color_code] || color_code}#{string}#{CODES[:reset]}"
45
+ end
46
+
47
+ # Set up shortcut methods to all the codes define in CODES.
48
+ self.class_eval(CODES.keys.reject {|color| color == :reset }.map do |color|
49
+ "def #{color}(string); colorize(string, :#{color}); end"
50
+ end.join("\n"))
51
+ end
52
+ end
@@ -0,0 +1,389 @@
1
+ # coding: utf-8
2
+
3
+ # Command is the main point of entry for boom commands; shell arguments are
4
+ # passd through to Command, which then filters and parses through indivdual
5
+ # commands and reroutes them to constituent object classes.
6
+ #
7
+ # Command also keeps track of one connection to Storage, which is how new data
8
+ # changes are persisted to disk. It takes care of any data changes by calling
9
+ # Boom::Command#save.
10
+ #
11
+ module Boom
12
+ class Command
13
+ class << self
14
+ include Boom::Color
15
+ include Boom::Output
16
+
17
+ # Public: accesses the in-memory JSON representation.
18
+ #
19
+ # Returns a Storage instance.
20
+ def storage
21
+ Boom.storage
22
+ end
23
+
24
+ # Public: executes a command.
25
+ #
26
+ # args - The actual commands to operate on. Can be as few as zero
27
+ # arguments or as many as three.
28
+ def execute(*args)
29
+ command = check_if_remote args
30
+
31
+ major = args.shift
32
+ minor = args.empty? ? nil : args.join(' ')
33
+
34
+ return overview unless command
35
+ delegate(command, major, minor)
36
+ end
37
+
38
+
39
+ def check_if_remote args
40
+ command = args.shift
41
+
42
+ if command == "remote"
43
+ Boom.use_remote
44
+ command = args.shift
45
+ end
46
+ command
47
+ end
48
+
49
+ private :check_if_remote
50
+
51
+ # Public: gets $stdin.
52
+ #
53
+ # Returns the $stdin object. This method exists to help with easy mocking
54
+ # or overriding.
55
+ def stdin
56
+ $stdin
57
+ end
58
+
59
+ # Public: prints a tidy overview of your Lists in descending order of
60
+ # number of Items.
61
+ #
62
+ # Returns nothing.
63
+ def overview
64
+ storage.lists.each do |list|
65
+ output " #{list.name} (#{list.items.size})"
66
+ end
67
+ s = "You don't have anything yet! To start out, create a new list:"
68
+ s << "\n $ boom <list-name>"
69
+ s << "\nAnd then add something to your list!"
70
+ s << "\n $ boom <list-name> <item-name> <item-value>"
71
+ s << "\nYou can then grab your new item:"
72
+ s << "\n $ boom <item-name>"
73
+ output s if storage.lists.size == 0
74
+ end
75
+
76
+ # Public: prints the detailed view of all your Lists and all their
77
+ # Items.
78
+ #
79
+ # Returns nothing.
80
+ def all
81
+ storage.lists.each do |list|
82
+ output " #{list.name}"
83
+ list.items.each do |item|
84
+ output " #{item.short_name}:#{item.spacer} #{item.value}"
85
+ end
86
+ end
87
+ end
88
+
89
+ # Public: allows main access to most commands.
90
+ #
91
+ # Returns output based on method calls.
92
+ def delegate(command, major, minor)
93
+ return all if command == 'all'
94
+ return edit if command == 'edit'
95
+ return switch(major) if command == 'switch'
96
+ return show_storage if command == 'storage'
97
+ return version if command == "-v"
98
+ return version if command == "--version"
99
+ return help if command == 'help'
100
+ return help if command[0] == 45 || command[0] == '-' # any - dash options are pleas for help
101
+ return echo(major,minor) if command == 'echo' || command == 'e'
102
+ return open(major,minor) if command == 'open' || command == 'o'
103
+ return random(major) if command == 'random' || command == 'rand' || command == 'r'
104
+
105
+ # if we're operating on a List
106
+ if storage.list_exists?(command)
107
+ return delete_list(command) if major == 'delete'
108
+ return detail_list(command) unless major
109
+ unless minor == 'delete'
110
+ return add_item(command,major,minor) if minor
111
+ return add_item(command,major,stdin.read) if stdin.stat.size > 0
112
+ return search_list_for_item(command, major)
113
+ end
114
+ end
115
+
116
+ if minor == 'delete' and storage.item_exists?(major)
117
+ return delete_item(command, major)
118
+ end
119
+
120
+ return search_items(command) if storage.item_exists?(command)
121
+
122
+ return create_list(command, major, stdin.read) if !minor && stdin.stat.size > 0
123
+ return create_list(command, major, minor)
124
+ end
125
+
126
+ # Public: shows the current user's storage.
127
+ #
128
+ # Returns nothing.
129
+ def show_storage
130
+ output "You're currently using #{Boom.config.attributes['backend']}."
131
+ end
132
+
133
+ # Public: switch to a new backend.
134
+ #
135
+ # backend - the String of the backend desired
136
+ #
137
+ # Returns nothing.
138
+ def switch(backend)
139
+ Storage.backend = backend
140
+ output "We've switched you over to #{backend}."
141
+ rescue NameError
142
+ output "We couldn't find that storage engine. Check the name and try again."
143
+ end
144
+
145
+ # Public: prints all Items over a List.
146
+ #
147
+ # name - the List object to iterate over
148
+ #
149
+ # Returns nothing.
150
+ def detail_list(name)
151
+ list = List.find(name)
152
+ list.items.sort{ |x,y| x.name <=> y.name }.each do |item|
153
+ output " #{item.short_name}:#{item.spacer} #{item.value}"
154
+ end
155
+ end
156
+
157
+ # Public: opens the Item.
158
+ #
159
+ # Returns nothing.
160
+ def open(major, minor)
161
+ if storage.list_exists?(major)
162
+ list = List.find(major)
163
+ if minor
164
+ item = storage.items.detect { |item| item.name == minor }
165
+ output "#{cyan("Boom!")} We just opened #{yellow(Platform.open(item))} for you."
166
+ else
167
+ list.items.each { |item| Platform.open(item) }
168
+ output "#{cyan("Boom!")} We just opened all of #{yellow(major)} for you."
169
+ end
170
+ else
171
+ item = storage.items.detect { |item| item.name == major }
172
+ output "#{cyan("Boom!")} We just opened #{yellow(Platform.open(item))} for you."
173
+ end
174
+ end
175
+
176
+ # Public: Opens a random item
177
+ #
178
+ # Returns nothing.
179
+ def random(major)
180
+ if major.nil?
181
+ index = rand(storage.items.size)
182
+ item = storage.items[index]
183
+ elsif storage.list_exists?(major)
184
+ list = List.find(major)
185
+ index = rand(list.items.size)
186
+ item = list.items[index]
187
+ else
188
+ output "We couldn't find that list."
189
+ end
190
+ open(item.name, nil) unless item.nil?
191
+ end
192
+
193
+ # Public: echoes only the Item's value without copying
194
+ #
195
+ # item_name - the String term to search for in all Item names
196
+ #
197
+ # Returns nothing
198
+ def echo(major, minor)
199
+ unless minor
200
+ item = storage.items.detect do |item|
201
+ item.name == major
202
+ end
203
+ return output "#{yellow(major)} #{red("not found")}" unless item
204
+ else
205
+ list = List.find(major)
206
+ item = list.find_item(minor)
207
+ return output "#{yellow(minor)} #{red("not found in")} #{yellow(major)}" unless item
208
+ end
209
+ output item.value
210
+ end
211
+
212
+ # Public: add a new List.
213
+ #
214
+ # name - the String name of the List.
215
+ # item - the String name of the Item
216
+ # value - the String value of Item
217
+ #
218
+ # Example
219
+ #
220
+ # Commands.list_create("snippets")
221
+ # Commands.list_create("hotness", "item", "value")
222
+ #
223
+ # Returns the newly created List and creates an item when asked.
224
+ def create_list(name, item = nil, value = nil)
225
+ lists = (storage.lists << List.new(name))
226
+ storage.lists = lists
227
+ output "#{cyan("Boom!")} Created a new list called #{yellow(name)}."
228
+ save
229
+ add_item(name, item, value) unless value.nil?
230
+ end
231
+
232
+ # Public: remove a named List.
233
+ #
234
+ # name - the String name of the List.
235
+ #
236
+ # Example
237
+ #
238
+ # Commands.delete_list("snippets")
239
+ #
240
+ # Returns nothing.
241
+ def delete_list(name)
242
+ output "You sure you want to delete everything in #{yellow(name)}? (y/n):"
243
+ if $stdin.gets.chomp == 'y'
244
+ List.delete(name)
245
+ output "#{cyan("Boom!")} Deleted all your #{yellow(name)}."
246
+ save
247
+ else
248
+ output "Just kidding then."
249
+ end
250
+ end
251
+
252
+ # Public: add a new Item to a list.
253
+ #
254
+ # list - the String name of the List to associate with this Item
255
+ # name - the String name of the Item
256
+ # value - the String value of the Item
257
+ #
258
+ # Example
259
+ #
260
+ # Commands.add_item("snippets","sig","- @holman")
261
+ #
262
+ # Returns the newly created Item.
263
+ def add_item(list,name,value)
264
+ list = List.find(list)
265
+ list.add_item(Item.new(name,value))
266
+ output "#{cyan("Boom!")} #{yellow(name)} in #{yellow(list.name)} is #{yellow(value)}. Got it."
267
+ save
268
+ end
269
+
270
+ # Public: remove a named Item.
271
+ #
272
+ # list_name - the String name of the List.
273
+ # name - the String name of the Item.
274
+ #
275
+ # Example
276
+ #
277
+ # Commands.delete_item("a-list-name", "an-item-name")
278
+ #
279
+ # Returns nothing.
280
+ def delete_item(list_name,name)
281
+ if storage.list_exists?(list_name)
282
+ list = List.find(list_name)
283
+ if list.delete_item(name)
284
+ output "#{cyan("Boom!")} #{yellow(name)} is gone forever."
285
+ save
286
+ else
287
+ output "#{yellow(name)} #{red("not found in")} #{yellow(list_name)}"
288
+ end
289
+ else
290
+ output "We couldn't find that list."
291
+ end
292
+ end
293
+
294
+ # Public: search for an Item in all lists by name. Drops the
295
+ # corresponding entry into your clipboard.
296
+ #
297
+ # name - the String term to search for in all Item names
298
+ #
299
+ # Returns the matching Item.
300
+ def search_items(name)
301
+ item = storage.items.detect do |item|
302
+ item.name == name
303
+ end
304
+
305
+ output "#{cyan("Boom!")} We just copied #{yellow(Platform.copy(item))} to your clipboard."
306
+ end
307
+
308
+ # Public: search for an Item in a particular list by name. Drops the
309
+ # corresponding entry into your clipboard if found.
310
+ #
311
+ # list_name - the String name of the List in which to scope the search
312
+ # item_name - the String term to search for in all Item names
313
+ #
314
+ # Returns the matching Item if found.
315
+ def search_list_for_item(list_name, item_name)
316
+ list = List.find(list_name)
317
+ item = list.find_item(item_name)
318
+
319
+ if item
320
+ output "#{cyan("Boom!")} We just copied #{yellow(Platform.copy(item))} to your clipboard."
321
+ else
322
+ output "#{yellow(item_name)} #{red("not found in")} #{yellow(list_name)}"
323
+ end
324
+ end
325
+
326
+ # Public: save in-memory data to disk.
327
+ #
328
+ # Returns whether or not data was saved.
329
+ def save
330
+ storage.save
331
+ end
332
+
333
+ # Public: the version of boom that you're currently running.
334
+ #
335
+ # Returns a String identifying the version number.
336
+ def version
337
+ output "You're running boom #{Boom::VERSION}. Congratulations!"
338
+ end
339
+
340
+ # Public: launches JSON file in an editor for you to edit manually.
341
+ #
342
+ # Returns nothing.
343
+ def edit
344
+ if storage.respond_to?("json_file")
345
+ output "#{cyan("Boom!")} #{Platform.edit(storage.json_file)}"
346
+ else
347
+ output "This storage backend does not store #{cyan("Boom!")} data on your computer"
348
+ end
349
+ end
350
+
351
+ # Public: prints all the commands of boom.
352
+ #
353
+ # Returns nothing.
354
+ def help
355
+ text = %{
356
+ - boom: help ---------------------------------------------------
357
+
358
+ boom display high-level overview
359
+ boom all show all items in all lists
360
+ boom edit edit the boom JSON file in $EDITOR
361
+ boom help this help text
362
+ boom storage shows which storage backend you're using
363
+ boom switch <storage> switches to a different storage backend
364
+
365
+ boom <list> create a new list
366
+ boom <list> show items for a list
367
+ boom <list> delete deletes a list
368
+
369
+ boom <list> <name> <value> create a new list item
370
+ boom <name> copy item's value to clipboard
371
+ boom <list> <name> copy item's value to clipboard
372
+ boom open <name> open item's url in browser
373
+ boom open <list> <name> open all item's url in browser for a list
374
+ boom random open a random item's url in browser
375
+ boom random <list> open a random item's url for a list in browser
376
+ boom echo <name> echo the item's value without copying
377
+ boom echo <list> <name> echo the item's value without copying
378
+ boom <list> <name> delete deletes an item
379
+
380
+ all other documentation is located at:
381
+ https://github.com/holman/boom
382
+ }.gsub(/^ {8}/, '') # strip the first eight spaces of every line
383
+
384
+ output text
385
+ end
386
+
387
+ end
388
+ end
389
+ end