appl 1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. checksums.yaml +7 -0
  2. data/bin/intar +156 -0
  3. data/doc/demoappl +69 -0
  4. data/lib/appl.rb +302 -0
  5. data/lib/intar.rb +451 -0
  6. metadata +65 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c17ebbb3f574a1d27df9610144f9881c3b8ef910
4
+ data.tar.gz: 6e68ffdb0dd1e1c227dc371f29e9d93fb7a1abef
5
+ SHA512:
6
+ metadata.gz: c16e0eb7be520d7eace0ab904c838d61ab3eaa29b34d3f2fda9d5c23b35f03721f244ffb814389399bb6f1a175ba68cdd39a2b9fefb7f4cc7bb4f84ce8dbeef0
7
+ data.tar.gz: 98cf8b5bde3d91115a0763e0561718f04a99d560134d52448f68384662af9814cad6fdc44a283352e00897cf323fd450e4f1c9e77de7c052f9a75ab7dbc4ae51
data/bin/intar ADDED
@@ -0,0 +1,156 @@
1
+ #!/usr/local/bin/ruby
2
+
3
+ #
4
+ # intar -- Interactive Ruby evaluation
5
+ #
6
+
7
+ require "intar"
8
+ require "appl"
9
+
10
+
11
+ class IntarApp < Application
12
+
13
+ NAME = "intar"
14
+ VERSION = APPL_VERSION
15
+ SUMMARY = "Interactive Ruby"
16
+ COPYRIGHT = "(C) 2008-2013 Bertram Scharpf <software@bertram-scharpf.de>"
17
+ LICENSE = "BSD"
18
+ AUTHOR = "Bertram Scharpf <software@bertram-scharpf.de>"
19
+
20
+ DESCRIPTION = <<EOT
21
+ Prompt for Ruby statements, evaluate them. This is a replacement
22
+ for "irb". The underlying library may be entered from inside any
23
+ Ruby program.
24
+
25
+ Example:
26
+
27
+ $ intar -p '%(33 1)c%t%c%> '
28
+
29
+ EOT
30
+
31
+ def quiet!
32
+ Intar.show = nil
33
+ end
34
+ def show= s
35
+ Intar.show = s.to_i
36
+ end
37
+ def prompt= p
38
+ Intar.prompt = p if p
39
+ end
40
+ def bw!
41
+ Intar.colour = false
42
+ end
43
+ def catch_exit!
44
+ Intar.catch_exit = (Intar.catch_exit||0) + 3
45
+ end
46
+ def histall!
47
+ Intar.histhid = false
48
+ end
49
+ def histfile= h
50
+ Intar.histfile = nil_if_none h
51
+ end
52
+ def histmax= m
53
+ Intar.histmax = (Integer m rescue m.to_i)
54
+ end
55
+ def configfile= c
56
+ @configfile = nil_if_none c
57
+ end
58
+
59
+ define_option "p", :prompt=, "STR",
60
+ "prompt - see source code for % escapes"
61
+ alias_option "p", "prompt"
62
+
63
+ define_option "q", :quiet!, "don't show results"
64
+ alias_option "q", "quiet"
65
+
66
+ define_option "S", :show=, "N", 1, "show result line limit (0=all)"
67
+ alias_option "S", "show"
68
+
69
+ define_option "r", :require, "FILE", "Ruby require"
70
+ alias_option "r", "require"
71
+
72
+ define_option "bw", :bw!, "black & white"
73
+
74
+ define_option "c", :configfile=, "FILE", ".intarrc",
75
+ "config file, NONE means none"
76
+ alias_option "c", "configfile"
77
+
78
+ define_option "H", :histfile=, "FILE", ".intar_history",
79
+ "history file, NONE means none"
80
+ alias_option "H", "histfile"
81
+
82
+ define_option "m", :histmax=, "NUM",
83
+ "maximum history entries to save"
84
+ alias_option "m", "histmax"
85
+
86
+ define_option "A", :histall!,
87
+ "pass lines starting with blanks to history"
88
+ alias_option "A", "histall"
89
+
90
+ define_option "E", :encoding=, "ENC", "set encoding (like ruby -E)"
91
+ alias_option "E", "encoding"
92
+
93
+ define_option "x", :catch_exit!,
94
+ "On exit exception: Wait 3 seconds for interrupt"
95
+ alias_option "x", "catchexit"
96
+ alias_option "x", "catch-exit"
97
+
98
+ define_option "h", :help, "show options"
99
+ alias_option "h", "help"
100
+ define_option "V", :version, "show version"
101
+ alias_option "V", "version"
102
+
103
+ def run
104
+ # @debug = true # Only development.
105
+ read_cfg
106
+ main = eval "self", TOPLEVEL_BINDING
107
+ Intar.open main do |i|
108
+ i.run *(@args.shift @args.length)
109
+ end
110
+ end
111
+
112
+ private
113
+
114
+ def encoding= ei
115
+ e, i = ei.split ":"
116
+ Encoding.default_external = e if e and not e.empty?
117
+ Encoding.default_internal = i if i and not i.empty?
118
+ [ $stdin, $stdout, $stderr].each do |io|
119
+ io.set_encoding e, i
120
+ end
121
+ end
122
+
123
+
124
+ def read_cfg
125
+ c = @configfile
126
+ return unless c
127
+ unless c[ File::SEPARATOR] or (File.exists? c) then
128
+ c = File.expand_path c, "~"
129
+ return unless File.exists? c
130
+ end
131
+ r = File.read c
132
+ r.prepend "$SAFE = 1#$/"
133
+ Thread.new {
134
+ begin
135
+ eval r, TOPLEVEL_BINDING
136
+ rescue Exception
137
+ $@.pop 2
138
+ e = $@.shift
139
+ puts "#{e}: #$! (#{$!.class})"
140
+ $@.each { |l| puts "\t#{l}" }
141
+ raise "Error in config file #{c}"
142
+ end
143
+ }.value
144
+ end
145
+
146
+ def nil_if_none var
147
+ case var
148
+ when "", "NONE" then nil
149
+ else var
150
+ end
151
+ end
152
+
153
+ end
154
+
155
+ IntarApp.run
156
+
data/doc/demoappl ADDED
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #
4
+ # demoappl -- Just test the Application class
5
+ #
6
+
7
+ require "appl"
8
+
9
+ class DemoAppl < Application
10
+
11
+ NAME = "demoappl"
12
+ VERSION = "1.0"
13
+ SUMMARY = "Just test the Application class"
14
+ COPYRIGHT = "(C) 2011 Bertram Scharpf"
15
+ LICENSE = "For internal use only"
16
+ AUTHOR = "Bertram Scharpf <software@bertram-scharpf.de>"
17
+
18
+ DESCRIPTION = <<EOT
19
+ This is an example how to define an Application subclass.
20
+ The program just prints out its parameters.
21
+
22
+ EOT
23
+
24
+ OPTIONS_ENV = "DEMO_OPTS"
25
+
26
+ attr_writer :param, :default_param
27
+ attr_bang :switch, :raise, :debug
28
+
29
+ define_option "s", :switch!, "dummy switch"
30
+ alias_option "s", "switch"
31
+
32
+ define_option "p", :param=, "PARAM", "dummy parameter"
33
+ alias_option "p", "param"
34
+
35
+ define_option "d", :default_param=, "PARAM:dddd",
36
+ "dummy parameter with default value"
37
+ alias_option "d", "default-param"
38
+
39
+ define_option "r", :raise!, "raise an exception"
40
+ alias_option "r", "raise"
41
+
42
+ define_option "g", :debug!, "lots of ugly debugging information"
43
+ alias_option "g", "debug"
44
+
45
+ define_option "h", :help, "show this options list"
46
+ alias_option "h", "help"
47
+ define_option "V", :version, "show version information"
48
+ alias_option "V", "version"
49
+
50
+ UNKNOWN = "Sorry, unknown option"
51
+ STOPOPT = "no more options"
52
+ UNPROCA = "Warning: unprocessed arguments left"
53
+
54
+ def run
55
+ puts inspect
56
+ raise "stop" if @raise
57
+ ENV[ OPTIONS_ENV] or
58
+ puts "Try to set the environment variable #{OPTIONS_ENV}."
59
+ @args.clear
60
+ end
61
+
62
+ end
63
+
64
+ if false then
65
+ DemoAppl.run %w(-s -p rrrr qqqq -x -y -z)
66
+ else
67
+ DemoAppl.run
68
+ end
69
+
data/lib/appl.rb ADDED
@@ -0,0 +1,302 @@
1
+ #
2
+ # appl.rb -- Application class
3
+ #
4
+
5
+
6
+ class Application
7
+
8
+ APPL_VERSION = "1.2".freeze
9
+
10
+ OPTIONS_ENV = nil
11
+
12
+ STOPOPT = "stop option processing"
13
+ UNKNOWN = "Unknown option"
14
+ UNPROCA = "Warning: unprocessed arguments"
15
+
16
+ class OptionError < StandardError ; end
17
+ class Done < Exception ; end
18
+
19
+ def initialize args = nil
20
+ @args = args||self.class.cmdline_arguments
21
+ self.class.each_option { |opt,desc,arg,dfl,act|
22
+ begin
23
+ send act, dfl if dfl
24
+ rescue NoMethodError
25
+ raise OptionError, "Option action `#{act}' is not defined."
26
+ end
27
+ }
28
+ while @args.first =~ /\A-/ do
29
+ opt = $'
30
+ @args.shift
31
+ if opt =~ /\A-/ then
32
+ break if !$' or $'.empty?
33
+ opt = $'
34
+ if opt =~ /^=/ then opt = $` ; @args.unshift $' end
35
+ act = self.class.option_act @args, opt, nil
36
+ send *act
37
+ else
38
+ until not opt or opt.empty? do
39
+ c = opt.slice! 0, 1
40
+ if opt =~ /^=/ then opt = nil ; @args.unshift $' end
41
+ act = self.class.option_act @args, c, opt
42
+ send *act
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ def help
49
+ c = self.class
50
+ puts c.version
51
+ puts
52
+ puts c::DESCRIPTION
53
+ puts
54
+ c.show_options
55
+ if block_given? then
56
+ puts
57
+ yield
58
+ end
59
+ raise Done
60
+ end
61
+
62
+ def version
63
+ self.class.show_version
64
+ raise Done
65
+ end
66
+
67
+ VERSION = "0"
68
+ NAME = "appl"
69
+ SUMMARY = "Dummy application"
70
+ DESCRIPTION = <<-EOT
71
+ This base class does nothing by default.
72
+ EOT
73
+
74
+ def run
75
+ end
76
+
77
+ def execute
78
+ run
79
+ if @args.any? then
80
+ u = @args.join " "
81
+ puts "#{self.class::UNPROCA}: #{u}"
82
+ end
83
+ 0
84
+ rescue SignalException
85
+ raise if @debug
86
+ self.class.show_message $!.inspect
87
+ 128 + ($!.signo||2) # Ruby 1.8 returns signo +nil+; assume SIGINT
88
+ rescue
89
+ raise if @debug
90
+ self.class.show_message "Error: #$!", "(#{$!.class})"
91
+ $!.to_i rescue 1
92
+ end
93
+
94
+ HASH_ORDER = RUBY_VERSION >= "1.9"
95
+
96
+ class <<self
97
+
98
+ def run args = nil
99
+ e = execute args
100
+ exit e
101
+ end
102
+
103
+ def version
104
+ if self::VERSION =~ %r/\s/ then
105
+ self::VERSION
106
+ else
107
+ "#{self::NAME} #{self::VERSION} -- #{self::SUMMARY}"
108
+ end
109
+ end
110
+
111
+ private
112
+
113
+ def execute args = nil
114
+ i = new args
115
+ i.execute
116
+ rescue Done
117
+ 0
118
+ rescue OptionError
119
+ show_message $!
120
+ 127
121
+ end
122
+
123
+ def inherited sub
124
+ sub.instance_eval { @options, @aliases = {}, {} }
125
+ end
126
+
127
+ def attr_bang *syms
128
+ syms.each { |sym|
129
+ define_method :"#{sym}!" do
130
+ instance_variable_set :"@#{sym}", true
131
+ end
132
+ }
133
+ nil
134
+ end
135
+
136
+ public
137
+
138
+ def define_option opt, *param
139
+ delete_option opt
140
+ act = param.shift
141
+ desc = param.pop
142
+ arg = param.shift
143
+ if arg then
144
+ if param.empty? then
145
+ arg, dfl = arg.split /:/, 2
146
+ if dfl =~ /\A:/ then
147
+ dfl = $'.to_sym
148
+ end
149
+ else
150
+ dfl = param.shift
151
+ end
152
+ end
153
+ d = param.map { |x| "#{x}#$/" }.join
154
+ desc.insert 0, d
155
+ @options[ opt.to_s] = [ desc, arg, dfl, act]
156
+ nil
157
+ end
158
+ alias def_option define_option
159
+
160
+ def alias_option orig, opt
161
+ unalias_option opt
162
+ @aliases[ opt.to_s] = orig.to_s
163
+ nil
164
+ end
165
+
166
+ def delete_option opt
167
+ self < Application or return
168
+ superclass.delete_option opt
169
+ @options.delete opt
170
+ @aliases.reject! { |k,v| v == opt }
171
+ nil
172
+ end
173
+
174
+ def unalias_option opt
175
+ self < Application or return
176
+ superclass.unalias_option opt
177
+ @aliases.delete opt
178
+ nil
179
+ end
180
+
181
+ protected
182
+
183
+ def find_option_act opt
184
+ self < Application or return
185
+ @options[ opt] || @options[ @aliases[ opt]] ||
186
+ (superclass.find_option_act opt)
187
+ end
188
+
189
+ def all_options
190
+ if self < Application then
191
+ r = superclass.all_options
192
+ r.update @options
193
+ else
194
+ {}
195
+ end
196
+ end
197
+
198
+ def all_aliases
199
+ if self < Application then
200
+ r = superclass.all_aliases
201
+ r.update @aliases
202
+ else
203
+ {}
204
+ end
205
+ end
206
+
207
+ def options_desc &block
208
+ a = Hash.new do |h,k| h[ k] = [] end
209
+ all_aliases.each { |k,v|
210
+ a[ v].push k
211
+ }
212
+ a.values.each { |v| v.sort! } unless HASH_ORDER
213
+ each_option { |opt,desc,arg,dfl,|
214
+ yield opt, arg, dfl, desc
215
+ a[ opt].each { |l|
216
+ yield l, nil, nil, nil
217
+ }
218
+ }
219
+ yield "", nil, nil, self::STOPOPT
220
+ end
221
+
222
+ public
223
+
224
+ def each_option
225
+ o = all_options
226
+ o = o.sort_by { |k,v| k.swapcase } unless HASH_ORDER
227
+ o.each { |opt,(desc,arg,dfl,act)|
228
+ case dfl
229
+ when Symbol then dfl = const_get dfl
230
+ end
231
+ yield opt, desc, arg, dfl, act
232
+ }
233
+ end
234
+
235
+ def option_act args, opt, rest
236
+ dada = find_option_act opt
237
+ dada or raise OptionError, "#{self::UNKNOWN}: `#{opt}'."
238
+ desc, arg, dfl, act = *dada
239
+ r = [ act]
240
+ if arg then
241
+ p = rest.slice! 0, rest.length if rest and not rest.empty?
242
+ r.push p||args.shift
243
+ end
244
+ r
245
+ end
246
+
247
+ def show_options
248
+ options_desc do |opt,arg,dfl,desc|
249
+ opt = opt.length == 1 ? "-#{opt}" : "--#{opt}"
250
+ arg &&= "#{arg}"
251
+ dfl &&= "[#{dfl}]"
252
+ arg << dfl if arg && dfl
253
+ puts " %-10s %-12s %s" % [ opt, arg, desc]
254
+ end
255
+ end
256
+
257
+ def show_version
258
+ puts version
259
+ puts self::COPYRIGHT if const_defined? :COPYRIGHT
260
+ puts "License: #{self::LICENSE}" if const_defined? :LICENSE
261
+ a = []
262
+ a.push self::AUTHOR if const_defined? :AUTHOR
263
+ a.concat self::AUTHORS if const_defined? :AUTHORS
264
+ if a.any? then
265
+ a.flatten!
266
+ a.uniq!
267
+ j = a.join ", "
268
+ h = a.length == 1 ? "Author" : "Authors"
269
+ puts "#{h}: #{j}"
270
+ end
271
+ end
272
+
273
+ def show_message msg, extra = nil
274
+ if $stderr.tty? then
275
+ msg = "\e[31;1m#{msg}\e[m"
276
+ if extra then
277
+ extra = "\e[31m#{extra}\e[m"
278
+ end
279
+ end
280
+ if extra then
281
+ msg = [ msg, extra].join " "
282
+ end
283
+ $stderr.puts msg
284
+ end
285
+
286
+ def cmdline_arguments
287
+ r = []
288
+ oe = self::OPTIONS_ENV
289
+ eo = ENV[ oe] if oe
290
+ if eo then
291
+ eo.scan /"((?:\\.|[^"])*")|[^" \t]+/ do
292
+ r.push $1 ? (eval $1) : $&
293
+ end
294
+ end
295
+ r.concat $*
296
+ r
297
+ end
298
+
299
+ end
300
+
301
+ end
302
+
data/lib/intar.rb ADDED
@@ -0,0 +1,451 @@
1
+ #
2
+ # intar.rb -- Interactive Ruby evaluation
3
+ #
4
+
5
+
6
+ =begin rdoc
7
+
8
+ This could be opened not only by the Intar executable but also
9
+ everywhere inside your Ruby program.
10
+
11
+ = Example 1
12
+
13
+ require "intar"
14
+
15
+ Intar.prompt = "str(%(length)i):%03n%> "
16
+ a = "hello"
17
+ Intar.run a
18
+
19
+
20
+ = Example 2
21
+
22
+ require "intar"
23
+
24
+ class C
25
+ end
26
+
27
+ class IntarC < Intar
28
+ @show = 3
29
+ @prompt = "%(33 1)c%t%c%> "
30
+ @histfile = ".intarc_history"
31
+
32
+ class <<self
33
+ def open
34
+ super C.new
35
+ end
36
+ end
37
+ end
38
+
39
+ IntarC.open do |ia| ia.run end
40
+
41
+ =end
42
+
43
+
44
+ require "readline"
45
+ require "supplement"
46
+ require "supplement/terminal"
47
+
48
+
49
+ class Object
50
+ def empty_binding
51
+ binding
52
+ end
53
+ end
54
+
55
+ class Intar
56
+
57
+ class History
58
+
59
+ IND = " "
60
+ IND_RE = /^#{IND}/
61
+
62
+ def initialize filename
63
+ @filename = filename
64
+ return unless @filename
65
+ h = "~" unless @filename.starts_with "./"
66
+ @filename = File.expand_path @filename, h
67
+ File.exists? @filename and File.open @filename do |h|
68
+ c = []
69
+ h.each { |l|
70
+ case l
71
+ when IND_RE then c.push $'
72
+ else push c.join.chomp ; c.clear
73
+ end
74
+ }
75
+ push c.join.chomp
76
+ end
77
+ @num = Readline::HISTORY.length
78
+ end
79
+
80
+ def finish max
81
+ return unless @filename
82
+ @num.times { Readline::HISTORY.shift }
83
+ File.open @filename, "a" do |h|
84
+ a = []
85
+ while (c = Readline::HISTORY.shift) do a.push c end
86
+ if a.any? then
87
+ h.puts "# #{Time.now}"
88
+ a.each { |c|
89
+ c.each_line { |l| h.puts IND + l }
90
+ h.puts "-"
91
+ }
92
+ i = a.length
93
+ h.puts "# #{i} #{entry_str i} added"
94
+ end
95
+ end
96
+ n = File.open @filename do |h|
97
+ h.inject 0 do |i,l| i += 1 if l =~ IND_RE ; i end
98
+ end
99
+ i = max - n
100
+ if i < 0 then
101
+ f = nil
102
+ File.open @filename do |h|
103
+ h.each_line { |l|
104
+ f.push l if f
105
+ case l
106
+ when IND_RE then i += 1
107
+ else f ||= [] if i >= 0
108
+ end
109
+ }
110
+ end
111
+ f and File.open @filename, "w" do |h| h.puts f end
112
+ end
113
+ rescue Errno
114
+ # Forget it if there isn't enough disk space.
115
+ end
116
+
117
+ def push l
118
+ Readline::HISTORY.push l unless l.empty?
119
+ end
120
+
121
+ private
122
+
123
+ def entry_str i
124
+ i == 1 ? "entry" : "entries"
125
+ end
126
+
127
+ end
128
+
129
+ class <<self
130
+
131
+ attr_accessor :prompt, :show, :colour
132
+
133
+ attr_reader :histfile
134
+ def histfile= hf
135
+ @histfile = hf
136
+ if @history then
137
+ @history.finish @histmax
138
+ @history = History.new @histfile
139
+ end
140
+ end
141
+
142
+ # Maximum number of history entries.
143
+ attr_accessor :histmax
144
+
145
+ # Whether to hide entries starting with whitespace.
146
+ attr_accessor :histhid
147
+
148
+ # Shell prefix and Pipe suffix
149
+ attr_accessor :sh_pref, :pi_suff
150
+
151
+ # Whether <code>Kernel#exit</code> should be caught.
152
+ attr_accessor :catch_exit
153
+
154
+ private
155
+
156
+ def inherited sub
157
+ sub.class_eval {
158
+ s = superclass
159
+ @prompt = s.prompt
160
+ @show = s.show
161
+ @colour = s.colour
162
+ @histfile = s.histfile
163
+ @histmax = s.histmax
164
+ @histhid = s.histhid
165
+ @sh_pref = s.sh_pref
166
+ @pi_suff = s.pi_suff
167
+ @catch_exit = s.catch_exit
168
+ }
169
+ end
170
+
171
+ def history_file
172
+ if @history then
173
+ yield
174
+ else
175
+ @history = History.new @histfile
176
+ begin
177
+ yield
178
+ ensure
179
+ @history.finish @histmax
180
+ @history = nil
181
+ end
182
+ end
183
+ end
184
+
185
+ public
186
+
187
+ private :new
188
+ def open obj
189
+ history_file do
190
+ i = new obj
191
+ yield i
192
+ end
193
+ end
194
+
195
+ def run obj
196
+ open obj do |i| i.run end
197
+ end
198
+
199
+ def hist_add l
200
+ return if @histhid and l == /\A[ \t]+/
201
+ lst = Readline::HISTORY[-1] if Readline::HISTORY.length > 0
202
+ @history.push l unless l == lst
203
+ end
204
+
205
+ end
206
+
207
+ self.prompt = "%(32)c%i%c:%1c%03n%c%> "
208
+ self.show = 1
209
+ self.colour = true
210
+ self.histfile = nil
211
+ self.histmax = 500
212
+ self.histhid = true
213
+ self.sh_pref = "."
214
+ self.pi_suff = " |"
215
+ self.catch_exit = nil
216
+
217
+ private
218
+
219
+ def initialize obj
220
+ @obj = obj
221
+ @n = 0
222
+ end
223
+
224
+ OLDSET = <<-EOT
225
+ _, __, ___ = nil, nil, nil
226
+ proc { |r,n|
227
+ Array === __ or __ = []
228
+ Hash === ___ or ___ = {}
229
+ unless r.nil? or r.equal? __ or r.equal? ___ then
230
+ _ = r
231
+ __.delete r rescue nil
232
+ __.unshift r
233
+ ___[ n] = r
234
+ end
235
+ }
236
+ EOT
237
+
238
+ autoload :Etc, "etc"
239
+ autoload :Socket, "socket"
240
+
241
+ def cur_prompt
242
+ t = Time.now
243
+ self.class.prompt.gsub /%(?:
244
+ \(([^\)]*)\)
245
+ |
246
+ ([+-]?[0-9]+(?:\.[0-9]+)?)
247
+ )?(.)/nx do
248
+ case $3
249
+ when "s" then @obj.to_s
250
+ when "i" then $1.notempty? ? (@obj.send $1) : @obj.inspect
251
+ when "n" then "%#$2d" % @n
252
+ when "t" then t.strftime $1||"%X"
253
+ when "u" then Etc.getpwuid.name
254
+ when "h" then Socket.gethostname
255
+ when "w" then cwd_short
256
+ when "W" then File.basename cwd_short
257
+ when "c" then (colour *($1 || $2 || "").split.map { |x| x.to_i }).to_s
258
+ when ">" then Process.uid == 0 ? "#" : ">"
259
+ when "%" then $3
260
+ else $&
261
+ end
262
+ end
263
+ end
264
+
265
+ def colour *c
266
+ if self.class.colour then
267
+ s = c.map { |i| "%d" % i }.join ";"
268
+ "\e[#{s}m"
269
+ end
270
+ end
271
+
272
+ def switchcolour *c
273
+ s = colour *c
274
+ print s if s
275
+ end
276
+
277
+ def cwd_short
278
+ r = Dir.pwd
279
+ h = Etc.getpwuid.dir
280
+ r[ 0, h.length] == h and r[ 0, h.length] = "~"
281
+ r
282
+ end
283
+
284
+ def readline
285
+ r, @previous = @previous, nil
286
+ r or @n += 1
287
+ cp = cur_prompt
288
+ loop do
289
+ begin
290
+ l = Readline.readline r ? "" : cp
291
+ rescue Interrupt
292
+ puts "^C -- #{$!.inspect}"
293
+ retry
294
+ end
295
+ if r then
296
+ break if l.nil?
297
+ r << $/ << l
298
+ break if l.empty?
299
+ else
300
+ return if l.nil?
301
+ next unless l =~ /\S/
302
+ r = l
303
+ break unless l =~ /\\+\z/ and $&.length % 2 != 0
304
+ end
305
+ end
306
+ cp.strip!
307
+ cp.gsub! /\e\[[0-9]*(;[0-9]*)*m/, ""
308
+ @file = "#{self.class}/#{cp}"
309
+ self.class.hist_add r
310
+ r
311
+ end
312
+
313
+ # :stopdoc:
314
+ ARROW = "=> "
315
+ ELLIPSIS = "..."
316
+ # :startdoc:
317
+
318
+ def display r
319
+ return if r.nil?
320
+ show = (self.class.show or return)
321
+ i = r.inspect
322
+ if show > 0 then
323
+ siz, = $stdout.winsize
324
+ siz *= show
325
+ siz -= ARROW.length
326
+ if i.length > siz then
327
+ i.cut! siz-ELLIPSIS.length
328
+ i << ELLIPSIS
329
+ end
330
+ end
331
+ i.prepend ARROW
332
+ puts i
333
+ end
334
+
335
+ def pager doit
336
+ if doit then
337
+ IO.popen ENV[ "PAGER"]||"more", "w" do |pg|
338
+ begin
339
+ stdout = $stdout.dup
340
+ $stdout.reopen pg
341
+ yield
342
+ ensure
343
+ $stdout.reopen stdout
344
+ end
345
+ end
346
+ else
347
+ yield
348
+ end
349
+ end
350
+
351
+ public
352
+
353
+ class Exit < Exception ; end
354
+ class CmdFailed < Exception ; end
355
+
356
+ def run *precmds
357
+ bind = @obj.empty_binding
358
+ precmds.each { |l| eval l, bind }
359
+ oldset = eval OLDSET, bind
360
+ @cl = eval "caller.length", bind
361
+ while l = readline do
362
+ re_sh_pref = /\A#{Regexp.quote self.class.sh_pref}/
363
+ re_pi_suff = /#{Regexp.quote self.class.pi_suff}\z/
364
+ switchcolour
365
+ begin
366
+ pg = l.slice! re_pi_suff
367
+ r = pager pg do
368
+ unless l =~ re_sh_pref then
369
+ eval l, bind, @file
370
+ else
371
+ call_system $', bind
372
+ end
373
+ end
374
+ oldset.call r, @n
375
+ display r
376
+ rescue Exit
377
+ wait_exit and break
378
+ rescue CmdFailed
379
+ oldset.call $?, @n
380
+ switchcolour 33
381
+ puts "Exit code: #{$?.exitstatus}"
382
+ rescue LoadError
383
+ oldset.call $!, @n
384
+ show_exception
385
+ rescue ScriptError
386
+ if l[ $/] then
387
+ switchcolour 33
388
+ puts $!
389
+ else
390
+ @previous = l
391
+ end
392
+ rescue SystemExit
393
+ break if wait_exit
394
+ oldset.call $!, @n
395
+ show_exception
396
+ rescue Exception
397
+ oldset.call $!, @n
398
+ show_exception
399
+ ensure
400
+ switchcolour
401
+ end
402
+ end
403
+ puts
404
+ ensure
405
+ done
406
+ end
407
+
408
+ protected
409
+
410
+ def done
411
+ end
412
+
413
+ private
414
+
415
+ def wait_exit
416
+ c = self.class.catch_exit
417
+ c and c.times { print "." ; $stdout.flush ; sleep 1 }
418
+ true
419
+ rescue Interrupt
420
+ puts
421
+ end
422
+
423
+ def show_exception
424
+ unless $!.to_s.empty? then
425
+ switchcolour 31, 1
426
+ print $!
427
+ print " " unless $!.to_s =~ /\s\z/
428
+ end
429
+ switchcolour 31, 22
430
+ puts "(#{$!.class})"
431
+ switchcolour 33
432
+ bt = $@.dup
433
+ if bt.length > @cl then
434
+ bt.pop @cl
435
+ bt.push bt.pop[ /(.*:\d+):.*/, 1]
436
+ end
437
+ puts bt
438
+ end
439
+
440
+ def call_system l, bind
441
+ l.strip!
442
+ raise Exit if l.empty?
443
+ eot = "EOT0001"
444
+ eot.succ! while l[ eot]
445
+ l = eval "<<#{eot}\n#{l}\n#{eot}", bind, @file
446
+ system l or raise CmdFailed
447
+ nil
448
+ end
449
+
450
+ end
451
+
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: appl
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.2'
5
+ platform: ruby
6
+ authors:
7
+ - Bertram Scharpf
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-08-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: supplement
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2'
27
+ description: |
28
+ A base class for command line applications doing options parsing
29
+ and generating exit codes.
30
+ email: "<software@bertram-scharpf.de>"
31
+ executables:
32
+ - intar
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - bin/intar
37
+ - doc/demoappl
38
+ - lib/appl.rb
39
+ - lib/intar.rb
40
+ homepage: http://www.bertram-scharpf.de/software/appl
41
+ licenses:
42
+ - BSD
43
+ metadata: {}
44
+ post_install_message:
45
+ rdoc_options: []
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements:
59
+ - Just Ruby
60
+ rubyforge_project: NONE
61
+ rubygems_version: 2.6.4
62
+ signing_key:
63
+ specification_version: 4
64
+ summary: Easy option parsing
65
+ test_files: []