thunder-1.9.3 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 528092a539ffde4742c527b3757d3657a1d10fd6
4
+ data.tar.gz: f797fbdc1b56571d4118d09b8216bf6e3bde064a
5
+ SHA512:
6
+ metadata.gz: 15608baef0732e5aef4313c4b5f82ed937dc7b07e6f2d2a501a50514c6644ace5cb7c8ded0e4ae1f1fc48d0b34f51b3fb5c981f7357322e6ec6841122c888aea
7
+ data.tar.gz: bd682da336ba10ba84bb8ba101db246b78621fdb3302fe2b372626190e96ad642cee41cd018a31f3dbe511907b564c3b8503e3f34703d112e3dc3e993c865228
data/DESIGN.md ADDED
@@ -0,0 +1,20 @@
1
+ This document will document my various design decisions
2
+
3
+ Option Parsing
4
+ --------------
5
+ Rather than include option parsing as part of the Thunder Core, I farm out the work through an adapter.
6
+ The default adapter is only loaded if no other adapter is specified, and uses the ruby std-lib builtin OptParse library.
7
+
8
+ Help Formatting
9
+ ---------------
10
+ Similar to option parsing in that the functionality is provided via an adapter.
11
+ The default implementation uses a style similar to rake and thor. This was chosen since it is the easiest to parse using regular expressions, barring developer error.
12
+
13
+ Subcommands
14
+ -----------
15
+ One of the stated design goals was to provide subcommand support, ala svn, git, gem and rails.
16
+ It has occured to me that as a result of the way I farm out option parsing and help formatting, this information is not passed on to the subcommand. As a result, each subcommand would need to declare which formatter to use, if the default one is not desired. This is possible to work around, but it would add an extra parameter or two to the start method, which I'd like to avoid. As there is no other way to solve this without resorting to smelly code, I'm going to put off this decision until it actually becomes relevant (it may in the future, who knows?)
17
+
18
+ Singleton vs. Instance #start()
19
+ -------------------------------
20
+ Thor runs the start through the class singleton. But it also requires you to inherit from the Thor class. This means that more complex classes cannot be turned into adhoc command line utilities, which I view as a significant limitation against the integration of command line and code. Bridging that gap is exactly what this library is supposed to do.
data/README.md ADDED
@@ -0,0 +1,44 @@
1
+ [Thunder](http://stevenkaras.github.com/thunder)
2
+ =======
3
+ Ruby gem for quick and easy command line interfaces
4
+
5
+ [![Code Climate](https://codeclimate.com/github/stevenkaras/thunder.png)](https://codeclimate.com/github/stevenkaras/thunder)
6
+
7
+ Usage
8
+ -----
9
+
10
+ gem install thunder
11
+
12
+ or if you're running [ruby 1.8.7](http://www.ruby-lang.org/en/news/2011/10/06/plans-for-1-8-7):
13
+
14
+ gem install thunder-1.8.7
15
+
16
+ see '[sample](http://github.com/stevenkaras/thunder/blob/master/sample)' for a quick overview of all features
17
+
18
+ Philosophy
19
+ ----------
20
+ The command line is the most basic interface we have with a computer. It makes sense that we should invest as much time and effort as possible to build tools to work with the command line, and tie into code we write as quickly as possible. Sadly, this has not taken place as much as one would expect. This library steps up to bridge the admittedly small gap between your standard shell and ruby code.
21
+
22
+ While this is possible, and even easy using existing libraries, they either violate the principle of locality, or provide too many services.
23
+
24
+ The overarching philosophy here is that a library should do one or two things, and do them extremely well.
25
+
26
+ Goals
27
+ -----
28
+ Provide a simple, DRY syntatic sugar library that provides the necessary services to create command line mappings to ruby methods. It will not provide any other services.
29
+
30
+ Notable features as of the current release:
31
+
32
+ * options parsing for commands
33
+ * default commands
34
+ * subcommands (ala git)
35
+ * integrated help system
36
+ * bash completion script generator
37
+
38
+ Development
39
+ -----------
40
+ If you'd like to contribute, fork, commit, and request a pull. I'll get around to it. No special dependencies, or anything fancy
41
+
42
+ License
43
+ -------
44
+ MIT License
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ require 'rake/testtask'
2
+
3
+ require File.expand_path("../lib/thunder/version", __FILE__)
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.test_files = FileList['test/*_test.rb']
7
+ t.test_files = FileList['test/test_*.rb']
8
+ t.test_files = FileList['spec/*_spec.rb']
9
+ t.test_files = FileList['spec/spec_*.rb']
10
+ t.libs << 'spec'
11
+ t.libs << 'test'
12
+ end
13
+
14
+ desc "Run tests"
15
+ task :default => :test
16
+
17
+ desc "Build the gem"
18
+ task :build do
19
+ system "gem build thunder.gemspec"
20
+ end
21
+
22
+ task :gem => :build do
23
+ system "gem uninstall -a thunder"
24
+ system "gem install thunder-#{Thunder::VERSION}.gem"
25
+ end
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.expand_path("../../lib", __FILE__)
4
+
5
+ require 'thunder'
6
+ require 'erb'
7
+
8
+ def thunder_commands(spec)
9
+ spec[:commands].map(&:first).map(&:to_s)
10
+ end
11
+
12
+ def thunder_options(command)
13
+ return nil unless command[:options]
14
+
15
+ result = []
16
+ command[:options].each do |opt, option|
17
+ result << "--#{opt}"
18
+ next unless option[:short]
19
+ result << "-#{option[:short]}"
20
+ end
21
+ return result
22
+ end
23
+
24
+ def indent(text, amount, indent=" ")
25
+ result = ""
26
+ text.lines do |line|
27
+ result << indent * amount + line
28
+ end
29
+ return result
30
+ end
31
+
32
+ def thunder_completion(depth, spec)
33
+ template = ERB.new <<-TEMPLATE, nil, "%>"
34
+ if ((COMP_CWORD == <%= depth+1 %>)); then
35
+ # display only commands
36
+ words="<%= thunder_commands(spec).join(" ") %>"
37
+ else
38
+ case ${COMP_WORDS[<%= depth+1 %>]} in
39
+ % spec[:commands].each do |name, command|
40
+ % name = name.to_s
41
+ <%= name %>)
42
+ % if name == "help"
43
+ words="<%= thunder_commands(spec).join(" ") %>"
44
+ % end
45
+ % if command[:options]
46
+ if [[ ${COMP_WORDS[COMP_CWORD]:0:1} == "-" ]]; then
47
+ words="<%= thunder_options(command).join(" ") %>"
48
+ fi
49
+ % end
50
+ % if command[:subcommand]
51
+ <%= indent(thunder_completion(depth+1, command[:subcommand].class.thunder), 8) %>
52
+ % end
53
+ ;;
54
+ % end
55
+ esac
56
+ fi
57
+ TEMPLATE
58
+ return template.result(binding)
59
+ end
60
+
61
+ module Thunder
62
+ def start(args=ARGV.dup, options={})
63
+ template = ERB.new <<-TEMPLATE, nil, "%>"
64
+ #!/bin/bash
65
+
66
+ % progname = File.basename(ARGV.first)
67
+ __<%= progname %>_completion() {
68
+ local words=""
69
+ <%= indent(thunder_completion(0, self.class.thunder), 4) %>
70
+ COMPREPLY=($(compgen -W "$words" -- ${COMP_WORDS[COMP_CWORD]}))
71
+ }
72
+
73
+ complete -o default -o nospace -F __<%= progname %>_completion <%= progname %>
74
+ TEMPLATE
75
+ puts template.result(binding)
76
+ end
77
+ end
78
+
79
+ if ARGV.size != 1
80
+ puts "Usage: #{File.basename(__FILE__)} THUNDER_SCRIPT"
81
+ puts
82
+ puts "Prints out the suggested template for a bash completion script for the given thunder script"
83
+ exit 1
84
+ end
85
+
86
+ load File.expand_path(ARGV.first)
data/bin/thunder-spec ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.expand_path("../../lib", __FILE__)
4
+
5
+ require 'thunder'
6
+ require 'pp'
7
+
8
+ module Thunder
9
+ def start(args=ARGV.dup, options={})
10
+ spec = self.class.thunder
11
+ pp renderThunderSpec(spec)
12
+ end
13
+ end
14
+
15
+ def renderThunderSpec(spec)
16
+ spec[:commands].each do |name, command|
17
+ command[:subcommand] = renderThunderSpec(command[:subcommand].class.thunder) if command[:subcommand]
18
+ end
19
+ spec
20
+ end
21
+
22
+ if ARGV.size != 1
23
+ puts "Usage: #{File.basename(__FILE__)} THUNDER_SCRIPT"
24
+ puts
25
+ puts "Dumps the thunder spec to the command line for debugging and analysis"
26
+ exit 1
27
+ end
28
+
29
+ load File.expand_path(ARGV.first)
data/lib/thunder.rb ADDED
@@ -0,0 +1,252 @@
1
+ # Provides a simple, yet powerful ability to quickly and easily tie Ruby methods
2
+ # with command line actions.
3
+ #
4
+ # The syntax is very similar to Thor, so switching over should be extremely easy
5
+ module Thunder
6
+
7
+ # Used to indicate a boolean true or false value for options processing
8
+ class Boolean; end
9
+
10
+ # Start the object as a command line program,
11
+ # processing the given arguments and using the provided options.
12
+ #
13
+ # @param args [<String>] the command line arguments [ARGV]
14
+ # @param options [{Symbol => *}] the default options to use [{}]
15
+ def start(args=ARGV.dup, options={})
16
+ command_spec = determine_command(args)
17
+
18
+ unless command_spec
19
+ return
20
+ end
21
+
22
+ if command_spec[:name] == :help && command_spec[:default_help]
23
+ return get_help(args, options)
24
+ end
25
+
26
+ parsed_options = process_options(args, command_spec)
27
+ options.merge!(parsed_options) if parsed_options
28
+ if command_spec[:subcommand]
29
+ return command_spec[:subcommand].start(args, options)
30
+ end
31
+ if parsed_options
32
+ args << options
33
+ end
34
+
35
+ if command_spec[:params]
36
+ min = command_spec[:params].count { |param| param.first == :req}
37
+ if args.size < min
38
+ ARGV.insert((ARGV.size - args.size) - 1, "help")
39
+ puts help_command(command_spec)
40
+ return
41
+ end
42
+ max = if command_spec[:params].map(&:first).include?(:rest)
43
+ nil
44
+ else
45
+ command_spec[:params].size
46
+ end
47
+ if !max.nil? && args.size > max
48
+ ARGV.insert((ARGV.size - args.size) - 1, "help")
49
+ puts help_command(command_spec)
50
+ return
51
+ end
52
+ end
53
+ return send command_spec[:name], *args
54
+ end
55
+
56
+ protected
57
+ # Determine the command to use from the given arguments
58
+ #
59
+ # @param args [<String>] the arguments to process
60
+ # @return [Hash,nil] the command specification for the given arguments,
61
+ # or nil if there is no appropriate command
62
+ def determine_command(args)
63
+ if args.empty?
64
+ return self.class.thunder[:commands][self.class.thunder[:default_command]]
65
+ end
66
+ command_name = args.first.to_sym
67
+ command_spec = self.class.thunder[:commands][command_name]
68
+ if command_spec
69
+ args.shift
70
+ else
71
+ command_spec = self.class.thunder[:commands][self.class.thunder[:default_command]]
72
+ end
73
+ return command_spec
74
+ end
75
+
76
+ # Process command line options from the given argument list
77
+ #
78
+ # @param args [<String>] the argument list to process
79
+ # @param command_spec [Hash] the command specification to use
80
+ # @return [{Symbol => *},nil] the options
81
+ def process_options(args, command_spec)
82
+ return nil unless command_spec[:options]
83
+
84
+ unless self.class.thunder[:options_processor]
85
+ require 'thunder/options/optparse'
86
+ self.class.thunder[:options_processor] = Thunder::OptParseAdapter
87
+ end
88
+ self.class.thunder[:options_processor].process_options(args, command_spec)
89
+ end
90
+
91
+ # get help on the provided subjects
92
+ #
93
+ # @param args [<String>] the arguments list
94
+ # @param options [Hash] any included options
95
+ def get_help(args, options)
96
+ if args.size == 0
97
+ puts help_list(self.class.thunder[:commands])
98
+ else
99
+ puts help_command(determine_command(args))
100
+ end
101
+ end
102
+
103
+ # Render a usage list of the given commands
104
+ #
105
+ # @param commands [<Hash>] the commands to list
106
+ # @return [String] the rendered help
107
+ def help_list(commands)
108
+ unless self.class.thunder[:help_formatter]
109
+ require 'thunder/help/default'
110
+ self.class.thunder[:help_formatter] = Thunder::DefaultHelp
111
+ end
112
+ self.class.thunder[:help_formatter].help_list(commands)
113
+ end
114
+
115
+ # Render detailed help on a specific command
116
+ #
117
+ # @param command_spec [Hash] the command to render detailed help for
118
+ # @return [String] the rendered help
119
+ def help_command(command_spec)
120
+ unless self.class.thunder[:help_formatter]
121
+ require 'thunder/help/default'
122
+ self.class.thunder[:help_formatter] = Thunder::DefaultHelp
123
+ end
124
+ self.class.thunder[:help_formatter].help_command(command_spec)
125
+ end
126
+
127
+ public
128
+ # @api private
129
+ # Automatically extends the singleton with {ClassMethods}
130
+ def self.included(base)
131
+ base.send :extend, ClassMethods
132
+ end
133
+
134
+ # This module provides methods for any class that includes Thunder
135
+ module ClassMethods
136
+
137
+ # @api private
138
+ # Get the thunder configuration
139
+ def thunder
140
+ @thunder ||= {
141
+ default_command: :help,
142
+ commands: {
143
+ help: {
144
+ name: :help,
145
+ usage: "help [COMMAND]",
146
+ description: "list available commands or describe a specific command",
147
+ long_description: nil,
148
+ options: nil,
149
+ default_help: true
150
+ },
151
+ }
152
+ }
153
+ end
154
+
155
+ # @api private
156
+ # Registers a method as a thunder task
157
+ def method_added(method)
158
+ add_command(method.to_sym) do |command|
159
+ command[:params] = instance_method(method).parameters
160
+ end
161
+ end
162
+
163
+ # Set the options processor.
164
+ #
165
+ # @param processor [#process_options]
166
+ def options_processor(processor)
167
+ thunder[:options_processor] = processor
168
+ end
169
+
170
+ # Set the help formatter.
171
+ #
172
+ # @param formatter [#help_list,#help_command]
173
+ def help_formatter(formatter)
174
+ thunder[:help_formatter] = formatter
175
+ end
176
+
177
+ # Set the default command to be executed when no suitable command is found.
178
+ #
179
+ # @param command [Symbol] the default command
180
+ def default_command(command)
181
+ thunder[:default_command] = command
182
+ end
183
+
184
+ # Describe the next method (or subcommand). A longer description can be given
185
+ # using the {#longdesc} command
186
+ #
187
+ # @param usage [String] the perscribed usage of the command
188
+ # @param description [String] a short description of what the command does
189
+ def desc(usage, description="")
190
+ thunder[:usage], thunder[:description] = usage, description
191
+ end
192
+
193
+ # Provide a long description for the next method (or subcommand).
194
+ #
195
+ # @param description [String] a long description of what the command does
196
+ def longdesc(description)
197
+ thunder[:long_description] = description
198
+ end
199
+
200
+ # Define an option for the next method (or subcommand)
201
+ #
202
+ # @param name [Symbol,String] the long name of this option
203
+ # @option options :short [String] the short version of the option [the first letter of the option name]
204
+ # @option options :type [Class] the datatype of this option [Boolean]
205
+ # @option options :desc [String] the long description of this option [""]
206
+ # @option options :default [*] the default value
207
+ #
208
+ # @example
209
+ # option :output_file, type: String
210
+ #
211
+ # @example
212
+ # option "verbose", desc: "print extra information"
213
+ def option(name, options={})
214
+ name = name.to_sym
215
+ options[:name] = name
216
+ options[:short] ||= name[0]
217
+ options[:type] ||= Boolean
218
+ options[:desc] ||= ""
219
+ thunder[:options] ||= {}
220
+ thunder[:options][name] = options
221
+ end
222
+
223
+ # Define a subcommand
224
+ #
225
+ # @param command [Symbol,String] the command that transfers processing to the provided handler
226
+ # @param handler [Thunder] the handler that processes the request
227
+ def subcommand(command, handler)
228
+ add_command(command.to_sym) do |subcommand|
229
+ subcommand[:subcommand] = handler
230
+ end
231
+ end
232
+
233
+ private
234
+ def add_command(command, &block)
235
+ attributes = [:usage, :description, :options, :long_description]
236
+ return unless attributes.reduce(nil) { |a, key| a || thunder[key] }
237
+ thunder[:commands][command] = {
238
+ name: command,
239
+ }
240
+ attributes.each do |key|
241
+ thunder[:commands][command][key] = thunder.delete(key)
242
+ end
243
+ if block
244
+ if block.arity == 0
245
+ block.call
246
+ else
247
+ block.call thunder[:commands][command]
248
+ end
249
+ end
250
+ end
251
+ end
252
+ end
@@ -0,0 +1,94 @@
1
+ module Thunder
2
+ # Provides an easy to parse help formatter
3
+ class DefaultHelp
4
+ class << self
5
+
6
+ # @see Thunder#help_command(command_spec)
7
+ def help_command(command_spec)
8
+ preamble = determine_preamble
9
+ footer = ""
10
+ footer << command_spec[:description] + "\n" if command_spec[:description]
11
+ footer << command_spec[:long_description] + "\n" if command_spec[:long_description]
12
+ footer << "\n" + format_options(command_spec[:options]) if command_spec[:options]
13
+ output = <<-EOS
14
+ Usage:
15
+ #{preamble} #{command_spec[:usage]}
16
+
17
+ #{footer.strip}
18
+ EOS
19
+ output.rstrip
20
+ end
21
+
22
+ # @see Thunder#help_list(commands)
23
+ def help_list(commands)
24
+ preamble = determine_preamble
25
+ help = []
26
+ commands.each do |name, command_spec|
27
+ help << short_help(preamble, command_spec)
28
+ end
29
+ render_table(help)
30
+ end
31
+
32
+ private
33
+
34
+ # format a set of option specs
35
+ #
36
+ # @param options [<Hash>] the option specs to format
37
+ # @return [String]
38
+ def format_options(options)
39
+ data = []
40
+ options.each do |name, option_spec|
41
+ data << format_option(option_spec)
42
+ end
43
+ "Options:\n" + render_table(data, ": ")
44
+ end
45
+
46
+ # format an option
47
+ #
48
+ # @param option_spec [Hash] the option spec to format
49
+ # @return [(String, String)] the formatted option and its description
50
+ def format_option(option_spec)
51
+ usage = " -#{option_spec[:short]}, --#{option_spec[:name]}"
52
+ usage << " [#{option_spec[:name].to_s.upcase}]" unless option_spec[:type] == Boolean
53
+ return usage, option_spec[:desc]
54
+ end
55
+
56
+ # determine the preamble
57
+ #
58
+ # @return [String] the preamble
59
+ def determine_preamble
60
+ preamble = "#{File.basename($0)}"
61
+ ARGV.each do |arg|
62
+ break if arg == "help"
63
+ preamble << " #{arg}"
64
+ end
65
+ preamble
66
+ end
67
+
68
+ # render the short help string for a command
69
+ #
70
+ # @param preamble [String] the preamble
71
+ # @param command_spec [Hash]
72
+ # @return [String] the short help string for the given command
73
+ def short_help(preamble, command_spec)
74
+ return " #{preamble} #{command_spec[:usage]}", command_spec[:description]
75
+ end
76
+
77
+ # render a two-column table
78
+ #
79
+ # @param data [(String,String)]
80
+ # @param separator [String]
81
+ # @return [String] a two-column table
82
+ def render_table(data, separator = " # ")
83
+ column_width = data.group_by do |row|
84
+ row.first.size
85
+ end.max.first
86
+ "".tap do |output|
87
+ data.each do |row|
88
+ output << "%-#{column_width}s#{separator}%s\n" % row
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,43 @@
1
+ require 'optparse'
2
+
3
+ module Thunder
4
+ # Provides an adapter to the optparse library included in the Ruby std-lib
5
+ class OptParseAdapter
6
+ # @see Thunder#process_options
7
+ def self.process_options(args, command_spec)
8
+ return {} unless command_spec[:options]
9
+
10
+ options = {}
11
+ command_spec[:options_processor] ||= OptionParser.new do |parser|
12
+ command_spec[:options].each do |name, option_spec|
13
+ opt = []
14
+ opt << "-#{option_spec[:short]}"
15
+ opt << if option_spec[:type] == Boolean
16
+ "--[no-]#{name}"
17
+ else
18
+ "--#{name} [#{name.to_s.upcase}]"
19
+ end
20
+ opt << option_spec[:type] unless option_spec[:type] == Boolean
21
+ opt << option_spec[:desc]
22
+ parser.on(*opt) do |value|
23
+ options[name] = value
24
+ end
25
+ end
26
+ end
27
+ command_spec[:options_processor].parse!(args)
28
+
29
+ # set default values
30
+ command_spec[:options].each do |name, option_spec|
31
+ next if options.has_key? name
32
+ next unless option_spec[:default]
33
+ options[name] = option_spec[:default]
34
+ end
35
+
36
+ return options
37
+ rescue OptionParser::InvalidOption => e
38
+ puts e
39
+ puts "Try --help for help."
40
+ exit 1
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,27 @@
1
+ require 'trollop'
2
+
3
+ module Thunder
4
+ # provides an adapter to the popular trollop option parsing library (requires trollop.rb be on the load path)
5
+ class TrollopAdapter
6
+ # @see Thunder#process_options
7
+ def self.process_options(args, command_spec)
8
+ return nil unless command_spec[:options]
9
+ #TODO: fix the unspecified option bug
10
+ command_spec[:option_processor] ||= Trollop::Parser.new do
11
+ command_spec[:options].each do |name, option_spec|
12
+ opt_options = {}
13
+ description = option_spec[:desc] || ""
14
+ type = option_spec[:type]
15
+ type = :flag if type == Thunder::Boolean
16
+ opt_options[:type] = type
17
+ default_value = option_spec[:default]
18
+ opt_options[:default] = default_value if default_value
19
+ opt_options[:short] = "-" + option_spec[:short]
20
+
21
+ opt name, description, opt_options
22
+ end
23
+ end
24
+ command_spec[:option_processor].parse(args)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,4 @@
1
+ module Thunder
2
+ # Version string for gemspec
3
+ VERSION = "0.6.1"
4
+ end
@@ -0,0 +1,19 @@
1
+ require 'minitest/autorun'
2
+
3
+ class ThunderSpec < MiniTest::Spec
4
+
5
+ describe "a simple example" do
6
+ it "should be easy to use" do
7
+ # test is subjective. Awaiting singularity for objective analysis
8
+ end
9
+ it "should pass on arguments" do
10
+ # TODO: write this test(s)
11
+ end
12
+ it "should pass options" do
13
+ # TODO: write these tests
14
+ end
15
+ it "should allow use of subcommands" do
16
+ # TODO: write some more tests
17
+ end
18
+ end
19
+ end
metadata ADDED
@@ -0,0 +1,57 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: thunder-1.9.3
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.6.1
5
+ platform: ruby
6
+ authors:
7
+ - Steven Karas
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-06-11 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Thunder does command line interfaces. Nothing more, nothing less.
14
+ email: steven.karas@gmail.com
15
+ executables:
16
+ - thunder-spec
17
+ - thunder-completion
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - lib/thunder/help/default.rb
22
+ - lib/thunder/version.rb
23
+ - lib/thunder/options/optparse.rb
24
+ - lib/thunder/options/trollop.rb
25
+ - lib/thunder.rb
26
+ - spec/spec_thunder.rb
27
+ - Rakefile
28
+ - DESIGN.md
29
+ - README.md
30
+ - bin/thunder-spec
31
+ - bin/thunder-completion
32
+ homepage: http://stevenkaras.github.com/thunder
33
+ licenses:
34
+ - MIT
35
+ metadata: {}
36
+ post_install_message:
37
+ rdoc_options: []
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - '>='
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ requirements: []
51
+ rubyforge_project:
52
+ rubygems_version: 2.0.3
53
+ signing_key:
54
+ specification_version: 4
55
+ summary: Thunder makes command lines apps easy!
56
+ test_files: []
57
+ has_rdoc: