main 0.0.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,54 +1,113 @@
1
1
  module Attributes
2
- VERSION = '3.2.0'
2
+ Attributes::VERSION = '4.0.0' unless defined? Attributes::VERSION
3
+ def self.version() Attributes::VERSION end
3
4
 
4
- def version() VERSION end
5
+ class List < ::Array
6
+ def << element
7
+ super
8
+ self
9
+ ensure
10
+ uniq!
11
+ index!
12
+ end
13
+ def index!
14
+ @index ||= Hash.new
15
+ each{|element| @index[element] = true}
16
+ end
17
+ def include? element
18
+ @index ||= Hash.new
19
+ @index[element] ? true : false
20
+ end
21
+ def initializers
22
+ @initializers ||= Hash.new
23
+ end
24
+ end
5
25
 
6
26
  def attributes *a, &b
7
27
  unless a.empty?
8
- hashes, names = a.partition{|x| Hash === x}
28
+ returned = Hash.new
9
29
 
30
+ hashes, names = a.partition{|x| Hash === x}
10
31
  names_and_defaults = {}
11
32
  hashes.each{|h| names_and_defaults.update h}
12
33
  names.flatten.compact.each{|name| names_and_defaults.update name => nil}
13
34
 
35
+ initializers = __attributes__.initializers
36
+
14
37
  names_and_defaults.each do |name, default|
15
- init = b || lambda { default }
16
- ivar, getter, setter, query = "@#{ name }", "#{ name }", "#{ name }=", "#{ name }?"
38
+ raise NameError, "bad instance variable name '@#{ name }'" if "@#{ name }" =~ %r/[!?=]$/o
39
+ name = name.to_s
17
40
 
18
- define_method(setter) do |value|
19
- instance_variable_set ivar, value
41
+ initialize = b || lambda { default }
42
+ initializer = lambda do |this|
43
+ Object.instance_method('instance_eval').bind(this).call &initialize
20
44
  end
45
+ initializer_id = initializer.object_id
46
+ __attributes__.initializers[name] = initializer
21
47
 
22
- define_method(getter) do |*value|
23
- unless value.empty?
24
- send setter, value.shift
25
- else
26
- defined = instance_eval "defined? #{ ivar }"
27
- send setter, instance_eval(&init) unless defined
28
- instance_variable_get ivar
48
+ module_eval <<-code
49
+ def #{ name }=(value)
50
+ @#{ name } = value
29
51
  end
30
- end
31
-
32
- alias_method query, getter
52
+ code
53
+ module_eval <<-code
54
+ def #{ name }(*value)
55
+ return self.#{ name } = value.first unless value.empty?
56
+ #{ name }! unless defined? @#{ name }
57
+ @#{ name }
58
+ end
59
+ code
60
+ module_eval <<-code
61
+ def #{ name }!
62
+ initializer = ObjectSpace._id2ref #{ initializer_id }
63
+ self.#{ name } = initializer.call(self)
64
+ @#{ name }
65
+ end
66
+ code
67
+ module_eval <<-code
68
+ def #{ name }?
69
+ #{ name }
70
+ end
71
+ code
33
72
 
34
- (attributes << name).uniq!
35
- attributes
73
+ attributes << name
74
+ returned[name] = initializer
36
75
  end
76
+
77
+ returned
37
78
  else
38
- @attributes ||= []
79
+ begin
80
+ __attribute_list__
81
+ rescue NameError
82
+ singleton_class =
83
+ class << self
84
+ self
85
+ end
86
+ klass = self
87
+ singleton_class.module_eval do
88
+ attribute_list = List.new
89
+ define_method('attribute_list'){ klass == self ? attribute_list : raise(NameError) }
90
+ alias_method '__attribute_list__', 'attribute_list'
91
+ end
92
+ __attribute_list__
93
+ end
39
94
  end
40
95
  end
41
96
 
42
- def attribute(*a, &b) attributes *a, &b end
97
+ %w( __attributes__ __attribute__ attribute ).each{|dst| alias_method dst, 'attributes'}
43
98
  end
44
99
 
45
100
  class Object
46
101
  def attributes *a, &b
47
- sc = class << self; self; end
102
+ sc =
103
+ class << self
104
+ self
105
+ end
48
106
  sc.attributes *a, &b
49
107
  end
50
-
51
- def attribute(*a, &b) attributes *a, &b end
108
+ %w( __attributes__ __attribute__ attribute ).each{|dst| alias_method dst, 'attributes'}
52
109
  end
53
110
 
54
- class Module; include Attributes; end
111
+ class Module
112
+ include Attributes
113
+ end
data/lib/main/base.rb CHANGED
@@ -1,10 +1,78 @@
1
1
  module Main
2
2
  class Base
3
3
  class << self
4
- attribute( 'program' ){ File.basename $0 }
4
+ def wrap_run!
5
+ const_set :RUN, instance_method(:run)
6
+
7
+ class_eval do
8
+ def run *a, &b
9
+ begin
10
+ argv.push "--#{ argv.shift }" if argv.first == 'help'
11
+
12
+ return mode_run! if mode_given?
13
+
14
+ parse_parameters
15
+
16
+ if params['help'] and params['help'].given?
17
+ print usage.to_s
18
+ exit
19
+ end
20
+
21
+ pre_run
22
+
23
+ self.class.const_get(:RUN).bind(self).call(*a, &b)
24
+
25
+ post_run
26
+
27
+ finalize
28
+ rescue Exception => e
29
+ handle_exception e
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ def method_added m
36
+ return if @in_method_added
37
+ super if defined? super
38
+ @in_method_added = true
39
+ begin
40
+ wrap_run! if m.to_s == 'run'
41
+ ensure
42
+ @in_method_added = false
43
+ end
44
+ end
45
+
46
+ def self.inheritable_attribute name, &block
47
+ block ||= lambda{}
48
+ attribute( name ){
49
+ catch :value do
50
+ if parent?
51
+ value = parent.send name
52
+ value =
53
+ begin
54
+ Util.mcp value
55
+ rescue
56
+ value.clone rescue value.dup
57
+ end
58
+ throw :value, value
59
+ end
60
+ instance_eval &block
61
+ end
62
+ }
63
+ end
64
+
5
65
  attribute( 'name' ){ File.basename $0 }
6
- attribute( 'synopsis' ){ Usage.default_synopsis self }
66
+ attribute( 'synopsis' ){ Usage.default_synopsis(self) }
7
67
  attribute( 'description' )
68
+ attribute( 'usage' ){ Usage.default_usage self }
69
+ attribute( 'modes' ){ Mode.list }
70
+ attribute( 'mode_name' ){ 'main' }
71
+ def mode_name=(value) @mode_name = Mode.new value end
72
+ attribute( 'parent' ){ nil }
73
+ attribute( 'children' ){ Set.new }
74
+
75
+ attribute( 'program' ){ File.basename $0 }
8
76
  attribute( 'author' )
9
77
  attribute( 'version' )
10
78
  attribute( 'stdin' ){ $stdin }
@@ -16,12 +84,46 @@ module Main
16
84
  attribute( 'exit_success' ){ EXIT_SUCCESS }
17
85
  attribute( 'exit_failure' ){ EXIT_FAILURE }
18
86
  attribute( 'exit_warn' ){ EXIT_WARN }
19
- attribute( 'usage' ){ Usage.default_usage self }
87
+ inheritable_attribute( 'parameters' ){ Parameter::List[] }
20
88
 
21
- def parameters
22
- @parameters ||= Parameter::List[]
89
+ def create parent = Base, *a, &b
90
+ Class.new parent do |child|
91
+ child.parent = parent unless parent == Base
92
+ parent.children.add child
93
+ child.context do
94
+ child.class_eval &b if b
95
+ child.default_options!
96
+ end
97
+ end
23
98
  end
24
99
 
100
+ def context &block
101
+ @@context ||= []
102
+ unless block
103
+ @@context.last
104
+ else
105
+ begin
106
+ @@context.push self
107
+ block.call @@context.last
108
+ ensure
109
+ @@context.pop
110
+ end
111
+ end
112
+ end
113
+
114
+ def fully_qualified_mode
115
+ list = []
116
+ ancestors.each do |ancestor|
117
+ break unless ancestor < Base
118
+ list << ancestor.mode_name
119
+ end
120
+ list.reverse[1..-1]
121
+ end
122
+
123
+ def run(&b) define_method(:run, &b) end
124
+ end
125
+
126
+ module DSL
25
127
  def parameter *a, &b
26
128
  parameters << Parameter.new(*a, &b)
27
129
  end
@@ -33,7 +135,7 @@ module Main
33
135
  alias_method 'switch', 'option'
34
136
 
35
137
  def default_options!
36
- option 'help', 'h'
138
+ option 'help', 'h' unless parameters.has_option?('help', 'h')
37
139
  end
38
140
 
39
141
  def argument *a, &b
@@ -51,10 +153,16 @@ module Main
51
153
  end
52
154
  alias_method 'env', 'environment'
53
155
 
54
- def run *a, &b
55
- define_method 'run' &b
156
+ def mode name, &b
157
+ klass =
158
+ create context do
159
+ mode_name name
160
+ module_eval &b if b
161
+ end
162
+ modes.add klass
56
163
  end
57
164
  end
165
+ extend DSL
58
166
 
59
167
  attribute 'argv'
60
168
  attribute 'env'
@@ -77,14 +185,6 @@ module Main
77
185
  alias_method "#{ dst }?", "params?"
78
186
  end
79
187
 
80
- %w( exit_success! exit_failure! exit_warn! ).each do |code|
81
- module_eval <<-code
82
- def #{ code } *a, &b
83
- exit #{ code.chop }
84
- end
85
- code
86
- end
87
-
88
188
  %w( debug info warn fatal error ).each do |m|
89
189
  module_eval <<-code
90
190
  def #{ m } *a, &b
@@ -137,6 +237,7 @@ module Main
137
237
  @logger.level = logger_level
138
238
  end
139
239
  end
240
+ @logger
140
241
  end
141
242
 
142
243
  def setup_io_restoration
@@ -160,12 +261,7 @@ module Main
160
261
  rescue
161
262
  $stdin = @stdin
162
263
  ::Object.const_set 'STDIN', @stdin
163
- #p STDIN
164
- #p STDIN.read
165
264
  end
166
- #p 'STDIN' => STDIN
167
- #p '@stdin' => @stdin
168
- #p '$stdin' => $stdin
169
265
  end
170
266
  end
171
267
 
@@ -209,10 +305,37 @@ module Main
209
305
  end
210
306
  def post_parse_parameters() :hook end
211
307
 
212
- def pre_run() end
308
+ def pre_run() :hook end
213
309
  def run
214
310
  raise NotImplementedError, 'run not defined'
215
311
  end
216
- def post_run() end
312
+ def post_run() :hook end
313
+
314
+ def mode_given?
315
+ self.class.modes.find_by_mode argv.first, :quiet => true if argv.first
316
+ end
317
+
318
+ def mode_run!
319
+ mode = self.class.modes.find_by_mode argv.shift
320
+ klass = self.class.modes[mode] or abort "bad mode <#{ mode }>"
321
+ main = klass.new @argv, @env, @opts
322
+ main.run
323
+ end
324
+
325
+ def handle_exception e
326
+ if e.respond_to? :status
327
+ exit_status(( e.status ))
328
+ end
329
+
330
+ if Softspoken === e or SystemExit === e
331
+ stderr.puts e.message unless(SystemExit === e and e.message.to_s == 'exit')
332
+ else
333
+ fatal{ e }
334
+ end
335
+
336
+ exit_status(( exit_failure )) if exit_status == exit_success
337
+ exit_status(( Integer(exit_status) rescue(exit_status ? 0 : 1) ))
338
+ exit exit_status
339
+ end
217
340
  end
218
341
  end
@@ -1,15 +1,19 @@
1
1
  module Main
2
- def self.new *a, &b
3
- klass = Class.new Base
4
- klass.default_options!
5
- klass.class_eval &b if b
6
- main = klass.new *a, &b
7
- Proxy.new main
2
+ def Main.create *a, &b
3
+ ::Main::Base.create(::Main::Base, *a, &b)
4
+ end
5
+
6
+ def Main.new *a, &b
7
+ create(::Main::Base, &b).new *a
8
+ end
9
+
10
+ def Main.run argv = ARGV, env = ENV, opts = {}, &block
11
+ Base.create(&block).new(argv, env, opts).run
8
12
  end
9
13
 
10
14
  module ::Kernel
11
- def Main *a, &b
12
- ::Main.new(*a, &b).run
15
+ def Main argv = ARGV, env = ENV, opts = {}, &block
16
+ ::Main.run argv, env, opts, &block
13
17
  end
14
18
  alias_method 'main', 'Main'
15
19
  end
data/lib/main/mode.rb ADDED
@@ -0,0 +1,42 @@
1
+ class Mode < ::String
2
+ class Error < ::StandardError; end
3
+ class Duplicate < Error; end
4
+ class Ambigous < Error
5
+ include Main::Softspoken
6
+ end
7
+
8
+ class List < ::Array
9
+ def initialize *a, &b
10
+ super
11
+ ensure
12
+ self.fields = []
13
+ end
14
+ def add klass
15
+ mode_name = Mode.new klass.mode_name
16
+ raise Duplicate, mode_name if has_key? mode_name
17
+ self[mode_name] = klass
18
+ end
19
+ def find_by_mode m, options = {}
20
+ quiet = options['quiet'] || options[:quiet]
21
+ each_pair do |mode, klass|
22
+ return mode if mode == m
23
+ end
24
+ candidates = []
25
+ each_pair do |mode, klass|
26
+ candidates << mode if mode.index(m) == 0
27
+ end
28
+ case candidates.size
29
+ when 0
30
+ nil
31
+ when 1
32
+ candidates.first
33
+ else
34
+ quiet ? nil : raise(Ambigous, m)
35
+ end
36
+ end
37
+ end
38
+
39
+ def self.list *a, &b
40
+ List.new *a, &b
41
+ end
42
+ end
@@ -1,8 +1,11 @@
1
1
  module Main
2
2
  class Parameter
3
- class Error < StandardError; attribute 'wrapped'; end
4
- class NotGiven < Error; end
3
+ class Error < StandardError
4
+ include Softspoken
5
+ attribute 'wrapped'
6
+ end
5
7
  class Arity < Error; end
8
+ class NotGiven < Arity; end
6
9
  class InValid < Error; end
7
10
  class NoneSuch < Error; end
8
11
  class AmbigousOption < Error; end
@@ -11,10 +14,6 @@ module Main
11
14
  class InvalidOption < Error; end
12
15
 
13
16
  class << self
14
- def new *a, &b
15
- raise
16
- end
17
-
18
17
  def wrapped_error w
19
18
  e = Error.new "(#{ w.message } (#{ w.class }))"
20
19
  e.wrapped = w
@@ -137,33 +136,35 @@ module Main
137
136
 
138
137
  def setup!
139
138
  return false unless given?
140
- validate_arity!
141
- cast!
142
- validate!
139
+ check_arity
140
+ apply_casting
141
+ check_validation
143
142
  true
144
143
  end
145
144
 
146
- def validate_arity!
147
- raise Arity, "#{ typename })" if values.size.zero? and argument_required?
148
- if values.size < arity
149
- if argument_required?
150
- raise Arity, "#{ typename }) #{ values.size }/#{ arity }" if(values.size < arity)
151
- elsif argument_optional?
152
- raise Arity, "#{ typename }) #{ values.size }/#{ arity }" if(values.size < arity and values.size > 0)
153
- end
145
+ def check_arity
146
+ (raise Arity, "#{ typename })" if values.size.zero? and argument_required?) unless arity == -1
147
+
148
+ if arity >= 0
149
+ min = arity
150
+ sign = ''
151
+ else
152
+ min = arity.abs - 1
153
+ sign = '-'
154
154
  end
155
- end
156
155
 
157
- def validate!
158
- if validate?
159
- values.each do |value|
160
- validate[value] or
161
- raise InValid, "#{ typename }=#{ value.inspect }"
156
+ arity = min
157
+
158
+ if values.size < arity
159
+ if argument_required? or argument_none?
160
+ raise Arity, "#{ typename }) #{ values.size }/#{ sign }#{ arity }" if(values.size < arity)
161
+ elsif argument_optional?
162
+ raise Arity, "#{ typename }) #{ values.size }/#{ sign }#{ arity }" if(values.size < arity and values.size > 0)
162
163
  end
163
164
  end
164
165
  end
165
166
 
166
- def cast!
167
+ def apply_casting
167
168
  if cast?
168
169
  op = cast.respond_to?('call') ? cast : Cast[cast]
169
170
  values.map! do |val|
@@ -172,15 +173,24 @@ module Main
172
173
  end
173
174
  end
174
175
 
176
+ def check_validation
177
+ if validate?
178
+ values.each do |value|
179
+ validate[value] or
180
+ raise InValid, "invalid: #{ typename }=#{ value.inspect }"
181
+ end
182
+ end
183
+ end
184
+
175
185
  class Argument < Parameter
176
- attribute 'required' => true
186
+ attribute 'required' => true
177
187
 
178
188
  attribute 'synopsis' do
179
189
  label = name
180
190
  op = required ? "->" : "~>"
181
191
  value = defaults.size > 0 ? "#{ name }=#{ defaults.join ',' }" : name
182
192
  value = "#{ cast }(#{ value })" if cast
183
- "#{ label } [ #{ arity } #{ op } #{ value } ]"
193
+ "#{ label } (#{ arity } #{ op } #{ value })"
184
194
  end
185
195
  end
186
196
 
@@ -196,7 +206,7 @@ module Main
196
206
  op = required ? "->" : "~>"
197
207
  value = defaults.size > 0 ? "#{ name }=#{ defaults.join ',' }" : name
198
208
  value = "#{ cast }(#{ value })" if cast
199
- "#{ label } [ #{ arity } #{ op } #{ value } ]"
209
+ "#{ label } (#{ arity } #{ op } #{ value })"
200
210
  else
201
211
  label
202
212
  end
@@ -212,7 +222,7 @@ module Main
212
222
  op = required ? "->" : "~>"
213
223
  value = defaults.size > 0 ? "#{ name }=#{ defaults.join ',' }" : name
214
224
  value = "#{ cast }(#{ value })" if cast
215
- "#{ label } [ #{ arity } #{ op } #{ value } ]"
225
+ "#{ label } (#{ arity } #{ op } #{ value })"
216
226
  end
217
227
  end
218
228
 
@@ -224,7 +234,7 @@ module Main
224
234
  op = required ? "->" : "~>"
225
235
  value = defaults.size > 0 ? "#{ name }=#{ defaults.join ',' }" : name
226
236
  value = "#{ cast }(#{ value })" if cast
227
- "#{ label } [ #{ arity } #{ op } #{ value } ]"
237
+ "#{ label } (#{ arity } #{ op } #{ value })"
228
238
  end
229
239
  end
230
240
 
@@ -241,7 +251,7 @@ module Main
241
251
  end
242
252
 
243
253
  def parse_options argv, params = nil
244
- params ||= select{|p| p.type == :option}
254
+ params ||= options
245
255
 
246
256
  spec, h, s = [], {}, {}
247
257
 
@@ -279,6 +289,7 @@ module Main
279
289
  c = Parameter.const_get e.class.name.split(/::/).last
280
290
  ex = c.new e.message
281
291
  ex.set_backtrace e.message
292
+ ex.extend Softspoken
282
293
  raise ex
283
294
  end
284
295
 
@@ -291,10 +302,23 @@ module Main
291
302
  params ||= select{|p| p.type == :argument}
292
303
 
293
304
  params.each do |p|
294
- p.arity.times do
295
- break if argv.empty?
296
- value = argv.shift
297
- p.add_value value
305
+ if p.arity >= 0
306
+ p.arity.times do
307
+ break if argv.empty?
308
+ value = argv.shift
309
+ p.add_value value
310
+ end
311
+ else
312
+ arity = p.arity.abs - 1
313
+ arity.times do
314
+ break if argv.empty?
315
+ value = argv.shift
316
+ p.add_value value
317
+ end
318
+ argv.size.times do
319
+ value = argv.shift
320
+ p.add_value value
321
+ end
298
322
  end
299
323
  end
300
324
 
@@ -351,11 +375,9 @@ module Main
351
375
 
352
376
  def defaults!
353
377
  each do |p|
354
- #p.add_value p.default if(p.default? and (not p.given?))
355
378
  if(p.defaults? and (not p.given?))
356
379
  p.defaults.each do |default|
357
- #p.add_value default
358
- p.values << default # so as NOT to set given?
380
+ p.values << default # so as NOT to set 'given?'
359
381
  end
360
382
  end
361
383
  end
@@ -363,7 +385,23 @@ module Main
363
385
 
364
386
  def validate!
365
387
  each do |p|
366
- raise NotGiven, p.typename if(p.required? and (not p.given?))
388
+ next if p.arity == -1
389
+ raise NotGiven, "#{ p.typename } not given" if(p.required? and (not p.given?))
390
+ end
391
+ end
392
+
393
+ [:parameter, :option, :argument, :keyword, :environment].each do |m|
394
+ define_method("#{ m }s"){ select{|p| p.type == m or m == :parameter} }
395
+
396
+ define_method("has_#{ m }?") do |name, *names|
397
+ catch :has do
398
+ names = Cast.list_of_string name, *names
399
+ send("#{ m }s").each do |param|
400
+ common = Cast.list_of_string(param.names) & names
401
+ throw :has, true unless common.empty?
402
+ end
403
+ false
404
+ end
367
405
  end
368
406
  end
369
407
  end
@@ -472,6 +510,7 @@ module Main
472
510
  end
473
511
 
474
512
  def arity value
513
+ value = -1 if value.to_s == '*'
475
514
  p.arity = Integer value
476
515
  end
477
516
  def arity?