linen 0.5.0 → 0.6.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.
@@ -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