boom 0.0.10 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.markdown +8 -1
- data/README.markdown +6 -126
- data/boom.gemspec +10 -2
- data/lib/boom/command.rb +38 -8
- data/lib/boom/config.rb +89 -0
- data/lib/boom/storage/base.rb +81 -0
- data/lib/boom/storage/json.rb +69 -0
- data/lib/boom/storage/mongodb.rb +139 -0
- data/lib/boom/storage/redis.rb +61 -0
- data/lib/boom/storage.rb +11 -116
- data/lib/boom.rb +16 -3
- data/test/examples/config_json.json +3 -0
- data/test/examples/test_json.json +3 -0
- data/test/helper.rb +5 -3
- data/test/test_command.rb +22 -0
- data/test/test_config.rb +25 -0
- metadata +13 -4
data/CHANGELOG.markdown
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
# boom changes
|
2
2
|
|
3
|
-
##
|
3
|
+
## 0.1.0
|
4
|
+
- boom has been rewritten to use multiple backends. Use `boom switch <backend>`
|
5
|
+
to switch from the default JSON backend. Currently only Redis is supported.
|
6
|
+
Pull Requests are welcome.
|
7
|
+
|
8
|
+
## 0.0.10
|
4
9
|
- `boom open` will open the Item's URL in a browser, or it'll open all the URLs
|
5
10
|
in a List for you. Thanks [lwe](https://github.com/lwe).
|
6
11
|
- Values for item creation can have spaces, and then they get concat'ed as one
|
@@ -10,6 +15,8 @@
|
|
10
15
|
- Also started `completion/`, a place to drop in scripts to set up completion
|
11
16
|
support for boom. Starting out with [thbishop](https://github.com/thbishop)'s
|
12
17
|
bash script, but if anyone has something for zsh I'd kiss them a bit.
|
18
|
+
- `boom echo` (and `boom e`) just echos the value; great for command-line
|
19
|
+
scripts and junk! Thanks [bschaeffer](https://github.com/bschaeffer).
|
13
20
|
|
14
21
|
## 0.0.9
|
15
22
|
- Backport `Symbol#to_proc` for 1.8.6 support (thanks
|
data/README.markdown
CHANGED
@@ -1,138 +1,18 @@
|
|
1
1
|
# B O O M
|
2
2
|
|
3
|
-
|
3
|
+
## About
|
4
4
|
|
5
|
-
|
5
|
+
boom manages your text snippets. On the command line. I just blew your mind.
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
- **Your own [del.icio.us](http://delicious.com)-esque URL tracker.** When I
|
12
|
-
make [clever animated
|
13
|
-
gifs](http://github.com/holman/dotfiles/blob/master/bin/gifme) of my
|
14
|
-
coworkers, I tend to lose the URL, which is a total bummer since I want to
|
15
|
-
repeatedly repost these images well past their funny expiration date. boom
|
16
|
-
lets me easily access the good stuff for years to come.
|
17
|
-
- **Commonly-used email replies.** Everyone's got those stock replies in their
|
18
|
-
pocket for a few common use cases. Rather than keep some files strewn about
|
19
|
-
with the responses, boom gives me them on my ever-present command line.
|
20
|
-
- **Simple todos.** You can super-quickly drop items into lists and remove them
|
21
|
-
when finished. I'm a big fan of simple, straightforward stuff. Plus, it's a
|
22
|
-
Dropbox away from simple cloud syncing. Someone get Cultured Code on the line
|
23
|
-
THIS MAY BE RELEVANT TO THEIR INTERESTS!
|
24
|
-
|
25
|
-
We store everything in one JSON file in your home directory: `~/.boom`. The
|
26
|
-
structure is simple, too. Each individual item is tossed on a `list`, and you
|
27
|
-
can have multiple lists.
|
28
|
-
|
29
|
-
## Show me the boom
|
30
|
-
|
31
|
-
** Overview **
|
32
|
-
|
33
|
-
$ boom
|
34
|
-
gifs (5)
|
35
|
-
email (4)
|
36
|
-
|
37
|
-
** Create a list **
|
38
|
-
|
39
|
-
$ boom urls
|
40
|
-
Boom! Created a new list called "urls".
|
41
|
-
|
42
|
-
** Add an item **
|
43
|
-
|
44
|
-
# boom <list> <name> <value>
|
45
|
-
$ boom urls github https://github.com
|
46
|
-
Boom! "github" in "urls" is "https://github.com". Got it.
|
47
|
-
|
48
|
-
** Copy an item's value to your clipboard **
|
49
|
-
|
50
|
-
$ boom github
|
51
|
-
Boom! Just copied https://github.com to your clipboard.
|
52
|
-
|
53
|
-
$ boom urls github
|
54
|
-
Boom! Just copied https://github.com to your clipboard.
|
55
|
-
|
56
|
-
** List items in a list **
|
57
|
-
|
58
|
-
$ boom urls
|
59
|
-
blog http://zachholman.com
|
60
|
-
github http://github.com
|
61
|
-
|
62
|
-
** Delete a list **
|
63
|
-
|
64
|
-
$ boom urls delete
|
65
|
-
You sure you want to delete everything in "urls"? (y/n): y
|
66
|
-
Boom! Deleted all your urls.
|
67
|
-
|
68
|
-
** Delete an item **
|
69
|
-
|
70
|
-
# boom urls github delete
|
71
|
-
Boom! "github" is gone forever.
|
72
|
-
|
73
|
-
** List everything **
|
74
|
-
|
75
|
-
$ boom all
|
76
|
-
enemies
|
77
|
-
@kneath: he's got dreamy eyes. he must die.
|
78
|
-
@rtomayko: i must murder him for his mac and cheese recipe.
|
79
|
-
@luckiestmonkey: she hates recycling.
|
80
|
-
urls
|
81
|
-
blog: http://zachholman.com
|
82
|
-
github: https://github.com
|
83
|
-
|
84
|
-
** Open in browser **
|
85
|
-
|
86
|
-
$ boom open facebook-stalking
|
87
|
-
Boom! We just opened all of "shoes" for you.
|
88
|
-
|
89
|
-
In other words, this will open all the links in `facebook-stalking` in your
|
90
|
-
browser. You creep. You can also just open up one:
|
91
|
-
|
92
|
-
$ boom open twitter
|
93
|
-
Boom! We just opened http://twitter.com for you.
|
94
|
-
|
95
|
-
** Echo an item**
|
96
|
-
|
97
|
-
# boom echo <list> <name>
|
98
|
-
# boom echo <name>
|
99
|
-
$ git clone $(boom echo github)holman/boom
|
100
|
-
Cloning into boom...
|
101
|
-
|
102
|
-
Impossible... no. It's silly, but not impossible.
|
103
|
-
|
104
|
-
** Help **
|
105
|
-
|
106
|
-
$ boom help
|
107
|
-
|
108
|
-
** Manual edit **
|
109
|
-
|
110
|
-
If you want to edit the underlying JSON directly, make sure your `$EDITOR`
|
111
|
-
environment variable is set, and run:
|
112
|
-
|
113
|
-
$ boom edit
|
114
|
-
|
115
|
-
** It's just the command line, silly **
|
116
|
-
|
117
|
-
So don't forget all your other favorites are there to help you, too:
|
118
|
-
|
119
|
-
$ boom all | grep @luckiestmonkey
|
120
|
-
@luckiestmonkey: she hates recycling.
|
7
|
+
For more details about what boom is and how it works, check out
|
8
|
+
[boom's website](http://holman.github.com/boom). For full usage details
|
9
|
+
(including a complete list of commands), check out
|
10
|
+
[boom's wiki](https://github.com/holman/boom/wiki).
|
121
11
|
|
122
12
|
## Install
|
123
13
|
|
124
14
|
gem install boom
|
125
15
|
|
126
|
-
## Current Status
|
127
|
-
|
128
|
-
Precarious. I'm starting to use this a bunch now, but if you're tossing in
|
129
|
-
important business information (say, a carefully cultivated list of animated
|
130
|
-
.gifs), you'd be sitting pretty if you made a backup of `~/.boom` every now and
|
131
|
-
then, just in case. We're living on the edge, baby.
|
132
|
-
|
133
|
-
Soon enough, though, this'll be stable to the point where I can truthfully tell
|
134
|
-
myself that this time baby, `boom` will be, bulletttttttttttttproof ♫.
|
135
|
-
|
136
16
|
## Contribute
|
137
17
|
|
138
18
|
I'd love to include your contributions, friend. Make sure your methods are
|
data/boom.gemspec
CHANGED
@@ -13,8 +13,8 @@ Gem::Specification.new do |s|
|
|
13
13
|
## If your rubyforge_project name is different, then edit it and comment out
|
14
14
|
## the sub! line in the Rakefile
|
15
15
|
s.name = 'boom'
|
16
|
-
s.version = '0.0
|
17
|
-
s.date = '2011-
|
16
|
+
s.version = '0.1.0'
|
17
|
+
s.date = '2011-02-28'
|
18
18
|
s.rubyforge_project = 'boom'
|
19
19
|
|
20
20
|
## Make sure your summary is short. The description may be as long
|
@@ -75,14 +75,22 @@ Gem::Specification.new do |s|
|
|
75
75
|
completion/boom.zsh
|
76
76
|
lib/boom.rb
|
77
77
|
lib/boom/command.rb
|
78
|
+
lib/boom/config.rb
|
78
79
|
lib/boom/core_ext/symbol.rb
|
79
80
|
lib/boom/item.rb
|
80
81
|
lib/boom/list.rb
|
81
82
|
lib/boom/platform.rb
|
82
83
|
lib/boom/storage.rb
|
84
|
+
lib/boom/storage/base.rb
|
85
|
+
lib/boom/storage/json.rb
|
86
|
+
lib/boom/storage/mongodb.rb
|
87
|
+
lib/boom/storage/redis.rb
|
88
|
+
test/examples/config_json.json
|
89
|
+
test/examples/test_json.json
|
83
90
|
test/examples/urls.json
|
84
91
|
test/helper.rb
|
85
92
|
test/test_command.rb
|
93
|
+
test/test_config.rb
|
86
94
|
test/test_item.rb
|
87
95
|
test/test_list.rb
|
88
96
|
]
|
data/lib/boom/command.rb
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
#
|
7
7
|
# Command also keeps track of one connection to Storage, which is how new data
|
8
8
|
# changes are persisted to disk. It takes care of any data changes by calling
|
9
|
-
# Boom::Command#save
|
9
|
+
# Boom::Command#save.
|
10
10
|
#
|
11
11
|
module Boom
|
12
12
|
class Command
|
@@ -50,6 +50,13 @@ module Boom
|
|
50
50
|
storage.lists.each do |list|
|
51
51
|
output " #{list.name} (#{list.items.size})"
|
52
52
|
end
|
53
|
+
s = "You don't have anything yet! To start out, create a new list:"
|
54
|
+
s << "\n $ boom <list-name>"
|
55
|
+
s << "\nAnd then add something to your list!"
|
56
|
+
s << "\n $ boom <list-name> <item-name> <item-value>"
|
57
|
+
s << "\nYou can then grab your new item:"
|
58
|
+
s << "\n $ boom <item-name>"
|
59
|
+
output s if storage.lists.size == 0
|
53
60
|
end
|
54
61
|
|
55
62
|
# Public: prints the detailed view of all your Lists and all their
|
@@ -71,10 +78,12 @@ module Boom
|
|
71
78
|
def delegate(command, major, minor)
|
72
79
|
return all if command == 'all'
|
73
80
|
return edit if command == 'edit'
|
81
|
+
return switch(major) if command == 'switch'
|
82
|
+
return show_storage if command == 'storage'
|
74
83
|
return help if command == 'help'
|
75
84
|
return help if command[0] == 45 || command[0] == '-' # any - dash options are pleas for help
|
76
85
|
return echo(major,minor) if command == 'echo' || command == 'e'
|
77
|
-
return open(major,minor) if command == 'open'
|
86
|
+
return open(major,minor) if command == 'open' || command == 'o'
|
78
87
|
|
79
88
|
# if we're operating on a List
|
80
89
|
if storage.list_exists?(command)
|
@@ -95,6 +104,25 @@ module Boom
|
|
95
104
|
return create_list(command)
|
96
105
|
end
|
97
106
|
|
107
|
+
# Public: shows the current user's storage.
|
108
|
+
#
|
109
|
+
# Returns nothing.
|
110
|
+
def show_storage
|
111
|
+
output "You're currently using #{Boom.config.attributes['backend']}."
|
112
|
+
end
|
113
|
+
|
114
|
+
# Public: switch to a new backend.
|
115
|
+
#
|
116
|
+
# backend - the String of the backend desired
|
117
|
+
#
|
118
|
+
# Returns nothing.
|
119
|
+
def switch(backend)
|
120
|
+
Storage.backend = backend
|
121
|
+
output "We've switched you over to #{backend}."
|
122
|
+
rescue NameError
|
123
|
+
output "We couldn't find that storage engine. Check the name and try again."
|
124
|
+
end
|
125
|
+
|
98
126
|
# Public: prints all Items over a List.
|
99
127
|
#
|
100
128
|
# name - the List object to iterate over
|
@@ -153,7 +181,7 @@ module Boom
|
|
153
181
|
lists = (storage.lists << List.new(name))
|
154
182
|
storage.lists = lists
|
155
183
|
output "Boom! Created a new list called \"#{name}\"."
|
156
|
-
save
|
184
|
+
save
|
157
185
|
end
|
158
186
|
|
159
187
|
# Public: remove a named List.
|
@@ -170,7 +198,7 @@ module Boom
|
|
170
198
|
if $stdin.gets.chomp == 'y'
|
171
199
|
List.delete(name)
|
172
200
|
output "Boom! Deleted all your #{name}."
|
173
|
-
save
|
201
|
+
save
|
174
202
|
else
|
175
203
|
output "Just kidding then."
|
176
204
|
end
|
@@ -191,7 +219,7 @@ module Boom
|
|
191
219
|
list = List.find(list)
|
192
220
|
list.add_item(Item.new(name,value))
|
193
221
|
output "Boom! \"#{name}\" in \"#{list.name}\" is \"#{value}\". Got it."
|
194
|
-
save
|
222
|
+
save
|
195
223
|
end
|
196
224
|
|
197
225
|
# Public: remove a named Item.
|
@@ -208,7 +236,7 @@ module Boom
|
|
208
236
|
list = List.find(list_name)
|
209
237
|
list.delete_item(name)
|
210
238
|
output "Boom! \"#{name}\" is gone forever."
|
211
|
-
save
|
239
|
+
save
|
212
240
|
end
|
213
241
|
|
214
242
|
# Public: search for an Item in all lists by name. Drops the
|
@@ -246,8 +274,8 @@ module Boom
|
|
246
274
|
# Public: save in-memory data to disk.
|
247
275
|
#
|
248
276
|
# Returns whether or not data was saved.
|
249
|
-
def save
|
250
|
-
storage.save
|
277
|
+
def save
|
278
|
+
storage.save
|
251
279
|
end
|
252
280
|
|
253
281
|
# Public: launches JSON file in an editor for you to edit manually. Uses
|
@@ -270,6 +298,8 @@ module Boom
|
|
270
298
|
boom all show all items in all lists
|
271
299
|
boom edit edit the boom JSON file in $EDITOR
|
272
300
|
boom help this help text
|
301
|
+
boom storage shows which storage backend you're using
|
302
|
+
boom switch <storage> switches to a different storage backend
|
273
303
|
|
274
304
|
boom <list> create a new list
|
275
305
|
boom <list> show items for a list
|
data/lib/boom/config.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
#
|
4
|
+
# Config manages all the config information for boom and its backends. It's a
|
5
|
+
# simple JSON Hash that gets persisted to `~/.boom` on-disk. You may access it
|
6
|
+
# as a Hash:
|
7
|
+
#
|
8
|
+
# config.attributes = { :backend => "JSON" }
|
9
|
+
# config.attributes[:backend]
|
10
|
+
# # => "json"
|
11
|
+
#
|
12
|
+
# config.attributes[:backend] = "Redis"
|
13
|
+
# config.attributes[:backend]
|
14
|
+
# # => "redis"
|
15
|
+
#
|
16
|
+
module Boom
|
17
|
+
class Config
|
18
|
+
|
19
|
+
# The main config file for boom
|
20
|
+
FILE = "#{ENV['HOME']}/.boom.conf"
|
21
|
+
|
22
|
+
# Public: The attributes Hash for configuration options. The attributes
|
23
|
+
# needed are dictated by each backend, but the `backend` option must be
|
24
|
+
# present.
|
25
|
+
attr_reader :attributes
|
26
|
+
|
27
|
+
# Public: creates a new instance of Config.
|
28
|
+
#
|
29
|
+
# This will load the attributes from boom's config file, or bootstrap it
|
30
|
+
# if this is a new install. Bootstrapping defaults to the JSON backend.
|
31
|
+
#
|
32
|
+
# Returns nothing.
|
33
|
+
def initialize
|
34
|
+
bootstrap unless File.exist?(file)
|
35
|
+
load_attributes
|
36
|
+
end
|
37
|
+
|
38
|
+
# Public: accessor for the configuration file.
|
39
|
+
#
|
40
|
+
# Returns the String file path.
|
41
|
+
def file
|
42
|
+
FILE
|
43
|
+
end
|
44
|
+
|
45
|
+
# Public: saves an empty, barebones hash to @attributes for the purpose of
|
46
|
+
# new user setup.
|
47
|
+
#
|
48
|
+
# Returns whether the attributes were saved.
|
49
|
+
def bootstrap
|
50
|
+
@attributes = {
|
51
|
+
:backend => 'JSON'
|
52
|
+
}
|
53
|
+
save
|
54
|
+
end
|
55
|
+
|
56
|
+
# Public: assigns a hash to the configuration attributes object. The
|
57
|
+
# contents of the attributes hash depends on what the backend needs. A
|
58
|
+
# `backend` key MUST be present, however.
|
59
|
+
#
|
60
|
+
# attrs - the Hash representation of attributes to persist to disk.
|
61
|
+
#
|
62
|
+
# Examples
|
63
|
+
#
|
64
|
+
# config.attributes = {"backend" => "json"}
|
65
|
+
#
|
66
|
+
# Returns whether the attributes were saved.
|
67
|
+
def attributes=(attrs)
|
68
|
+
@attributes = attrs
|
69
|
+
save
|
70
|
+
end
|
71
|
+
|
72
|
+
# Public: loads and parses the JSON tree from disk into memory and stores
|
73
|
+
# it in the attributes Hash.
|
74
|
+
#
|
75
|
+
# Returns nothing.
|
76
|
+
def load_attributes
|
77
|
+
@attributes = Yajl::Parser.new.parse(File.new(file, 'r'))
|
78
|
+
end
|
79
|
+
|
80
|
+
# Public: writes the in-memory JSON Hash to disk.
|
81
|
+
#
|
82
|
+
# Returns nothing.
|
83
|
+
def save
|
84
|
+
json = Yajl::Encoder.encode(attributes, :pretty => true)
|
85
|
+
File.open(file, 'w') {|f| f.write(json) }
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
# Storage is the middleman between changes the client makes in-memory and how
|
4
|
+
# it's actually persisted to disk (and vice-versa). There are also a few
|
5
|
+
# convenience methods to run searches and operations on the in-memory hash.
|
6
|
+
#
|
7
|
+
module Boom
|
8
|
+
module Storage
|
9
|
+
class Base
|
10
|
+
|
11
|
+
# Public: initializes a Storage instance by loading in your persisted data from adapter.
|
12
|
+
#
|
13
|
+
# Returns the Storage instance.
|
14
|
+
def initialize
|
15
|
+
@lists = []
|
16
|
+
bootstrap
|
17
|
+
populate
|
18
|
+
end
|
19
|
+
|
20
|
+
# run bootstrap tasks for the storage
|
21
|
+
def bootstrap ; end
|
22
|
+
|
23
|
+
# populate the in-memory store with all the lists and items
|
24
|
+
def populate ; end
|
25
|
+
|
26
|
+
# save the data
|
27
|
+
def save ; end
|
28
|
+
|
29
|
+
# Public: the in-memory collection of all Lists attached to this Storage
|
30
|
+
# instance.
|
31
|
+
#
|
32
|
+
# lists - an Array of individual List items
|
33
|
+
#
|
34
|
+
# Returns nothing.
|
35
|
+
attr_writer :lists
|
36
|
+
|
37
|
+
# Public: the list of Lists in your JSON data, sorted by number of items
|
38
|
+
# descending.
|
39
|
+
#
|
40
|
+
# Returns an Array of List objects.
|
41
|
+
def lists
|
42
|
+
@lists.sort_by { |list| -list.items.size }
|
43
|
+
end
|
44
|
+
|
45
|
+
# Public: tests whether a named List exists.
|
46
|
+
#
|
47
|
+
# name - the String name of a List
|
48
|
+
#
|
49
|
+
# Returns true if found, false if not.
|
50
|
+
def list_exists?(name)
|
51
|
+
@lists.detect { |list| list.name == name }
|
52
|
+
end
|
53
|
+
|
54
|
+
# Public: all Items in storage.
|
55
|
+
#
|
56
|
+
# Returns an Array of all Items.
|
57
|
+
def items
|
58
|
+
@lists.collect(&:items).flatten
|
59
|
+
end
|
60
|
+
|
61
|
+
# Public: tests whether a named Item exists.
|
62
|
+
#
|
63
|
+
# name - the String name of an Item
|
64
|
+
#
|
65
|
+
# Returns true if found, false if not.
|
66
|
+
def item_exists?(name)
|
67
|
+
items.detect { |item| item.name == name }
|
68
|
+
end
|
69
|
+
|
70
|
+
# Public: creates a Hash of the representation of the in-memory data
|
71
|
+
# structure. This percolates down to Items by calling to_hash on the List,
|
72
|
+
# which in tern calls to_hash on individual Items.
|
73
|
+
#
|
74
|
+
# Returns a Hash of the entire data set.
|
75
|
+
def to_hash
|
76
|
+
{ :lists => lists.collect(&:to_hash) }
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
#
|
3
|
+
# Json is the default storage option for boom. It writes a Json file to
|
4
|
+
# ~/.boom. Pretty neat, huh?
|
5
|
+
#
|
6
|
+
module Boom
|
7
|
+
module Storage
|
8
|
+
class Json < Base
|
9
|
+
|
10
|
+
JSON_FILE = "#{ENV['HOME']}/.boom"
|
11
|
+
|
12
|
+
# Public: the path to the Json file used by boom.
|
13
|
+
#
|
14
|
+
# Returns the String path of boom's Json representation.
|
15
|
+
def json_file
|
16
|
+
JSON_FILE
|
17
|
+
end
|
18
|
+
|
19
|
+
# Takes care of bootstrapping the Json file, both in terms of creating the
|
20
|
+
# file and in terms of creating a skeleton Json schema.
|
21
|
+
#
|
22
|
+
# Return true if successfully saved.
|
23
|
+
def bootstrap
|
24
|
+
return if File.exist?(json_file)
|
25
|
+
FileUtils.touch json_file
|
26
|
+
File.open(json_file, 'w') {|f| f.write(to_json) }
|
27
|
+
save
|
28
|
+
end
|
29
|
+
|
30
|
+
# Take a Json representation of data and explode it out into the consituent
|
31
|
+
# Lists and Items for the given Storage instance.
|
32
|
+
#
|
33
|
+
# Returns nothing.
|
34
|
+
def populate
|
35
|
+
storage = Yajl::Parser.new.parse(File.new(json_file, 'r'))
|
36
|
+
|
37
|
+
storage['lists'].each do |lists|
|
38
|
+
lists.each do |list_name, items|
|
39
|
+
@lists << list = List.new(list_name)
|
40
|
+
|
41
|
+
items.each do |item|
|
42
|
+
item.each do |name,value|
|
43
|
+
list.add_item(Item.new(name,value))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Public: persists your in-memory objects to disk in Json format.
|
51
|
+
#
|
52
|
+
# lists_Json - list in Json format
|
53
|
+
#
|
54
|
+
# Returns true if successful, false if unsuccessful.
|
55
|
+
def save
|
56
|
+
File.open(json_file, 'w') {|f| f.write(to_json) }
|
57
|
+
end
|
58
|
+
|
59
|
+
# Public: the Json representation of the current List and Item assortment
|
60
|
+
# attached to the Storage instance.
|
61
|
+
#
|
62
|
+
# Returns a String Json representation of its Lists and their Items.
|
63
|
+
def to_json
|
64
|
+
Yajl::Encoder.encode(to_hash, :pretty => true)
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
# Storage adapter that saves data from boom to MongoDB instead of JSON file.
|
4
|
+
#
|
5
|
+
# This was grabbed from antonlindstrom's fork, but he wrote it before the 0.1.0
|
6
|
+
# changes to boom. It would need to be updated to use Boom::Config and to
|
7
|
+
# inherit methods from Boom::Storage::Base. Until then, this is chillin' here
|
8
|
+
# until it gets fixed.
|
9
|
+
|
10
|
+
module Boom
|
11
|
+
module Storage
|
12
|
+
class MongoDB
|
13
|
+
|
14
|
+
MONGO_CFG = "#{ENV['HOME']}/.boomdb.conf"
|
15
|
+
|
16
|
+
# Public: the path to the JSON Mongo config file, userdata.
|
17
|
+
#
|
18
|
+
# Returns the String path of boom's MongoDB userdata
|
19
|
+
def mongo_cfg
|
20
|
+
MONGO_CFG
|
21
|
+
end
|
22
|
+
|
23
|
+
# Public: initializes a Storage instance by loading in your persisted data.
|
24
|
+
#
|
25
|
+
# Returns the Storage instance.
|
26
|
+
def initialize
|
27
|
+
require 'mongo'
|
28
|
+
|
29
|
+
@lists = []
|
30
|
+
@mongo_coll = nil
|
31
|
+
mongo_initialize(mongo_cfg) # Initialize the MongoDB and set data in memory
|
32
|
+
collect
|
33
|
+
end
|
34
|
+
|
35
|
+
# Public: return the list from mongodb
|
36
|
+
#
|
37
|
+
# Returns list
|
38
|
+
def lists
|
39
|
+
@lists
|
40
|
+
end
|
41
|
+
|
42
|
+
# Public: Save to MongoDB
|
43
|
+
#
|
44
|
+
# lists_json - the list to be saved in JSON format
|
45
|
+
#
|
46
|
+
# Returns whatever mongo returns.
|
47
|
+
def save(lists_json)
|
48
|
+
doc = @mongo_coll.find_one()
|
49
|
+
@mongo_coll.update({"_id" => doc["_id"]}, {'boom' => lists_json})
|
50
|
+
end
|
51
|
+
|
52
|
+
# INTERNAL METHODS ##########################################################
|
53
|
+
|
54
|
+
# Take a JSON representation of data and explode it out into the consituent
|
55
|
+
# Lists and Items for the given Storage instance.
|
56
|
+
#
|
57
|
+
# Returns nothing.
|
58
|
+
def collect
|
59
|
+
|
60
|
+
s = @mongo_coll.find_one['boom']
|
61
|
+
storage = Yajl::Parser.new.parse(s)
|
62
|
+
|
63
|
+
return if storage['lists'].nil?
|
64
|
+
|
65
|
+
storage['lists'].each do |lists|
|
66
|
+
lists.each do |list_name, items|
|
67
|
+
@lists << list = List.new(list_name)
|
68
|
+
|
69
|
+
items.each do |item|
|
70
|
+
item.each do |name,value|
|
71
|
+
list.add_item(Item.new(name,value))
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
##### MONGODB ######
|
79
|
+
|
80
|
+
# Initialize MongoDB and set data in memory
|
81
|
+
#
|
82
|
+
# config_file - The MongoDB config_file path defined
|
83
|
+
#
|
84
|
+
# Returns database obj.
|
85
|
+
def mongo_initialize(config_file)
|
86
|
+
bootstrap_config(config_file) unless File.exists?(config_file)
|
87
|
+
config = parse_mongo_cfg(config_file)
|
88
|
+
|
89
|
+
db = Mongo::Connection.new(config['host'], config['port']).db(config['database'])
|
90
|
+
auth = db.authenticate(config['username'], config['password'])
|
91
|
+
|
92
|
+
# Get collection collection;
|
93
|
+
@mongo_coll = db.collection(config['collection'])
|
94
|
+
|
95
|
+
@mongo_coll.insert("boom" => '{"lists": [{}]}') if @mongo_coll.find_one.nil?
|
96
|
+
|
97
|
+
return @mongo_coll
|
98
|
+
end
|
99
|
+
|
100
|
+
# Parse Mongo JSON Config
|
101
|
+
#
|
102
|
+
# config_file - The MongoDB config_file path defined
|
103
|
+
#
|
104
|
+
# Returns a hash of Mongo Userdata
|
105
|
+
def parse_mongo_cfg(config_file)
|
106
|
+
|
107
|
+
mongod = Hash.new
|
108
|
+
config = Yajl::Parser.new.parse(File.open(config_file, 'r'))
|
109
|
+
|
110
|
+
config.each_pair do |type, data|
|
111
|
+
mongod[type] = data
|
112
|
+
end
|
113
|
+
return mongod
|
114
|
+
end
|
115
|
+
|
116
|
+
# Run a default config
|
117
|
+
#
|
118
|
+
# config_file - The MongoDB config_file path defined
|
119
|
+
#
|
120
|
+
# Returns File obj.
|
121
|
+
def bootstrap_config(config_file)
|
122
|
+
config = Hash.new
|
123
|
+
|
124
|
+
config['host'] = 'localhost'
|
125
|
+
config['port'] = '27017'
|
126
|
+
config['database'] = 'boom'
|
127
|
+
config['username'] = 'boom'
|
128
|
+
config['password'] = 's3cr3t'
|
129
|
+
config['collection'] = 'boom'
|
130
|
+
|
131
|
+
# Write to CFG
|
132
|
+
json_cfg = Yajl::Encoder.encode(config, :pretty => true)
|
133
|
+
File.open(config_file, 'w') {|f| f.write(json_cfg) }
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
#
|
3
|
+
# Sup, Redis.
|
4
|
+
#
|
5
|
+
require 'digest'
|
6
|
+
require 'redis'
|
7
|
+
|
8
|
+
module Boom
|
9
|
+
module Storage
|
10
|
+
class Redis < Base
|
11
|
+
|
12
|
+
def redis
|
13
|
+
@redis ||= ::Redis.new :host => Boom.config.attributes["redis"]["host"],
|
14
|
+
:port => Boom.config.attributes["redis"]["port"]
|
15
|
+
end
|
16
|
+
|
17
|
+
def bootstrap
|
18
|
+
end
|
19
|
+
|
20
|
+
def populate
|
21
|
+
lists = redis.smembers("boom:lists") || []
|
22
|
+
|
23
|
+
lists.each do |sha|
|
24
|
+
list_name = redis.get("boom:lists:#{sha}:name")
|
25
|
+
@lists << list = List.new(list_name)
|
26
|
+
|
27
|
+
shas = redis.lrange("boom:lists:#{sha}:items",0,-1) || []
|
28
|
+
|
29
|
+
shas.each do |item_sha|
|
30
|
+
name = redis.get "boom:items:#{item_sha}:name"
|
31
|
+
value = redis.get "boom:items:#{item_sha}:value"
|
32
|
+
list.add_item(Item.new(name, value))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def clear
|
38
|
+
redis.del "boom:lists"
|
39
|
+
redis.del "boom:items"
|
40
|
+
end
|
41
|
+
|
42
|
+
def save
|
43
|
+
clear
|
44
|
+
|
45
|
+
lists.each do |list|
|
46
|
+
list_sha = Digest::SHA1.hexdigest(list.name)
|
47
|
+
redis.set "boom:lists:#{list_sha}:name", list.name
|
48
|
+
redis.sadd "boom:lists", list_sha
|
49
|
+
|
50
|
+
list.items.each do |item|
|
51
|
+
item_sha = Digest::SHA1.hexdigest(item.name)
|
52
|
+
redis.rpush "boom:lists:#{list_sha}:items", item_sha
|
53
|
+
redis.set "boom:items:#{item_sha}:name", item.name
|
54
|
+
redis.set "boom:items:#{item_sha}:value", item.value
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/boom/storage.rb
CHANGED
@@ -1,126 +1,21 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
3
|
+
#
|
4
|
+
# Storage is the interface between multiple Backends. You can use Storage
|
5
|
+
# directly without having to worry about which Backend is in use.
|
6
6
|
#
|
7
7
|
module Boom
|
8
|
-
|
8
|
+
module Storage
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
def json_file
|
16
|
-
JSON_FILE
|
17
|
-
end
|
18
|
-
|
19
|
-
# Public: initializes a Storage instance by loading in your persisted data.
|
20
|
-
#
|
21
|
-
# Returns the Storage instance.
|
22
|
-
def initialize
|
23
|
-
@lists = []
|
24
|
-
explode_json(json_file)
|
25
|
-
end
|
26
|
-
|
27
|
-
# Public: the in-memory collection of all Lists attached to this Storage
|
28
|
-
# instance.
|
29
|
-
#
|
30
|
-
# lists - an Array of individual List items
|
31
|
-
#
|
32
|
-
# Returns nothing.
|
33
|
-
attr_writer :lists
|
34
|
-
|
35
|
-
# Public: the list of Lists in your JSON data, sorted by number of items
|
36
|
-
# descending.
|
37
|
-
#
|
38
|
-
# Returns an Array of List objects.
|
39
|
-
def lists
|
40
|
-
@lists.sort_by { |list| -list.items.size }
|
41
|
-
end
|
42
|
-
|
43
|
-
# Public: tests whether a named List exists.
|
44
|
-
#
|
45
|
-
# name - the String name of a List
|
46
|
-
#
|
47
|
-
# Returns true if found, false if not.
|
48
|
-
def list_exists?(name)
|
49
|
-
@lists.detect { |list| list.name == name }
|
50
|
-
end
|
51
|
-
|
52
|
-
# Public: all Items in storage.
|
53
|
-
#
|
54
|
-
# Returns an Array of all Items.
|
55
|
-
def items
|
56
|
-
@lists.collect(&:items).flatten
|
57
|
-
end
|
58
|
-
|
59
|
-
# Public: tests whether a named Item exists.
|
60
|
-
#
|
61
|
-
# name - the String name of an Item
|
62
|
-
#
|
63
|
-
# Returns true if found, false if not.
|
64
|
-
def item_exists?(name)
|
65
|
-
items.detect { |item| item.name == name }
|
66
|
-
end
|
67
|
-
|
68
|
-
# Public: persists your in-memory objects to disk in JSON format.
|
69
|
-
#
|
70
|
-
# Returns true if successful, false if unsuccessful.
|
71
|
-
def save!
|
72
|
-
File.open(json_file, 'w') {|f| f.write(to_json) }
|
73
|
-
end
|
74
|
-
|
75
|
-
# Public: the JSON representation of the current List and Item assortment
|
76
|
-
# attached to the Storage instance.
|
77
|
-
#
|
78
|
-
# Returns a String JSON representation of its Lists and their Items.
|
79
|
-
def to_json
|
80
|
-
Yajl::Encoder.encode(to_hash, :pretty => true)
|
81
|
-
end
|
82
|
-
|
83
|
-
# Public: creates a Hash of the representation of the in-memory data
|
84
|
-
# structure. This percolates down to Items by calling to_hash on the List,
|
85
|
-
# which in tern calls to_hash on individual Items.
|
86
|
-
#
|
87
|
-
# Returns a Hash of the entire data set.
|
88
|
-
def to_hash
|
89
|
-
{ :lists => lists.collect(&:to_hash) }
|
90
|
-
end
|
91
|
-
|
92
|
-
|
93
|
-
# INTERNAL METHODS ##########################################################
|
94
|
-
|
95
|
-
# Take a JSON representation of data and explode it out into the consituent
|
96
|
-
# Lists and Items for the given Storage instance.
|
97
|
-
#
|
98
|
-
# Returns nothing.
|
99
|
-
def explode_json(json)
|
100
|
-
bootstrap_json unless File.exist?(json)
|
101
|
-
|
102
|
-
storage = Yajl::Parser.new.parse(File.new(json, 'r'))
|
103
|
-
|
104
|
-
storage['lists'].each do |lists|
|
105
|
-
lists.each do |list_name, items|
|
106
|
-
@lists << list = List.new(list_name)
|
107
|
-
|
108
|
-
items.each do |item|
|
109
|
-
item.each do |name,value|
|
110
|
-
list.add_item(Item.new(name,value))
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
10
|
+
def self.backend=(backend)
|
11
|
+
backend = backend.capitalize
|
12
|
+
Boom::Storage.const_get(backend)
|
13
|
+
Boom.config.attributes['backend'] = backend.downcase
|
14
|
+
Boom.config.save
|
115
15
|
end
|
116
16
|
|
117
|
-
|
118
|
-
|
119
|
-
#
|
120
|
-
# Return true if successfully saved.
|
121
|
-
def bootstrap_json
|
122
|
-
FileUtils.touch json_file
|
123
|
-
save!
|
17
|
+
def self.backend
|
18
|
+
Boom::Storage.const_get(Boom.config.attributes['backend'].capitalize).new
|
124
19
|
end
|
125
20
|
|
126
21
|
end
|
data/lib/boom.rb
CHANGED
@@ -12,16 +12,29 @@ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
|
12
12
|
|
13
13
|
require 'boom/platform'
|
14
14
|
require 'boom/command'
|
15
|
+
require 'boom/config'
|
15
16
|
require 'boom/item'
|
16
17
|
require 'boom/list'
|
18
|
+
|
17
19
|
require 'boom/storage'
|
20
|
+
require 'boom/storage/base'
|
21
|
+
require 'boom/storage/json'
|
22
|
+
require 'boom/storage/redis'
|
23
|
+
require 'boom/storage/mongodb'
|
18
24
|
|
19
25
|
require 'boom/core_ext/symbol'
|
20
26
|
|
21
27
|
module Boom
|
22
|
-
VERSION = '0.0
|
28
|
+
VERSION = '0.1.0'
|
29
|
+
|
30
|
+
extend self
|
23
31
|
|
24
|
-
def
|
25
|
-
@storage ||= Boom::Storage.
|
32
|
+
def storage
|
33
|
+
@storage ||= Boom::Storage.backend
|
26
34
|
end
|
35
|
+
|
36
|
+
def config
|
37
|
+
@config ||= Boom::Config.new
|
38
|
+
end
|
39
|
+
|
27
40
|
end
|
data/test/helper.rb
CHANGED
@@ -16,7 +16,9 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
16
16
|
require 'boom'
|
17
17
|
|
18
18
|
def boom_json(name)
|
19
|
-
|
20
|
-
|
21
|
-
Boom.stubs(:
|
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.stubs(:storage).returns(Boom::Storage::Json.new)
|
22
24
|
end
|
data/test/test_command.rb
CHANGED
@@ -36,6 +36,13 @@ class TestCommand < Test::Unit::TestCase
|
|
36
36
|
Boom::Command.captured_output
|
37
37
|
end
|
38
38
|
|
39
|
+
def test_overview_for_empty
|
40
|
+
storage = Boom::Storage
|
41
|
+
storage.stubs(:lists).returns([])
|
42
|
+
Boom::Command.stubs(:storage).returns(storage)
|
43
|
+
assert_match /have anything yet!/, command(nil)
|
44
|
+
end
|
45
|
+
|
39
46
|
def test_overview
|
40
47
|
assert_equal ' urls (2)', command(nil)
|
41
48
|
end
|
@@ -139,4 +146,19 @@ class TestCommand < Test::Unit::TestCase
|
|
139
146
|
def test_echo_list_item_does_not_exist
|
140
147
|
assert_match /"wrong" not found in "urls"/, command('echo urls wrong')
|
141
148
|
end
|
149
|
+
|
150
|
+
def test_show_storage
|
151
|
+
assert_match /You're currently using json/, command('storage')
|
152
|
+
end
|
153
|
+
|
154
|
+
def test_nonexistant_storage_switch
|
155
|
+
Boom::Config.any_instance.stubs(:save).returns(true)
|
156
|
+
assert_match /couldn't find that storage engine/, command('switch dkdkdk')
|
157
|
+
end
|
158
|
+
|
159
|
+
def test_storage_switch
|
160
|
+
Boom::Config.any_instance.stubs(:save).returns(true)
|
161
|
+
assert_match /We've switched you over to redis/, command('switch redis')
|
162
|
+
end
|
163
|
+
|
142
164
|
end
|
data/test/test_config.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'helper'
|
4
|
+
|
5
|
+
class TestConfig < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
Boom::Config.any_instance.stubs(:file).
|
9
|
+
returns("test/examples/test_json.json")
|
10
|
+
|
11
|
+
@config = Boom::Config.new
|
12
|
+
@config.stubs(:save).returns(true)
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_bootstraps_config
|
16
|
+
@config.bootstrap
|
17
|
+
assert_equal ({:backend => 'JSON'}), @config.attributes
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_attributes
|
21
|
+
@config.attributes[:wu_tang] = 'clan'
|
22
|
+
assert_equal 'clan', @config.attributes[:wu_tang]
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: boom
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
+
- 1
|
8
9
|
- 0
|
9
|
-
|
10
|
-
version: 0.0.10
|
10
|
+
version: 0.1.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Zach Holman
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
18
|
+
date: 2011-02-28 00:00:00 -08:00
|
19
19
|
default_executable: boom
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -77,14 +77,22 @@ files:
|
|
77
77
|
- completion/boom.zsh
|
78
78
|
- lib/boom.rb
|
79
79
|
- lib/boom/command.rb
|
80
|
+
- lib/boom/config.rb
|
80
81
|
- lib/boom/core_ext/symbol.rb
|
81
82
|
- lib/boom/item.rb
|
82
83
|
- lib/boom/list.rb
|
83
84
|
- lib/boom/platform.rb
|
84
85
|
- lib/boom/storage.rb
|
86
|
+
- lib/boom/storage/base.rb
|
87
|
+
- lib/boom/storage/json.rb
|
88
|
+
- lib/boom/storage/mongodb.rb
|
89
|
+
- lib/boom/storage/redis.rb
|
90
|
+
- test/examples/config_json.json
|
91
|
+
- test/examples/test_json.json
|
85
92
|
- test/examples/urls.json
|
86
93
|
- test/helper.rb
|
87
94
|
- test/test_command.rb
|
95
|
+
- test/test_config.rb
|
88
96
|
- test/test_item.rb
|
89
97
|
- test/test_list.rb
|
90
98
|
has_rdoc: true
|
@@ -123,5 +131,6 @@ specification_version: 2
|
|
123
131
|
summary: boom lets you access text snippets over your command line.
|
124
132
|
test_files:
|
125
133
|
- test/test_command.rb
|
134
|
+
- test/test_config.rb
|
126
135
|
- test/test_item.rb
|
127
136
|
- test/test_list.rb
|