linen 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -18,8 +18,8 @@ require 'string_extensions'
18
18
 
19
19
 
20
20
  module Linen
21
- VERSION = "0.5.0"
22
- SVNRev = %q$Rev: 119 $
21
+ VERSION = "0.6.0"
22
+ SVNRev = %q$Rev: 137 $
23
23
 
24
24
 
25
25
  def self::plugins
@@ -38,7 +38,8 @@ end
38
38
  require 'linen/plugin_registry'
39
39
  require 'linen/plugin'
40
40
  require 'linen/argument'
41
- require 'linen/command'
41
+ require 'linen/simple_command'
42
+ require 'linen/two_phase_command'
42
43
 
43
44
 
44
45
  ### handlers
@@ -19,7 +19,19 @@ class Linen::Plugin::Argument
19
19
  end
20
20
 
21
21
 
22
- def process( value )
22
+ def process( value, opts_hash = IndifferentHash.new )
23
+ opts_hash[ :mode ] ||= :simple
24
+
25
+ # if we're in edit mode, we need to take care of a couple of
26
+ # special cases before falling into the regular handling loop
27
+ if opts_hash[ :mode ] == :edit or opts_hash[ :mode ] == 'edit'
28
+ if value.empty? # means the user just hit enter. Return nil to indicate the value should be left alone
29
+ return nil
30
+ elsif value =~ /^\s+$/ # user entered only spaces. Empty string means "clear out previous value"
31
+ return ""
32
+ end
33
+ end
34
+
23
35
  raise Linen::Plugin::ArgumentError unless value =~ @regex
24
36
  return value unless @process
25
37
 
@@ -38,4 +38,5 @@ class Linen::CLI::AmbiguousCommandError < Linen::CLI::AbstractAmbiguityError
38
38
  end
39
39
  end
40
40
 
41
- class Linen::WorkspaceError < RuntimeError ; end
41
+ class Linen::WorkspaceError < RuntimeError ; end
42
+ class Linen::HandlerError < RuntimeError ; end
@@ -58,6 +58,9 @@ class Linen::CLI < Linen::Handler
58
58
  else
59
59
  plugin, command, *args = canonicalize( input ).split
60
60
 
61
+ plugin = Linen.plugins[ plugin ]
62
+ command = plugin.commands[ command ]
63
+
61
64
  execute_command plugin, command, args
62
65
  end
63
66
  end
@@ -82,9 +85,9 @@ class Linen::CLI < Linen::Handler
82
85
  private
83
86
  #######
84
87
 
85
- # def self::say( input )
86
- # puts input
87
- # end
88
+ def self::say( input = "" )
89
+ puts input
90
+ end
88
91
 
89
92
 
90
93
  def self::canonicalize( input )
@@ -108,10 +111,19 @@ class Linen::CLI < Linen::Handler
108
111
 
109
112
 
110
113
  def self::execute_command( plugin, command, args )
111
- plugin = Linen.plugins[ plugin ]
112
- command = plugin.commands[ command ]
114
+ if command.is_a? Linen::Plugin::SimpleCommand
115
+ execute_simple_command plugin, command, args
116
+ elsif command.is_a? Linen::Plugin::TwoPhaseCommand
117
+ execute_two_phase_command plugin, command, args
118
+ else
119
+ raise Linen::HandlerError, "Don't know how to execute a #{command.class}"
120
+ end
121
+ end
122
+
123
+
124
+ def self::execute_simple_command( plugin, command, args )
113
125
  workspace = Linen::Workspace.new
114
- results = command.validate_arguments( args )
126
+ workspace.add_values command.validate_arguments( args )
115
127
 
116
128
  input = ''
117
129
  if command.requires_confirmation?
@@ -133,10 +145,64 @@ class Linen::CLI < Linen::Handler
133
145
 
134
146
  return unless input =~ /^y/i
135
147
  end
136
-
137
- workspace.add_values( results )
148
+
138
149
  command.execute( workspace )
139
150
  end
151
+
152
+
153
+ def self::execute_two_phase_command( plugin, command, args )
154
+ workspace = Linen::Workspace.new
155
+
156
+ # the first argument will always be the lookup argument,
157
+ # so shift it off since we don't want it below
158
+ object = command.lookup_object( args.shift )
159
+ workspace.add_values object
160
+
161
+ puts <<-END
162
+
163
+ You will now be prompted to edit one or more fields. To accept the
164
+ current value, just hit enter. To clear it, hit <space> and then
165
+ enter. Otherwise, enter a new value.
166
+
167
+ Current values
168
+ --------------
169
+ END
170
+ command.inspect( workspace )
171
+ puts # blank line for clarity
172
+
173
+ current_values = command.editable_attrs.inject( Hash.new ) {|hash, value|
174
+ hash[ value ] = object[ command.lookup_attr ].send( value )
175
+ hash
176
+ }
177
+
178
+ workspace.add_values current_values
179
+ workspace.add_values command.validate_arguments( args )
180
+
181
+ input = ''
182
+ if command.requires_confirmation?
183
+ puts "\nRunning '#{plugin.short_name} #{command.name}' with arguments:"
184
+
185
+ puts results.map { |arg, value|
186
+ next unless value
187
+
188
+ output = value
189
+ output = value.join( ', ' ) if value.is_a? Array
190
+
191
+ "#{arg}: #{output}"
192
+ }.join( "\n" )
193
+
194
+ while input !~ /^(y|n)/i
195
+ input = Readline.readline( "\nContinue [y/N]? ")
196
+ input = "n" if input == ''
197
+ end
198
+
199
+ return unless input =~ /^y/i
200
+ end
201
+
202
+ puts
203
+
204
+ command.execute( workspace )
205
+ end
140
206
 
141
207
 
142
208
  def self::reprompt( prompt = "Re-enter: " )
@@ -69,10 +69,16 @@ class Linen::Plugin
69
69
  end
70
70
 
71
71
 
72
- def self::command( name, &block )
72
+ def self::command( name, hash = {}, &block )
73
73
  @commands ||= IndifferentHash.new
74
74
 
75
- @commands[ name ] = Command.new( self, name, &block )
75
+ if hash[ :lookup_first ]
76
+ command = TwoPhaseCommand.new( self, name, &block )
77
+ else
78
+ command = SimpleCommand.new( self, name, &block )
79
+ end
80
+
81
+ @commands[ name ] = command
76
82
  end
77
83
 
78
84
 
@@ -7,7 +7,7 @@
7
7
  # * Ben Bleything <bbleything@laika.com> #
8
8
  ##############################################################
9
9
 
10
- class Linen::Plugin::Command
10
+ class Linen::Plugin::SimpleCommand
11
11
  attr_reader :name
12
12
 
13
13
  def initialize( plugin, name, &block )
@@ -0,0 +1,177 @@
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::TwoPhaseCommand
11
+ attr_reader :name, :editable_attrs, :lookup_attr
12
+
13
+ def initialize( plugin, name, &block )
14
+ @plugin = plugin
15
+ @name = name
16
+ @arguments = []
17
+ @help_text = "No help for #{plugin.short_name} #{name}"
18
+
19
+ self.instance_eval &block
20
+ end
21
+
22
+
23
+ def execute( workspace = Linen::Workspace.new )
24
+ return workspace.instance_eval( &@action_proc )
25
+ end
26
+
27
+
28
+ def help
29
+ output = []
30
+
31
+ output << @help_text.wrap
32
+ output << nil # blank line
33
+
34
+ # this map turns our list of args into a list like this:
35
+ # <arg1> <arg2> <arg3> <arg4>...
36
+ arg_list = @arguments.map {|a| "<#{a.to_s}>"}.join( ' ' )
37
+
38
+ output << "Usage: #{@plugin.short_name} #{name} #{arg_list}"
39
+
40
+ return output.join( "\n" )
41
+ end
42
+
43
+
44
+ #############################
45
+ # PLUGIN DEFINITION METHODS #
46
+ #############################
47
+
48
+
49
+ # def one_of( *args )
50
+ # raise Linen::Plugin::ArgumentError,
51
+ # "You may not specify both required and one_of arguments" if @argument_type == :required
52
+ #
53
+ # @argument_type = :one_of
54
+ #
55
+ # args.each do |arg|
56
+ # raise Linen::Plugin::ArgumentError,
57
+ # "Argument '#{arg}' has not been defined" unless @plugin.arguments.include? arg
58
+ #
59
+ # @arguments << arg
60
+ # end
61
+ # end
62
+ #
63
+ #
64
+ # def required_arguments( *args )
65
+ # raise Linen::Plugin::ArgumentError,
66
+ # "You may not specify both required and one_of arguments" if @argument_type == :one_of
67
+ #
68
+ # @argument_type = :required
69
+ #
70
+ # args.each do |arg|
71
+ # raise Linen::Plugin::ArgumentError,
72
+ # "Argument '#{arg}' has not been defined" unless @plugin.arguments.include? arg
73
+ #
74
+ # @arguments << arg
75
+ # end
76
+ # end
77
+ # alias required_argument required_arguments
78
+
79
+ def lookup_by( attr_name, &block )
80
+ @lookup_attr = attr_name
81
+ @lookup_block = block
82
+ end
83
+
84
+
85
+ def editable_attributes( *attr_list )
86
+ @editable_attrs = attr_list
87
+ end
88
+
89
+
90
+ def help_message( message )
91
+ @help_text = message
92
+ end
93
+
94
+
95
+ def action( &block )
96
+ @action_proc = block
97
+ end
98
+
99
+
100
+ def require_confirmation
101
+ @require_confirmation = true
102
+ end
103
+
104
+
105
+ def inspect( workspace = Linen::Workspace.new, &block )
106
+ if block_given?
107
+ @inspect_proc = block
108
+ else
109
+ return workspace.instance_eval( &@inspect_proc )
110
+ end
111
+ end
112
+
113
+
114
+ ##################
115
+ # HELPER METHODS #
116
+ ##################
117
+
118
+ def lookup_object( input )
119
+ results = IndifferentHash.new
120
+
121
+ argument = @plugin.arguments[ @lookup_attr ]
122
+ arg_value = input
123
+
124
+ begin
125
+ arg_value = Linen::CLI.reprompt( argument.prompt ) if arg_value.nil?
126
+
127
+ processed_argument = argument.process( arg_value )
128
+
129
+ results[ @lookup_attr ] = @lookup_block.call( processed_argument )
130
+ rescue Linen::Plugin::ArgumentError => e
131
+ # reset arg_value to nil so we get prompted on retry
132
+ arg_value = nil
133
+
134
+ retry
135
+ end
136
+
137
+ return results
138
+ end
139
+
140
+ ### FIXME:bbleything
141
+ ###
142
+ ### The reprompting stuff below is going to come back to bite us when we try to
143
+ ### add batch mode later... that said, in the interest of getting things out the
144
+ ### door, we're going to leave it be for now.
145
+ def validate_arguments( args )
146
+ results = IndifferentHash.new
147
+
148
+ @editable_attrs.each do |arg_name|
149
+ argument = @plugin.arguments[ arg_name ]
150
+ arg_value = args.shift
151
+
152
+ begin
153
+ arg_value = Linen::CLI.reprompt( argument.prompt ) if arg_value.nil?
154
+
155
+ result = argument.process( arg_value, :mode => :edit )
156
+
157
+ if result.nil?
158
+ # user just hit enter; leave value as-is. noop.
159
+ elsif result == ""
160
+ results[ arg_name ] = ''
161
+ else
162
+ results[ arg_name ] = result
163
+ end
164
+ rescue Linen::Plugin::ArgumentError => e
165
+ # reset arg_value to nil so we get prompted on retry
166
+ arg_value = nil ; retry
167
+ end
168
+
169
+ return results
170
+ end
171
+ end
172
+
173
+
174
+ def requires_confirmation?
175
+ @require_confirmation
176
+ end
177
+ 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.5.0
6
+ version: 0.6.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:
@@ -35,13 +35,14 @@ files:
35
35
  - lib/indifferent_hash.rb
36
36
  - lib/linen
37
37
  - lib/linen/argument.rb
38
- - lib/linen/command.rb
39
38
  - lib/linen/exceptions.rb
40
39
  - lib/linen/handler.rb
41
40
  - lib/linen/handlers
42
41
  - lib/linen/handlers/cli.rb
43
42
  - lib/linen/plugin.rb
44
43
  - lib/linen/plugin_registry.rb
44
+ - lib/linen/simple_command.rb
45
+ - lib/linen/two_phase_command.rb
45
46
  - lib/linen/workspace.rb
46
47
  - lib/linen.rb
47
48
  - lib/string_extensions.rb