kaboom 0.3.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/CHANGELOG.markdown +107 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +24 -0
- data/LICENSE.markdown +21 -0
- data/README.markdown +74 -0
- data/Rakefile +150 -0
- data/bin/boom +8 -0
- data/bin/kaboom +8 -0
- data/completion/README.md +7 -0
- data/completion/boom.bash +17 -0
- data/completion/boom.zsh +29 -0
- data/kaboom.gemspec +117 -0
- data/lib/kaboom.rb +59 -0
- data/lib/kaboom/color.rb +52 -0
- data/lib/kaboom/command.rb +389 -0
- data/lib/kaboom/config.rb +116 -0
- data/lib/kaboom/core_ext/symbol.rb +7 -0
- data/lib/kaboom/item.rb +72 -0
- data/lib/kaboom/list.rb +100 -0
- data/lib/kaboom/output.rb +13 -0
- data/lib/kaboom/platform.rb +103 -0
- data/lib/kaboom/remote.rb +47 -0
- data/lib/kaboom/storage.rb +22 -0
- data/lib/kaboom/storage/base.rb +91 -0
- data/lib/kaboom/storage/gist.rb +125 -0
- data/lib/kaboom/storage/json.rb +76 -0
- data/lib/kaboom/storage/keychain.rb +135 -0
- data/lib/kaboom/storage/mongodb.rb +96 -0
- data/lib/kaboom/storage/redis.rb +79 -0
- data/test/examples/config_json.json +3 -0
- data/test/examples/test_json.json +3 -0
- data/test/examples/urls.json +1 -0
- data/test/helper.rb +25 -0
- data/test/output_interceptor.rb +28 -0
- data/test/test_color.rb +30 -0
- data/test/test_command.rb +227 -0
- data/test/test_config.rb +27 -0
- data/test/test_item.rb +54 -0
- data/test/test_list.rb +79 -0
- data/test/test_platform.rb +52 -0
- data/test/test_remote.rb +30 -0
- metadata +151 -0
@@ -0,0 +1,96 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
# Storage adapter that saves data from boom to MongoDB instead of JSON file.
|
4
|
+
begin
|
5
|
+
require 'mongo'
|
6
|
+
rescue LoadError
|
7
|
+
end
|
8
|
+
|
9
|
+
module Boom
|
10
|
+
module Storage
|
11
|
+
class Mongodb < Base
|
12
|
+
|
13
|
+
def self.sample_config
|
14
|
+
%(
|
15
|
+
{"backend": "mongodb",
|
16
|
+
"mongodb": {
|
17
|
+
"port": "",
|
18
|
+
"host": ""
|
19
|
+
"database": ""
|
20
|
+
"username": ""
|
21
|
+
"password": ""
|
22
|
+
}
|
23
|
+
})
|
24
|
+
end
|
25
|
+
|
26
|
+
# Public: Initialize MongoDB connection and check dep.
|
27
|
+
#
|
28
|
+
# Returns Mongo connection
|
29
|
+
def mongo
|
30
|
+
@mongo ||= ::Mongo::Connection.new(
|
31
|
+
Boom.config.attributes["mongodb"]["host"],
|
32
|
+
Boom.config.attributes["mongodb"]["port"]
|
33
|
+
).db(Boom.config.attributes["mongodb"]["database"])
|
34
|
+
|
35
|
+
@mongo.authenticate(
|
36
|
+
Boom.config.attributes['mongodb']['username'],
|
37
|
+
Boom.config.attributes['mongodb']['password']
|
38
|
+
)
|
39
|
+
|
40
|
+
# Return connection
|
41
|
+
@mongo
|
42
|
+
rescue Exception => exception
|
43
|
+
handle exception, "You don't have the Mongo gem installed yet:\n gem install mongo"
|
44
|
+
end
|
45
|
+
|
46
|
+
# Public: The MongoDB collection
|
47
|
+
#
|
48
|
+
# Returns the MongoDB collection
|
49
|
+
def collection
|
50
|
+
@collection ||= mongo.collection(Boom.config.attributes["mongodb"]["collection"])
|
51
|
+
end
|
52
|
+
|
53
|
+
# Public: Bootstrap
|
54
|
+
#
|
55
|
+
# Returns
|
56
|
+
def bootstrap
|
57
|
+
collection.insert("boom" => '{"lists": [{}]}') if collection.find_one.nil?
|
58
|
+
end
|
59
|
+
|
60
|
+
# Public: Populates the memory list from MongoDB
|
61
|
+
#
|
62
|
+
# Returns nothing
|
63
|
+
def populate
|
64
|
+
storage = MultiJson.decode(collection.find_one['boom']) || []
|
65
|
+
|
66
|
+
storage['lists'].each do |lists|
|
67
|
+
lists.each do |list_name, items|
|
68
|
+
@lists << list = List.new(list_name)
|
69
|
+
|
70
|
+
items.each do |item|
|
71
|
+
item.each do |name,value|
|
72
|
+
list.add_item(Item.new(name,value))
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Public: Save to MongoDB
|
80
|
+
#
|
81
|
+
# Returns Mongo ID
|
82
|
+
def save
|
83
|
+
doc = collection.find_one()
|
84
|
+
collection.update({"_id" => doc["_id"]}, {'boom' => to_json})
|
85
|
+
end
|
86
|
+
|
87
|
+
# Public: Convert to JSON
|
88
|
+
#
|
89
|
+
# Returns
|
90
|
+
def to_json
|
91
|
+
MultiJson.encode(to_hash)
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
#
|
3
|
+
# Sup, Redis.
|
4
|
+
#
|
5
|
+
begin
|
6
|
+
require 'digest'
|
7
|
+
require 'redis'
|
8
|
+
rescue LoadError
|
9
|
+
end
|
10
|
+
|
11
|
+
module Boom
|
12
|
+
module Storage
|
13
|
+
class Redis < Base
|
14
|
+
include Boom::Color
|
15
|
+
include Boom::Output
|
16
|
+
|
17
|
+
def self.sample_config
|
18
|
+
%(
|
19
|
+
{"backend": "redis",
|
20
|
+
"redis": {
|
21
|
+
"port": "6379",
|
22
|
+
"host": "<host>"
|
23
|
+
}
|
24
|
+
}
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
def redis
|
29
|
+
@redis ||= ::Redis.new :host => Boom.config.attributes["redis"]["host"],
|
30
|
+
:port => Boom.config.attributes["redis"]["port"]
|
31
|
+
rescue Exception => exception
|
32
|
+
handle exception, "You don't have Redis installed yet:\n gem install redis"
|
33
|
+
end
|
34
|
+
|
35
|
+
def bootstrap
|
36
|
+
end
|
37
|
+
|
38
|
+
def populate
|
39
|
+
lists = redis.smembers("boom:lists") || []
|
40
|
+
|
41
|
+
lists.each do |sha|
|
42
|
+
list_name = redis.get("boom:lists:#{sha}:name")
|
43
|
+
@lists << list = List.new(list_name)
|
44
|
+
|
45
|
+
shas = redis.lrange("boom:lists:#{sha}:items",0,-1) || []
|
46
|
+
|
47
|
+
shas.each do |item_sha|
|
48
|
+
name = redis.get "boom:items:#{item_sha}:name"
|
49
|
+
value = redis.get "boom:items:#{item_sha}:value"
|
50
|
+
list.add_item(Item.new(name, value))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def clear
|
56
|
+
redis.del "boom:lists"
|
57
|
+
redis.del "boom:items"
|
58
|
+
end
|
59
|
+
|
60
|
+
def save
|
61
|
+
clear
|
62
|
+
|
63
|
+
lists.each do |list|
|
64
|
+
list_sha = Digest::SHA1.hexdigest(list.name)
|
65
|
+
redis.set "boom:lists:#{list_sha}:name", list.name
|
66
|
+
redis.sadd "boom:lists", list_sha
|
67
|
+
|
68
|
+
list.items.each do |item|
|
69
|
+
item_sha = Digest::SHA1.hexdigest(item.name)
|
70
|
+
redis.rpush "boom:lists:#{list_sha}:items", item_sha
|
71
|
+
redis.set "boom:items:#{item_sha}:name", item.name
|
72
|
+
redis.set "boom:items:#{item_sha}:value", item.value
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
{"lists":[{"urls":[{"github":"https://github.com"},{"blog":"http://zachholman.com"}]}]}
|
data/test/helper.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'rubygems'
|
7
|
+
require 'redgreen'
|
8
|
+
rescue LoadError
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'mocha'
|
12
|
+
|
13
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
14
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
15
|
+
|
16
|
+
require 'kaboom'
|
17
|
+
|
18
|
+
def boom_json(name)
|
19
|
+
root = File.expand_path(File.dirname(__FILE__))
|
20
|
+
Boom::Storage::Json.any_instance.stubs(:save).returns(true)
|
21
|
+
Boom::Storage::Json.any_instance.stubs(:json_file).
|
22
|
+
returns("#{root}/examples/#{name}.json")
|
23
|
+
Boom.use_remote false
|
24
|
+
Boom.stubs(:storage).returns(Boom::Storage::Json.new)
|
25
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Boom
|
2
|
+
module Output
|
3
|
+
|
4
|
+
def capture_output
|
5
|
+
@output = ''
|
6
|
+
end
|
7
|
+
|
8
|
+
def captured_output
|
9
|
+
@output
|
10
|
+
end
|
11
|
+
|
12
|
+
def output(s)
|
13
|
+
@output << s
|
14
|
+
end
|
15
|
+
|
16
|
+
extend self
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Intercept STDOUT and collect it
|
21
|
+
class Boom::Command
|
22
|
+
|
23
|
+
|
24
|
+
def self.save!
|
25
|
+
# no-op
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
data/test/test_color.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestColor < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def test_colorize
|
6
|
+
assert_equal "\e[35mBoom!\e[0m",
|
7
|
+
Boom::Color.colorize("Boom!", :magenta)
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_magenta
|
11
|
+
assert_equal "\e[35mMagenta!\e[0m",
|
12
|
+
Boom::Color.magenta("Magenta!")
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_red
|
16
|
+
assert_equal "\e[31mRed!\e[0m",
|
17
|
+
Boom::Color.red("Red!")
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_yellow
|
21
|
+
assert_equal "\e[33mYellow!\e[0m",
|
22
|
+
Boom::Color.yellow("Yellow!")
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_cyan
|
26
|
+
assert_equal "\e[36mCyan!\e[0m",
|
27
|
+
Boom::Color.cyan("Cyan!")
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,227 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'helper'
|
4
|
+
require 'output_interceptor'
|
5
|
+
|
6
|
+
class TestCommand < Test::Unit::TestCase
|
7
|
+
|
8
|
+
def setup
|
9
|
+
boom_json :urls
|
10
|
+
end
|
11
|
+
|
12
|
+
def command(cmd)
|
13
|
+
cmd = cmd.split(' ') if cmd
|
14
|
+
Boom::Command.capture_output
|
15
|
+
Boom::Command.execute(*cmd)
|
16
|
+
output = Boom::Command.captured_output
|
17
|
+
output.gsub(/\e\[\d\d?m/, '')
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_use_remote
|
21
|
+
response = command('remote the_office fun awesome')
|
22
|
+
|
23
|
+
assert_match /a new list called the_office/, response
|
24
|
+
assert_match /fun in the_office is awesome/, response
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_remote_checks_for_acceptable_config
|
28
|
+
response = command('remote the_office fun awesome')
|
29
|
+
|
30
|
+
assert_match /a new list called the_office/, response
|
31
|
+
assert_match /fun in the_office is awesome/, response
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_overview_for_empty
|
35
|
+
storage = Boom::Storage
|
36
|
+
storage.stubs(:lists).returns([])
|
37
|
+
Boom::Command.stubs(:storage).returns(storage)
|
38
|
+
assert_match /have anything yet!/, command(nil)
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_overview
|
42
|
+
assert_equal ' urls (2)', command(nil)
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_list_detail
|
46
|
+
assert_match /github/, command('urls')
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_list_all
|
50
|
+
cmd = command('all')
|
51
|
+
assert_match /urls/, cmd
|
52
|
+
assert_match /github/, cmd
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_list_creation
|
56
|
+
assert_match /a new list called newlist/, command('newlist')
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_list_creation_with_item
|
60
|
+
assert_match /a new list called newlist.* item in newlist/, command('newlist item blah')
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_list_creation_with_item_stdin
|
64
|
+
STDIN.stubs(:read).returns('blah')
|
65
|
+
STDIN.stubs(:stat)
|
66
|
+
STDIN.stat.stubs(:size).returns(4)
|
67
|
+
|
68
|
+
assert_match /a new list called newlist.* item in newlist is blah/, command('newlist item')
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_item_access
|
72
|
+
IO.stubs(:popen)
|
73
|
+
assert_match /copied https:\/\/github\.com to your clipboard/,
|
74
|
+
command('github')
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_item_access_scoped_by_list
|
78
|
+
IO.stubs(:popen)
|
79
|
+
assert_match /copied https:\/\/github\.com to your clipboard/,
|
80
|
+
command('urls github')
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_item_open_item
|
84
|
+
Boom::Platform.stubs(:system).returns('')
|
85
|
+
assert_match /opened https:\/\/github\.com for you/, command('open github')
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_item_open_specific_item
|
89
|
+
Boom::Platform.stubs(:system).returns('')
|
90
|
+
assert_match /opened https:\/\/github\.com for you/, command('open urls github')
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_item_open_lists
|
94
|
+
Boom::Platform.stubs(:system).returns('')
|
95
|
+
assert_match /opened all of urls for you/, command('open urls')
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_item_creation
|
99
|
+
assert_match /twitter in urls/,
|
100
|
+
command('urls twitter http://twitter.com/holman')
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_item_creation_long_value
|
104
|
+
assert_match /is tanqueray hendricks bombay/,
|
105
|
+
command('urls gins tanqueray hendricks bombay')
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_list_deletion_no
|
109
|
+
STDIN.stubs(:gets).returns('n')
|
110
|
+
assert_match /Just kidding then/, command('urls delete')
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_list_deletion_yes
|
114
|
+
STDIN.stubs(:gets).returns('y')
|
115
|
+
assert_match /Deleted all your urls/, command('urls delete')
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_item_deletion
|
119
|
+
assert_match /github is gone forever/, command('urls github delete')
|
120
|
+
end
|
121
|
+
|
122
|
+
def test_edit
|
123
|
+
Boom::Platform.stubs(:system).returns('')
|
124
|
+
assert_match 'Make your edits', command('edit')
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_help
|
128
|
+
assert_match 'boom help', command('help')
|
129
|
+
assert_match 'boom help', command('-h')
|
130
|
+
assert_match 'boom help', command('--help')
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_noop_options
|
134
|
+
assert_match 'boom help', command('--anything')
|
135
|
+
assert_match 'boom help', command('-d')
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_nonexistent_item_access_scoped_by_list
|
139
|
+
assert_match /twitter not found in urls/, command('urls twitter')
|
140
|
+
end
|
141
|
+
|
142
|
+
def test_echo_item
|
143
|
+
assert_match /https:\/\/github\.com/, command('echo github')
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_echo_item_shorthand
|
147
|
+
assert_match /https:\/\/github\.com/, command('e github')
|
148
|
+
end
|
149
|
+
|
150
|
+
def test_echo_item_does_not_exist
|
151
|
+
assert_match /wrong not found/, command('echo wrong')
|
152
|
+
end
|
153
|
+
|
154
|
+
def test_echo_list_item
|
155
|
+
assert_match /https:\/\/github\.com/, command('echo urls github')
|
156
|
+
end
|
157
|
+
|
158
|
+
def test_echo_list_item_does_not_exist
|
159
|
+
assert_match /wrong not found in urls/, command('echo urls wrong')
|
160
|
+
end
|
161
|
+
|
162
|
+
def test_show_storage
|
163
|
+
Boom::Config.any_instance.stubs(:attributes).returns('backend' => 'json')
|
164
|
+
assert_match /You're currently using json/, command('storage')
|
165
|
+
end
|
166
|
+
|
167
|
+
def test_nonexistant_storage_switch
|
168
|
+
Boom::Config.any_instance.stubs(:save).returns(true)
|
169
|
+
assert_match /couldn't find that storage engine/, command('switch dkdkdk')
|
170
|
+
end
|
171
|
+
|
172
|
+
def test_storage_switch
|
173
|
+
Boom::Config.any_instance.stubs(:save).returns(true)
|
174
|
+
assert_match /We've switched you over to redis/, command('switch redis')
|
175
|
+
end
|
176
|
+
|
177
|
+
def test_version_switch
|
178
|
+
assert_match /#{Boom::VERSION}/, command('-v')
|
179
|
+
end
|
180
|
+
|
181
|
+
def test_version_long
|
182
|
+
assert_match /#{Boom::VERSION}/, command('--version')
|
183
|
+
end
|
184
|
+
|
185
|
+
def test_stdin_pipes
|
186
|
+
stub = Object.new
|
187
|
+
stub.stubs(:stat).returns([1,2])
|
188
|
+
stub.stubs(:read).returns("http://twitter.com")
|
189
|
+
Boom::Command.stubs(:stdin).returns(stub)
|
190
|
+
assert_match /twitter in urls/, command('urls twitter')
|
191
|
+
end
|
192
|
+
|
193
|
+
def test_random
|
194
|
+
Boom::Platform.stubs(:system).returns('')
|
195
|
+
assert_match /opened .+ for you/, command('random')
|
196
|
+
end
|
197
|
+
|
198
|
+
def test_random_from_list
|
199
|
+
Boom::Platform.stubs(:system).returns('')
|
200
|
+
assert_match /(github|zachholman)/, command('random urls')
|
201
|
+
end
|
202
|
+
|
203
|
+
def test_random_list_not_exist
|
204
|
+
Boom::Platform.stubs(:system).returns('')
|
205
|
+
assert_match /couldn't find that list\./, command('random 39jc02jlskjbbac9')
|
206
|
+
end
|
207
|
+
|
208
|
+
def test_delete_item_list_not_exist
|
209
|
+
assert_match /couldn't find that list\./, command('urlz github delete')
|
210
|
+
end
|
211
|
+
|
212
|
+
def test_delete_item_wrong_list
|
213
|
+
command('urlz twitter https://twitter.com/')
|
214
|
+
assert_match /github not found in urlz/, command('urlz github delete')
|
215
|
+
end
|
216
|
+
|
217
|
+
def test_delete_item_different_name
|
218
|
+
command('foo bar baz')
|
219
|
+
assert_match /bar is gone forever/, command('foo bar delete')
|
220
|
+
end
|
221
|
+
|
222
|
+
def test_delete_item_same_name
|
223
|
+
command('duck duck goose')
|
224
|
+
assert_match /duck is gone forever/, command('duck duck delete')
|
225
|
+
end
|
226
|
+
|
227
|
+
end
|