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 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,4 @@
1
+ ---
2
+ :patch: 0
3
+ :major: 0
4
+ :minor: 1
@@ -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
@@ -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
@@ -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
+