boom 0.0.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/License +21 -0
- data/Rakefile +150 -0
- data/Readme.markdown +116 -0
- data/bin/boom +8 -0
- data/boom.gemspec +90 -0
- data/lib/boom/clipboard.rb +22 -0
- data/lib/boom/command.rb +212 -0
- data/lib/boom/item.rb +37 -0
- data/lib/boom/list.rb +44 -0
- data/lib/boom/storage.rb +125 -0
- data/lib/boom.rb +19 -0
- data/test/examples/urls.json +1 -0
- data/test/helper.rb +33 -0
- data/test/test_command.rb +85 -0
- data/test/test_item.rb +21 -0
- data/test/test_list.rb +26 -0
- metadata +123 -0
data/License
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) Zach Holman, http://zachholman.com
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
#############################################################################
|
6
|
+
#
|
7
|
+
# Helper functions
|
8
|
+
#
|
9
|
+
#############################################################################
|
10
|
+
|
11
|
+
def name
|
12
|
+
@name ||= Dir['*.gemspec'].first.split('.').first
|
13
|
+
end
|
14
|
+
|
15
|
+
def version
|
16
|
+
line = File.read("lib/#{name}.rb")[/^\s*VERSION\s*=\s*.*/]
|
17
|
+
line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
|
18
|
+
end
|
19
|
+
|
20
|
+
def date
|
21
|
+
Date.today.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
def rubyforge_project
|
25
|
+
name
|
26
|
+
end
|
27
|
+
|
28
|
+
def gemspec_file
|
29
|
+
"#{name}.gemspec"
|
30
|
+
end
|
31
|
+
|
32
|
+
def gem_file
|
33
|
+
"#{name}-#{version}.gem"
|
34
|
+
end
|
35
|
+
|
36
|
+
def replace_header(head, header_name)
|
37
|
+
head.sub!(/(\.#{header_name}\s*= ').*'/) { "#{$1}#{send(header_name)}'"}
|
38
|
+
end
|
39
|
+
|
40
|
+
#############################################################################
|
41
|
+
#
|
42
|
+
# Standard tasks
|
43
|
+
#
|
44
|
+
#############################################################################
|
45
|
+
|
46
|
+
task :default => :test
|
47
|
+
|
48
|
+
require 'rake/testtask'
|
49
|
+
Rake::TestTask.new(:test) do |test|
|
50
|
+
test.libs << 'lib' << 'test'
|
51
|
+
test.pattern = 'test/**/test_*.rb'
|
52
|
+
test.verbose = true
|
53
|
+
end
|
54
|
+
|
55
|
+
desc "Generate RCov test coverage and open in your browser"
|
56
|
+
task :coverage do
|
57
|
+
require 'rcov'
|
58
|
+
sh "rm -fr coverage"
|
59
|
+
sh "rcov test/test_*.rb"
|
60
|
+
sh "open coverage/index.html"
|
61
|
+
end
|
62
|
+
|
63
|
+
require 'rake/rdoctask'
|
64
|
+
Rake::RDocTask.new do |rdoc|
|
65
|
+
rdoc.rdoc_dir = 'rdoc'
|
66
|
+
rdoc.title = "#{name} #{version}"
|
67
|
+
rdoc.rdoc_files.include('README*')
|
68
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
69
|
+
end
|
70
|
+
|
71
|
+
desc "Open an irb session preloaded with this library"
|
72
|
+
task :console do
|
73
|
+
sh "irb -rubygems -r ./lib/#{name}.rb"
|
74
|
+
end
|
75
|
+
|
76
|
+
#############################################################################
|
77
|
+
#
|
78
|
+
# Custom tasks (add your own tasks here)
|
79
|
+
#
|
80
|
+
#############################################################################
|
81
|
+
|
82
|
+
|
83
|
+
|
84
|
+
#############################################################################
|
85
|
+
#
|
86
|
+
# Packaging tasks
|
87
|
+
#
|
88
|
+
#############################################################################
|
89
|
+
|
90
|
+
desc "Create tag v#{version} and build and push #{gem_file} to Rubygems"
|
91
|
+
task :release => :build do
|
92
|
+
unless `git branch` =~ /^\* master$/
|
93
|
+
puts "You must be on the master branch to release!"
|
94
|
+
exit!
|
95
|
+
end
|
96
|
+
sh "git commit --allow-empty -a -m 'Release #{version}'"
|
97
|
+
sh "git tag v#{version}"
|
98
|
+
sh "git push origin master"
|
99
|
+
sh "git push origin v#{version}"
|
100
|
+
sh "gem push pkg/#{name}-#{version}.gem"
|
101
|
+
end
|
102
|
+
|
103
|
+
desc "Build #{gem_file} into the pkg directory"
|
104
|
+
task :build => :gemspec do
|
105
|
+
sh "mkdir -p pkg"
|
106
|
+
sh "gem build #{gemspec_file}"
|
107
|
+
sh "mv #{gem_file} pkg"
|
108
|
+
end
|
109
|
+
|
110
|
+
desc "Generate #{gemspec_file}"
|
111
|
+
task :gemspec => :validate do
|
112
|
+
# read spec file and split out manifest section
|
113
|
+
spec = File.read(gemspec_file)
|
114
|
+
head, manifest, tail = spec.split(" # = MANIFEST =\n")
|
115
|
+
|
116
|
+
# replace name version and date
|
117
|
+
replace_header(head, :name)
|
118
|
+
replace_header(head, :version)
|
119
|
+
replace_header(head, :date)
|
120
|
+
#comment this out if your rubyforge_project has a different name
|
121
|
+
replace_header(head, :rubyforge_project)
|
122
|
+
|
123
|
+
# determine file list from git ls-files
|
124
|
+
files = `git ls-files`.
|
125
|
+
split("\n").
|
126
|
+
sort.
|
127
|
+
reject { |file| file =~ /^\./ }.
|
128
|
+
reject { |file| file =~ /^(rdoc|pkg)/ }.
|
129
|
+
map { |file| " #{file}" }.
|
130
|
+
join("\n")
|
131
|
+
|
132
|
+
# piece file back together and write
|
133
|
+
manifest = " s.files = %w[\n#{files}\n ]\n"
|
134
|
+
spec = [head, manifest, tail].join(" # = MANIFEST =\n")
|
135
|
+
File.open(gemspec_file, 'w') { |io| io.write(spec) }
|
136
|
+
puts "Updated #{gemspec_file}"
|
137
|
+
end
|
138
|
+
|
139
|
+
desc "Validate #{gemspec_file}"
|
140
|
+
task :validate do
|
141
|
+
libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}"]
|
142
|
+
unless libfiles.empty?
|
143
|
+
puts "Directory `lib` should only contain a `#{name}.rb` file and `#{name}` dir."
|
144
|
+
exit!
|
145
|
+
end
|
146
|
+
unless Dir['VERSION*'].empty?
|
147
|
+
puts "A `VERSION` file at root level violates Gem best practices."
|
148
|
+
exit!
|
149
|
+
end
|
150
|
+
end
|
data/Readme.markdown
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
# B O O M
|
2
|
+
|
3
|
+
See? Look, a tiny explosion: \*
|
4
|
+
|
5
|
+
## What's a boom?
|
6
|
+
|
7
|
+
boom lets you access text snippets over your command line. I'm personally
|
8
|
+
aiming for exactly two use cases, but I'm almost positive there are thirteen
|
9
|
+
more. Here's a couple examples:
|
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
|
+
** It's just the command line, silly **
|
85
|
+
|
86
|
+
So don't forget all your other favorites are there to help you, too:
|
87
|
+
|
88
|
+
$ boom all | grep @luckiestmonkey
|
89
|
+
@luckiestmonkey: she hates recycling.
|
90
|
+
|
91
|
+
## Install
|
92
|
+
|
93
|
+
gem install boom
|
94
|
+
|
95
|
+
## Current Status
|
96
|
+
|
97
|
+
Precarious. I'm starting to use this a bunch now, but if you're tossing in
|
98
|
+
important business information (say, a carefully cultivated list of animated
|
99
|
+
.gifs), you'd be sitting pretty if you made a backup of `~/.boom` every now and
|
100
|
+
then, just in case. We're living on the edge, baby.
|
101
|
+
|
102
|
+
Also, don't mistype anything. Everything should be 100% perfect if you type all
|
103
|
+
the commands correctly. If you don't, it'll probably ungracefully error out and
|
104
|
+
a puppy in Kansas will probably be murdered. You should be fine, though. As
|
105
|
+
long as you don't have butter fingers.
|
106
|
+
|
107
|
+
Soon enough, though, this'll be stable to the point where I can truthfully tell
|
108
|
+
myself that this time baby, `boom` will be, bulletttttttttttttproof ♫.
|
109
|
+
|
110
|
+
## I love you
|
111
|
+
|
112
|
+
[Zach Holman](http://zachholman.com) made this. Ping me on Twitter —
|
113
|
+
[@holman](http://twitter.com/holman) — if you're having issues, want me to
|
114
|
+
merge in your pull request, or are using boom in a cool way. I'm kind of hoping
|
115
|
+
this is generic enough that people do some fun things with it. First one to use
|
116
|
+
`boom` to calculate their tax liability wins.
|
data/bin/boom
ADDED
data/boom.gemspec
ADDED
@@ -0,0 +1,90 @@
|
|
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 = 'boom'
|
16
|
+
s.version = '0.0.1'
|
17
|
+
s.date = '2010-11-24'
|
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"]
|
35
|
+
s.email = 'github.com@zachholman.com'
|
36
|
+
s.homepage = 'https://github.com/holman/boom'
|
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"]
|
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]
|
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('yajl-ruby', "~> 0.7.8")
|
58
|
+
|
59
|
+
## List your development dependencies here. Development dependencies are
|
60
|
+
## those that are only needed during development
|
61
|
+
s.add_development_dependency('mocha', "~> 0.9.9")
|
62
|
+
|
63
|
+
## Leave this section as-is. It will be automatically generated from the
|
64
|
+
## contents of your Git repository via the gemspec task. DO NOT REMOVE
|
65
|
+
## THE MANIFEST COMMENTS, they are used as delimiters by the task.
|
66
|
+
# = MANIFEST =
|
67
|
+
s.files = %w[
|
68
|
+
License
|
69
|
+
Rakefile
|
70
|
+
Readme.markdown
|
71
|
+
bin/boom
|
72
|
+
boom.gemspec
|
73
|
+
lib/boom.rb
|
74
|
+
lib/boom/clipboard.rb
|
75
|
+
lib/boom/command.rb
|
76
|
+
lib/boom/item.rb
|
77
|
+
lib/boom/list.rb
|
78
|
+
lib/boom/storage.rb
|
79
|
+
test/examples/urls.json
|
80
|
+
test/helper.rb
|
81
|
+
test/test_command.rb
|
82
|
+
test/test_item.rb
|
83
|
+
test/test_list.rb
|
84
|
+
]
|
85
|
+
# = MANIFEST =
|
86
|
+
|
87
|
+
## Test files will be grabbed from the file list. Make sure the path glob
|
88
|
+
## matches what you actually use.
|
89
|
+
s.test_files = s.files.select { |path| path =~ /^test\/test_.*\.rb/ }
|
90
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Clipboard is a centralized point to shell out to each individual platform's
|
2
|
+
# clipboard, pasteboard, or whatever they decide to call it.
|
3
|
+
#
|
4
|
+
module Boom
|
5
|
+
class Clipboard
|
6
|
+
class << self
|
7
|
+
|
8
|
+
# Public: copies a given Item's value to the clipboard. This method is
|
9
|
+
# designed to handle multiple platforms.
|
10
|
+
#
|
11
|
+
# Returns nothing.
|
12
|
+
def copy(item)
|
13
|
+
if RUBY_PLATFORM =~ /darwin/
|
14
|
+
`echo '#{item.value}' | tr -d "\n" | pbcopy`
|
15
|
+
end
|
16
|
+
|
17
|
+
"Boom! We just copied #{item.value} to your clipboard."
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/boom/command.rb
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
# Command is the main point of entry for boom commands; shell arguments are
|
2
|
+
# passd through to Command, which then filters and parses through indivdual
|
3
|
+
# commands and reroutes them to constituent object classes.
|
4
|
+
#
|
5
|
+
# Command also keeps track of one connection to Storage, which is how new data
|
6
|
+
# changes are persisted to disk. It takes care of any data changes by calling
|
7
|
+
# Boom::Command#save!.
|
8
|
+
#
|
9
|
+
module Boom
|
10
|
+
class Command
|
11
|
+
class << self
|
12
|
+
|
13
|
+
attr_accessor :storage
|
14
|
+
|
15
|
+
# Public: executes a command.
|
16
|
+
#
|
17
|
+
# storage - The Storage instance off which to run commands. This is
|
18
|
+
# likely just Boom::Storage.new, since Boom::Storage should
|
19
|
+
# pick up the appropriate JSON file paths on its own.
|
20
|
+
# args - The actual commands to operate on. Can be as few as zero
|
21
|
+
# arguments or as many as three.
|
22
|
+
def execute(storage,*args)
|
23
|
+
@storage = storage
|
24
|
+
|
25
|
+
command = args[0]
|
26
|
+
major = args[1]
|
27
|
+
minor = args[2]
|
28
|
+
|
29
|
+
return overview unless command
|
30
|
+
delegate(command, major, minor)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Public: prints any given string.
|
34
|
+
#
|
35
|
+
# s = String output
|
36
|
+
#
|
37
|
+
# Prints to STDOUT and returns. This method exists to standardize output
|
38
|
+
# and for easy mocking or overriding.
|
39
|
+
def output(s)
|
40
|
+
puts(s)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Public: prints a tidy overview of your Lists in descending order of
|
44
|
+
# number of Items.
|
45
|
+
#
|
46
|
+
# Returns nothing.
|
47
|
+
def overview
|
48
|
+
storage.lists.each do |list|
|
49
|
+
output " #{list.name} (#{list.items.size})"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Public: prints the detailed view of all your Lists and all their
|
54
|
+
# Items.
|
55
|
+
#
|
56
|
+
# Returns nothing.
|
57
|
+
def all
|
58
|
+
storage.lists.each do |list|
|
59
|
+
output " #{list.name}"
|
60
|
+
list.items.each do |item|
|
61
|
+
output " #{item.name}: #{item.value}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Public: allows main access to most commands.
|
67
|
+
#
|
68
|
+
# Returns output based on method calls.
|
69
|
+
def delegate(command, major, minor)
|
70
|
+
return all if command == 'all'
|
71
|
+
|
72
|
+
# if we're operating on a List
|
73
|
+
if storage.list_exists?(command)
|
74
|
+
return list_delete(command) if major == 'delete'
|
75
|
+
return list_detail(command) unless major
|
76
|
+
unless minor == 'delete'
|
77
|
+
return add_item(command,major,minor) if minor
|
78
|
+
return search_list_for_item(command, major)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
return search_items(command) if storage.item_exists?(command)
|
83
|
+
|
84
|
+
if minor == 'delete' and storage.item_exists?(major)
|
85
|
+
return item_delete(major)
|
86
|
+
end
|
87
|
+
|
88
|
+
return list_create(command)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Public: prints all Items over a List.
|
92
|
+
#
|
93
|
+
# list - the List object to iterate over
|
94
|
+
#
|
95
|
+
# Returns nothing.
|
96
|
+
def list_detail(list_name)
|
97
|
+
list = storage.lists.first { |list| list.name == list_name }
|
98
|
+
list.items.sort{ |x,y| x.name <=> y.name }.each do |item|
|
99
|
+
output " #{item.name}: #{item.value}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Public: add a new List.
|
104
|
+
#
|
105
|
+
# name - the String name of the List.
|
106
|
+
#
|
107
|
+
# Example
|
108
|
+
#
|
109
|
+
# Commands.list_create("snippets")
|
110
|
+
#
|
111
|
+
# Returns the newly created List.
|
112
|
+
def list_create(name)
|
113
|
+
lists = (storage.lists << List.new(name))
|
114
|
+
storage.lists = lists
|
115
|
+
output "Boom! Created a new list called \"#{name}\"."
|
116
|
+
save!
|
117
|
+
end
|
118
|
+
|
119
|
+
# Public: remove a named List.
|
120
|
+
#
|
121
|
+
# name - the String name of the List.
|
122
|
+
#
|
123
|
+
# Example
|
124
|
+
#
|
125
|
+
# Commands.delete_list("snippets")
|
126
|
+
#
|
127
|
+
# Returns nothing.
|
128
|
+
def list_delete(name)
|
129
|
+
lists = storage.lists.reverse.reject { |list| list.name == name }
|
130
|
+
output "You sure you want to delete everything in \"#{name}\"? (y/n):"
|
131
|
+
if $stdin.gets.chomp == 'y'
|
132
|
+
storage.lists = lists
|
133
|
+
output "Boom! Deleted all your #{name}."
|
134
|
+
save!
|
135
|
+
else
|
136
|
+
output "Just kidding then."
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Public: add a new Item to a list.
|
141
|
+
#
|
142
|
+
# list - the String name of the List to associate with this Item
|
143
|
+
# name - the String name of the Item
|
144
|
+
# value - the String value of the Item
|
145
|
+
#
|
146
|
+
# Example
|
147
|
+
#
|
148
|
+
# Commands.add_item("snippets","sig","- @holman")
|
149
|
+
#
|
150
|
+
# Returns the newly created Item.
|
151
|
+
def add_item(list,name,value)
|
152
|
+
list = storage.lists.find{|storage_list| storage_list.name == list}
|
153
|
+
list.add_item(Item.new(name,value))
|
154
|
+
output "Boom! \"#{name}\" in \"#{list.name}\" is \"#{value}\". Got it."
|
155
|
+
save!
|
156
|
+
end
|
157
|
+
|
158
|
+
# Public: remove a named Item.
|
159
|
+
#
|
160
|
+
# name - the String name of the Item.
|
161
|
+
#
|
162
|
+
# Example
|
163
|
+
#
|
164
|
+
# Commands.delete_item("an-item-name")
|
165
|
+
#
|
166
|
+
# Returns nothing.
|
167
|
+
def item_delete(name)
|
168
|
+
storage.lists = storage.lists.each do |list|
|
169
|
+
list.items.reject! { |item| item.name == name }
|
170
|
+
end
|
171
|
+
output "Boom! \"#{name}\" is gone forever."
|
172
|
+
save!
|
173
|
+
end
|
174
|
+
|
175
|
+
# Public: search for an Item in all lists by name. Drops the
|
176
|
+
# corresponding entry into your clipboard.
|
177
|
+
#
|
178
|
+
# name - the String term to search for in all Item names
|
179
|
+
#
|
180
|
+
# Returns the matching Item.
|
181
|
+
def search_items(name)
|
182
|
+
item = storage.items.detect do |item|
|
183
|
+
item.name == name
|
184
|
+
end
|
185
|
+
|
186
|
+
output Clipboard.copy(item)
|
187
|
+
end
|
188
|
+
|
189
|
+
# Public: search for an Item in a particular list by name. Drops the
|
190
|
+
# corresponding entry into your clipboard.
|
191
|
+
#
|
192
|
+
# list_name - the String name of the List in which to scope the search
|
193
|
+
# item_name - the String term to search for in all Item names
|
194
|
+
#
|
195
|
+
# Returns the matching Item.
|
196
|
+
def search_list_for_item(list_name, item_name)
|
197
|
+
list = storage.lists.first { |list| list.name == list_name }
|
198
|
+
item = list.items.first { |item| item.name == item_name }
|
199
|
+
|
200
|
+
output Clipboard.copy(item)
|
201
|
+
end
|
202
|
+
|
203
|
+
# Public: save in-memory data to disk.
|
204
|
+
#
|
205
|
+
# Returns whether or not data was saved.
|
206
|
+
def save!
|
207
|
+
storage.save!
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
data/lib/boom/item.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# The representation of the base unit in boom. An Item contains just a name and
|
2
|
+
# a value. It doesn't know its parent relationship explicitly; the parent List
|
3
|
+
# object instead knows which Items it contains.
|
4
|
+
#
|
5
|
+
module Boom
|
6
|
+
class Item
|
7
|
+
|
8
|
+
# Public: the String name of the Item
|
9
|
+
attr_accessor :name
|
10
|
+
|
11
|
+
# Public: the String value of the Item
|
12
|
+
attr_accessor :value
|
13
|
+
|
14
|
+
# Public: creates a new Item object.
|
15
|
+
#
|
16
|
+
# name - the String name of the Item
|
17
|
+
# value - the String value of the Item
|
18
|
+
#
|
19
|
+
# Examples
|
20
|
+
#
|
21
|
+
# Item.new("github", "http://github.com")
|
22
|
+
#
|
23
|
+
# Returns the newly initialized Item.
|
24
|
+
def initialize(name,value)
|
25
|
+
@name = name
|
26
|
+
@value = value
|
27
|
+
end
|
28
|
+
|
29
|
+
# Public: creates a Hash for this Item.
|
30
|
+
#
|
31
|
+
# Returns a Hash of its data.
|
32
|
+
def to_hash
|
33
|
+
{ @name => @value }
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
data/lib/boom/list.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# The List contains many Items. They exist as buckets in which to categorize
|
2
|
+
# individual Items. The relationship is maintained in a simple array on the
|
3
|
+
# List-level.
|
4
|
+
#
|
5
|
+
module Boom class List
|
6
|
+
|
7
|
+
# Public: creates a new List instance in-memory.
|
8
|
+
#
|
9
|
+
# name - The name of the List. Fails if already used.
|
10
|
+
#
|
11
|
+
# Returns the unpersisted List instance.
|
12
|
+
def initialize(name)
|
13
|
+
@items = []
|
14
|
+
@name = name
|
15
|
+
end
|
16
|
+
|
17
|
+
# Public: lets you access the array of items contained within this List.
|
18
|
+
#
|
19
|
+
# Returns an Array of Items.
|
20
|
+
attr_accessor :items
|
21
|
+
|
22
|
+
# Public: the name of the List.
|
23
|
+
#
|
24
|
+
# Returns the String name.
|
25
|
+
attr_accessor :name
|
26
|
+
|
27
|
+
# Public: associates an Item with this List.
|
28
|
+
#
|
29
|
+
# item - the Item object to associate with this List.
|
30
|
+
#
|
31
|
+
# Returns the current set of items.
|
32
|
+
def add_item(item)
|
33
|
+
@items << item
|
34
|
+
end
|
35
|
+
|
36
|
+
# Public: a Hash representation of this List.
|
37
|
+
#
|
38
|
+
# Returns a Hash of its own data and its child Items.
|
39
|
+
def to_hash
|
40
|
+
{ name => items.collect(&:to_hash) }
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
data/lib/boom/storage.rb
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
# Storage is the middleman between changes the client makes in-memory and how
|
2
|
+
# it's actually persisted to disk (and vice-versa). There are also a few
|
3
|
+
# convenience methods to run searches and operations on the in-memory hash.
|
4
|
+
#
|
5
|
+
module Boom
|
6
|
+
class Storage
|
7
|
+
|
8
|
+
JSON_FILE = "#{ENV['HOME']}/.boom"
|
9
|
+
|
10
|
+
# Public: the path to the JSON file used by boom.
|
11
|
+
#
|
12
|
+
# Returns the String path of boom's JSON representation.
|
13
|
+
def json_file
|
14
|
+
JSON_FILE
|
15
|
+
end
|
16
|
+
|
17
|
+
# Public: initializes a Storage instance by loading in your persisted data.
|
18
|
+
#
|
19
|
+
# Returns the Storage instance.
|
20
|
+
def initialize
|
21
|
+
@lists = []
|
22
|
+
explode_json(json_file)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Public: the in-memory collection of all Lists attached to this Storage
|
26
|
+
# instance.
|
27
|
+
#
|
28
|
+
# lists - an Array of individual List items
|
29
|
+
#
|
30
|
+
# Returns nothing.
|
31
|
+
attr_writer :lists
|
32
|
+
|
33
|
+
# Public: the list of Lists in your JSON data, sorted by number of items
|
34
|
+
# descending.
|
35
|
+
#
|
36
|
+
# Returns an Array of List objects.
|
37
|
+
def lists
|
38
|
+
@lists.sort_by { |list| -list.items.size }
|
39
|
+
end
|
40
|
+
|
41
|
+
# Public: tests whether a named List exists.
|
42
|
+
#
|
43
|
+
# name - the String name of a List
|
44
|
+
#
|
45
|
+
# Returns true if found, false if not.
|
46
|
+
def list_exists?(name)
|
47
|
+
@lists.detect { |list| list.name == name }
|
48
|
+
end
|
49
|
+
|
50
|
+
# Public: all Items in storage.
|
51
|
+
#
|
52
|
+
# Returns an Array of all Items.
|
53
|
+
def items
|
54
|
+
@lists.collect(&:items).flatten
|
55
|
+
end
|
56
|
+
|
57
|
+
# Public: tests whether a named Item exists.
|
58
|
+
#
|
59
|
+
# name - the String name of an Item
|
60
|
+
#
|
61
|
+
# Returns true if found, false if not.
|
62
|
+
def item_exists?(name)
|
63
|
+
items.detect { |item| item.name == name }
|
64
|
+
end
|
65
|
+
|
66
|
+
# Public: persists your in-memory objects to disk in JSON format.
|
67
|
+
#
|
68
|
+
# Returns true if successful, false if unsuccessful.
|
69
|
+
def save!
|
70
|
+
File.open(json_file, 'w') {|f| f.write(to_json) }
|
71
|
+
end
|
72
|
+
|
73
|
+
# Public: the JSON representation of the current List and Item assortment
|
74
|
+
# attached to the Storage instance.
|
75
|
+
#
|
76
|
+
# Returns a String JSON representation of its Lists and their Items.
|
77
|
+
def to_json
|
78
|
+
Yajl::Encoder.encode(to_hash)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Public: creates a Hash of the representation of the in-memory data
|
82
|
+
# structure. This percolates down to Items by calling to_hash on the List,
|
83
|
+
# which in tern calls to_hash on individual Items.
|
84
|
+
#
|
85
|
+
# Returns a Hash of the entire data set.
|
86
|
+
def to_hash
|
87
|
+
{ :lists => lists.collect(&:to_hash) }
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
# INTERNAL METHODS ##########################################################
|
92
|
+
|
93
|
+
# Take a JSON representation of data and explode it out into the consituent
|
94
|
+
# Lists and Items for the given Storage instance.
|
95
|
+
#
|
96
|
+
# Returns nothing.
|
97
|
+
def explode_json(json)
|
98
|
+
bootstrap_json unless File.exist?(json)
|
99
|
+
|
100
|
+
storage = Yajl::Parser.new.parse(File.new(json, 'r'))
|
101
|
+
|
102
|
+
storage['lists'].each do |lists|
|
103
|
+
lists.each do |list_name, items|
|
104
|
+
@lists << list = List.new(list_name)
|
105
|
+
|
106
|
+
items.each do |item|
|
107
|
+
item.each do |name,value|
|
108
|
+
list.add_item(Item.new(name,value))
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Takes care of bootstrapping the JSON file, both in terms of creating the
|
116
|
+
# file and in terms of creating a skeleton JSON schema.
|
117
|
+
#
|
118
|
+
# Return true if successfully saved.
|
119
|
+
def bootstrap_json
|
120
|
+
FileUtils.touch json_file
|
121
|
+
save!
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
end
|
data/lib/boom.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
begin
|
2
|
+
require 'rubygems'
|
3
|
+
rescue LoadError
|
4
|
+
end
|
5
|
+
|
6
|
+
require 'fileutils'
|
7
|
+
require 'yajl'
|
8
|
+
|
9
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
10
|
+
|
11
|
+
require 'boom/clipboard'
|
12
|
+
require 'boom/command'
|
13
|
+
require 'boom/item'
|
14
|
+
require 'boom/list'
|
15
|
+
require 'boom/storage'
|
16
|
+
|
17
|
+
module Boom
|
18
|
+
VERSION = '0.0.1'
|
19
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
{"lists":[{"urls":[{"github":"https://github.com"},{"blog":"http://zachholman.com"}]}]}
|
data/test/helper.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'rubygems'
|
5
|
+
require 'redgreen'
|
6
|
+
rescue LoadError
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'mocha'
|
10
|
+
|
11
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
12
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
13
|
+
|
14
|
+
require 'boom'
|
15
|
+
|
16
|
+
# test/spec/mini 3
|
17
|
+
# http://gist.github.com/25455
|
18
|
+
# chris@ozmm.org
|
19
|
+
# file:lib/test/spec/mini.rb
|
20
|
+
def context(*args, &block)
|
21
|
+
return super unless (name = args.first) && block
|
22
|
+
require 'test/unit'
|
23
|
+
klass = Class.new(defined?(ActiveSupport::TestCase) ? ActiveSupport::TestCase : Test::Unit::TestCase) do
|
24
|
+
def self.test(name, &block)
|
25
|
+
define_method("test_#{name.gsub(/\W/,'_')}", &block) if block
|
26
|
+
end
|
27
|
+
def self.xtest(*args) end
|
28
|
+
def self.setup(&block) define_method(:setup, &block) end
|
29
|
+
def self.teardown(&block) define_method(:teardown, &block) end
|
30
|
+
end
|
31
|
+
(class << klass; self end).send(:define_method, :name) { name.gsub(/\W/,'_') }
|
32
|
+
klass.class_eval &block
|
33
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
# Intercept STDOUT and collect it
|
4
|
+
class Boom::Command
|
5
|
+
|
6
|
+
def self.capture_output
|
7
|
+
@output = ''
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.captured_output
|
11
|
+
@output
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.output(s)
|
15
|
+
@output << s
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.save!
|
19
|
+
# no-op
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
class TestCommand < Test::Unit::TestCase
|
25
|
+
|
26
|
+
def setup
|
27
|
+
Boom::Storage.any_instance.stubs(:json_file).
|
28
|
+
returns('test/examples/urls.json')
|
29
|
+
@storage = Boom::Storage.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def command(cmd)
|
33
|
+
cmd = cmd.split(' ') if cmd
|
34
|
+
Boom::Command.capture_output
|
35
|
+
Boom::Command.execute(@storage,*cmd)
|
36
|
+
Boom::Command.captured_output
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_overview
|
40
|
+
assert_equal ' urls (2)', command(nil)
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_list_detail
|
44
|
+
assert_match /github/, command('urls')
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_list_all
|
48
|
+
cmd = command('all')
|
49
|
+
assert_match /urls/, cmd
|
50
|
+
assert_match /github/, cmd
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_list_creation
|
54
|
+
assert_match /a new list called "newlist"/, command('newlist')
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_item_access
|
58
|
+
assert_match /copied https:\/\/github\.com to your clipboard/,
|
59
|
+
command('github')
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_item_access_scoped_by_list
|
63
|
+
assert_match /copied https:\/\/github\.com to your clipboard/,
|
64
|
+
command('urls github')
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_item_creation
|
68
|
+
assert_match /"twitter" in "urls"/,
|
69
|
+
command('urls twitter http://twitter.com/holman')
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_list_deletion_no
|
73
|
+
STDIN.stubs(:gets).returns('n')
|
74
|
+
assert_match /Just kidding then/, command('urls delete')
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_list_deletion_yes
|
78
|
+
STDIN.stubs(:gets).returns('y')
|
79
|
+
assert_match /Deleted all your urls/, command('urls delete')
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_item_deletion
|
83
|
+
assert_match /"github" is gone forever/, command('urls github delete')
|
84
|
+
end
|
85
|
+
end
|
data/test/test_item.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestItem < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@item = Boom::Item.new('github','https://github.com')
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_name
|
10
|
+
assert_equal 'github', @item.name
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_value
|
14
|
+
assert_equal 'https://github.com', @item.value
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_to_hash
|
18
|
+
assert_equal 1, @item.to_hash.size
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
data/test/test_list.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestList < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@list = Boom::List.new('urls')
|
7
|
+
@item = Boom::Item.new('github','https://github.com')
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_name
|
11
|
+
assert_equal 'urls', @list.name
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_add_items
|
15
|
+
assert_equal 0, @list.items.size
|
16
|
+
@list.add_item(@item)
|
17
|
+
assert_equal 1, @list.items.size
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_to_hash
|
21
|
+
assert_equal 0, @list.to_hash[@list.name].size
|
22
|
+
@list.add_item(@item)
|
23
|
+
assert_equal 1, @list.to_hash[@list.name].size
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: boom
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Zach Holman
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-11-24 00:00:00 -08:00
|
19
|
+
default_executable: boom
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: yajl-ruby
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 19
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
- 7
|
33
|
+
- 8
|
34
|
+
version: 0.7.8
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: mocha
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 41
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
- 9
|
49
|
+
- 9
|
50
|
+
version: 0.9.9
|
51
|
+
type: :development
|
52
|
+
version_requirements: *id002
|
53
|
+
description: |-
|
54
|
+
God it's about every day where I think to myself, gadzooks,
|
55
|
+
I keep typing *REPETITIVE_BORING_TASK* over and over. Wouldn't it be great if
|
56
|
+
I had something like boom to store all these commonly-used text snippets for
|
57
|
+
me? Then I realized that was a worthless idea since boom hadn't been created
|
58
|
+
yet and I had no idea what that statement meant. At some point I found the
|
59
|
+
code for boom in a dark alleyway and released it under my own name because I
|
60
|
+
wanted to look smart.
|
61
|
+
email: github.com@zachholman.com
|
62
|
+
executables:
|
63
|
+
- boom
|
64
|
+
extensions: []
|
65
|
+
|
66
|
+
extra_rdoc_files:
|
67
|
+
- Readme.markdown
|
68
|
+
- License
|
69
|
+
files:
|
70
|
+
- License
|
71
|
+
- Rakefile
|
72
|
+
- Readme.markdown
|
73
|
+
- bin/boom
|
74
|
+
- boom.gemspec
|
75
|
+
- lib/boom.rb
|
76
|
+
- lib/boom/clipboard.rb
|
77
|
+
- lib/boom/command.rb
|
78
|
+
- lib/boom/item.rb
|
79
|
+
- lib/boom/list.rb
|
80
|
+
- lib/boom/storage.rb
|
81
|
+
- test/examples/urls.json
|
82
|
+
- test/helper.rb
|
83
|
+
- test/test_command.rb
|
84
|
+
- test/test_item.rb
|
85
|
+
- test/test_list.rb
|
86
|
+
has_rdoc: true
|
87
|
+
homepage: https://github.com/holman/boom
|
88
|
+
licenses: []
|
89
|
+
|
90
|
+
post_install_message:
|
91
|
+
rdoc_options:
|
92
|
+
- --charset=UTF-8
|
93
|
+
require_paths:
|
94
|
+
- lib
|
95
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
96
|
+
none: false
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
hash: 3
|
101
|
+
segments:
|
102
|
+
- 0
|
103
|
+
version: "0"
|
104
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
hash: 3
|
110
|
+
segments:
|
111
|
+
- 0
|
112
|
+
version: "0"
|
113
|
+
requirements: []
|
114
|
+
|
115
|
+
rubyforge_project: boom
|
116
|
+
rubygems_version: 1.3.7
|
117
|
+
signing_key:
|
118
|
+
specification_version: 2
|
119
|
+
summary: boom lets you access text snippets over your command line.
|
120
|
+
test_files:
|
121
|
+
- test/test_command.rb
|
122
|
+
- test/test_item.rb
|
123
|
+
- test/test_list.rb
|