choice 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ module Choice
2
+ module Version #:nodoc:
3
+ STRING = "0.1.0"
4
+ MAJOR = 0
5
+ MINOR = 1
6
+ TINY = 0
7
+ end
8
+ end
@@ -0,0 +1,186 @@
1
+ module Choice
2
+ # This module writes to the screen. As of now, its only real use is writing
3
+ # the help screen.
4
+ module Writer #:nodoc: all
5
+
6
+ # Some constants used for printing and line widths
7
+ SHORT_LENGTH = 6
8
+ SHORT_BREAK_LENGTH = 2
9
+ LONG_LENGTH = 29
10
+ PRE_DESC_LENGTH = SHORT_LENGTH + SHORT_BREAK_LENGTH + LONG_LENGTH
11
+
12
+
13
+ # The main method. Takes a hash of arguments with the following possible
14
+ # keys, running them through the appropriate method:
15
+ # banner, header, options, footer
16
+ #
17
+ # Can also be told where to print (default STDOUT) and not to exit after
18
+ # printing the help screen, which it does by default.
19
+ def self.help(args, target = STDOUT, dont_exit = false)
20
+ # Set our printing target.
21
+ self.target = target
22
+
23
+ # The banner method needs to know about the passed options if it's going
24
+ # to do its magic. Only really needs :options if :banner is nil.
25
+ banner(args[:banner], args[:options])
26
+
27
+ # Run these three methods, passing in the appropriate hash element.
28
+ %w[header options footer].each do |meth|
29
+ send(meth, args[meth.to_sym])
30
+ end
31
+
32
+ # Exit. Unless you don't want to.
33
+ exit unless dont_exit
34
+ end
35
+
36
+ class <<self
37
+ private
38
+
39
+ # Print a passed banner or assemble the default banner, which is usage.
40
+ def banner(banner, options)
41
+ if banner
42
+ puts banner
43
+ else
44
+ # Usage needs to know about the defined options.
45
+ usage(options)
46
+ end
47
+ end
48
+
49
+ # Print our header, which is just lines after the banner and before the
50
+ # options block. Needs an array, prints each element as a line.
51
+ def header(header)
52
+ if header.is_a?(Array) and header.size > 0
53
+ header.each { |line| puts line }
54
+ end
55
+ end
56
+
57
+ # Print out the options block by going through each option and printing
58
+ # it as a line (or more). Expects an array.
59
+ def options(options)
60
+ # Do nothing if there's nothing to do.
61
+ return if options.nil? || !options.size
62
+
63
+ # If the option is a hash, run it through option_line. Otherwise
64
+ # just print it out as is.
65
+ options.each do |name, option|
66
+ if option.respond_to?(:to_h)
67
+ option_line(option.to_h)
68
+ else
69
+ puts name
70
+ end
71
+ end
72
+ end
73
+
74
+ # The heavy lifting: print a line for an option. Has intimate knowledge
75
+ # of what keys are expected.
76
+ def option_line(option)
77
+ # Expect a hash
78
+ return unless option.is_a?(Hash)
79
+
80
+ # Make this easier on us
81
+ short = option['short']
82
+ long = option['long']
83
+ line = ''
84
+
85
+ # Get the short part.
86
+ line << sprintf("%#{SHORT_LENGTH}s", short)
87
+ line << sprintf("%-#{SHORT_BREAK_LENGTH}s", (',' if short && long))
88
+
89
+ # Get the long part.
90
+ line << sprintf("%-#{LONG_LENGTH}s", long)
91
+
92
+ # Print what we have so far
93
+ print line
94
+
95
+ # If there's a desc, print it.
96
+ if option['desc']
97
+ # If the line is too long, spill over to the next line
98
+ if line.length > PRE_DESC_LENGTH
99
+ puts
100
+ print " " * PRE_DESC_LENGTH
101
+ end
102
+
103
+ puts option['desc'].shift
104
+
105
+ # If there is more than one desc line, print each one in succession
106
+ # as separate lines.
107
+ option['desc'].each do |desc|
108
+ puts ' '*37 + desc
109
+ end
110
+
111
+ else
112
+ # No desc, just print a newline.
113
+ puts
114
+
115
+ end
116
+ end
117
+
118
+ # Expects an array, prints each element as a line.
119
+ def footer(footer)
120
+ footer.each { |line| puts line } unless footer.nil?
121
+ end
122
+
123
+ # Prints the usage statement, e.g. Usage prog.rb [-abc]
124
+ # Expects an array.
125
+ def usage(options)
126
+ # Really we just need an enumerable.
127
+ return unless options.respond_to?(:each)
128
+
129
+ # Start off the options with a dash.
130
+ opts = '-'
131
+
132
+ # Figure out the option shorts.
133
+ options.dup.each do |option|
134
+ # We really need an array here.
135
+ next unless option.is_a?(Array)
136
+
137
+ # Grab the hash of the last element, which should be the second
138
+ # element.
139
+ option = option.last.to_h
140
+
141
+ # Add the short to the options string.
142
+ opts << option['short'].sub('-','') if option['short']
143
+ end
144
+
145
+ # Figure out if we actually got any options.
146
+ opts = if opts =~ /^-(.+)/
147
+ " [#{opts}]"
148
+ end.to_s
149
+
150
+ # Print it out, with our newly aquired options string.
151
+ puts "Usage: #{program}" << opts
152
+ end
153
+
154
+ # Figure out the name of this program based on what was run.
155
+ def program
156
+ if (/(\/|\\)/ =~ $0) then File.basename($0) else $0 end
157
+ end
158
+
159
+ # Set where we print.
160
+ def target=(target)
161
+ @@target = target
162
+ end
163
+
164
+ # Where do we print?
165
+ def target
166
+ @@target
167
+ end
168
+
169
+ # Fake puts
170
+ def puts(str = nil)
171
+ str = '' if str.nil?
172
+ print(str + "\n")
173
+ end
174
+
175
+ # Fake printf
176
+ def printf(format, *args)
177
+ print(sprintf(format, *args))
178
+ end
179
+
180
+ # Fake print -- just add to target, which may not be STDOUT.
181
+ def print(str)
182
+ target << str
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,119 @@
1
+ $:.unshift "../lib:lib"
2
+ require 'test/unit'
3
+ require 'choice'
4
+
5
+ class TestChoice < Test::Unit::TestCase
6
+
7
+ def setup
8
+ Choice.reset
9
+ Choice.dont_exit_on_help = true
10
+ end
11
+
12
+ def test_choices
13
+ Choice.options do
14
+ header "Tell me about yourself?"
15
+ header ""
16
+ option :band do
17
+ short "-b"
18
+ long "--band=BAND"
19
+ cast String
20
+ desc "Your favorite band."
21
+ validate /\w+/
22
+ end
23
+ option :animal do
24
+ short "-a"
25
+ long "--animal=ANIMAL"
26
+ cast String
27
+ desc "Your favorite animal."
28
+ end
29
+ footer ""
30
+ footer "--help This message"
31
+ end
32
+
33
+ band = 'LedZeppelin'
34
+ animal = 'Reindeer'
35
+
36
+ args = ['-b', band, "--animal=#{animal}"]
37
+ Choice.args = args
38
+
39
+ assert_equal band, Choice.choices['band']
40
+ assert_equal animal, Choice.choices[:animal]
41
+ assert_equal ["Tell me about yourself?", ""], Choice.header
42
+ assert_equal ["", "--help This message"], Choice.footer
43
+ end
44
+
45
+ def test_failed_parse
46
+ assert Hash.new, Choice.parse
47
+ end
48
+
49
+ HELP_STRING = ''
50
+ def test_help
51
+ Choice.output_to(HELP_STRING)
52
+ Choice.args = ['-m', 'lunch', '--help']
53
+
54
+ Choice.options do
55
+ banner "Usage: choice [-mu]"
56
+ header ""
57
+ option :meal do
58
+ short '-m'
59
+ desc 'Your favorite meal.'
60
+ end
61
+
62
+ separator ""
63
+ separator "And you eat it with..."
64
+
65
+ option :utencil do
66
+ short "-u"
67
+ long "--utencil=[UTENCIL]"
68
+ desc "Your favorite eating utencil."
69
+ end
70
+ end
71
+
72
+ help_string = <<-HELP
73
+ Usage: choice [-mu]
74
+
75
+ -m Your favorite meal.
76
+
77
+ And you eat it with...
78
+ -u, --utencil=[UTENCIL] Your favorite eating utencil.
79
+ HELP
80
+
81
+ assert_equal help_string, HELP_STRING
82
+ end
83
+
84
+ UNKNOWN_STRING = ''
85
+ def test_unknown_argument
86
+ Choice.output_to(UNKNOWN_STRING)
87
+ Choice.args = ['-m', 'lunch', '--motorcycles']
88
+
89
+ Choice.options do
90
+ banner "Usage: choice [-mu]"
91
+ header ""
92
+ option :meal do
93
+ short '-m'
94
+ desc 'Your favorite meal.'
95
+ end
96
+
97
+ separator ""
98
+ separator "And you eat it with..."
99
+
100
+ option :utencil do
101
+ short "-u"
102
+ long "--utencil=[UTENCIL]"
103
+ desc "Your favorite eating utencil."
104
+ end
105
+ end
106
+
107
+ help_string = <<-HELP
108
+ Usage: choice [-mu]
109
+
110
+ -m Your favorite meal.
111
+
112
+ And you eat it with...
113
+ -u, --utencil=[UTENCIL] Your favorite eating utencil.
114
+ HELP
115
+
116
+ assert_equal help_string, UNKNOWN_STRING
117
+ end
118
+
119
+ end
@@ -0,0 +1,76 @@
1
+ $:.unshift "../lib:lib"
2
+ require 'test/unit'
3
+ require 'choice/lazyhash'
4
+
5
+ class TestLazyHash < Test::Unit::TestCase
6
+
7
+ def test_symbol
8
+ name = 'Igor'
9
+ age = 41
10
+
11
+ hash = Choice::LazyHash.new
12
+ hash['name'] = name
13
+ hash[:age] = age
14
+
15
+ assert_equal name, hash[:name]
16
+ assert_equal age, hash[:age]
17
+ end
18
+
19
+ def test_string
20
+ name = "Frank Stein"
21
+ age = 30
22
+
23
+ hash = Choice::LazyHash.new
24
+ hash[:name] = name
25
+ hash['age'] = age
26
+
27
+ assert_equal name, hash['name']
28
+ assert_equal age, hash['age']
29
+ end
30
+
31
+ def test_store_and_fetch
32
+ name = 'Jimini Jeremiah'
33
+ job = 'Interior Decorator'
34
+
35
+ hash = Choice::LazyHash.new
36
+ hash.store('name', name)
37
+ hash.store(:job, job)
38
+
39
+ assert_equal name, hash.fetch(:name)
40
+ assert_equal job, hash.fetch('job')
41
+ end
42
+
43
+ def test_messages
44
+ star = 'Sol'
45
+ planet = 'Mars'
46
+
47
+ hash = Choice::LazyHash.new
48
+ hash.star = star
49
+ hash.planet = planet
50
+
51
+ assert_equal star, hash.star
52
+ assert_equal planet, hash.planet
53
+ end
54
+
55
+ def test_from_hash
56
+ state = 'Nebraska'
57
+ country = 'Mexico'
58
+
59
+ hash = { :state => state, :country => country }
60
+ lazy = Choice::LazyHash.new(hash)
61
+
62
+ assert_equal state, lazy['state']
63
+ assert_equal country, lazy[:country]
64
+ end
65
+
66
+ def test_to_lazyhash
67
+ hash = { :name => 'Jimmy', :age => 25 }
68
+ lazy = hash.to_lazyhash
69
+
70
+ assert_equal hash[:name], lazy.name
71
+ assert_equal hash[:name], lazy[:name]
72
+ assert_equal hash[:age], lazy.age
73
+ assert_equal hash[:age], lazy[:age]
74
+ end
75
+
76
+ end
@@ -0,0 +1,144 @@
1
+ $:.unshift "../lib:lib"
2
+ require 'test/unit'
3
+ require 'choice/option'
4
+
5
+ class TestOption < Test::Unit::TestCase
6
+ def setup
7
+ Choice.reset
8
+ @option = Choice::Option.new
9
+ end
10
+
11
+ def test_desc
12
+ line_one = "This is a description."
13
+ line_two = "I can add many lines."
14
+ line_three = "There is no limit."
15
+
16
+ assert_equal false, @option.desc?
17
+
18
+ @option.desc line_one
19
+ @option.desc line_two
20
+
21
+ assert @option.desc?
22
+ assert_equal [line_one, line_two], @option.desc
23
+
24
+ @option.desc line_three
25
+ assert @option.desc?
26
+ assert_equal [line_one, line_two, line_three], @option.desc
27
+ end
28
+
29
+ def test_choice
30
+ short = "-p"
31
+
32
+ assert_equal false, @option.short?
33
+
34
+ @option.short short
35
+
36
+ assert @option.short?
37
+ assert_equal short, @option.short
38
+ end
39
+
40
+ def test_no_choice
41
+ default = 42
42
+
43
+ assert_raise(Choice::Option::ParseError) do
44
+ @option.defaut?
45
+ end
46
+
47
+ assert_raise(Choice::Option::ParseError) do
48
+ @option.defaut default
49
+ end
50
+
51
+ assert_raise(Choice::Option::ParseError) do
52
+ @option.defaut
53
+ end
54
+
55
+ end
56
+
57
+ def test_block_choice
58
+ assert_equal false, @option.action?
59
+
60
+ @option.action do
61
+ 1 + 1
62
+ end
63
+
64
+ assert @option.action?
65
+ assert_block(&@option.action)
66
+ end
67
+
68
+ def test_default_option
69
+ option = Choice::Option.new('port')
70
+
71
+ assert_equal '-p', option.short
72
+ assert_equal '--port=PORT', option.long
73
+ end
74
+
75
+ def test_format
76
+ @option = Choice::Option.new do
77
+ validate /^\w+$/
78
+ end
79
+
80
+ assert_equal /^\w+$/, @option.validate
81
+ end
82
+
83
+ def test_dsl
84
+ @option = Choice::Option.new do
85
+ short "-h"
86
+ long "--host=HOST"
87
+ cast String
88
+ desc "The hostname."
89
+ desc "(Alphanumeric only)"
90
+ filter do
91
+ 5 * 10
92
+ end
93
+ end
94
+
95
+ assert_equal "-h", @option.short
96
+ assert_equal "--host=HOST", @option.long
97
+ assert_equal String, @option.cast
98
+ assert_equal ["The hostname.", "(Alphanumeric only)"], @option.desc
99
+ assert_equal proc { 5 * 10 }.call, @option.filter.call
100
+ end
101
+
102
+ def test_to_a
103
+ desc = "This is your description."
104
+ short = "-t"
105
+ long = "--test=METHOD"
106
+ default = :to_a
107
+
108
+ @option.desc desc
109
+ @option.short short
110
+ @option.long long
111
+ @option.default default
112
+ @option.action { 1 + 1 }
113
+ array = @option.to_a
114
+
115
+ assert_equal Choice::Option, @option.class
116
+ assert_equal Array, array.class
117
+ assert array.include?([desc])
118
+ assert array.include?(short)
119
+ assert array.include?(long)
120
+ assert array.include?(default)
121
+ assert_equal proc { 1 + 1 }.call, array.select { |a| a.is_a? Proc }.first.call
122
+ end
123
+
124
+ def test_to_h
125
+ desc = "This is your description."
126
+ short = "-t"
127
+ long = "--test=METHOD"
128
+ cast = Integer
129
+
130
+ @option.desc desc
131
+ @option.short short
132
+ @option.long long
133
+ @option.cast cast
134
+ @option.filter { 2 + 2 }
135
+ hash = @option.to_h
136
+
137
+ assert_equal Choice::Option, @option.class
138
+ assert_equal Hash, hash.class
139
+ assert_equal [desc], hash['desc']
140
+ assert_equal short, hash['short']
141
+ assert_equal cast, hash['cast']
142
+ assert_equal proc { 2 + 2 }.call, hash['filter'].call
143
+ end
144
+ end