choice 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.
@@ -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