linen 0.2.0 → 0.3.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/lib/linen.rb +4 -2
- data/lib/linen/argument.rb +38 -0
- data/lib/linen/cli.rb +18 -32
- data/lib/linen/command.rb +167 -0
- data/lib/linen/plugin.rb +1 -90
- data/lib/linen/workspace.rb +9 -0
- metadata +3 -1
data/lib/linen.rb
CHANGED
@@ -18,8 +18,8 @@ require 'string_extensions'
|
|
18
18
|
|
19
19
|
|
20
20
|
module Linen
|
21
|
-
VERSION = "0.
|
22
|
-
SVNRev = %q$Rev:
|
21
|
+
VERSION = "0.3.0"
|
22
|
+
SVNRev = %q$Rev: 71 $
|
23
23
|
|
24
24
|
|
25
25
|
def self::plugins
|
@@ -36,6 +36,8 @@ end
|
|
36
36
|
### Plugin Infrastructure
|
37
37
|
require 'linen/plugin_registry'
|
38
38
|
require 'linen/plugin'
|
39
|
+
require 'linen/argument'
|
40
|
+
require 'linen/command'
|
39
41
|
|
40
42
|
|
41
43
|
### Other Infrastructure
|
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
##############################################################
|
4
|
+
# Copyright 2007, LAIKA, Inc. #
|
5
|
+
# #
|
6
|
+
# Authors: #
|
7
|
+
# * Ben Bleything <bbleything@laika.com> #
|
8
|
+
##############################################################
|
9
|
+
|
10
|
+
class Linen::Plugin::Argument
|
11
|
+
attr_reader :prompt
|
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
|
20
|
+
|
21
|
+
|
22
|
+
def convert( value )
|
23
|
+
return value unless @conversion
|
24
|
+
return @conversion.call( value )
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
def validate( value )
|
29
|
+
if @validation.is_a? Proc
|
30
|
+
result = @validation.call( value )
|
31
|
+
else
|
32
|
+
result = ( value =~ @validation )
|
33
|
+
end
|
34
|
+
|
35
|
+
raise Linen::Plugin::ArgumentError, "Value '#{value}' is invalid for #{@name}." unless result
|
36
|
+
return value
|
37
|
+
end
|
38
|
+
end
|
data/lib/linen/cli.rb
CHANGED
@@ -12,7 +12,7 @@ class Linen::CLI
|
|
12
12
|
attr_accessor :prompt
|
13
13
|
end
|
14
14
|
|
15
|
-
@prompt = "linen> "
|
15
|
+
@prompt = "linen > "
|
16
16
|
|
17
17
|
def self::parse_command( input )
|
18
18
|
### * nil means ctrl-d, so exit.
|
@@ -103,42 +103,25 @@ class Linen::CLI
|
|
103
103
|
|
104
104
|
|
105
105
|
def self::execute_command( plugin, command, args )
|
106
|
-
plugin
|
107
|
-
command
|
108
|
-
|
106
|
+
plugin = Linen.plugins[ plugin ]
|
107
|
+
command = plugin.commands[ command ]
|
109
108
|
workspace = Linen::Workspace.new
|
109
|
+
results = command.validate_arguments( args )
|
110
110
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
arg_value = args.shift
|
115
|
-
|
116
|
-
begin
|
117
|
-
if arg_value.nil?
|
118
|
-
old_completion_proc = Readline.completion_proc
|
119
|
-
Readline.completion_proc = Proc.new {}
|
120
|
-
|
121
|
-
arg_value = Readline.readline( argument.prompt )
|
122
|
-
|
123
|
-
Readline.completion_proc = old_completion_proc
|
124
|
-
end
|
111
|
+
workspace.add_values( results )
|
112
|
+
command.execute( workspace )
|
113
|
+
end
|
125
114
|
|
126
|
-
argument.validate arg_value
|
127
115
|
|
128
|
-
|
129
|
-
|
116
|
+
def self::reprompt( prompt = "Re-enter: " )
|
117
|
+
old_completion_proc = Readline.completion_proc
|
118
|
+
Readline.completion_proc = proc {}
|
130
119
|
|
131
|
-
|
132
|
-
arg_value = nil
|
120
|
+
input = Readline.readline( prompt )
|
133
121
|
|
134
|
-
|
135
|
-
else
|
136
|
-
arg_value = argument.convert( arg_value )
|
137
|
-
workspace.set_value arg_name, arg_value
|
138
|
-
end
|
139
|
-
end
|
122
|
+
Readline.completion_proc = old_completion_proc
|
140
123
|
|
141
|
-
|
124
|
+
return input
|
142
125
|
end
|
143
126
|
|
144
127
|
|
@@ -208,8 +191,11 @@ To get help with a plugin, enter "help <plugin>". You may also enter
|
|
208
191
|
unless completed_command
|
209
192
|
refined_candidates = command_candidates.select {|c| c =~ /^#{command}/}
|
210
193
|
|
211
|
-
raise Linen::CLI::CommandNotFoundError,
|
212
|
-
|
194
|
+
raise Linen::CLI::CommandNotFoundError,
|
195
|
+
"Command '#{command}' not found." if refined_candidates.empty?
|
196
|
+
|
197
|
+
raise Linen::CLI::AmbiguousCommandError.new( refined_candidates, command ),
|
198
|
+
"The command you entered ('#{command}') is ambiguous; please select from the following:"
|
213
199
|
end
|
214
200
|
|
215
201
|
### if we've gotten here, we're golden. Everything is completed. Rejoice!
|
@@ -0,0 +1,167 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
##############################################################
|
4
|
+
# Copyright 2007, LAIKA, Inc. #
|
5
|
+
# #
|
6
|
+
# Authors: #
|
7
|
+
# * Ben Bleything <bbleything@laika.com> #
|
8
|
+
##############################################################
|
9
|
+
|
10
|
+
class Linen::Plugin::Command
|
11
|
+
def initialize( plugin, name, &block )
|
12
|
+
@plugin = plugin
|
13
|
+
@name = name
|
14
|
+
@arguments = []
|
15
|
+
@help_text = "No help for #{plugin.short_name} #{name}"
|
16
|
+
|
17
|
+
self.instance_eval &block
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
def execute( workspace = Linen::Workspace.new )
|
22
|
+
return workspace.instance_eval( &@action_proc )
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
def help
|
27
|
+
output = []
|
28
|
+
|
29
|
+
output << @help_text.wrap
|
30
|
+
output << nil # blank line
|
31
|
+
|
32
|
+
# this map turns our list of args into a list like this:
|
33
|
+
# <arg1> <arg2> <arg3> <arg4>...
|
34
|
+
arg_list = arguments.map {|a| "<#{a.to_s}>"}.join( ' ' )
|
35
|
+
|
36
|
+
output << "Usage: #{@plugin.short_name} #{name} #{arg_list}"
|
37
|
+
|
38
|
+
return output.join( "\n" )
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
#############################
|
43
|
+
# PLUGIN DEFINITION METHODS #
|
44
|
+
#############################
|
45
|
+
|
46
|
+
|
47
|
+
def one_of( *args )
|
48
|
+
raise Linen::Plugin::ArgumentError,
|
49
|
+
"You may not specify both required and one_of arguments" if @argument_type == :required
|
50
|
+
|
51
|
+
@argument_type = :one_of
|
52
|
+
|
53
|
+
args.each do |arg|
|
54
|
+
raise Linen::Plugin::ArgumentError,
|
55
|
+
"Argument '#{arg}' has not been defined" unless @plugin.arguments.include? arg
|
56
|
+
|
57
|
+
@arguments << arg
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
def required_arguments( *args )
|
63
|
+
raise Linen::Plugin::ArgumentError,
|
64
|
+
"You may not specify both required and one_of arguments" if @argument_type == :one_of
|
65
|
+
|
66
|
+
@argument_type = :required
|
67
|
+
|
68
|
+
args.each do |arg|
|
69
|
+
raise Linen::Plugin::ArgumentError,
|
70
|
+
"Argument '#{arg}' has not been defined" unless @plugin.arguments.include? arg
|
71
|
+
|
72
|
+
@arguments << arg
|
73
|
+
end
|
74
|
+
end
|
75
|
+
alias required_argument required_arguments
|
76
|
+
|
77
|
+
|
78
|
+
def help_message( message )
|
79
|
+
@help_text = message
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
def action( &block )
|
84
|
+
@action_proc = block
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
##################
|
89
|
+
# HELPER METHODS #
|
90
|
+
##################
|
91
|
+
|
92
|
+
def validate_arguments( args )
|
93
|
+
if @argument_type == :one_of
|
94
|
+
return validate_one_of_arguments( args.first )
|
95
|
+
else # @argument_type == :required
|
96
|
+
return validate_required_arguments( args )
|
97
|
+
end
|
98
|
+
|
99
|
+
# this is a Can't Happen(tm) so I'm comfortable with the crappy
|
100
|
+
# exception string. :P
|
101
|
+
raise "Something has happened!"
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
###########
|
106
|
+
private #
|
107
|
+
###########
|
108
|
+
|
109
|
+
### FIXME:bbleything
|
110
|
+
###
|
111
|
+
### The reprompting stuff below is going to come back to bite us when we try to
|
112
|
+
### add batch mode later... that said, in the interest of getting things out the
|
113
|
+
### door, we're going to leave it be for now.
|
114
|
+
|
115
|
+
def validate_one_of_arguments( arg )
|
116
|
+
results = IndifferentHash.new
|
117
|
+
|
118
|
+
begin
|
119
|
+
@arguments.each do |arg_name|
|
120
|
+
argument = @plugin.arguments[ arg_name ]
|
121
|
+
|
122
|
+
results[ arg_name ] = argument.validate( arg ) rescue nil
|
123
|
+
end
|
124
|
+
|
125
|
+
raise Linen::Plugin::ArgumentError,
|
126
|
+
"The value you entered ('#{arg}') is invalid for all arguments." if
|
127
|
+
results.values.compact.empty?
|
128
|
+
|
129
|
+
rescue Linen::Plugin::ArgumentError => e
|
130
|
+
print "#{e} "
|
131
|
+
|
132
|
+
arg = Linen::CLI.reprompt.split.first
|
133
|
+
|
134
|
+
retry
|
135
|
+
end
|
136
|
+
|
137
|
+
return results
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
def validate_required_arguments( args )
|
142
|
+
results = IndifferentHash.new
|
143
|
+
|
144
|
+
@arguments.each do |arg_name|
|
145
|
+
argument = @plugin.arguments[ arg_name ]
|
146
|
+
arg_value = args.shift
|
147
|
+
|
148
|
+
begin
|
149
|
+
arg_value = Linen::CLI.reprompt( argument.prompt ) if arg_value.nil?
|
150
|
+
|
151
|
+
argument.validate arg_value
|
152
|
+
|
153
|
+
rescue Linen::Plugin::ArgumentError => e
|
154
|
+
print "#{e} "
|
155
|
+
|
156
|
+
# reset arg_value to nil so we get prompted on retry
|
157
|
+
arg_value = nil
|
158
|
+
|
159
|
+
retry
|
160
|
+
else
|
161
|
+
results[ arg_name ] = argument.convert( arg_value )
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
return results
|
166
|
+
end
|
167
|
+
end
|
data/lib/linen/plugin.rb
CHANGED
@@ -59,7 +59,7 @@ class Linen::Plugin
|
|
59
59
|
### so return the hash.
|
60
60
|
def self::arguments( *args )
|
61
61
|
@defined_arguments ||= IndifferentHash.new
|
62
|
-
|
62
|
+
|
63
63
|
return @defined_arguments if args.empty?
|
64
64
|
|
65
65
|
args.each do |arg|
|
@@ -98,92 +98,3 @@ class Linen::Plugin
|
|
98
98
|
@description = input
|
99
99
|
end
|
100
100
|
end
|
101
|
-
|
102
|
-
class Linen::Plugin::Argument
|
103
|
-
attr_reader :name, :prompt
|
104
|
-
|
105
|
-
def initialize( plugin, name, opts )
|
106
|
-
@plugin = plugin
|
107
|
-
@name = name
|
108
|
-
@prompt = opts[:prompt] || "Please enter the value for #{@name}"
|
109
|
-
@validation = opts[:validation] || /^\w+$/
|
110
|
-
@conversion = opts[:conversion] || nil
|
111
|
-
end
|
112
|
-
|
113
|
-
|
114
|
-
def convert( value )
|
115
|
-
return value unless @conversion
|
116
|
-
return @conversion.call( value )
|
117
|
-
end
|
118
|
-
|
119
|
-
|
120
|
-
def validate( value )
|
121
|
-
if @validation.is_a? Proc
|
122
|
-
result = @validation.call( value )
|
123
|
-
else
|
124
|
-
result = ( value =~ @validation )
|
125
|
-
end
|
126
|
-
|
127
|
-
raise Linen::Plugin::ArgumentError, "Value '#{value}' is invalid for #{self.name}. " unless result
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
class Linen::Plugin::Command
|
132
|
-
attr_reader :name, :arguments
|
133
|
-
|
134
|
-
def initialize( plugin, name, &block )
|
135
|
-
|
136
|
-
@plugin = plugin
|
137
|
-
@name = name
|
138
|
-
@arguments = []
|
139
|
-
@help_text = "No help for #{plugin.short_name} #{name}"
|
140
|
-
|
141
|
-
self.instance_eval &block
|
142
|
-
end
|
143
|
-
|
144
|
-
|
145
|
-
def execute( workspace = Linen::Workspace.new )
|
146
|
-
return workspace.instance_eval( &@action_proc )
|
147
|
-
end
|
148
|
-
|
149
|
-
|
150
|
-
def help
|
151
|
-
output = []
|
152
|
-
|
153
|
-
output << @help_text.wrap
|
154
|
-
output << nil # blank line
|
155
|
-
|
156
|
-
# this map turns our list of args into a list like this:
|
157
|
-
# <arg1> <arg2> <arg3> <arg4>...
|
158
|
-
arg_list = arguments.map {|a| "<#{a.to_s}>"}.join( ' ' )
|
159
|
-
|
160
|
-
output << "Usage: #{@plugin.short_name} #{name} #{arg_list}"
|
161
|
-
|
162
|
-
return output.join( "\n" )
|
163
|
-
end
|
164
|
-
|
165
|
-
|
166
|
-
#######
|
167
|
-
private
|
168
|
-
#######
|
169
|
-
|
170
|
-
def required_arguments( *args )
|
171
|
-
args.each do |arg|
|
172
|
-
raise Linen::Plugin::ArgumentError,
|
173
|
-
"Argument '#{arg}' has not been defined" unless @plugin.arguments.include? arg
|
174
|
-
|
175
|
-
@arguments << arg
|
176
|
-
end
|
177
|
-
end
|
178
|
-
alias required_argument required_arguments
|
179
|
-
|
180
|
-
|
181
|
-
def help_message( message )
|
182
|
-
@help_text = message
|
183
|
-
end
|
184
|
-
|
185
|
-
|
186
|
-
def action( &block )
|
187
|
-
@action_proc = block
|
188
|
-
end
|
189
|
-
end
|
data/lib/linen/workspace.rb
CHANGED
@@ -17,4 +17,13 @@ class Linen::Workspace
|
|
17
17
|
# add ivar
|
18
18
|
instance_variable_set "@#{name}", value
|
19
19
|
end
|
20
|
+
|
21
|
+
def add_values( hash )
|
22
|
+
raise ArgumentError,
|
23
|
+
"must send a hash" unless hash.is_a? Hash
|
24
|
+
|
25
|
+
hash.each do |k,v|
|
26
|
+
self.set_value k, v
|
27
|
+
end
|
28
|
+
end
|
20
29
|
end
|
metadata
CHANGED
@@ -3,7 +3,7 @@ rubygems_version: 0.9.4
|
|
3
3
|
specification_version: 1
|
4
4
|
name: linen
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
6
|
+
version: 0.3.0
|
7
7
|
date: 2007-09-10 00:00:00 -07:00
|
8
8
|
summary: Linen - A pluggable command-line interface library
|
9
9
|
require_paths:
|
@@ -34,7 +34,9 @@ files:
|
|
34
34
|
- LICENSE
|
35
35
|
- lib/indifferent_hash.rb
|
36
36
|
- lib/linen
|
37
|
+
- lib/linen/argument.rb
|
37
38
|
- lib/linen/cli.rb
|
39
|
+
- lib/linen/command.rb
|
38
40
|
- lib/linen/exceptions.rb
|
39
41
|
- lib/linen/plugin.rb
|
40
42
|
- lib/linen/plugin_registry.rb
|