main 0.0.2 → 2.0.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.
@@ -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?