boom 0.0.10 → 0.1.0
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 +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
|