app-ctx 0.1.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/README +109 -0
- data/lib/app-ctx.rb +228 -0
- data/lib/app-ctx/dlog.rb +164 -0
- data/test/t_app-ctx.rb +199 -0
- metadata +49 -0
data/README
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
|
2
|
+
Application Context
|
3
|
+
|
4
|
+
Every application needs configuration and app-ctx provides a concise way of
|
5
|
+
doing it.
|
6
|
+
|
7
|
+
For all applications (you are not a mouseclicker, are u?), once in a while
|
8
|
+
you need to supply some configuration values to overrule the built-in
|
9
|
+
defaults. The app-ctx gem does unify and organize built-in constants,
|
10
|
+
config files and commandline option with a clearly defined priority, from
|
11
|
+
low to high:
|
12
|
+
|
13
|
+
- procedural: App::Config#set_default_values
|
14
|
+
- default values YAML file from next to the $0 script
|
15
|
+
- user supplied configuration file, eg.: --config=/tmp/foo.yml
|
16
|
+
- command line options and flags: --foo --bar=foo
|
17
|
+
|
18
|
+
But for your application it is of no interesst from where the values are
|
19
|
+
coming: command line option: "--port=1234", a user configuration file or
|
20
|
+
from the applications built-in default values. Therefor +app-ctx+ combines
|
21
|
+
value settings from various sources into a single configuration hash.
|
22
|
+
|
23
|
+
basically you have two ways to use it:
|
24
|
+
|
25
|
+
require 'app-ctx' # of course,and than...
|
26
|
+
|
27
|
+
1. closures (see examples/run_with_block.rb)
|
28
|
+
|
29
|
+
App::ctx.run do |context| ... end
|
30
|
+
|
31
|
+
or 2. with a mainclass(see examples/run_with_class.rb)
|
32
|
+
|
33
|
+
App::ctx.run YourClassHere
|
34
|
+
|
35
|
+
The context object provides:
|
36
|
+
|
37
|
+
values: the combined key and value settings
|
38
|
+
argv: remaining argument(not the options) of the command line
|
39
|
+
defaults_path: full path to the defaults file
|
40
|
+
|
41
|
+
for the second case(with a mainclass) an application instance of this class
|
42
|
+
is created. The first argument is than taken as method name and executed,
|
43
|
+
again with a context oject provided:
|
44
|
+
|
45
|
+
prompt: ruby example/run_with_class show
|
46
|
+
|
47
|
+
will result in the :show method beeing called:
|
48
|
+
|
49
|
+
context = Config.new...
|
50
|
+
...
|
51
|
+
app = Simple.new...
|
52
|
+
app.show(context)
|
53
|
+
|
54
|
+
|
55
|
+
Conversions
|
56
|
+
|
57
|
+
Commandline options are strings only, but sometimes you need strongly typed
|
58
|
+
primitive values. +app-ctx+ does automatically convert integer and float
|
59
|
+
values, see "examples/conversions.rb" for:
|
60
|
+
|
61
|
+
prompt: ./examples/conversions.rb
|
62
|
+
{:i=>23, :f=>3.14}
|
63
|
+
typeof 'i': Fixnum
|
64
|
+
typeof 'f': Float
|
65
|
+
prompt: ./examples/conversions.rb -i=17 -f=2.12
|
66
|
+
{:i=>17, :f=>2.12}
|
67
|
+
typeof 'i': Fixnum
|
68
|
+
typeof 'f': Float
|
69
|
+
prompt: ./examples/conversions.rb -i=i -f=f
|
70
|
+
{:i=>"i", :f=>"f"}
|
71
|
+
typeof 'i': String
|
72
|
+
typeof 'f': String
|
73
|
+
|
74
|
+
|
75
|
+
Flags/Boolean values
|
76
|
+
|
77
|
+
Flags(options without values) are converted to boolean values, see
|
78
|
+
examples/boolean.rb:
|
79
|
+
|
80
|
+
dluesebrink dl-mbook ruby/app-ctx: r examples/boolean.rb
|
81
|
+
{:bool=>false}
|
82
|
+
typeof 'bool': FalseClass
|
83
|
+
dluesebrink dl-mbook ruby/app-ctx: r examples/boolean.rb --bool
|
84
|
+
{:bool=>true}
|
85
|
+
typeof 'bool': TrueClass
|
86
|
+
|
87
|
+
|
88
|
+
Ruby Conversions
|
89
|
+
|
90
|
+
When Fixnum, Float and boolean conversion are not enough, as a last resort,
|
91
|
+
you can use ruby code directly for evaluation of option values. Replacing
|
92
|
+
'=' with ':' results in assigning the ruby evaluation value to the key, see
|
93
|
+
examples/ruby_conv.rb:
|
94
|
+
|
95
|
+
prompt: examples/ruby_conv.rb
|
96
|
+
{:r=>1..10, :a=>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]}
|
97
|
+
typeof 'r': Range
|
98
|
+
typeof 'a': Array
|
99
|
+
prompt: ./examples/ruby_conv.rb -r:2..3 -a:'(-1..1).to_a'
|
100
|
+
{:r=>2..3, :a=>[-1, 0, 1]}
|
101
|
+
typeof 'r': Range
|
102
|
+
typeof 'a': Array
|
103
|
+
prompt: ./examples/ruby_conv.rb -r:2..3 -a:\(-1..1\).to_a
|
104
|
+
{:r=>2..3, :a=>[-1, 0, 1]}
|
105
|
+
typeof 'r': Range
|
106
|
+
typeof 'a': Array
|
107
|
+
|
108
|
+
|
109
|
+
# vim: set tw=80 syntax=txt nosmartindent:
|
data/lib/app-ctx.rb
ADDED
@@ -0,0 +1,228 @@
|
|
1
|
+
|
2
|
+
require 'yaml'
|
3
|
+
require 'rdoc/usage'
|
4
|
+
|
5
|
+
require 'app-ctx/dlog'
|
6
|
+
|
7
|
+
module App
|
8
|
+
|
9
|
+
# the priority of configuation settings is:
|
10
|
+
# 1. command line options
|
11
|
+
# 2. configuration from custom file, eg.: --config=/tmp/foo.yml
|
12
|
+
# 3. built-in default values from yaml file next to $0 script
|
13
|
+
class Config
|
14
|
+
|
15
|
+
include DL::LoggerMixin
|
16
|
+
|
17
|
+
attr_reader :values, :argv, :defaults_path
|
18
|
+
|
19
|
+
# params are :argv and :defaults_path which are defaulting to ARGV and
|
20
|
+
# the applications defaults which are loaded from a yaml file next to
|
21
|
+
# the '$0' application script.
|
22
|
+
def initialize params = {}
|
23
|
+
params = {
|
24
|
+
:argv => ARGV,
|
25
|
+
:defaults_path => App.config_path($0)
|
26
|
+
}.update(params)
|
27
|
+
|
28
|
+
@argv = params[:argv]
|
29
|
+
@defaults_path = params[:defaults_path]
|
30
|
+
|
31
|
+
load_config_file(@defaults_path)
|
32
|
+
@values, @argv = parse_command_line @argv
|
33
|
+
end
|
34
|
+
|
35
|
+
# programatic way to define the default values other then from defaults
|
36
|
+
# file.
|
37
|
+
def set_default_values defaults
|
38
|
+
@values = defaults.merge(@values)
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
def to_s
|
43
|
+
<<-EOT
|
44
|
+
args : #{argv.inspect}
|
45
|
+
values : #{values.inspect}
|
46
|
+
defaults : '#{defaults_path}'
|
47
|
+
user config : '#{values[:config]}'
|
48
|
+
EOT
|
49
|
+
end
|
50
|
+
|
51
|
+
# overwrite current configuration with values from file
|
52
|
+
#
|
53
|
+
def load_config_file cpath
|
54
|
+
@values ||= {}
|
55
|
+
if File.exists? cpath = File.expand_path(cpath)
|
56
|
+
begin
|
57
|
+
@values.update YAML.load_file(cpath)
|
58
|
+
info "loaded(#{cpath})"
|
59
|
+
debug "config values: #{@values.inspect}"
|
60
|
+
rescue => e
|
61
|
+
warn "failed to load: #{cpath}", e
|
62
|
+
end
|
63
|
+
else
|
64
|
+
debug "no such config file: #{cpath}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# methods below are related to option parsing and are used only at
|
69
|
+
# object creastions time from the c'tor
|
70
|
+
private
|
71
|
+
|
72
|
+
# overide default configuration with command line arguments.
|
73
|
+
# --flag becomes [ :flag => true]
|
74
|
+
# --option=value becomes [ :option => "value"]
|
75
|
+
# --int=1 becomes [ :int => 1]
|
76
|
+
# --float=2.3 becomes [ :float => 2.3]
|
77
|
+
# --txt="foo bar" becomes [ :txt => "foo bar"]
|
78
|
+
#
|
79
|
+
# --list:(1..10) becomes [ :list => (1..10) ]
|
80
|
+
def parse_command_line argv
|
81
|
+
c = {}
|
82
|
+
argv = argv.clone.delete_if do |a|
|
83
|
+
#if m = /^--(\w+)(=(.+))?$/.match(a)
|
84
|
+
if m = /^(-(\w)|--(\w\w+))(=(.+))?$/.match(a)
|
85
|
+
debug "#{m.to_a.inspect}" if m
|
86
|
+
val = parse_arg m[5]
|
87
|
+
key = m[2] || m[3]
|
88
|
+
c[key.to_sym] = val
|
89
|
+
|
90
|
+
# eval as ruby snippet
|
91
|
+
elsif m = /^(-(\w)|--(\w\w+)):(.+)$/.match(a)
|
92
|
+
debug "#{m.to_a.inspect}" if m
|
93
|
+
val = eval m[4]
|
94
|
+
key = m[2] || m[3]
|
95
|
+
c[key.to_sym] = val
|
96
|
+
end
|
97
|
+
end
|
98
|
+
debug "command line options: #{c.inspect}"
|
99
|
+
|
100
|
+
# overload "--config=..." file before command line options
|
101
|
+
if cpath = c[:config]
|
102
|
+
if file_config = load_config_file(cpath)
|
103
|
+
c = file_config.update(c)
|
104
|
+
else
|
105
|
+
warn "configuration file not found: '#{cpath}'"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
#[@values.dup.update(c), av]
|
110
|
+
@values.update(c)
|
111
|
+
[@values, argv]
|
112
|
+
end
|
113
|
+
|
114
|
+
def parse_arg val
|
115
|
+
return true unless val # options are booleans
|
116
|
+
Integer(val) rescue Float(val) rescue parse_string(val)
|
117
|
+
end
|
118
|
+
|
119
|
+
def parse_string s
|
120
|
+
if s.match %r{^['"].*['"]$}
|
121
|
+
s[1..-2]
|
122
|
+
else
|
123
|
+
s
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
class Container
|
129
|
+
|
130
|
+
Messages = {
|
131
|
+
:no_entrypoint => <<-'EOM'
|
132
|
+
|
133
|
+
::app-ctx::
|
134
|
+
|
135
|
+
-> sorry, your main class (#{clazz}) seems not to have a default
|
136
|
+
-> 'application_main' entrypoint and no method argument was given, so i don't
|
137
|
+
-> know what to do.
|
138
|
+
|
139
|
+
thank you, good bye and have a nice day
|
140
|
+
|
141
|
+
EOM
|
142
|
+
}
|
143
|
+
|
144
|
+
def inform message_id, binding
|
145
|
+
m = Messages[message_id]
|
146
|
+
puts eval("\"#{m}\"", binding)
|
147
|
+
end
|
148
|
+
|
149
|
+
def initialize context
|
150
|
+
@context = context
|
151
|
+
end
|
152
|
+
|
153
|
+
def execute clazz = nil
|
154
|
+
|
155
|
+
if block_given?
|
156
|
+
warn "attached block takes precedence over class!" if clazz
|
157
|
+
return yield(@context)
|
158
|
+
end
|
159
|
+
|
160
|
+
# create applicaton instance with or without context c'tor (this is
|
161
|
+
# IoC: Constructor Dependency Injection)
|
162
|
+
app = clazz.new(@context) rescue clazz.new
|
163
|
+
|
164
|
+
setter_injection app, @context
|
165
|
+
|
166
|
+
begin
|
167
|
+
# first try the default entry point
|
168
|
+
app.application_main @context
|
169
|
+
|
170
|
+
rescue NoMethodError => e
|
171
|
+
if app.respond_to? :application_main
|
172
|
+
DL.logger.error "failed invokation", e
|
173
|
+
else
|
174
|
+
if 0 == @context.argv.length
|
175
|
+
inform :no_entrypoint, binding()
|
176
|
+
RDoc::usage
|
177
|
+
return
|
178
|
+
else
|
179
|
+
# one or more args => first arg is method with rest
|
180
|
+
# as params
|
181
|
+
begin
|
182
|
+
op = @context.argv.shift
|
183
|
+
app.send(op, @context)
|
184
|
+
rescue => e
|
185
|
+
DL.logger.error "oops: #{e}", e
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
app # the created application instance
|
192
|
+
end
|
193
|
+
|
194
|
+
# IoC setter dependency injection:
|
195
|
+
# try all context keys as setter class
|
196
|
+
def setter_injection obj, context
|
197
|
+
# try setters for keys from context(this is IoC: Setter dependency
|
198
|
+
# injection)
|
199
|
+
context.values.each do |key, value|
|
200
|
+
#p "#{key} #{value}"
|
201
|
+
begin
|
202
|
+
obj.send("#{key}=", value)
|
203
|
+
rescue NoMethodError
|
204
|
+
#ntdh
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
private :setter_injection
|
209
|
+
end
|
210
|
+
|
211
|
+
class << self
|
212
|
+
def run params = {}, &block
|
213
|
+
# create application container from command line context
|
214
|
+
container = Container.new(Config.new(params))
|
215
|
+
if block_given?
|
216
|
+
container.execute {|context| block.call(context) }
|
217
|
+
else
|
218
|
+
container.execute params[:class]
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def config_path app_path
|
223
|
+
defaults_path = File.expand_path(app_path)
|
224
|
+
ext = File.extname(defaults_path)
|
225
|
+
defaults_path.sub((ext == "" && /$/ || /#{ext}$/), ".yml")
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
data/lib/app-ctx/dlog.rb
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
# once (shamelessly) stolen(for testing(and squeezed)) from rails...
|
2
|
+
# now degenerated beyong comprehension
|
3
|
+
# awaiting a major refactoring to get a managble configuration...
|
4
|
+
|
5
|
+
module DL
|
6
|
+
|
7
|
+
AllLoggersByPrefix = {} # list of all loggers
|
8
|
+
|
9
|
+
class Logger
|
10
|
+
|
11
|
+
LevelMetrics = {
|
12
|
+
:fatal => 0.0,
|
13
|
+
:error => 1.0,
|
14
|
+
:warn => 2.0,
|
15
|
+
:info => 3.0,
|
16
|
+
:debug => 4.0,
|
17
|
+
}
|
18
|
+
|
19
|
+
# XXX das mit diesem config kram ist alles noch ziemlicher mist...
|
20
|
+
DefaultLogger = {
|
21
|
+
:prefix => "???",
|
22
|
+
:out => $stderr,
|
23
|
+
:fmt => "%c %23s %s%s\n", # "%c %-13s: %s%s\n",
|
24
|
+
#:include_levels => /^(fatal|error|warn|info)/i, # no debug
|
25
|
+
:include_levels => nil, # all inclusive
|
26
|
+
#:exclude_levels => nil,
|
27
|
+
:exclude_levels => /^debug/i, # no debug on default
|
28
|
+
:quiet => false,
|
29
|
+
:level => 3.0,
|
30
|
+
}
|
31
|
+
Config = {
|
32
|
+
:default => DefaultLogger,
|
33
|
+
}
|
34
|
+
|
35
|
+
attr_accessor :prefix, :out, :fmt, :include_levels, :exclude_levels
|
36
|
+
attr_accessor :quiet, :level
|
37
|
+
|
38
|
+
def initialize prefix, config = DefaultLogger
|
39
|
+
@nesting_level = 0
|
40
|
+
@config = DefaultLogger.dup
|
41
|
+
@config[:prefix] = prefix
|
42
|
+
configure config
|
43
|
+
end
|
44
|
+
|
45
|
+
def configure settings
|
46
|
+
@config.update(settings)
|
47
|
+
@prefix = @config[:prefix]
|
48
|
+
@out = @config[:out]
|
49
|
+
@fmt = @config[:fmt]
|
50
|
+
@include_levels = @config[:include_levels]
|
51
|
+
@exclude_levels = @config[:exclude_levels]
|
52
|
+
@quiet = @config[:quiet]
|
53
|
+
@level = @config[:level]
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.[] prefix
|
57
|
+
self.get prefix
|
58
|
+
end
|
59
|
+
|
60
|
+
def log(status, message, exception, &block)
|
61
|
+
unless quiet
|
62
|
+
#return "excluded" if exclude_levels and exclude_levels.match(status)
|
63
|
+
#if include_levels and !include_levels.match(status)
|
64
|
+
# return "not included"
|
65
|
+
#end
|
66
|
+
msg = @fmt % [status[0], prefix, ' ' * @nesting_level, message]
|
67
|
+
@out.print(msg)
|
68
|
+
if exception
|
69
|
+
callstack = exception.backtrace
|
70
|
+
@out.print " !! #{exception}\n\t#{callstack.join("\n\t")}\n"
|
71
|
+
begin
|
72
|
+
reason = exception.reason
|
73
|
+
callstack = reason.backtrace
|
74
|
+
txt = "#{reason}\n\t#{callstack.join("\n\t")}\n"
|
75
|
+
@out.print "(Cause)>> #{txt}"
|
76
|
+
rescue
|
77
|
+
# noop
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
indent(&block) if block_given?
|
82
|
+
end
|
83
|
+
|
84
|
+
def flush
|
85
|
+
@out.flush
|
86
|
+
end
|
87
|
+
|
88
|
+
def indent(&block)
|
89
|
+
@nesting_level += 1
|
90
|
+
if block_given?
|
91
|
+
begin
|
92
|
+
block.call
|
93
|
+
ensure
|
94
|
+
outdent
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def outdent
|
100
|
+
@nesting_level -= 1
|
101
|
+
if block_given?
|
102
|
+
begin
|
103
|
+
block.call
|
104
|
+
ensure
|
105
|
+
indent
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
def method_missing(method, *args, &block)
|
112
|
+
message, execption = args
|
113
|
+
if LevelMetrics[method.to_sym] <= level
|
114
|
+
log(method.to_s.upcase, message, execption, &block)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.logger(prefix = $0, out = $stdout)
|
120
|
+
prefix = self.find_prefix(prefix)
|
121
|
+
unless log = AllLoggersByPrefix[prefix]
|
122
|
+
c = Logger::Config[prefix] || {}
|
123
|
+
#c[:fmt] = "%c %23s %s%s\n"
|
124
|
+
log = (AllLoggersByPrefix[prefix] = Logger.new(prefix, c))
|
125
|
+
end
|
126
|
+
log
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.find_prefix prefix
|
130
|
+
raise "nil prefix not yet implemented!" unless prefix
|
131
|
+
return prefix if prefix.kind_of? String
|
132
|
+
return prefix.name if prefix.kind_of? Class
|
133
|
+
return prefix.name if prefix.kind_of? Module
|
134
|
+
return prefix.class.name
|
135
|
+
end
|
136
|
+
|
137
|
+
module LoggerMixin
|
138
|
+
def logger
|
139
|
+
@dl_class_logger ||= DL.logger(self) # class logger
|
140
|
+
end
|
141
|
+
|
142
|
+
def debug *args
|
143
|
+
logger.debug *args
|
144
|
+
end
|
145
|
+
def info *args
|
146
|
+
logger.info *args
|
147
|
+
end
|
148
|
+
def warn *args
|
149
|
+
logger.warn *args
|
150
|
+
end
|
151
|
+
def error *args
|
152
|
+
logger.error *args
|
153
|
+
end
|
154
|
+
def fatal *args
|
155
|
+
logger.fatal *args
|
156
|
+
end
|
157
|
+
def log_level
|
158
|
+
logger.level
|
159
|
+
end
|
160
|
+
def set_log_level level
|
161
|
+
logger.level= level
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
data/test/t_app-ctx.rb
ADDED
@@ -0,0 +1,199 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'pp'
|
5
|
+
|
6
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
7
|
+
require 'app-ctx'
|
8
|
+
|
9
|
+
class AppModuleTest < Test::Unit::TestCase
|
10
|
+
|
11
|
+
class With_application_main
|
12
|
+
attr_reader :context
|
13
|
+
attr_accessor :baroque
|
14
|
+
# application_main is the default catch-all entry point for app-ctx
|
15
|
+
def application_main context
|
16
|
+
puts "#{self}: #{context.inspect}"
|
17
|
+
@context = context
|
18
|
+
end
|
19
|
+
end
|
20
|
+
def test_default_entry_point
|
21
|
+
puts "\n --> #{self.name}"
|
22
|
+
argv = ["-f", "--foo=bar", "--baroque=gaga", "arg1", "arg2"]
|
23
|
+
app = App::run :class => With_application_main, :argv => argv
|
24
|
+
assert_not_nil app
|
25
|
+
assert_equal With_application_main, app.class
|
26
|
+
assert_not_nil app.context
|
27
|
+
assert_equal({:f=>true, :foo=>"bar", :baroque =>"gaga"}, app.context.values)
|
28
|
+
assert_equal ["arg1", "arg2"], app.context.argv
|
29
|
+
assert_equal "gaga", app.baroque
|
30
|
+
end
|
31
|
+
|
32
|
+
class ArgumentEntryPoint
|
33
|
+
attr_reader :context
|
34
|
+
def entrypoint context
|
35
|
+
puts "#{self}: #{context.inspect}"
|
36
|
+
@context = context
|
37
|
+
end
|
38
|
+
end
|
39
|
+
def test_run_without_point
|
40
|
+
puts "\n --> #{self.name}"
|
41
|
+
argv = ["-f", "--foo=bar", "entrypoint", "first_method_arg"]
|
42
|
+
app = App::run :class => ArgumentEntryPoint, :argv => argv
|
43
|
+
assert_not_nil app
|
44
|
+
assert_equal ArgumentEntryPoint, app.class
|
45
|
+
assert_not_nil app.context
|
46
|
+
assert_equal({:f=>true, :foo=>"bar"}, app.context.values)
|
47
|
+
assert_equal ["first_method_arg"], app.context.argv
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
class AppConfigTest < Test::Unit::TestCase
|
53
|
+
|
54
|
+
include App
|
55
|
+
|
56
|
+
def test_config_block
|
57
|
+
puts "\n --> #{self.name}"
|
58
|
+
App::Config.new do |context|
|
59
|
+
puts "--------------->context: #{context.inspect}"
|
60
|
+
assert_not_nil context
|
61
|
+
assert_equal({}, context.values)
|
62
|
+
end
|
63
|
+
|
64
|
+
App::Config.new :argv=>["-f", "--foo=bar", "arg1", "arg2"] do |context|
|
65
|
+
puts "--------------->context: #{context.inspect}"
|
66
|
+
assert_not_nil context
|
67
|
+
assert_equal({:f=>true, :foo=>"bar"}, context.values)
|
68
|
+
assert_equal ["arg1", "arg2"], context.argv
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_load_config_file
|
73
|
+
puts "\n --> #{self.name}"
|
74
|
+
c = App::Config.new
|
75
|
+
# config file does not exist:w
|
76
|
+
c.load_config_file File.join(File.dirname($0), "no-such-file")
|
77
|
+
assert_equal({}, c.values)
|
78
|
+
|
79
|
+
# load configuration from file
|
80
|
+
c.load_config_file File.join(File.dirname($0), "config-1.yml")
|
81
|
+
assert_equal({
|
82
|
+
:f=>3.21, :i=>17, :empty_hash=>{}, :assoc=>{:key=>"foo"}
|
83
|
+
}, c.values)
|
84
|
+
|
85
|
+
# config overloading
|
86
|
+
c.load_config_file File.join(File.dirname($0), "config-2.yml")
|
87
|
+
assert_equal({
|
88
|
+
:f=>3.21, :i=>17, :empty_hash=>{},
|
89
|
+
:assoc=>{:key=>"bar"}, :f2=>-3.21
|
90
|
+
}, c.values)
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_configure_defaults
|
94
|
+
puts "\n --> #{self.name}"
|
95
|
+
c = App::Config.new
|
96
|
+
assert_equal ARGV, c.argv
|
97
|
+
assert_equal({}, c.values)
|
98
|
+
|
99
|
+
# find the default values config file next to the $0 script
|
100
|
+
assert_equal File.expand_path($0).sub(/.rb$/, ".yml"), c.defaults_path
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_config_path
|
104
|
+
puts "\n --> #{self.name}"
|
105
|
+
|
106
|
+
cpath = App.config_path "foo.bar"
|
107
|
+
assert_not_nil cpath
|
108
|
+
assert_equal File.expand_path(File.join(".", "foo.yml")), cpath
|
109
|
+
|
110
|
+
cpath = App.config_path "/tmp/foo.rb.rb"
|
111
|
+
assert_not_nil cpath
|
112
|
+
assert_equal File.expand_path(File.join("/", "tmp", "foo.rb.yml")), cpath
|
113
|
+
|
114
|
+
# XXX this might fail on windows??
|
115
|
+
cpath = App.config_path "~/foo.bar"
|
116
|
+
assert_not_nil cpath
|
117
|
+
assert_equal File.expand_path(File.join("~", "foo.yml")), cpath
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_parse_ruby_options
|
121
|
+
puts "\n --> #{self.name}"
|
122
|
+
|
123
|
+
c = Config.new :argv => ["--list:(1..10).to_a"]
|
124
|
+
cfg, av = c.values, c.argv
|
125
|
+
assert_equal({:list => (1..10).to_a}, cfg)
|
126
|
+
|
127
|
+
c = Config.new :argv => ["--list:(1..10)"]
|
128
|
+
cfg, av = c.values, c.argv
|
129
|
+
assert_equal({:list => (1..10)}, cfg)
|
130
|
+
assert cfg[:list].kind_of?(Range)
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_parse_command_line_options
|
134
|
+
puts "\n --> #{self.name}"
|
135
|
+
|
136
|
+
c = Config.new :argv => []
|
137
|
+
cfg, av = c.values, c.argv
|
138
|
+
assert_equal({}, cfg)
|
139
|
+
assert_equal [], av
|
140
|
+
|
141
|
+
c = Config.new :argv => ["-a"]
|
142
|
+
cfg, av = c.values, c.argv
|
143
|
+
assert cfg[:a]
|
144
|
+
assert ! cfg[:b]
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_parse_command_line_int_float_flags
|
148
|
+
puts "\n --> #{self.name}"
|
149
|
+
|
150
|
+
c = Config.new :argv => ["-a", "--int=5", "--float=2.34"]
|
151
|
+
cfg, av = c.values, c.argv
|
152
|
+
assert_equal({:a=>true,:int=>5, :float=>2.34}, cfg)
|
153
|
+
assert_equal [], av
|
154
|
+
end
|
155
|
+
|
156
|
+
def test_various_string_values
|
157
|
+
puts "\n --> #{self.name}"
|
158
|
+
|
159
|
+
c = Config.new :argv => ["xxx"]
|
160
|
+
cfg, av = c.values, c.argv
|
161
|
+
assert_equal ["xxx"], av
|
162
|
+
assert_equal({}, cfg)
|
163
|
+
|
164
|
+
c = Config.new :argv => ["foo", "bar"]
|
165
|
+
cfg, av = c.values, c.argv
|
166
|
+
assert_equal({}, cfg)
|
167
|
+
assert_equal ["foo", "bar"], av
|
168
|
+
|
169
|
+
c = Config.new :argv => ["--foo", "bar"]
|
170
|
+
cfg, av = c.values, c.argv
|
171
|
+
assert_equal({:foo=>true}, cfg)
|
172
|
+
assert_equal ["bar"], av
|
173
|
+
|
174
|
+
c = Config.new :argv => ["foo", "--bar"]
|
175
|
+
cfg, av = c.values, c.argv
|
176
|
+
assert_equal({:bar=>true}, cfg)
|
177
|
+
assert_equal ["foo"], av
|
178
|
+
|
179
|
+
c = Config.new :argv => ["--key=val", "foo"]
|
180
|
+
cfg, av = c.values, c.argv
|
181
|
+
assert_equal({:key=>'val'}, cfg)
|
182
|
+
assert_equal ["foo"], av
|
183
|
+
|
184
|
+
c = Config.new :argv => ["--key=val", "--key=ooo"]
|
185
|
+
cfg, av = c.values, c.argv
|
186
|
+
assert_equal({:key=>'ooo'}, cfg)
|
187
|
+
assert_equal [], av
|
188
|
+
|
189
|
+
c = Config.new :argv => ["-a='foo bar'", '-b="bar foo"']
|
190
|
+
cfg, av = c.values, c.argv
|
191
|
+
assert_equal [], av
|
192
|
+
assert_equal({:a=>"foo bar", :b=>"bar foo"}, cfg)
|
193
|
+
|
194
|
+
c = Config.new :argv => ["--aa='foo bar'", '--bb="bar foo"']
|
195
|
+
cfg, av = c.values, c.argv
|
196
|
+
assert_equal [], av
|
197
|
+
assert_equal({:aa=>"foo bar", :bb=>"bar foo"}, cfg)
|
198
|
+
end
|
199
|
+
end
|
metadata
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.11
|
3
|
+
specification_version: 1
|
4
|
+
name: app-ctx
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.1.2
|
7
|
+
date: 2006-09-18 00:00:00 +02:00
|
8
|
+
summary: command line application startup and config context
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: dirk.luesebrink@idmedia.com
|
12
|
+
homepage: http://www.idmedia.com/
|
13
|
+
rubyforge_project:
|
14
|
+
description:
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
authors:
|
29
|
+
- Dirk Luesebrink
|
30
|
+
files:
|
31
|
+
- lib/app-ctx.rb
|
32
|
+
- lib/app-ctx/dlog.rb
|
33
|
+
- test/t_app-ctx.rb
|
34
|
+
- README
|
35
|
+
test_files:
|
36
|
+
- test/t_app-ctx.rb
|
37
|
+
rdoc_options:
|
38
|
+
- --main
|
39
|
+
- README
|
40
|
+
extra_rdoc_files:
|
41
|
+
- README
|
42
|
+
executables: []
|
43
|
+
|
44
|
+
extensions: []
|
45
|
+
|
46
|
+
requirements: []
|
47
|
+
|
48
|
+
dependencies: []
|
49
|
+
|