linen 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,15 +8,15 @@
8
8
  ##############################################################
9
9
 
10
10
  class IndifferentHash < Hash
11
- def []( key )
12
- candidate = self.fetch( key ) rescue false
13
- return candidate if candidate
11
+ def []( key )
12
+ candidate = self.fetch( key ) rescue false
13
+ return candidate if candidate
14
14
 
15
- [ :to_s, :intern ].each do |modifier|
16
- candidate = self.fetch( key.send(modifier) ) if key.respond_to? modifier rescue false
17
- return candidate if candidate
18
- end
15
+ [ :to_s, :intern ].each do |modifier|
16
+ candidate = self.fetch( key.send(modifier) ) if key.respond_to? modifier rescue false
17
+ return candidate if candidate
18
+ end
19
19
 
20
- return nil
21
- end
20
+ return nil
21
+ end
22
22
  end
@@ -18,18 +18,18 @@ require 'string_extensions'
18
18
 
19
19
 
20
20
  module Linen
21
- VERSION = "0.3.1"
22
- SVNRev = %q$Rev: 84 $
21
+ VERSION = "0.3.2"
22
+ SVNRev = %q$Rev: 86 $
23
23
 
24
24
 
25
- def self::plugins
26
- return Linen::PluginRegistry.instance
27
- end
25
+ def self::plugins
26
+ return Linen::PluginRegistry.instance
27
+ end
28
28
 
29
29
 
30
- def self::start
31
- Linen::CLI.start_loop
32
- end
30
+ def self::start
31
+ Linen::CLI.start_loop
32
+ end
33
33
  end
34
34
 
35
35
 
@@ -8,31 +8,31 @@
8
8
  ##############################################################
9
9
 
10
10
  class Linen::Plugin::Argument
11
- attr_reader :prompt
11
+ attr_reader :prompt
12
12
 
13
- def initialize( plugin, name, opts )
14
- @plugin = plugin
15
- @name = name
16
- @prompt = opts[:prompt] || "Please enter the value for #{@name}"
17
- @validation = opts[:validation] || /^\w+$/
18
- @conversion = opts[:conversion] || nil
19
- end
13
+ def initialize( plugin, name, opts )
14
+ @plugin = plugin
15
+ @name = name
16
+ @prompt = opts[:prompt] || "Please enter the value for #{@name}"
17
+ @validation = opts[:validation] || /^\w+$/
18
+ @conversion = opts[:conversion] || nil
19
+ end
20
20
 
21
21
 
22
- def convert( value )
23
- return value unless @conversion
24
- return @conversion.call( value )
25
- end
22
+ def convert( value )
23
+ return value unless @conversion
24
+ return @conversion.call( value )
25
+ end
26
26
 
27
27
 
28
- def validate( value )
29
- if @validation.is_a? Proc
30
- result = @validation.call( value )
31
- else
32
- result = ( value =~ @validation )
33
- end
28
+ def validate( value )
29
+ if @validation.is_a? Proc
30
+ result = @validation.call( value )
31
+ else
32
+ result = ( value =~ @validation )
33
+ end
34
34
 
35
- raise Linen::Plugin::ArgumentError, "Value '#{value}' is invalid for #{@name}." unless result
36
- return value
37
- end
35
+ raise Linen::Plugin::ArgumentError, "Value '#{value}' is invalid for #{@name}." unless result
36
+ return value
37
+ end
38
38
  end
@@ -8,144 +8,144 @@
8
8
  ##############################################################
9
9
 
10
10
  class Linen::CLI
11
- class << self
12
- attr_accessor :prompt
13
- end
14
-
15
- @prompt = "linen > "
16
-
17
- def self::parse_command( input )
18
- ### * nil means ctrl-d, so exit.
19
- ### * if they said "quit" or "exit", do so
20
- ### * Size == 0 means empty command, so just return.
21
- ###
22
- ### otherwise, add to history.
23
- if input.nil?
24
- ### blank line to make ctrl-d not make the error live on the existing line
25
- puts ; cleanup and exit
26
- elsif input.chomp.size == 0 # empty string
27
- return
28
- else
29
- Readline::HISTORY.push( input )
30
- end
31
-
32
- plugin, command, *arguments = input.split
33
-
34
- if ['quit', 'exit'].abbrev.include? plugin
35
- cleanup and exit
36
- elsif ['help', '?'].abbrev.include? plugin
37
- # they entered "help <plugin> <command> or some subset there of, which means
38
- # that we have the plugin in command and the command in the first element of args.
39
- plugin = command.dup rescue nil
40
- command = arguments.shift rescue nil
41
-
42
- if plugin and command
43
- plugin, command = canonicalize( "#{plugin} #{command}" ).split rescue nil
44
-
45
- # if either plugin or command is nil, lookup will fail; bail
46
- return unless plugin = Linen.plugins[ plugin ]
47
- return unless command = plugin.commands[ command ]
48
-
49
- puts command.help
50
- elsif plugin
51
- return unless plugin = Linen.plugins[ canonicalize( plugin ) ]
52
- puts plugin.help
53
- else
54
- help
55
- end
56
- elsif plugin.nil? or command.nil?
57
- puts "You must enter both a plugin name and a command."
58
- else
59
- plugin, command, *args = canonicalize( input ).split
60
-
61
- execute_command plugin, command, args
62
- end
63
- end
64
-
65
-
66
- def self::start_loop
67
- loop do
68
- begin
69
- input = Readline.readline( @prompt )
70
- rescue Interrupt
71
- puts "\nPlease type 'quit' or 'exit' to quit."
72
- else
73
- parse_command input
74
- end
75
-
76
- puts # blank line to clean things up
77
- end
78
- end
79
-
80
-
81
- #######
82
- private
83
- #######
84
-
85
- def self::canonicalize( input )
86
- begin
87
- expansion = expand_command( input )
88
- rescue Linen::CLI::PluginNotFoundError, Linen::CLI::CommandNotFoundError, Linen::CLI::AmbiguousPluginError, Linen::CLI::AmbiguousCommandError => e
89
- puts e
90
- end
91
-
92
- return expansion
93
- end
94
-
95
-
96
- def self::cleanup
97
- puts "Exiting..."
98
-
99
- Linen.plugins.each do |p|
100
- p.cleanup
101
- end
102
- end
103
-
104
-
105
- def self::execute_command( plugin, command, args )
106
- plugin = Linen.plugins[ plugin ]
107
- command = plugin.commands[ command ]
108
- workspace = Linen::Workspace.new
109
- results = command.validate_arguments( args )
110
-
111
- input = ''
112
- if command.requires_confirmation?
113
- puts "Running '#{plugin.short_name} #{command.name}' with arguments:"
114
-
115
- puts results.map { |arg, value|
116
- next unless value
117
- "#{arg}: #{value}"
118
- }.join( "\n" )
119
-
120
- while input !~ /^(y|n)/i
121
- input = Readline.readline( "\nContinue [y/N]? ")
122
- input = "n" if input == ''
123
- end
124
- end
125
-
126
- if input =~ /^y/i
127
- puts # blank line
128
-
129
- workspace.add_values( results )
130
- command.execute( workspace )
131
- end
132
- end
133
-
134
-
135
- def self::reprompt( prompt = "Re-enter: " )
136
- old_completion_proc = Readline.completion_proc
137
- Readline.completion_proc = proc {}
11
+ class << self
12
+ attr_accessor :prompt
13
+ end
14
+
15
+ @prompt = "linen > "
16
+
17
+ def self::parse_command( input )
18
+ ### * nil means ctrl-d, so exit.
19
+ ### * if they said "quit" or "exit", do so
20
+ ### * Size == 0 means empty command, so just return.
21
+ ###
22
+ ### otherwise, add to history.
23
+ if input.nil?
24
+ ### blank line to make ctrl-d not make the error live on the existing line
25
+ puts ; cleanup and exit
26
+ elsif input.chomp.size == 0 # empty string
27
+ return
28
+ else
29
+ Readline::HISTORY.push( input )
30
+ end
31
+
32
+ plugin, command, *arguments = input.split
33
+
34
+ if ['quit', 'exit'].abbrev.include? plugin
35
+ cleanup and exit
36
+ elsif ['help', '?'].abbrev.include? plugin
37
+ # they entered "help <plugin> <command> or some subset there of, which means
38
+ # that we have the plugin in command and the command in the first element of args.
39
+ plugin = command.dup rescue nil
40
+ command = arguments.shift rescue nil
41
+
42
+ if plugin and command
43
+ plugin, command = canonicalize( "#{plugin} #{command}" ).split rescue nil
44
+
45
+ # if either plugin or command is nil, lookup will fail; bail
46
+ return unless plugin = Linen.plugins[ plugin ]
47
+ return unless command = plugin.commands[ command ]
48
+
49
+ puts command.help
50
+ elsif plugin
51
+ return unless plugin = Linen.plugins[ canonicalize( plugin ) ]
52
+ puts plugin.help
53
+ else
54
+ help
55
+ end
56
+ elsif plugin.nil? or command.nil?
57
+ puts "You must enter both a plugin name and a command."
58
+ else
59
+ plugin, command, *args = canonicalize( input ).split
60
+
61
+ execute_command plugin, command, args
62
+ end
63
+ end
64
+
65
+
66
+ def self::start_loop
67
+ loop do
68
+ begin
69
+ input = Readline.readline( @prompt )
70
+ rescue Interrupt
71
+ puts "\nPlease type 'quit' or 'exit' to quit."
72
+ else
73
+ parse_command input
74
+ end
75
+
76
+ puts # blank line to clean things up
77
+ end
78
+ end
79
+
80
+
81
+ #######
82
+ private
83
+ #######
84
+
85
+ def self::canonicalize( input )
86
+ begin
87
+ expansion = expand_command( input )
88
+ rescue Linen::CLI::PluginNotFoundError, Linen::CLI::CommandNotFoundError, Linen::CLI::AmbiguousPluginError, Linen::CLI::AmbiguousCommandError => e
89
+ puts e
90
+ end
91
+
92
+ return expansion
93
+ end
94
+
95
+
96
+ def self::cleanup
97
+ puts "Exiting..."
98
+
99
+ Linen.plugins.each do |p|
100
+ p.cleanup
101
+ end
102
+ end
103
+
104
+
105
+ def self::execute_command( plugin, command, args )
106
+ plugin = Linen.plugins[ plugin ]
107
+ command = plugin.commands[ command ]
108
+ workspace = Linen::Workspace.new
109
+ results = command.validate_arguments( args )
110
+
111
+ input = ''
112
+ if command.requires_confirmation?
113
+ puts "\nRunning '#{plugin.short_name} #{command.name}' with arguments:"
114
+
115
+ puts results.map { |arg, value|
116
+ next unless value
117
+ "#{arg}: #{value}"
118
+ }.join( "\n" )
119
+
120
+ while input !~ /^(y|n)/i
121
+ input = Readline.readline( "\nContinue [y/N]? ")
122
+ input = "n" if input == ''
123
+ end
124
+ end
125
+
126
+ if input =~ /^y/i
127
+ puts # blank line
138
128
 
139
- input = Readline.readline( prompt )
129
+ workspace.add_values( results )
130
+ command.execute( workspace )
131
+ end
132
+ end
140
133
 
141
- Readline.completion_proc = old_completion_proc
142
-
143
- return input
144
- end
145
134
 
135
+ def self::reprompt( prompt = "Re-enter: " )
136
+ old_completion_proc = Readline.completion_proc
137
+ Readline.completion_proc = proc {}
146
138
 
147
- def self::help
148
- puts <<-END
139
+ input = Readline.readline( prompt )
140
+
141
+ Readline.completion_proc = old_completion_proc
142
+
143
+ return input
144
+ end
145
+
146
+
147
+ def self::help
148
+ puts <<-END
149
149
  Usage: <plugin> <command> [<argument>, <argument>...]
150
150
 
151
151
  You may shorten the plugin and commands as long as the abbreviation
@@ -154,92 +154,92 @@ is non-ambiguous. For example, given two plugins 'addition' and
154
154
 
155
155
  Available plugins and commands:
156
156
 
157
- END
157
+ END
158
158
 
159
- Linen.plugins.each do |plugin|
160
- puts "- #{plugin.short_name}"
159
+ Linen.plugins.each do |plugin|
160
+ puts "- #{plugin.short_name}"
161
161
 
162
- plugin.commands.each do |name, command|
163
- puts " - #{name}"
164
- end
165
- end
162
+ plugin.commands.each do |name, command|
163
+ puts " - #{name}"
164
+ end
165
+ end
166
166
 
167
- puts <<-END
167
+ puts <<-END
168
168
 
169
169
  To get help with a plugin, enter "help <plugin>". You may also enter
170
170
  "help <plugin> <command>" for help on a specific command.
171
- END
172
- end
173
-
174
-
175
- ### First, try to complete the plugin name. If we can't,
176
- ### raise an exception saying so.
177
- ###
178
- ### Second, try to complete the command name. If we can't,
179
- ### raise an exception saying so.
180
- ###
181
- ### The caller is now responsible for flow control.
182
- def self::expand_command( str )
183
- plugin_candidates = Linen.plugins.collect {|p| p.short_name}.sort
184
-
185
- ### empty string means we're trying to complete the plugin with nothing to go on
186
- raise Linen::CLI::AmbiguousPluginError.new( plugin_candidates ) if str.empty?
187
-
188
- plugin, command, *arguments = str.split
189
-
190
- ### attempt to complete the plugin, raising an exception if it failes
191
- completed_plugin = plugin_candidates.abbrev[ plugin ]
192
-
193
- unless completed_plugin
194
- refined_candidates = plugin_candidates.select {|p| p =~ /^#{plugin}/}
195
-
196
- raise Linen::CLI::PluginNotFoundError, "Plugin '#{plugin}' not found." if refined_candidates.empty?
197
- raise Linen::CLI::AmbiguousPluginError.new( refined_candidates, plugin )
198
- end
199
-
200
- ### if there's no command entered and no space after the plugin,
201
- ### just return the plugin
202
- return completed_plugin if command.nil? and str !~ /\s$/
203
-
204
- ### If we've gotten here, we've now got the plugin in completed_plugin,
205
- ### so attempt to complete the command
206
- command_candidates = Linen.plugins[ completed_plugin ].commands.keys.map {|k| k.to_s}.sort
207
-
208
- completed_command = command_candidates.abbrev[ command ]
209
-
210
- unless completed_command
211
- refined_candidates = command_candidates.select {|c| c =~ /^#{command}/}
212
-
213
- raise Linen::CLI::CommandNotFoundError,
214
- "Command '#{command}' not found." if refined_candidates.empty?
215
-
216
- raise Linen::CLI::AmbiguousCommandError.new( refined_candidates, command ),
217
- "The command you entered ('#{command}') is ambiguous; please select from the following:"
218
- end
219
-
220
- ### if we've gotten here, we're golden. Everything is completed. Rejoice!
221
- output = completed_plugin
222
- output << " " + completed_command if completed_command
223
- output << " " + arguments.join(' ') unless arguments.empty?
224
-
225
- return output
226
- end
227
-
228
- Readline.basic_word_break_characters = ""
229
-
230
- Readline.completion_proc = proc do |str|
231
- begin
232
- output = expand_command( str )
233
- rescue Linen::CLI::PluginNotFoundError, Linen::CLI::CommandNotFoundError => e
234
- output = ''
235
- rescue Linen::CLI::AmbiguousPluginError => e
236
- output = e.candidates
237
- rescue Linen::CLI::AmbiguousCommandError => e
238
- output = e.candidates.map {|c| "#{str.split.first} #{c}"}
239
- ensure
240
- return output
241
- end
242
- end
171
+ END
172
+ end
173
+
174
+
175
+ ### First, try to complete the plugin name. If we can't,
176
+ ### raise an exception saying so.
177
+ ###
178
+ ### Second, try to complete the command name. If we can't,
179
+ ### raise an exception saying so.
180
+ ###
181
+ ### The caller is now responsible for flow control.
182
+ def self::expand_command( str )
183
+ plugin_candidates = Linen.plugins.collect {|p| p.short_name}.sort
184
+
185
+ ### empty string means we're trying to complete the plugin with nothing to go on
186
+ raise Linen::CLI::AmbiguousPluginError.new( plugin_candidates ) if str.empty?
187
+
188
+ plugin, command, *arguments = str.split
189
+
190
+ ### attempt to complete the plugin, raising an exception if it failes
191
+ completed_plugin = plugin_candidates.abbrev[ plugin ]
192
+
193
+ unless completed_plugin
194
+ refined_candidates = plugin_candidates.select {|p| p =~ /^#{plugin}/}
195
+
196
+ raise Linen::CLI::PluginNotFoundError, "Plugin '#{plugin}' not found." if refined_candidates.empty?
197
+ raise Linen::CLI::AmbiguousPluginError.new( refined_candidates, plugin )
198
+ end
199
+
200
+ ### if there's no command entered and no space after the plugin,
201
+ ### just return the plugin
202
+ return completed_plugin if command.nil? and str !~ /\s$/
203
+
204
+ ### If we've gotten here, we've now got the plugin in completed_plugin,
205
+ ### so attempt to complete the command
206
+ command_candidates = Linen.plugins[ completed_plugin ].commands.keys.map {|k| k.to_s}.sort
207
+
208
+ completed_command = command_candidates.abbrev[ command ]
209
+
210
+ unless completed_command
211
+ refined_candidates = command_candidates.select {|c| c =~ /^#{command}/}
212
+
213
+ raise Linen::CLI::CommandNotFoundError,
214
+ "Command '#{command}' not found." if refined_candidates.empty?
215
+
216
+ raise Linen::CLI::AmbiguousCommandError.new( refined_candidates, command ),
217
+ "The command you entered ('#{command}') is ambiguous; please select from the following:"
218
+ end
219
+
220
+ ### if we've gotten here, we're golden. Everything is completed. Rejoice!
221
+ output = completed_plugin
222
+ output << " " + completed_command if completed_command
223
+ output << " " + arguments.join(' ') unless arguments.empty?
224
+
225
+ return output
226
+ end
227
+
228
+ Readline.basic_word_break_characters = ""
229
+
230
+ Readline.completion_proc = proc do |str|
231
+ begin
232
+ output = expand_command( str )
233
+ rescue Linen::CLI::PluginNotFoundError, Linen::CLI::CommandNotFoundError => e
234
+ output = ''
235
+ rescue Linen::CLI::AmbiguousPluginError => e
236
+ output = e.candidates
237
+ rescue Linen::CLI::AmbiguousCommandError => e
238
+ output = e.candidates.map {|c| "#{str.split.first} #{c}"}
239
+ ensure
240
+ return output
241
+ end
242
+ end
243
243
  end
244
244
 
245
245
  Signal.trap( 'INT' ) { raise Interrupt }