main 0.0.1 → 0.0.2

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.
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