sbraford-go 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/History.txt +6 -0
- data/Manifest.txt +20 -0
- data/README.txt +64 -0
- data/Rakefile +16 -0
- data/bin/go +8 -0
- data/lib/go.rb +31 -0
- data/lib/go/application.rb +281 -0
- data/lib/go/extensions.rb +29 -0
- data/lib/go/installer.rb +90 -0
- data/lib/go/plugin.rb +49 -0
- data/lib/go/recursive_http_fetcher.rb +72 -0
- data/lib/plugins/hello.rb +9 -0
- data/test/fixtures/plugins/hello.rb +8 -0
- data/test/fixtures/plugins/palace/party_palace.rb +7 -0
- data/test/fixtures/plugins/panda.rb +13 -0
- data/test/fixtures/plugins/panda_party.rb +40 -0
- data/test/fixtures/plugins/todo.rb +18 -0
- data/test/fixtures/reserved/commands.rb +9 -0
- data/test/test_go.rb +48 -0
- metadata +98 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
Rakefile
|
5
|
+
bin/go
|
6
|
+
lib/go.rb
|
7
|
+
go.gemspec
|
8
|
+
lib/go/application.rb
|
9
|
+
lib/go/extensions.rb
|
10
|
+
lib/go/installer.rb
|
11
|
+
lib/go/plugin.rb
|
12
|
+
lib/go/recursive_http_fetcher.rb
|
13
|
+
lib/plugins/hello.rb
|
14
|
+
test/fixtures/plugins/hello.rb
|
15
|
+
test/fixtures/plugins/palace/party_palace.rb
|
16
|
+
test/fixtures/plugins/panda.rb
|
17
|
+
test/fixtures/plugins/panda_party.rb
|
18
|
+
test/fixtures/plugins/todo.rb
|
19
|
+
test/fixtures/reserved/commands.rb
|
20
|
+
test/test_go.rb
|
data/README.txt
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
= go
|
2
|
+
|
3
|
+
* Extensible command-line tool for ninjas, pirates & pandas. (http://github.com/sbraford/go/tree)
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Provides a command-line tool "go" which can then be extended by adding plugins that others
|
8
|
+
have created, or that you have made yourself.
|
9
|
+
|
10
|
+
Usage:
|
11
|
+
|
12
|
+
go <command> <options>
|
13
|
+
|
14
|
+
Example:
|
15
|
+
|
16
|
+
go twitter "tinkering with the Go gem"
|
17
|
+
go locate files containing "panda party"
|
18
|
+
go order dominos --size=large
|
19
|
+
|
20
|
+
== FEATURES/PROBLEMS:
|
21
|
+
|
22
|
+
* Go is still in an early alpha development stage
|
23
|
+
|
24
|
+
== TODO
|
25
|
+
|
26
|
+
* Figure out how to easily create/distribute go commands (plugins)
|
27
|
+
* Figure out the best way to share a go config across multiple machines (via git somehow, perhaps)
|
28
|
+
|
29
|
+
== REQUIREMENTS:
|
30
|
+
|
31
|
+
* none other than the gem dependencies for the go gem
|
32
|
+
|
33
|
+
== INSTALL:
|
34
|
+
|
35
|
+
First add GitHub as a source if you haven't already:
|
36
|
+
* gem sources -a http://gems.github.com
|
37
|
+
|
38
|
+
Install the gem via the GitHub gem repository:
|
39
|
+
* sudo gem install sbraford-go
|
40
|
+
|
41
|
+
== LICENSE:
|
42
|
+
|
43
|
+
(The MIT License)
|
44
|
+
|
45
|
+
Copyright (c) 2008 Shanti A. Braford
|
46
|
+
|
47
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
48
|
+
a copy of this software and associated documentation files (the
|
49
|
+
'Software'), to deal in the Software without restriction, including
|
50
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
51
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
52
|
+
permit persons to whom the Software is furnished to do so, subject to
|
53
|
+
the following conditions:
|
54
|
+
|
55
|
+
The above copyright notice and this permission notice shall be
|
56
|
+
included in all copies or substantial portions of the Software.
|
57
|
+
|
58
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
59
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
60
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
61
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
62
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
63
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
64
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
require './lib/go.rb'
|
6
|
+
|
7
|
+
Hoe.new('go', Go::VERSION) do |p|
|
8
|
+
# p.rubyforge_name = 'gox' # if different than lowercase project name
|
9
|
+
p.summary = 'Extensible command-line tool for ninjas, pirates & pandas'
|
10
|
+
p.url = 'http://github.com/sbraford/go/tree'
|
11
|
+
p.developer('Shanti A. Braford', 'shantibraford@gmail.com')
|
12
|
+
p.remote_rdoc_dir = ''
|
13
|
+
p.extra_deps << ['cooloptions', '>= 1.1.1']
|
14
|
+
end
|
15
|
+
|
16
|
+
# vim: syntax=Ruby
|
data/bin/go
ADDED
data/lib/go.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + "/go/")
|
2
|
+
|
3
|
+
# System gems
|
4
|
+
require 'rubygems'
|
5
|
+
require 'cooloptions'
|
6
|
+
|
7
|
+
# Go Core classes
|
8
|
+
require 'application'
|
9
|
+
require 'plugin'
|
10
|
+
require 'installer'
|
11
|
+
require 'extensions'
|
12
|
+
require 'recursive_http_fetcher'
|
13
|
+
|
14
|
+
# Ruby libs
|
15
|
+
require 'open-uri'
|
16
|
+
require 'uri'
|
17
|
+
|
18
|
+
module Go
|
19
|
+
VERSION = '0.1' unless defined?(VERSION)
|
20
|
+
|
21
|
+
# Current Rake Application
|
22
|
+
def self.application
|
23
|
+
@application ||= Go::Application.new(ARGV)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Set the current Rake application object.
|
27
|
+
def self.application=(app)
|
28
|
+
@application = app
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
@@ -0,0 +1,281 @@
|
|
1
|
+
module Go
|
2
|
+
|
3
|
+
class Application
|
4
|
+
# class << self
|
5
|
+
# end
|
6
|
+
attr_reader :args, :plugin_commands, :sorted_plugins, :options
|
7
|
+
attr_accessor :plugin_path
|
8
|
+
|
9
|
+
ReservedCommands = %w(commands edit generate help list setup install plugin)
|
10
|
+
|
11
|
+
# Initializes a new Go::Application object - should always be called via a Singleton, e.g.
|
12
|
+
# Go.application.run
|
13
|
+
def initialize(args = ARGV)
|
14
|
+
@args = args
|
15
|
+
@options = OpenStruct.new
|
16
|
+
end
|
17
|
+
|
18
|
+
# Main Go Application entry point.
|
19
|
+
# * Parses command-line options and calls methods accordingly
|
20
|
+
def run
|
21
|
+
@options = CoolOptions.parse!("[options] (commands|edit|generate|help|list|setup)") do |o|
|
22
|
+
o.desc "Go - the Extensible command-line tool for ninjas, pirates & pandas"
|
23
|
+
o.on "(v)erbose", "Turn on verbose mode", false
|
24
|
+
o.on "(f)orce", "Force (the action)", false
|
25
|
+
o.after do |r|
|
26
|
+
r.config_path = self.default_config_path
|
27
|
+
if !File.exist?(r.config_path)
|
28
|
+
setup
|
29
|
+
exit
|
30
|
+
end
|
31
|
+
r.full_command = @args.join(' ')
|
32
|
+
r.params = @args.slice(1, @args.size)
|
33
|
+
r.action = @args.empty? ? 'commands' : @args[0]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
case @options.action
|
38
|
+
when 'commands', 'list'
|
39
|
+
list_commands
|
40
|
+
when 'install'
|
41
|
+
install_plugin
|
42
|
+
when 'plugin'
|
43
|
+
case @options.params.first
|
44
|
+
when 'list'
|
45
|
+
list_commands
|
46
|
+
when 'install'
|
47
|
+
@option.params.shift
|
48
|
+
install_plugin
|
49
|
+
else
|
50
|
+
puts "Invalid command. Call go without any params for a list of valid commands."
|
51
|
+
list_commands
|
52
|
+
end
|
53
|
+
when 'generate'
|
54
|
+
generate
|
55
|
+
when 'setup'
|
56
|
+
setup
|
57
|
+
when 'edit'
|
58
|
+
edit
|
59
|
+
when 'help'
|
60
|
+
help
|
61
|
+
else
|
62
|
+
run_plugin_command
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
def install_plugin
|
68
|
+
if @options.params.empty?
|
69
|
+
raise "Please specify a URL from which to install the plugin, e.g.: go install http://gist.github.com/14933"
|
70
|
+
end
|
71
|
+
installer = Installer.new(@options.params.first, self)
|
72
|
+
installer.install
|
73
|
+
end
|
74
|
+
|
75
|
+
def run_plugin_command
|
76
|
+
init_plugins
|
77
|
+
plugin_pair = nil
|
78
|
+
# They may have passed "panda party palace", "panda party", or "panda"
|
79
|
+
# We want to give the plugin with the most words -> highest priority
|
80
|
+
@args.size.downto(1) do |i|
|
81
|
+
cmd = @args.slice(0, i).join(' ')
|
82
|
+
plugin_pair = @sorted_plugins.detect {|pair| pair.first == cmd}
|
83
|
+
break if plugin_pair
|
84
|
+
end
|
85
|
+
if plugin_pair
|
86
|
+
klass = plugin_pair[1]
|
87
|
+
plugin = klass.new(@options)
|
88
|
+
plugin.run
|
89
|
+
else
|
90
|
+
puts "No command found for action: #{@options.action}" unless test_env?
|
91
|
+
end
|
92
|
+
plugin_pair
|
93
|
+
end
|
94
|
+
|
95
|
+
def list_commands
|
96
|
+
justify = 40
|
97
|
+
init_plugins
|
98
|
+
puts "\n Builtin Go commands\n\n"
|
99
|
+
show_command('install', "install <plugin_url>")
|
100
|
+
show_command('list', "list")
|
101
|
+
show_command('setup', "setup")
|
102
|
+
puts "\n Plugin commands\n "
|
103
|
+
@sorted_plugins.each do |pair|
|
104
|
+
command, klass = *pair
|
105
|
+
usage = klass.usage_raw ? klass.usage_raw : command
|
106
|
+
show_command(command, usage)
|
107
|
+
end
|
108
|
+
puts "\n"
|
109
|
+
end
|
110
|
+
|
111
|
+
def generate
|
112
|
+
puts "TODO: generate a skeleton plugin via this method"
|
113
|
+
end
|
114
|
+
|
115
|
+
# Creates the default config file in: ~/.goconfig
|
116
|
+
def setup
|
117
|
+
File.open(self.default_config_path, 'w') do |f|
|
118
|
+
f.puts self.sample_config
|
119
|
+
end
|
120
|
+
|
121
|
+
FileUtils.mkdir_p(self.go_dir) if !File.exist?(self.go_dir)
|
122
|
+
|
123
|
+
install_default_plugins
|
124
|
+
|
125
|
+
puts setup_txt
|
126
|
+
end
|
127
|
+
|
128
|
+
# Edit the config file (using the system editor)
|
129
|
+
def edit
|
130
|
+
editor = ENV['editor'] || 'vi'
|
131
|
+
system "#{editor} #{self.default_config_path}"
|
132
|
+
end
|
133
|
+
|
134
|
+
# Displays the help text
|
135
|
+
def help
|
136
|
+
puts help_txt
|
137
|
+
end
|
138
|
+
|
139
|
+
def init_plugins!
|
140
|
+
init_plugins
|
141
|
+
end
|
142
|
+
|
143
|
+
def clear_plugins!
|
144
|
+
@plugin_commands, @sorted_plugins = nil, nil
|
145
|
+
end
|
146
|
+
|
147
|
+
def go_dir
|
148
|
+
@plugin_path || File.join(ENV['HOME'], '.go')
|
149
|
+
end
|
150
|
+
|
151
|
+
protected
|
152
|
+
|
153
|
+
def show_command(command_name, usage, justify = 40)
|
154
|
+
puts " #{command_name.ljust(justify)}\t# go #{usage}"
|
155
|
+
end
|
156
|
+
|
157
|
+
def init_plugins
|
158
|
+
contains = Dir.new(go_dir).entries
|
159
|
+
contains.delete("."); contains.delete("..")
|
160
|
+
contains.each do |entry|
|
161
|
+
path = File.join(go_dir, entry)
|
162
|
+
if File.directory?(path)
|
163
|
+
# Require all .rb files in this dir
|
164
|
+
subfiles = Dir.new(path).entries
|
165
|
+
subfiles.delete("."); subfiles.delete("..")
|
166
|
+
subfiles.each do |sub|
|
167
|
+
sub_path = File.join(path, sub)
|
168
|
+
if !File.directory?(sub_path) && File.extname(sub_path) == '.rb'
|
169
|
+
require sub_path
|
170
|
+
end
|
171
|
+
end
|
172
|
+
elsif File.extname(path) == '.rb'
|
173
|
+
# Require it if it's a ruby file
|
174
|
+
require path
|
175
|
+
end
|
176
|
+
end
|
177
|
+
init_plugin_commands
|
178
|
+
end
|
179
|
+
|
180
|
+
def init_plugin_commands
|
181
|
+
plugins = Go::Plugin.subclasses
|
182
|
+
puts "\n plugin classes = #{plugins.inspect}" if @options.verbose
|
183
|
+
@plugin_commands = {}
|
184
|
+
plugins.each do |p|
|
185
|
+
command = p.command.to_s
|
186
|
+
puts " Plugin: #{p} :: #{command}" if @options.verbose
|
187
|
+
if ReservedCommands.include?(command)
|
188
|
+
puts "WARNING: #{p} attempted to init plugin with reserved command '#{command}' (plugin *not* loaded)" unless test_env?
|
189
|
+
else
|
190
|
+
@plugin_commands[command] = p
|
191
|
+
end
|
192
|
+
end
|
193
|
+
@sorted_plugins = sort_plugins(@plugin_commands)
|
194
|
+
end
|
195
|
+
|
196
|
+
def sort_plugins(plugin_commands)
|
197
|
+
# Now sort 'em - alphabetically first, thgen with higher priority (more words in the command string)
|
198
|
+
# plugins coming next.
|
199
|
+
sorted_plugins = []
|
200
|
+
plugin_commands.each do |command, klass|
|
201
|
+
sorted_plugins << [command, klass]
|
202
|
+
end
|
203
|
+
sorted_plugins.sort! do |x,y|
|
204
|
+
cmd_x, cmd_y = x.first, y.first
|
205
|
+
cmd_x_size = cmd_x.split(' ').size
|
206
|
+
cmd_y_size = cmd_y.split(' ').size
|
207
|
+
if (cmd_x_size == cmd_y_size) && cmd_x_size == 1
|
208
|
+
# Single word command (both), simply sort alphabetically
|
209
|
+
(cmd_x <=> cmd_y)
|
210
|
+
else
|
211
|
+
# One of the commands has multiple words, install the mutli-word command ahead of the other one
|
212
|
+
# if there is a sub-match
|
213
|
+
sub_cmd_x = cmd_x.split(' ')[0]
|
214
|
+
sub_cmd_y = cmd_y.split(' ')[0]
|
215
|
+
if sub_cmd_x == sub_cmd_y
|
216
|
+
# e.g. 'panda' and 'panda party'
|
217
|
+
cmd_y_size <=> cmd_x_size
|
218
|
+
else
|
219
|
+
# Otherwise sort alphabetically
|
220
|
+
cmd_x <=> cmd_y
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
sorted_plugins
|
225
|
+
end
|
226
|
+
|
227
|
+
def default_config_path
|
228
|
+
File.join(ENV['HOME'], '.goconfig')
|
229
|
+
end
|
230
|
+
|
231
|
+
def install_default_plugins
|
232
|
+
gem_plugin_path = File.join(File.dirname(__FILE__), '..', 'plugins')
|
233
|
+
cmd = "cp -R #{gem_plugin_path}/* #{go_dir}"
|
234
|
+
system(cmd)
|
235
|
+
end
|
236
|
+
|
237
|
+
def help_txt
|
238
|
+
txt = <<EOF
|
239
|
+
To setup the initial config file, execute:
|
240
|
+
|
241
|
+
go setup
|
242
|
+
|
243
|
+
Edit go config using your system edtior:
|
244
|
+
|
245
|
+
go edit
|
246
|
+
|
247
|
+
EOF
|
248
|
+
end
|
249
|
+
|
250
|
+
def setup_txt
|
251
|
+
txt = <<EOF
|
252
|
+
|
253
|
+
Default config file has been created in:
|
254
|
+
|
255
|
+
~/.goconfig
|
256
|
+
|
257
|
+
This will be the primary entry-point for editing any config options required by Go plugins.
|
258
|
+
|
259
|
+
Default plugins have been placed in the go plugin directory:
|
260
|
+
|
261
|
+
~/.go
|
262
|
+
|
263
|
+
To see a list of available go commands, run go without any arguments:
|
264
|
+
|
265
|
+
go
|
266
|
+
|
267
|
+
EOF
|
268
|
+
end
|
269
|
+
|
270
|
+
def sample_config
|
271
|
+
"name: Change Me"
|
272
|
+
end
|
273
|
+
|
274
|
+
# Suppress noise when testing
|
275
|
+
def test_env?
|
276
|
+
defined?(Test::Unit::TestCase)
|
277
|
+
end
|
278
|
+
|
279
|
+
end
|
280
|
+
|
281
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class Time
|
2
|
+
# Prints the date like: Monday, June 10 of 2006 at 10:57AM
|
3
|
+
def pretty_print
|
4
|
+
return self.strftime("%A, %B %d of %Y at %I:%M %p")
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class String
|
9
|
+
def to_underscore #(klass)
|
10
|
+
self.dup.gsub(/::/, '/').
|
11
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
12
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
13
|
+
tr("-", "_").
|
14
|
+
downcase
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
class Module
|
20
|
+
def subclasses
|
21
|
+
classes = []
|
22
|
+
ObjectSpace.each_object do |klass|
|
23
|
+
next unless Module === klass
|
24
|
+
classes << klass if self > klass
|
25
|
+
end
|
26
|
+
classes
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
data/lib/go/installer.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
module Go
|
2
|
+
|
3
|
+
# Base class for Go plugins
|
4
|
+
class Installer
|
5
|
+
|
6
|
+
def initialize(url, app)
|
7
|
+
@url = raw_url(url)
|
8
|
+
@app = app
|
9
|
+
end
|
10
|
+
|
11
|
+
def install
|
12
|
+
begin
|
13
|
+
content = open(@url).read
|
14
|
+
rescue
|
15
|
+
raise "Could not read via open-uri from remote URL: #{@url}"
|
16
|
+
end
|
17
|
+
klass_name = nil
|
18
|
+
# uglier than regexes? rather this than "2 problrems"-style regex
|
19
|
+
klass_name = content.split('class ')[1].split(' ')[0] rescue nil
|
20
|
+
if klass_name
|
21
|
+
install_single(klass_name, content)
|
22
|
+
else
|
23
|
+
install_dir
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
def install_single(klass_name, content)
|
30
|
+
puts "Installing plugin: #{klass_name}"
|
31
|
+
filename = "#{klass_name.to_underscore}.rb"
|
32
|
+
filepath = File.join(@app.go_dir, filename)
|
33
|
+
if File.exist?(filepath) && !@app.options.force
|
34
|
+
puts "File already exists: #{filepath}"
|
35
|
+
puts "Epic FAIL: Cannot install new plugin #{klass_name} from url: #{@url}"; return
|
36
|
+
else klass_name
|
37
|
+
File.open(filepath, 'w') { |f| f.puts(content) }
|
38
|
+
end
|
39
|
+
if File.exist?(filepath)
|
40
|
+
puts "#{klass_name} Plugin installed to: #{filepath}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def install_dir
|
45
|
+
guess_name(@url)
|
46
|
+
puts "Installing plugin: #{@name}"
|
47
|
+
# CASE: directory tree to install
|
48
|
+
plugin_path = File.join(@app.go_dir, @name)
|
49
|
+
FileUtils.mkdir_p(plugin_path)
|
50
|
+
Dir.chdir(plugin_path) do
|
51
|
+
puts "fetching from '#{@url}'"
|
52
|
+
fetcher = RecursiveHTTPFetcher.new(@url, -1)
|
53
|
+
#fetcher.quiet = true unless @app.options.verbose
|
54
|
+
fetcher.fetch
|
55
|
+
end
|
56
|
+
puts "#{@name} Plugin installed to: #{plugin_path}"
|
57
|
+
end
|
58
|
+
|
59
|
+
# This method's code (c) Ryan Tomayko from the railties gem
|
60
|
+
def guess_name(url)
|
61
|
+
@name = File.basename(url)
|
62
|
+
if @name == 'trunk' || @name.empty?
|
63
|
+
@name = File.basename(File.dirname(url))
|
64
|
+
end
|
65
|
+
@name.gsub!(/\.git$/, '') if @name =~ /\.git$/
|
66
|
+
end
|
67
|
+
|
68
|
+
def raw_url(url)
|
69
|
+
uri = URI.parse(url)
|
70
|
+
case uri.host
|
71
|
+
when 'gist.github.com'
|
72
|
+
# We want the raw URL unless that was passed, e.g. :
|
73
|
+
# http://gist.github.com/raw/14933/8b960e3a0474ed8e72df2516dfd6066a73d75a1c
|
74
|
+
return url if url.include?('/raw/')
|
75
|
+
content = open(url).read rescue "Could not read: #{url}"
|
76
|
+
begin
|
77
|
+
"http://gist.github.com/raw/" + content.split("href=\"/raw/")[1].split('"')[0]
|
78
|
+
rescue
|
79
|
+
nil
|
80
|
+
end
|
81
|
+
when 'pastie.org', 'www.pastie.org'
|
82
|
+
return url if url.include?('.txt')
|
83
|
+
return "#{url}.txt"
|
84
|
+
else
|
85
|
+
url
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
data/lib/go/plugin.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
module Go
|
2
|
+
|
3
|
+
# Base class for Go plugins
|
4
|
+
class Plugin
|
5
|
+
|
6
|
+
class << self
|
7
|
+
attr_accessor :plugin_list
|
8
|
+
attr_accessor :command, :usage
|
9
|
+
|
10
|
+
def call_with(command)
|
11
|
+
@command = command
|
12
|
+
end
|
13
|
+
|
14
|
+
def usage_raw
|
15
|
+
@usage
|
16
|
+
end
|
17
|
+
|
18
|
+
# Ugly hack. Allows usage to be set or called via the same method, without
|
19
|
+
# 0 for 1 argument errors.
|
20
|
+
def usage(usage = nil)
|
21
|
+
if usage.nil?
|
22
|
+
"Usage: go #{@usage}"
|
23
|
+
else
|
24
|
+
@usage = usage
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
attr_accessor :options #, :command
|
31
|
+
|
32
|
+
# Initializes a new Go::Plugin class
|
33
|
+
# * Expects an OpenStruct returned from a CoolOptions.parse!(...) call
|
34
|
+
def initialize(options)
|
35
|
+
@options = options
|
36
|
+
# @config_file = options.config_path
|
37
|
+
# @verbose = options.verbose || false
|
38
|
+
#
|
39
|
+
# yml = File.open(@config_file)
|
40
|
+
# @config = YAML.load(yml)
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# Source via Rails (railties) - included here to avoid adding an extra dependency of railties to the gem.
|
2
|
+
#
|
3
|
+
# This is Free Software, copyright 2005 by Ryan Tomayko (rtomayko@gmail.com)
|
4
|
+
# and is licensed MIT: (http://www.opensource.org/licenses/mit-license.php)
|
5
|
+
|
6
|
+
class RecursiveHTTPFetcher
|
7
|
+
attr_accessor :quiet
|
8
|
+
def initialize(urls_to_fetch, level = 1, cwd = ".")
|
9
|
+
@level = level
|
10
|
+
@cwd = cwd
|
11
|
+
@urls_to_fetch = RUBY_VERSION >= '1.9' ? urls_to_fetch.lines : urls_to_fetch.to_a
|
12
|
+
@quiet = false
|
13
|
+
end
|
14
|
+
|
15
|
+
def ls
|
16
|
+
@urls_to_fetch.collect do |url|
|
17
|
+
if url =~ /^svn(\+ssh)?:\/\/.*/
|
18
|
+
`svn ls #{url}`.split("\n").map {|entry| "/#{entry}"} rescue nil
|
19
|
+
else
|
20
|
+
open(url) do |stream|
|
21
|
+
links("", stream.read)
|
22
|
+
end rescue nil
|
23
|
+
end
|
24
|
+
end.flatten
|
25
|
+
end
|
26
|
+
|
27
|
+
def push_d(dir)
|
28
|
+
@cwd = File.join(@cwd, dir)
|
29
|
+
FileUtils.mkdir_p(@cwd)
|
30
|
+
end
|
31
|
+
|
32
|
+
def pop_d
|
33
|
+
@cwd = File.dirname(@cwd)
|
34
|
+
end
|
35
|
+
|
36
|
+
def links(base_url, contents)
|
37
|
+
links = []
|
38
|
+
contents.scan(/href\s*=\s*\"*[^\">]*/i) do |link|
|
39
|
+
link = link.sub(/href="/i, "")
|
40
|
+
next if link =~ /svnindex.xsl$/
|
41
|
+
next if link =~ /^(\w*:|)\/\// || link =~ /^\./
|
42
|
+
links << File.join(base_url, link)
|
43
|
+
end
|
44
|
+
links
|
45
|
+
end
|
46
|
+
|
47
|
+
def download(link)
|
48
|
+
puts "+ #{File.join(@cwd, File.basename(link))}" unless @quiet
|
49
|
+
open(link) do |stream|
|
50
|
+
File.open(File.join(@cwd, File.basename(link)), "wb") do |file|
|
51
|
+
file.write(stream.read)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def fetch(links = @urls_to_fetch)
|
57
|
+
links.each do |l|
|
58
|
+
(l =~ /\/$/ || links == @urls_to_fetch) ? fetch_dir(l) : download(l)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def fetch_dir(url)
|
63
|
+
@level += 1
|
64
|
+
push_d(File.basename(url)) if @level > 0
|
65
|
+
open(url) do |stream|
|
66
|
+
contents = stream.read
|
67
|
+
fetch(links(url, contents))
|
68
|
+
end
|
69
|
+
pop_d if @level > 0
|
70
|
+
@level -= 1
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Panda < Go::Plugin
|
2
|
+
call_with :panda
|
3
|
+
usage 'panda "some text to say"'
|
4
|
+
|
5
|
+
def run
|
6
|
+
if @options.params.empty?
|
7
|
+
puts "Panda is speechless."
|
8
|
+
else
|
9
|
+
puts "Panda says, \"#{@options.params.first}\""
|
10
|
+
system("say #{@options.params.first}")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class PandaParty < Go::Plugin
|
2
|
+
call_with 'panda party'
|
3
|
+
|
4
|
+
def run
|
5
|
+
puts pandas
|
6
|
+
end
|
7
|
+
|
8
|
+
def pandas
|
9
|
+
p = <<EOF
|
10
|
+
|
11
|
+
ooo
|
12
|
+
o$$$$$$ o$$$$$o ooo
|
13
|
+
$$$$$$"""""""""$o$$$$$$$$oo"""""""o o$$$$$$o
|
14
|
+
""$ $$$$$" $$$$$$$$$
|
15
|
+
$ $ $$$$$$$
|
16
|
+
o ooooo "$$$"
|
17
|
+
o $$$o$$ o$$o o" ooo "$
|
18
|
+
$ """ "$ o$$ o o$" $ $$"$$$
|
19
|
+
o ooo ""$" o"$$" "$$$$$ $
|
20
|
+
oo$$$"" "o "$"" " o """ oo"
|
21
|
+
o$$$$o """"oo$$$$oo"$ $"$" o o"$$$$
|
22
|
+
o$$$$$$$$"$$$$$$$$$$$$$$$$$ $$$o$"""" "$$$
|
23
|
+
$$$$$$$$$o$$$$$$$$$$$$$$$$$o$$$$$$$o $$$$
|
24
|
+
$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$o oo$$$$$$$$o
|
25
|
+
o$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$oo$$$$$$$$$$$$
|
26
|
+
o$$$$$o"$$$$" "$$$"" $$$$$$$$$$$$$$o"$$$$$$$$$""o
|
27
|
+
o$$$$$$$ "$$$$$$$$$$$$$$o"$$$$$" $$o ooo
|
28
|
+
$$$$$$$$ $ "$$$$$$$$$$$$ $$$$$o$$$$$
|
29
|
+
$$$$$$$$o ooo$$$$$$$ """""""$" $$$$$$$$$$$"
|
30
|
+
$$$$$$$$ o$$$$$$$$$$$$$$$$$$$$o oo$$$$$$$$$o o$$$$$ $$$$$$$$$$$
|
31
|
+
$$$$$$$"o$$$$$$$$$$$$$$$$$$$$$$$oo$$$$$$$$$$$$$$$$$$$$$$$$$o$$$$$$$$"
|
32
|
+
o$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"$$$$$
|
33
|
+
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"""
|
34
|
+
$$$$$$$$ $$$$$$$$$$$$$$$$$$$$$$$$" "$$$$$$$$$$$$$$$$$$$$$$$$$$$$
|
35
|
+
"$$$$"" $$$$$$$$"""$$$$$$"""" "$$$$$$$$$$$$$$$$$""$$$$$"
|
36
|
+
$$$$$" """"$$$$$""
|
37
|
+
|
38
|
+
EOF
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Todo < Go::Plugin
|
2
|
+
call_with :todo
|
3
|
+
usage 'todo <path>'
|
4
|
+
|
5
|
+
def run
|
6
|
+
if @options.params.empty?
|
7
|
+
puts self.class.usage; return
|
8
|
+
end
|
9
|
+
path = @options.params.first
|
10
|
+
if !File.exist?(path)
|
11
|
+
puts "Path does not exist: #{path}"; return
|
12
|
+
end
|
13
|
+
cmd = "find #{path} -name \"*\" | xargs grep -C1 'TODO'"
|
14
|
+
r = IO.popen(cmd)
|
15
|
+
puts r.read
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# This plugin should not get loaded -- it's attempting to register a command over a reserved Go command
|
2
|
+
class Commands < Go::Plugin
|
3
|
+
call_with :commands
|
4
|
+
|
5
|
+
def run
|
6
|
+
puts "OOOPS - I should never be run, because I conflict with a builtin go command."
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
data/test/test_go.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require(File.join(File.dirname(__FILE__), '..', 'lib', 'go'))
|
2
|
+
require 'test/unit'
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
class TestGo < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def test_fixture_plugins_loaded_as_subclasses
|
8
|
+
@app = Go::Application.new(['hello'])
|
9
|
+
@app.plugin_path = File.join(File.dirname(__FILE__), 'fixtures', 'plugins')
|
10
|
+
@app.instance_eval('init_plugins')
|
11
|
+
assert_equal [Hello, Panda, PandaParty, PartyPalace, Todo].to_set, Go::Plugin.subclasses.to_set
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_plugin_with_reserved_command_should_not_get_included_in_command_tree
|
15
|
+
@app = Go::Application.new([])
|
16
|
+
@app.plugin_path = File.join(File.dirname(__FILE__), 'fixtures', 'reserved')
|
17
|
+
@app.instance_eval('init_plugins')
|
18
|
+
assert !@app.plugin_commands.keys.include?('commands')
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_command_with_most_required_params_has_highest_priority
|
22
|
+
@app = Go::Application.new(['panda party palace'])
|
23
|
+
@app.plugin_path = File.join(File.dirname(__FILE__), 'fixtures', 'plugins')
|
24
|
+
@app.instance_eval('init_plugins')
|
25
|
+
assert_equal ["panda party palace", PartyPalace], @app.run
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_command_with_next_most_params_has_next_highest_priority
|
29
|
+
@app = Go::Application.new(['panda party'])
|
30
|
+
@app.plugin_path = File.join(File.dirname(__FILE__), 'fixtures', 'plugins')
|
31
|
+
assert_equal ["panda party", PandaParty], @app.run
|
32
|
+
end
|
33
|
+
|
34
|
+
def pending_test_single_word_command_with_params
|
35
|
+
@app = Go::Application.new(['panda', 'this app makes me feel sexy'])
|
36
|
+
@app.plugin_path = File.join(File.dirname(__FILE__), 'fixtures', 'plugins')
|
37
|
+
@app.init_plugins!
|
38
|
+
assert_equal ["panda", Panda], @app.run
|
39
|
+
end
|
40
|
+
|
41
|
+
## Installer tests
|
42
|
+
def test_installer_parse_pastie_txt
|
43
|
+
app = Go::Application.new([])
|
44
|
+
app.plugin_path = File.join(File.dirname(__FILE__), 'fixtures', 'reserved')
|
45
|
+
inst = Go::Installer.new('http://pastie.org/285599.txt', app)
|
46
|
+
assert_equal 'http://pastie.org/285599.txt', inst.instance_eval('@url') #inst.send(:url)
|
47
|
+
end
|
48
|
+
end
|
metadata
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sbraford-go
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.1"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Shanti A. Braford
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-10-05 00:00:00 -07:00
|
13
|
+
default_executable: go
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: cooloptions
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.1.1
|
23
|
+
version:
|
24
|
+
- !ruby/object:Gem::Dependency
|
25
|
+
name: hoe
|
26
|
+
version_requirement:
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
requirements:
|
29
|
+
- - ">="
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: 1.7.0
|
32
|
+
version:
|
33
|
+
description: Provides a command-line tool "go" which can then be extended by adding plugins that others have created, or that you have made yourself.
|
34
|
+
email:
|
35
|
+
- shantibraford@gmail.com
|
36
|
+
executables:
|
37
|
+
- go
|
38
|
+
extensions: []
|
39
|
+
|
40
|
+
extra_rdoc_files:
|
41
|
+
- History.txt
|
42
|
+
- Manifest.txt
|
43
|
+
- README.txt
|
44
|
+
files:
|
45
|
+
- History.txt
|
46
|
+
- Manifest.txt
|
47
|
+
- README.txt
|
48
|
+
- Rakefile
|
49
|
+
- bin/go
|
50
|
+
- lib/go.rb
|
51
|
+
- lib/go/application.rb
|
52
|
+
- lib/go/extensions.rb
|
53
|
+
- lib/go/installer.rb
|
54
|
+
- lib/go/plugin.rb
|
55
|
+
- lib/go/recursive_http_fetcher.rb
|
56
|
+
- test/test_go.rb
|
57
|
+
- test/fixtures/plugins/hello.rb
|
58
|
+
- test/fixtures/plugins/panda.rb
|
59
|
+
- test/fixtures/plugins/panda_party.rb
|
60
|
+
- test/fixtures/plugins/todo.rb
|
61
|
+
- test/fixtures/plugins/palace/party_palace.rb
|
62
|
+
- test/fixtures/reserved/commands.rb
|
63
|
+
- lib/plugins/hello.rb
|
64
|
+
has_rdoc: true
|
65
|
+
homepage: http://github.com/sbraford/go/tree
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options:
|
68
|
+
- --main
|
69
|
+
- README.txt
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: "0"
|
77
|
+
version:
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: "0"
|
83
|
+
version:
|
84
|
+
requirements: []
|
85
|
+
|
86
|
+
rubyforge_project: go
|
87
|
+
rubygems_version: 1.2.0
|
88
|
+
signing_key:
|
89
|
+
specification_version: 2
|
90
|
+
summary: Extensible command-line tool for ninjas, pirates & pandas
|
91
|
+
test_files:
|
92
|
+
- test/test_go.rb
|
93
|
+
- test/fixtures/plugins/hello.rb
|
94
|
+
- test/fixtures/plugins/panda.rb
|
95
|
+
- test/fixtures/plugins/panda_party.rb
|
96
|
+
- test/fixtures/plugins/todo.rb
|
97
|
+
- test/fixtures/plugins/palace/party_palace.rb
|
98
|
+
- test/fixtures/reserved/commands.rb
|