main 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/main.rb CHANGED
@@ -1,24 +1,31 @@
1
1
  module Main
2
- VERSION = '0.0.1'
2
+ VERSION = '0.0.2'
3
+ def version() VERSION end
3
4
 
4
5
  LIBDIR = File.join(File.dirname(File.expand_path(__FILE__)), self.name.downcase, '')
5
6
 
7
+ EXIT_SUCCESS = 0
8
+ EXIT_FAILURE = 1
9
+ EXIT_WARN = 42
10
+ #
11
+ # built-in
12
+ #
6
13
  require 'logger'
7
14
  require 'enumerator'
8
-
9
- begin
10
- require 'rubygems'
11
- rescue LoadError
12
- end
13
- # TODO - inline these
14
- require 'attributes'
15
- require 'arrayfields'
16
-
15
+ #
16
+ # inlcuded from other codeforpeople.com projects
17
+ #
18
+ require LIBDIR + 'attributes'
19
+ require LIBDIR + 'arrayfields'
20
+ #
21
+ # main libs
22
+ #
17
23
  require LIBDIR + 'util'
18
24
  require LIBDIR + 'usage'
19
25
  require LIBDIR + 'cast'
20
26
  require LIBDIR + 'parameter'
21
27
  require LIBDIR + 'getoptlong'
28
+ require LIBDIR + 'proxy'
22
29
  require LIBDIR + 'base'
23
30
  require LIBDIR + 'factories'
24
31
  end
@@ -0,0 +1,347 @@
1
+ #
2
+ # The ArrayFields module implements methods which allow an Array to be indexed
3
+ # by String or Symbol. It is not required to manually use this module to extend
4
+ # Array's - they are auto-extended when Array#fields= is called
5
+ #
6
+ module ArrayFields
7
+ #{{{
8
+ VERSION = '3.6.0'
9
+ #
10
+ # multiton cache of fields - wraps fields and fieldpos map to save memory
11
+ #
12
+ class FieldSet
13
+ #{{{
14
+ class << self
15
+ #{{{
16
+ def new fields
17
+ #{{{
18
+ @sets ||= {}
19
+ obj = @sets[fields]
20
+ unless obj
21
+ obj = super
22
+ @sets[fields] = obj
23
+ end
24
+ obj
25
+ #}}}
26
+ end
27
+ #}}}
28
+ end
29
+ attr :fields
30
+ attr :fieldpos
31
+ def initialize fields
32
+ #{{{
33
+ raise ArgumentError, "<#{ fields.inspect }> not inject-able" unless
34
+ fields.respond_to? :inject
35
+
36
+ @fieldpos =
37
+ fields.inject({}) do |h, f|
38
+ unless String === f or Symbol === f
39
+ raise ArgumentError, "<#{ f.inspect }> neither String nor Symbol"
40
+ end
41
+ h[f] = h.size
42
+ h
43
+ end
44
+
45
+ @fields = fields
46
+ #}}}
47
+ end
48
+ def pos f
49
+ #{{{
50
+ return @fieldpos[f] if @fieldpos.has_key? f
51
+ f = f.to_s
52
+ return @fieldpos[f] if @fieldpos.has_key? f
53
+ f = f.intern
54
+ return @fieldpos[f] if @fieldpos.has_key? f
55
+ nil
56
+ #}}}
57
+ end
58
+ #}}}
59
+ end
60
+ #
61
+ # methods redefined to work with fields as well as numeric indexes
62
+ #
63
+ def [](idx, *args)
64
+ #{{{
65
+ if @fieldset and (String === idx or Symbol === idx)
66
+ pos = @fieldset.pos idx
67
+ return nil unless pos
68
+ super(pos, *args)
69
+ else
70
+ super
71
+ end
72
+ #}}}
73
+ end
74
+ alias slice []
75
+ def []=(idx, *args)
76
+ #{{{
77
+ if @fieldset and (String === idx or Symbol === idx)
78
+ pos = @fieldset.pos idx
79
+ unless pos
80
+ @fieldset.fields << idx
81
+ @fieldset.fieldpos[idx] = pos = size
82
+ end
83
+ super(pos, *args)
84
+ else
85
+ super
86
+ end
87
+ #}}}
88
+ end
89
+ def at idx
90
+ #{{{
91
+ if @fieldset and (String === idx or Symbol === idx)
92
+ pos = @fieldset.pos idx
93
+ return nil unless pos
94
+ super pos
95
+ else
96
+ super
97
+ end
98
+ #}}}
99
+ end
100
+ def delete_at idx
101
+ #{{{
102
+ if @fieldset and (String === idx or Symbol === idx)
103
+ pos = @fieldset.pos idx
104
+ return nil unless pos
105
+ super pos
106
+ else
107
+ super
108
+ end
109
+ #}}}
110
+ end
111
+ def fill(obj, *args)
112
+ #{{{
113
+ idx = args.first
114
+ if idx and @fieldset and (String === idx or Symbol === idx)
115
+ idx = args.shift
116
+ pos = @fieldset.pos idx
117
+ super(obj, pos, *args)
118
+ else
119
+ super
120
+ end
121
+ #}}}
122
+ end
123
+ def values_at(*idxs)
124
+ #{{{
125
+ idxs.flatten!
126
+ if @fieldset
127
+ idxs.map!{|i| (String === i or Symbol === i) ? @fieldset.pos(i) : i}
128
+ end
129
+ super(*idxs)
130
+ #}}}
131
+ end
132
+ alias indices values_at
133
+ alias indexes values_at
134
+ def slice!(*args)
135
+ #{{{
136
+ ret = self[*args]
137
+ self[*args] = nil
138
+ ret
139
+ #}}}
140
+ end
141
+ def each_with_field
142
+ #{{{
143
+ each_with_index do |elem, i|
144
+ yield elem, @fieldset.fields[i]
145
+ end
146
+ #}}}
147
+ end
148
+ #
149
+ # methods which give a hash-like interface
150
+ #
151
+ def each_pair
152
+ #{{{
153
+ each_with_index do |elem, i|
154
+ yield @fieldset.fields[i], elem
155
+ end
156
+ #}}}
157
+ end
158
+ def each_key
159
+ #{{{
160
+ @fieldset.each{|field| yield field}
161
+ #}}}
162
+ end
163
+ def each_value(*args, &block)
164
+ #{{{
165
+ each(*args, &block)
166
+ #}}}
167
+ end
168
+ def fetch key
169
+ #{{{
170
+ self[key] or raise IndexError, 'key not found'
171
+ #}}}
172
+ end
173
+ def has_key? key
174
+ #{{{
175
+ @fieldset.fields.include? key
176
+ #}}}
177
+ end
178
+ alias member? has_key?
179
+ alias key? has_key?
180
+ def has_value? value
181
+ #{{{
182
+ if respond_to? 'include?'
183
+ self.include? value
184
+ else
185
+ a = []
186
+ each{|val| a << val}
187
+ a.include? value
188
+ end
189
+ #}}}
190
+ end
191
+ alias value? has_value?
192
+ def keys
193
+ #{{{
194
+ fields
195
+ #}}}
196
+ end
197
+ def store key, value
198
+ #{{{
199
+ self[key] = value
200
+ #}}}
201
+ end
202
+ def values
203
+ #{{{
204
+ if respond_to? 'to_ary'
205
+ self.to_ary
206
+ else
207
+ a = []
208
+ each{|val| a << val}
209
+ a
210
+ end
211
+ #}}}
212
+ end
213
+ def to_hash
214
+ #{{{
215
+ if respond_to? 'to_ary'
216
+ h = {}
217
+ @fieldset.fields.zip(to_ary){|f,e| h[f] = e}
218
+ h
219
+ else
220
+ a = []
221
+ each{|val| a << val}
222
+ h = {}
223
+ @fieldset.fields.zip(a){|f,e| h[f] = e}
224
+ h
225
+ end
226
+ #}}}
227
+ end
228
+ alias to_h to_hash
229
+ def update other
230
+ #--{{{
231
+ other.each{|k,v| self[k] = v}
232
+ to_hash
233
+ #--}}}
234
+ end
235
+ def replace other
236
+ #--{{{
237
+ Hash === other ? update(other) : super
238
+ #--}}}
239
+ end
240
+ def invert
241
+ #--{{{
242
+ to_hash.invert
243
+ #--}}}
244
+ end
245
+ #}}}
246
+ end
247
+ #
248
+ # Fieldable encapsulates methods in common for classes which may have their
249
+ # fields set
250
+ #
251
+ module Fieldable
252
+ #{{{
253
+ #
254
+ # sets fields an dynamically extends this Array instance with methods for
255
+ # keyword access
256
+ #
257
+ def fields= fields
258
+ #{{{
259
+ extend ArrayFields unless defined? @fieldset
260
+
261
+ @fieldset =
262
+ if ArrayFields::FieldSet === fields
263
+ fields
264
+ else
265
+ ArrayFields::FieldSet.new fields
266
+ end
267
+ #}}}
268
+ end
269
+ #
270
+ # access to fieldset
271
+ #
272
+ attr_reader :fieldset
273
+ #
274
+ # access to field list
275
+ #
276
+ def fields
277
+ #{{{
278
+ @fieldset and @fieldset.fields
279
+ #}}}
280
+ end
281
+ #}}}
282
+ end
283
+ #
284
+ # The Array class is extened with a methods to allow keyword access
285
+ #
286
+ class Array
287
+ #{{{
288
+ include Fieldable
289
+ #}}}
290
+ end
291
+ #
292
+ # proxy class that allows an array to be wrapped in a way that still allows #
293
+ # keyword access. also facilitate usage of ArrayFields with arraylike objects.
294
+ # thnx to Sean O'Dell for the suggestion.
295
+ #
296
+ # sample usage
297
+ #
298
+ # fa = FieldedArray.new %w(zero one two), [0,1,2]
299
+ # p fa['zero'] #=> 0
300
+ #
301
+ #
302
+ class FieldedArray
303
+ #{{{
304
+ include Fieldable
305
+ class << self
306
+
307
+ def [](*pairs)
308
+ #{{{
309
+ pairs.flatten!
310
+ raise ArgumentError, "argument must be key/val paris" unless
311
+ (pairs.size % 2 == 0 and pairs.size >= 2)
312
+ fields, elements = [], []
313
+ #pairs.each do |f,e|
314
+ while((f = pairs.shift) and (e = pairs.shift))
315
+ raise ArgumentError, "field must be String or Symbol" unless
316
+ (String === f or Symbol === f)
317
+ fields << f and elements << e
318
+ end
319
+ new fields, elements
320
+ #}}}
321
+ end
322
+
323
+ end
324
+ def initialize fields, array
325
+ #{{{
326
+ @a = array
327
+ self.fields = fields
328
+ #}}}
329
+ end
330
+ def method_missing(meth, *args, &block)
331
+ #{{{
332
+ @a.send(meth, *args, &block)
333
+ #}}}
334
+ end
335
+ delegates =
336
+ #{{{
337
+ %w(
338
+ to_s
339
+ to_str
340
+ inspect
341
+ )
342
+ #}}}
343
+ delegates.each do |meth|
344
+ class_eval "def #{ meth }(*a,&b); @a.#{ meth }(*a,&b);end"
345
+ end
346
+ #}}}
347
+ end
@@ -0,0 +1,54 @@
1
+ module Attributes
2
+ VERSION = '3.2.0'
3
+
4
+ def version() VERSION end
5
+
6
+ def attributes *a, &b
7
+ unless a.empty?
8
+ hashes, names = a.partition{|x| Hash === x}
9
+
10
+ names_and_defaults = {}
11
+ hashes.each{|h| names_and_defaults.update h}
12
+ names.flatten.compact.each{|name| names_and_defaults.update name => nil}
13
+
14
+ names_and_defaults.each do |name, default|
15
+ init = b || lambda { default }
16
+ ivar, getter, setter, query = "@#{ name }", "#{ name }", "#{ name }=", "#{ name }?"
17
+
18
+ define_method(setter) do |value|
19
+ instance_variable_set ivar, value
20
+ end
21
+
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
29
+ end
30
+ end
31
+
32
+ alias_method query, getter
33
+
34
+ (attributes << name).uniq!
35
+ attributes
36
+ end
37
+ else
38
+ @attributes ||= []
39
+ end
40
+ end
41
+
42
+ def attribute(*a, &b) attributes *a, &b end
43
+ end
44
+
45
+ class Object
46
+ def attributes *a, &b
47
+ sc = class << self; self; end
48
+ sc.attributes *a, &b
49
+ end
50
+
51
+ def attribute(*a, &b) attributes *a, &b end
52
+ end
53
+
54
+ class Module; include Attributes; end
data/lib/main/base.rb CHANGED
@@ -1,9 +1,5 @@
1
1
  module Main
2
2
  class Base
3
- EXIT_SUCCESS = 0
4
- EXIT_FAILURE = 1
5
- EXIT_OK = 42
6
-
7
3
  class << self
8
4
  attribute( 'program' ){ File.basename $0 }
9
5
  attribute( 'name' ){ File.basename $0 }
@@ -11,20 +7,17 @@ module Main
11
7
  attribute( 'description' )
12
8
  attribute( 'author' )
13
9
  attribute( 'version' )
14
- attribute( 'logger' ){ STDERR }
10
+ attribute( 'stdin' ){ $stdin }
11
+ attribute( 'stdout' ){ $stdout }
12
+ attribute( 'stderr' ){ $stderr }
13
+ attribute( 'logger' ){ stderr }
14
+ attribute( 'logger_level' ){ Logger::INFO }
15
15
  attribute( 'exit_status' ){ EXIT_SUCCESS }
16
16
  attribute( 'exit_success' ){ EXIT_SUCCESS }
17
17
  attribute( 'exit_failure' ){ EXIT_FAILURE }
18
- attribute( 'exit_ok' ){ EXIT_OK }
18
+ attribute( 'exit_warn' ){ EXIT_WARN }
19
19
  attribute( 'usage' ){ Usage.default_usage self }
20
20
 
21
- def new *a, &b
22
- obj = allocate
23
- obj.__initialize__
24
- obj.instance_eval{ initialize *a, &b }
25
- obj
26
- end
27
-
28
21
  def parameters
29
22
  @parameters ||= Parameter::List[]
30
23
  end
@@ -67,10 +60,14 @@ module Main
67
60
  attribute 'env'
68
61
  attribute 'params'
69
62
  attribute 'logger'
63
+ attribute 'stdin'
64
+ attribute 'stdout'
65
+ attribute 'stderr'
70
66
 
71
67
  %w(
72
68
  program name synopsis description author version
73
- exit_status exit_success exit_failure exit_ok
69
+ exit_status exit_success exit_failure exit_warn
70
+ logger_level
74
71
  usage
75
72
  ).each{|a| attribute(a){ self.class.send a}}
76
73
 
@@ -80,7 +77,7 @@ module Main
80
77
  alias_method "#{ dst }?", "params?"
81
78
  end
82
79
 
83
- %w( exit_success! exit_failure! exit_ok! ).each do |code|
80
+ %w( exit_success! exit_failure! exit_warn! ).each do |code|
84
81
  module_eval <<-code
85
82
  def #{ code } *a, &b
86
83
  exit #{ code.chop }
@@ -96,55 +93,126 @@ module Main
96
93
  code
97
94
  end
98
95
 
99
- def __initialize__ argv = ARGV, env = ENV
100
- @argv, @env = argv, env
96
+ def pre_initialize() :hook end
97
+ def initialize argv = ARGV, env = ENV, opts = {}
98
+ @argv, @env, @opts = argv, env, opts
99
+ setup_finalizers
100
+ setup_io_restoration
101
+ setup_io_redirection
102
+ setup_logging
103
+ end
104
+ def post_initialize() :hook end
105
+
106
+ def setup_finalizers
107
+ @finalizers = finalizers = []
108
+ ObjectSpace.define_finalizer(self) do
109
+ while((f = finalizers.pop)); f.call; end
110
+ end
111
+ end
112
+
113
+ def finalize
114
+ while((f = @finalizers.pop)); f.call; end
115
+ end
101
116
 
102
- log = self.class.logger
117
+ def setup_io_redirection
118
+ self.stdin = @opts['stdin'] || @opts[:stdin] || self.class.stdin
119
+ self.stdout = @opts['stdout'] || @opts[:stdout] || self.class.stdout
120
+ self.stderr = @opts['stderr'] || @opts[:stderr] || self.class.stderr
121
+ end
103
122
 
104
- @logger =
123
+ def setup_logging
124
+ log = self.class.logger || stderr
125
+ self.logger = log
126
+ end
127
+ def logger= log
128
+ unless(defined?(@logger) and @logger == log)
105
129
  case log
106
130
  when Logger
107
- log
131
+ @logger = log
108
132
  when IO, StringIO
109
- Logger.new log
133
+ @logger = Logger.new log
134
+ @logger.level = logger_level
110
135
  else
111
- Logger.new *log
136
+ @logger = Logger.new *log
137
+ @logger.level = logger_level
112
138
  end
139
+ end
140
+ end
113
141
 
114
- __parse_parameters__
142
+ def setup_io_restoration
143
+ [STDIN, STDOUT, STDERR].each do |io|
144
+ dup = io.dup and @finalizers.push lambda{ io.reopen dup }
145
+ end
115
146
  end
116
147
 
117
- def __parse_parameters__
118
- self.class.parameters.parse @argv, @env
119
- #(@params = []).fields = []
120
- @params = Parameter::Table.new
121
- self.class.parameters.each do |p|
122
- @params[p.name.to_s] = p
148
+ def stdin= io
149
+ unless(defined?(@stdin) and (@stdin == io))
150
+ @stdin =
151
+ if io.respond_to? 'read'
152
+ io
153
+ else
154
+ fd = open io.to_s, 'r+'
155
+ @finalizers.push lambda{ fd.close }
156
+ fd
157
+ end
158
+ begin
159
+ STDIN.reopen @stdin
160
+ rescue
161
+ $stdin = @stdin
162
+ ::Object.const_set 'STDIN', @stdin
163
+ #p STDIN
164
+ #p STDIN.read
165
+ end
166
+ #p 'STDIN' => STDIN
167
+ #p '@stdin' => @stdin
168
+ #p '$stdin' => $stdin
123
169
  end
124
170
  end
125
171
 
126
- def __run__
127
- if @params['help'] and @params['help'].given?
128
- print usage.to_s
129
- exit
172
+ def stdout= io
173
+ unless(defined?(@stdout) and (@stdout == io))
174
+ @stdout =
175
+ if io.respond_to? 'write'
176
+ io
177
+ else
178
+ fd = open io.to_s, 'w+'
179
+ @finalizers.push lambda{ fd.close }
180
+ fd
181
+ end
182
+ STDOUT.reopen @stdout rescue($stdout = @stdout)
130
183
  end
184
+ end
131
185
 
132
- begin
133
- run
134
- rescue Exception => e
135
- if SystemExit === e
136
- exit_status e.status
137
- else
138
- fatal{ e }
139
- exit_status EXIT_FAILURE if exit_status == EXIT_SUCCESS
140
- end
186
+ def stderr= io
187
+ unless(defined?(@stderr) and (@stderr == io))
188
+ @stderr =
189
+ if io.respond_to? 'write'
190
+ io
191
+ else
192
+ fd = open io.to_s, 'w+'
193
+ @finalizers.push lambda{ fd.close }
194
+ fd
195
+ end
196
+ STDERR.reopen @stderr rescue($stderr = @stderr)
141
197
  end
142
- status = Integer(exit_status) rescue(exit_status ? 0 : 1)
143
- exit status
144
198
  end
199
+
200
+ def pre_parse_parameters() :hook end
201
+ def parse_parameters
202
+ pre_parse_parameters
203
+
204
+ self.class.parameters.parse @argv, @env
205
+ @params = Parameter::Table.new
206
+ self.class.parameters.each{|p| @params[p.name.to_s] = p}
207
+
208
+ post_parse_parameters
209
+ end
210
+ def post_parse_parameters() :hook end
145
211
 
212
+ def pre_run() end
146
213
  def run
147
214
  raise NotImplementedError, 'run not defined'
148
215
  end
216
+ def post_run() end
149
217
  end
150
218
  end