remi-optparse-simple 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +27 -0
- data/Rakefile +57 -0
- data/VERSION.yml +4 -0
- data/lib/optparse-simple.rb +161 -0
- data/spec/option_spec.rb +107 -0
- data/spec/optparse_simple_spec.rb +93 -0
- data/spec/spec_helper.rb +1 -0
- metadata +69 -0
data/README.rdoc
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
= optparse-simple
|
2
|
+
|
3
|
+
A partial pre-implementation of OptionParser, in particular that actual parsing part:
|
4
|
+
|
5
|
+
opts = OptionParser.new do |opts|
|
6
|
+
opts.in('-f', '--foo'){ ... }
|
7
|
+
end
|
8
|
+
|
9
|
+
opts.parse! args
|
10
|
+
|
11
|
+
I really love optparse but:
|
12
|
+
|
13
|
+
- I don't care about separators / banners / etc, I use other gems/libraries for that stuff
|
14
|
+
- optparse is a PITA to extend and the source code is <b>1,791</b> lines long!
|
15
|
+
|
16
|
+
This is a re-implementation of the part of optparse that actually parses.
|
17
|
+
|
18
|
+
This is <b>not</b> trying to be a whole framework for creating CLI applications.
|
19
|
+
There are already numerous great tools out there. Unfortunately, many of them
|
20
|
+
re-implement OptionParser, themselves, because there's no library out there (that
|
21
|
+
I know of) that <b>just</b> does the parsing part.
|
22
|
+
|
23
|
+
Our libraries need to be more UNIX-y! "Write programs that do one thing and do it well."
|
24
|
+
|
25
|
+
== install
|
26
|
+
|
27
|
+
<b>NOT YET COMPLETE</b>
|
data/Rakefile
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
require 'spec/rake/spectask'
|
5
|
+
|
6
|
+
begin
|
7
|
+
require 'jeweler'
|
8
|
+
Jeweler::Tasks.new do |s|
|
9
|
+
s.name = "optparse-simple"
|
10
|
+
s.summary = "a partial reimplementation of OptionParser"
|
11
|
+
s.email = "remi@remitaylor.com"
|
12
|
+
s.homepage = "http://github.com/remi/optparse-simple"
|
13
|
+
s.description = "a partial reimplementation of OptionParser"
|
14
|
+
s.authors = %w( remi )
|
15
|
+
s.files = FileList["[A-Z]*", "{bin,lib,spec,examples,rails_generators}/**/*"]
|
16
|
+
s.add_dependency 'remi-indifferent-variable-hash'
|
17
|
+
# s.executables = "optparse-simple"
|
18
|
+
end
|
19
|
+
rescue LoadError
|
20
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
21
|
+
end
|
22
|
+
|
23
|
+
Spec::Rake::SpecTask.new do |t|
|
24
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "Run all examples with RCov"
|
28
|
+
Spec::Rake::SpecTask.new('rcov') do |t|
|
29
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
30
|
+
t.rcov = true
|
31
|
+
end
|
32
|
+
|
33
|
+
Rake::RDocTask.new do |rdoc|
|
34
|
+
rdoc.rdoc_dir = 'rdoc'
|
35
|
+
rdoc.title = 'optparse-simple'
|
36
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
37
|
+
rdoc.rdoc_files.include('README.rdoc')
|
38
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
39
|
+
end
|
40
|
+
|
41
|
+
desc 'Confirm that gemspec is $SAFE'
|
42
|
+
task :safe do
|
43
|
+
require 'yaml'
|
44
|
+
require 'rubygems/specification'
|
45
|
+
data = File.read('optparse-simple.gemspec')
|
46
|
+
spec = nil
|
47
|
+
if data !~ %r{!ruby/object:Gem::Specification}
|
48
|
+
Thread.new { spec = eval("$SAFE = 3\n#{data}") }.join
|
49
|
+
else
|
50
|
+
spec = YAML.load(data)
|
51
|
+
end
|
52
|
+
spec.validate
|
53
|
+
puts spec
|
54
|
+
puts "OK"
|
55
|
+
end
|
56
|
+
|
57
|
+
task :default => :spec
|
data/VERSION.yml
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'indifferent-variable-hash'
|
5
|
+
|
6
|
+
class OptParseSimple
|
7
|
+
extend IndifferentVariableHash
|
8
|
+
|
9
|
+
# an Array of Options
|
10
|
+
attr_accessor :options
|
11
|
+
|
12
|
+
# DEFAULT configuration values
|
13
|
+
OptParseSimple.compatibility_mode = false # whether or not OptParseSimple acts like OptionParser
|
14
|
+
|
15
|
+
def initialize &block
|
16
|
+
block.call self
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse args
|
20
|
+
check_for_invalid_options(args) if OptParseSimple.compatibility_mode
|
21
|
+
options.each {|option| option.parse(args) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse! args
|
25
|
+
check_for_invalid_options(args) if OptParseSimple.compatibility_mode
|
26
|
+
options.each {|option| option.parse!(args) }
|
27
|
+
end
|
28
|
+
|
29
|
+
#:nodoc:
|
30
|
+
def options
|
31
|
+
@options ||= []
|
32
|
+
end
|
33
|
+
|
34
|
+
def on *args, &block
|
35
|
+
options << Option.new(*args, &block)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def check_for_invalid_options args
|
41
|
+
args_clone = args.clone
|
42
|
+
options.each {|option| option.match! args_clone }
|
43
|
+
raise "invalid option: #{ args_clone.first }" unless args_clone.empty?
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
class OptParseSimple #:nodoc:
|
49
|
+
|
50
|
+
#
|
51
|
+
# represents a single option, eg. its matchers (-f, --foo)
|
52
|
+
# and its block of logic to call, if matched
|
53
|
+
#
|
54
|
+
class Option
|
55
|
+
attr_accessor :short_string, :long_string, :accepts_argument, :proc
|
56
|
+
|
57
|
+
def initialize *args, &block
|
58
|
+
set_defaults
|
59
|
+
|
60
|
+
@proc = block
|
61
|
+
|
62
|
+
# take arguments (eg. '-f' or '--f' or '--f [foo]')
|
63
|
+
# and set #short_string, #long_string, and #accepts_argument
|
64
|
+
args.each do |arg|
|
65
|
+
case arg
|
66
|
+
when /^-(\w)/
|
67
|
+
@short_string = $1
|
68
|
+
when /^--(\w+)/
|
69
|
+
@long_string = $1
|
70
|
+
else
|
71
|
+
raise "Don't know how to handle argument: #{ arg.inspect }"
|
72
|
+
end
|
73
|
+
|
74
|
+
@accepts_argument = true if arg =~ /\[.*\]$/
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def short_string_with_dash
|
79
|
+
(short_string) ? "-#{ short_string }" : ''
|
80
|
+
end
|
81
|
+
|
82
|
+
def long_string_with_dashes
|
83
|
+
(long_string) ? "--#{ long_string }" : ''
|
84
|
+
end
|
85
|
+
|
86
|
+
def parse *args
|
87
|
+
args = args.first if args.first.is_a?(Array) and args.length == 1
|
88
|
+
do_parsing args, :run => true, :destructive => false
|
89
|
+
end
|
90
|
+
|
91
|
+
def parse! *args
|
92
|
+
args = args.first if args.first.is_a?(Array) and args.length == 1
|
93
|
+
do_parsing args, :run => true, :destructive => true
|
94
|
+
end
|
95
|
+
|
96
|
+
def match *args
|
97
|
+
args = args.first if args.first.is_a?(Array) and args.length == 1
|
98
|
+
do_parsing args, :run => false, :destructive => false
|
99
|
+
end
|
100
|
+
|
101
|
+
def match! *args
|
102
|
+
args = args.first if args.first.is_a?(Array) and args.length == 1
|
103
|
+
do_parsing args, :run => false, :destructive => true
|
104
|
+
end
|
105
|
+
|
106
|
+
alias accept_argument? accepts_argument
|
107
|
+
alias accept_arguments? accepts_argument
|
108
|
+
alias accepts_argument? accepts_argument
|
109
|
+
alias accepts_arguments? accepts_argument
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def do_parsing args, options = { }
|
114
|
+
options = { :run => false, :destructive => false }.merge(options)
|
115
|
+
|
116
|
+
if accepts_arguments?
|
117
|
+
match_with_arguments args, options
|
118
|
+
else
|
119
|
+
match_without_arguments args, options
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def match_without_arguments args, options
|
124
|
+
args.each do |arg|
|
125
|
+
if arg == short_string_with_dash || arg == long_string_with_dashes
|
126
|
+
args.delete arg if options[:destructive]
|
127
|
+
@proc.call if options[:run] and @proc.respond_to?(:call)
|
128
|
+
return true
|
129
|
+
end
|
130
|
+
end
|
131
|
+
return false
|
132
|
+
end
|
133
|
+
|
134
|
+
def match_with_arguments args, options
|
135
|
+
indexes_that_match = [] # indexes that match either the short or long string
|
136
|
+
args.each_with_index do |arg, index|
|
137
|
+
indexes_that_match << index if arg == short_string_with_dash || arg == long_string_with_dashes
|
138
|
+
if indexes_that_match.include?( index - 1 )
|
139
|
+
unless arg.start_with?('-')
|
140
|
+
this_argument = arg
|
141
|
+
if options[:destructive]
|
142
|
+
args.delete_at index - 1 # delete the last matching index
|
143
|
+
args.delete_at index - 1 # delete this index (-1 because we deleted matching)
|
144
|
+
end
|
145
|
+
if options[:run] and @proc.respond_to?(:call)
|
146
|
+
@proc.call this_argument
|
147
|
+
end
|
148
|
+
|
149
|
+
return true
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
return false
|
154
|
+
end
|
155
|
+
|
156
|
+
def set_defaults
|
157
|
+
@accepts_argument = false
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
data/spec/option_spec.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe OptParseSimple::Option do
|
4
|
+
|
5
|
+
before :all do
|
6
|
+
::Option = OptParseSimple::Option
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should hold onto the block it's initalized with" do
|
10
|
+
option = Option.new('-f'){ "hello from block" }
|
11
|
+
option.proc.should respond_to(:call)
|
12
|
+
option.proc.call.should == "hello from block"
|
13
|
+
|
14
|
+
option.accepts_arguments?.should be_false
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should have a short string' do
|
18
|
+
option = Option.new('-f')
|
19
|
+
option.short_string.should == 'f'
|
20
|
+
option.long_string.should be_nil
|
21
|
+
|
22
|
+
option.accepts_arguments?.should be_false
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should have a long string' do
|
26
|
+
option = Option.new('--foo')
|
27
|
+
option.long_string.should == 'foo'
|
28
|
+
option.short_string.should be_nil
|
29
|
+
|
30
|
+
option.accepts_arguments?.should be_false
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should have a short string and a long string' do
|
34
|
+
option = Option.new('-f', '--foo')
|
35
|
+
option.short_string.should == 'f'
|
36
|
+
option.long_string.should == 'foo'
|
37
|
+
|
38
|
+
# order shouldn't matter
|
39
|
+
option2 = Option.new('--foo', '-f')
|
40
|
+
option2.short_string.should == 'f'
|
41
|
+
option2.long_string.should == 'foo'
|
42
|
+
|
43
|
+
option.accepts_arguments?.should be_false
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should be able to accept an argument' do
|
47
|
+
Option.new('-f [x]').accepts_arguments?.should be_true
|
48
|
+
Option.new('--foo [abc]').accepts_arguments?.should be_true
|
49
|
+
Option.new('-f', '--f [123]').accepts_arguments?.should be_true
|
50
|
+
Option.new('-f [x]', '--f').accepts_arguments?.should be_true
|
51
|
+
Option.new('-f []', '--f').accepts_arguments?.should be_true
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should be able to determine if it #match a set of args' do
|
55
|
+
Option.new('-f').match(['-f']).should be_true
|
56
|
+
Option.new('-f').match(['-d']).should be_false
|
57
|
+
Option.new('-f').match(['-a', 'hi', '1', '-f', '-g']).should be_true
|
58
|
+
|
59
|
+
Option.new('-f [x]').match(['-f']).should be_false
|
60
|
+
Option.new('-f [x]').match(['-f', '-g']).should be_false
|
61
|
+
Option.new('-f [x]').match(['hi', '-f']).should be_false
|
62
|
+
Option.new('-f [x]').match(['hi', '-f', '5']).should be_true
|
63
|
+
Option.new('-f [x]').match(['hi', '-f', 'hi there']).should be_true
|
64
|
+
|
65
|
+
# should *not* run procs
|
66
|
+
Option.new('-f'){ @foo = 'w00t' }.match(['-f']).should be_true
|
67
|
+
@foo.should be_nil
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should be able to #parse args (and run proc)' do
|
71
|
+
@foo = nil
|
72
|
+
Option.new('-f'){ @foo = 'w00t' }.parse(['-x']).should be_false
|
73
|
+
@foo.should be_nil
|
74
|
+
Option.new('-f'){ @foo = 'w00t' }.parse(['-f']).should be_true
|
75
|
+
@foo.should == 'w00t'
|
76
|
+
|
77
|
+
# also shouldn't alter args
|
78
|
+
args = ['-x', '-f', '-g']
|
79
|
+
Option.new('-f'){ @foo = 'w00t again' }.parse(args).should be_true
|
80
|
+
@foo.should == 'w00t again'
|
81
|
+
args.should == ['-x', '-f', '-g']
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should be able to #parse! (destructively) args (and run proc)' do
|
85
|
+
# should alter args
|
86
|
+
@foo = nil
|
87
|
+
args = ['-x', '-f', '-g']
|
88
|
+
Option.new('-f'){ @foo = 'w00t' }.parse!(args).should be_true
|
89
|
+
@foo.should == 'w00t'
|
90
|
+
args.should == ['-x', '-g']
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should pass arguments to proc when #parse[!]'d (if accepts_arguments)" do
|
94
|
+
# should not modify
|
95
|
+
args = %w(-x -f 5 -y)
|
96
|
+
Option.new('-f [x]'){|x| @foo = "it is #{x}" }.parse(args).should be_true
|
97
|
+
@foo.should == "it is 5"
|
98
|
+
args.should == %w(-x -f 5 -y)
|
99
|
+
|
100
|
+
# should modify
|
101
|
+
args = %w(-x -f 8 -y)
|
102
|
+
Option.new('-f [x]'){|x| @foo = "it is #{x}" }.parse!(args).should be_true
|
103
|
+
@foo.should == "it is 8"
|
104
|
+
args.should == %w(-x -y)
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe OptParseSimple, 'parsing' do
|
4
|
+
|
5
|
+
before do
|
6
|
+
ARGV.clear
|
7
|
+
|
8
|
+
# make OptParseSimple act exactly like OptionParser
|
9
|
+
OptParseSimple.compatibility_mode = true
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'parse -f (compatibility mode)' do
|
13
|
+
[ OptionParser, OptParseSimple ].each do |parser|
|
14
|
+
@args = ['-a', '-f', '-x']
|
15
|
+
@foo = nil
|
16
|
+
|
17
|
+
opts = parser.new do |opts|
|
18
|
+
opts.on('-f'){ puts "-f block CALLED!"; @foo = 'w00t' }
|
19
|
+
end
|
20
|
+
|
21
|
+
@foo.should be_nil
|
22
|
+
|
23
|
+
lambda { opts.parse @args }.should raise_error(/invalid option: -a/)
|
24
|
+
|
25
|
+
# @foo.should == 'w00t'
|
26
|
+
# @args.should == ['-a', '-f', '-x']
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'parse! -f (compatibility mode)' do
|
31
|
+
[ OptionParser, OptParseSimple ].each do |parser|
|
32
|
+
@args = ['-a', '-f', '-x']
|
33
|
+
@foo = nil
|
34
|
+
|
35
|
+
opts = parser.new do |opts|
|
36
|
+
opts.on('-f'){ puts "-f block CALLED!"; @foo = 'w00t' }
|
37
|
+
end
|
38
|
+
|
39
|
+
@foo.should be_nil
|
40
|
+
|
41
|
+
lambda { opts.parse! @args }.should raise_error(/invalid option: -a/)
|
42
|
+
|
43
|
+
# @foo.should == 'w00t'
|
44
|
+
# @args.should == ['-a', '-x']
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'parse -f' do
|
49
|
+
OptParseSimple.compatibility_mode = false
|
50
|
+
|
51
|
+
@args = ['-a', '-f', '-x']
|
52
|
+
@foo = nil
|
53
|
+
|
54
|
+
opts = OptParseSimple.new do |opts|
|
55
|
+
opts.on('-f'){ puts "-f block CALLED!"; @foo = 'w00t' }
|
56
|
+
end
|
57
|
+
|
58
|
+
@foo.should be_nil
|
59
|
+
|
60
|
+
opts.parse @args
|
61
|
+
|
62
|
+
@foo.should == 'w00t'
|
63
|
+
@args.should == ['-a', '-f', '-x']
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'parse! -f' do
|
67
|
+
OptParseSimple.compatibility_mode = false
|
68
|
+
|
69
|
+
@args = ['-a', '-f', '-x']
|
70
|
+
@foo = nil
|
71
|
+
|
72
|
+
opts = OptParseSimple.new do |opts|
|
73
|
+
opts.on('-f'){ puts "-f block CALLED!"; @foo = 'w00t' }
|
74
|
+
end
|
75
|
+
|
76
|
+
@foo.should be_nil
|
77
|
+
|
78
|
+
opts.parse! @args
|
79
|
+
|
80
|
+
@foo.should == 'w00t'
|
81
|
+
@args.should == ['-a', '-x']
|
82
|
+
end
|
83
|
+
|
84
|
+
it '--foo'
|
85
|
+
|
86
|
+
it '-f, --foo'
|
87
|
+
|
88
|
+
it '-f X'
|
89
|
+
|
90
|
+
# TODO i think OptionParser supports the -f=X / --foo=X syntaxes ... i need to add support!
|
91
|
+
it '-f=X'
|
92
|
+
|
93
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../lib/optparse-simple'
|
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: remi-optparse-simple
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- remi
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-04-02 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: remi-indifferent-variable-hash
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
description: a partial reimplementation of OptionParser
|
26
|
+
email: remi@remitaylor.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files: []
|
32
|
+
|
33
|
+
files:
|
34
|
+
- Rakefile
|
35
|
+
- VERSION.yml
|
36
|
+
- README.rdoc
|
37
|
+
- lib/optparse-simple.rb
|
38
|
+
- spec/option_spec.rb
|
39
|
+
- spec/spec_helper.rb
|
40
|
+
- spec/optparse_simple_spec.rb
|
41
|
+
has_rdoc: true
|
42
|
+
homepage: http://github.com/remi/optparse-simple
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options:
|
45
|
+
- --inline-source
|
46
|
+
- --charset=UTF-8
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: "0"
|
60
|
+
version:
|
61
|
+
requirements: []
|
62
|
+
|
63
|
+
rubyforge_project:
|
64
|
+
rubygems_version: 1.2.0
|
65
|
+
signing_key:
|
66
|
+
specification_version: 2
|
67
|
+
summary: a partial reimplementation of OptionParser
|
68
|
+
test_files: []
|
69
|
+
|